Exercise :
(1) How many bits are necessary to record "Male", or "Female" ?
(2) Suppose a man lives no longer than 120 years, how many bits
do we need to record a man's age ?
(3) How many bits are needed for 0 to 3000 years ?
(4) For 12 months ?
(5) For 31 days ?
(6) For 7 days in a week (mon, tue, .... ) ?
Ans :
(1) 1.
(2) 7 (because 27 = 128)
(3) 12 (212 = 4096)
(4) 4 (24 = 16)
(5) 5 (25 = 32)
(6) 3 (23 = 8)
Exercise : Bit fields are used in the structure below
typedef struct
{char name[40];
unsigned sex : 1;
unsigned age : 7;
unsigned year : 12;
unsigned month : 4;
unsigned day : 5;
char tel[20];
} Mrec;
What is its size in bytes ? (i.e. sizeof(Mrec) = ? )Ans : Total number of bits is = 1 + 7 + 12 + 4 + 5 = 29, but the computer will make it 32 bits = 4 bytes, hence sizeof(Mrec) is 64.
Note:
If we prefix a number by "0" (zero, not letter "O"), that means it is an octal number.
If we prefix it by "0x", or "0X", that means hexadecimal.
Exercise : What are the decimal equivalent for the following numbers :-
Ans :
Exercise : What will the following code produce ?
int i;
i = 0;
if (i = 1)
{printf("i has the value 1\n");
}
Ans : It should be
if (i == 1) { .... }and not
if (i = 1) { .... }
In C, there is no assignment statement, but "assignment-expression".
Hence the expression above is 1, which is "true" (any non-zero value
is regarded as "true". 0 means "false"). The assignment part, i.e.
Hence the above code will put 1 into "i", then printf(...) will be executed.
This type of error is very common, and we should be careful.
|
In C, there is a "conditional expression" condition ? expression1 : expression2(Note : it is advisable to put brackets, e.g. (condition) ? expression1 : expression2though syntax does not require it. It is good programming practice to do so.) |
Exercise : Rewrite the following codes using "condition expression" :-
iflag = 0;
if (linecount > 20)
{iflag = 1;
}
Ans :
iflag = (linecount > 20) ? 1 : 0 ;
We have met comma before, in "for" statement, e.g.
for (sum=0., i=0; i<100; i++) { ..... }
The following is part of C's syntax (or "grammar")
statement-list = statement
| statement-list statement
statement = compound-statement
| selection-statement
| iteration-statement
| labeled-statement
| jump-statement
| expression-statement
compound-statement = { declaration-list(optional) statement-list(optional) }
selection-statement = if (expression) statement
| if (expression) statement else statement
| switch (expression) statement
iteration-statement =
while (expression) statement
| do statement while (expression) ;
| for (expression(optional) ; expression(optional); expression(optional)) statement
labeled-statement = identifier : statement
| case constant-expression : statement
| default : statement
jump-statement = goto identifier ;
| continue;
| break;
| return expression(optional);
expression-statement = expression(optional) ;
expression = assignment-expression
| expression , assignment-expression
|
Exercise : (Notice that "|" above means "or")
if (condition) {statement}
if (condition)
{statement}
else
{statement}
Can we omit the curly brackets, if it is a single statement and not a
compound statement ?Ans :
123
24.5
a + b
c * d + e * f - 10.7
func1(a , c+b, 2.3, 4, &p)
scanf("%d%lf", &iwhich, &density)
ar[4]
ptr->name
record.wages
ptr++
a<<3
a > 10.5 && a < 100
b == 6 || b == 7
iflag & jflag
iflag | jflag....
....
The syntax is
for (expr1(opt) ; expr2(opt); expr3(opt)) statementNotice that all three expressions, "expr1, expr2, expr3", are optional.
expr1 consists of initialization statements (abuse of the term "statement" ! correct one should be initialization expression !)
expr2 usually is a condition, while
expr3 consists of loop-update statements.
We should form the habit of putting ALL initialization statements in "expr1", and not put some of them outside "for".
We should also form the habit of putting all loop-update statements in "expr3", and not put them within tbe body.
Exercise : If all "expr1, expr2, expr3" are omitted, e.g.
for (;;)
{...
...
}
what does it mean?
Ans : It is an infinite loop, same as, e.g.
L20:
{ ...
...
goto L20;
}
Exercise : There are 3 kinds of iteration statements, what are they?
Ans : They are
Exercise : break is a keyword in C, and may be used for all three iteration statements. It is used to jump out of the iteration. Are there other ways to jump out of a loop ?
Ans : Yes. "goto label" will work always. And "goto" must be
used if we are to jump out of nested loops.
break jumps out of one loop only, the
loop where it is used.
(Note : Many people would advise against the use of "goto" statement. My personal opinion is for "goto". I studied Maths in university, and I love simplicity. In Maths proofs, it is very important that one can see through all the logical steps down to the basic axioms. I like the C language because it enables me to see through onto the microprocessor hardware instructions. I like "goto" and "if" for the same reason. I tried to use the various constructs "while, do .. while, do ... until, ... " and found my logical reasoning de-railed. I reverted back to the simple "if, goto", and I enjoy peace of mind ever since then.
Incidentally, Ian Sinclair (a British) who manufactured calculators and computers in the past, and who learned electronics and computer all by himself. He is able to see through the instructions in microprocessor down to the logical gates - "J-K master-slave flip-flops". It would be nice if you can see through them as he does. He has written quite a number of books, and in them, he explained how a microprocessor is implemented through logical gates. It would even be better if you can see through the "logic gates" down to the quantum world, of fundamental Physics!)
Exercise : continue is another C keyword. It can also be used for all three iteration statements. What is the purpose of " continue; " ?
Ans : It is used to skip over the rest of the statements in the body, and start the next iteration, e.g.
for ( .. ; .. ; .. )
{ ......
......
if (condition)
{continue;
}
/* Statements here are not executed if
...... condition is true */
......
}
In PERL (another computer language), the author uses "next" for "continue", and
"last" for "break".
The reason why the author of C used "continue;" is that, in the old days, many people in the science/engineering community used FORTRAN, and "continue" is a keyword in FORTRAN, and is the last statement in a loop, e.g.
do 100 i = 1, 20
.....
.....
100 continue
(for-loop in C is do-loop in FORTRAN)
do 100 i = 1, 20
.....
.....
if (condition) goto 100
.....
.....
.....
100 continue
Hence, the use of "continue" in C could be understood by many people.
Exercise : "break, continue" may be used in other statements too, not necessarily iteration-statements (e.g. to jump out of a compound statement) - True or False ?
Ans : False. They can only be used with iteration statements.
Exercise : How many arguments does the function "fa(..)" has, and what data types are they, from the following statement,
....
fa(1, 8, 2, &b);
....
Ans : fa(..) needs 4 arguments. The first 3 are probably numbers (whether they are integer, character, long, short, float, double, signed or unsigned, we have no way to know), the 4th one is probably a pointer.
Exercise : Suppose the program contains
#include <stdio.h>What is the purpose of putting the statement
extern void fa(double, double, double, double *);....int main()
....
{ double b;....}
....
fa(1, 8, 2, &b);
....
....
return 0;
extern void fa(double, double, double, double *);at the very beginning ? What will the compiler do with the statement
fa(1, 8, 2, &b);Will the compiler does the same if the declaration is
extern void fa(int, unsigned, float, double *);
Ans : "extern" is a C keyword. It informs the compiler that "fa(..)" is a subroutine defined elsewhere, and the linkage-editor should look for it in some libraries (or object files) outside this file.
The compiler knows now that fa(..) needs 4 arguments, the first three are of type double, and the last one, a pointer for double. When it encounters the statement
fa(1, 8, 2, &b);It will convert "1, 8, 2" into double and pushes them into stack before calling the subroutine.
But if the declaration is
extern void fa(int, unsigned, float, double *);It will convert "1" into an integer, "8" into an unsigned integer, "2" into a floating point number, and push them into the stack before calling the subroutine.
Such a declaration is called "supplying a function prototype to the compiler".
In the absence of a "function prototype declaration", compiler will take certain default actions. It will convert "char, short" into "int", and "float" into "double".
The rule is : we must supply a "function prototype" before calling a function. Or else the compiler will take default actions, (though compiler will usually warn us, if we use "gcc -Wall ... " (Warn all options)), and the result will be unpredictable.
Exercise : Study the example below, why have we omitted "extern" ?
#include <stdio.h>
void fa(double, double, double, double *);....void fa(double x, double y, double z, double *p)
....
{
....}
....
....int main()
....
{ double b;....}
....
fa(1, 8, 2, &b);
....
....
return 0;
Ans : Because the function "fa(..)" is defined in the same file, we omit "extern".
Exercise : What is the difference between
void fa(double, double, double, double *);and
void fa(double x, double y, double z, double *p) { ... }
Ans : The first is a "function prototype declaration", and the second, a "function definition".
Exercise : Suppose we arrange the program in such a way that functions are defined first before being called, e.g. if fa(..) calls fb(..) which in turns calls fc(..), and we write the program in the following way :
#include <stdio.h>Do we still have to supply "function prototypes" ?
....void fc( ... )
....
{
....}
....
void fb( ... )
{
....}
....
void fa( ... )
{
....}
....
....int main()
....
{....}
....
Ans : In this case, no.
But unless the program uses a small number of subroutines, and
we can remember their dependencies, we can rarely arrange the program this way, and
"function prototypes declaration" is necessary.
Exercise : We have to include many header files in a C program, e.g. <stdio.h>, <stdlib.h>, <string.h>, <math.h>, .... What do you think the header files contain ?
Ans : Mostly they contain "function prototype declarations", together with some constants, e.g. "#define EOF -1", "#define NULL 0", ... There are a lot of "#ifdef .... " declaratives too, to cater for different operating systems (e.g. PC, or Mac, or Digital, or HP, ... various operating systems they use.)
In Maths and Engineering, we often have to deal with matrices, and they are 2 dimensional arrays. Arrays of more than 2 dimensions are not uncommon in Maths/Engineering.
For multi-dimensional array, they are declared as, e.g.
double a[3][5], b[4][2][3];Then a[3][5] is a 2 dimensional array, and b[4][2][3] a 3 dimensional array.
a[3][5] * * * * * (You may visualize it as a 3 by 5 matrix)
* * * * *
* * * * *
b[4][2][3] 4 layers each of * * * (You may visualize it as 4 layers
* * * each layer a 2 by 3 matrix.)
|
For 1 dimensional arrays, they are initialized in the following way, e.g.
double a[3]= { 10.3, 20.7, 77.0 };For 2 dimensional arrays, e.g.
double b[3][2]= { {1.5, 2.6}, {-10, 27.3}, {6, 8.3} };For 3 dimensional arrays, e.g.
double c[2][2][3] = {{{1,2,3}, {4,5,6}}, {{7,8,9}, {10,11,12}}};and so on.
The compiler accesses an element in a 1 dimensional array via pointers (address registers). e.g.
double a[]={1, 2, 3, 4, 5};
double b, c;
b = a[2];
c = *(a + 2);
then both b and c have values = a[2] = 3.
Exercise : What is the difference of the following two statements : -
char a[] = "Hong Kong";And are the following assignment statements correct ?
char *ptr = "Hong Kong";
a = "Kowloon";
ptr = "Kowloon";
Ans :

Hence, functionally, compiler will treat
char a[3]; as char *a;
char a[4][5]; as char **a;
OR char *a[4]; (1 dimensional array of pointers)
double b[3]; as double *b;
double b[4][5]; as double **b;
OR double *b[4];
double b[4][5][6]; as double ***b;
OR double **b[4];
OR double *b[4][5]; (2 dimensional array of pointers)
double b[4][5][6][7] as double ****b;
OR double ***b[4];
OR double **b[4][5];
OR double *b[4][5][6]; (3 dimensional array of pointers)
|
Suppose we wish to write a subroutine that will add two matrices A and B and put the sum in another matrix C. The number of rows and the number of columns may be varied.
void m_add(a, ndima, b, ndimb, c, ndimc, nrow, ncol)
int ndima, ndimb, ndimc, nrow, ncol;
double a[][ndima], b[][ndimb], c[][ndimc];
{ int irow, jcol;
for (irow = 0; irow < nrow; irow++)
for (jcol = 0; jcol < ncol; jcol++)
{ c[irow][jcol] = a[irow][jcol] + b[irow][jcol];
}
return;
}
And to declare "function prototype, we would use either
The last two will generate warning messages, but will compile and work alright.void m_add(double [][], int, double [][], int, double [][], int, int, int); void m_add(double *[], int, double *[], int, double *[], int, int, int); void m_add(double **, int, double **, int, double **, int, int, int);
Exercise : Write a subroutine that will multiply matrix A and B and put the product in another matrix C. A is m by n matrix, and B is n by p matrix.
Ans :
void m_mult(a, ndima, b, ndimb, c, ndimc, nm, nn, np)
int ndima, ndimb, ndimc, nm, nn, np;
double a[][ndima], b[][ndimb], c[][ndimc];
{ int irow, jcol, k;
double sum;
for (irow = 0; irow < nm; irow++)
for (jcol = 0; jcol < np; jcol++)
{for (sum=0., k=0; k < nn; k++)
{sum += a[irow][k] * b[k][jcol];
}
c[irow][jcol] = sum;
}
return;
}
Exercise : Study the grammar below (NOT real C syntax ! Example only),
expression = assignment-expression
| conditional-expression
| unary-expression
| ....
....
....
additive-expression = multiplicative-expression
| additive-expression + multiplicative-expression
| additive-expression - multiplicative-expression
multiplicative-expression = unary-expression
| multiplicative-expression * unary-expression
| multiplicative-expression / unary-expression
unary-expression = primary-expression
| unary-expression[expression]
| unary-expression(expression,expression, ... )
| unary-expression.identifier
| unary-expression->identifier
primary-expression = identifer
| constant
| string
| (expression)
|
Ans : If you try to parse some expressions as the computer does, according to the example-grammar above, you will find
primary-expressions which consist of identifer, constant, or "...", or (...)
have the highest precedence.
Next are, e.g. a[3]
fa(3, 4, p+q, 20)
record.name
ptr->name
Next are multiplication and division
Finally comes addition and subtraction
For real C syntax, if you study Appendix A of the book, you will discover the precedence rule as follows (highest precedence first):
Primary-expression, e.g. 12.3, wage, "Hong Kong" (identifier, string)
Next are, e.g. a[3] (array element)
fa(3, 4, p+q, 20) (function)
record.name (structure element)
ptr->name (structure element via pointer)
Next ! (logical not)
~ (bitwise not)
++ (increment)
-- (decrement)
+ (unary plus sign)
- (unary minus sign)
* (indirect addressing, or load indirect)
& (load effective address)
Next * (multiplication)
/ (division)
% (modulo, or remainder)
Next + (addition)
- (subtraction)
Next << (shift left)
>> (shift right)
Next < (less than)
<= (less than or equal)
> (greatter than)
>= (greatter than or equal)
Next == (equal)
!= (not equal)
Next & (bitwise and)
Next ^ (bitwise exclusive or)
Next | (bitwise or)
Next && (logical and)
Next || (logical or)
Next .. ? .. : .. (conditional expression)
Next = (various assignment operators)
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
Finally , (comma operator that separate expressions)
If you cannot remember these, use bracekts, this is the safest way.
| [Previous] | [Home] | [Next] |