Index |
Programs compiled with gcc 3.4.5 on Dell Windows-XP. | ||||||||||||||||||||||||||||||||||||||||||||||||||
1) Books on C |
The one and only,
The C Programming Language by Kernighan and Ritchie To gain further depth in C, C Programming FAQs by Steve Summit | ||||||||||||||||||||||||||||||||||||||||||||||||||
2) main declarations |
main has only two valid declarations:
int main(void); int main(int argc, char** argv); Following declarations gets converted to one of the above two declarations: main(); void main(void); main(void); int main(int argc, char *argv[]); int main(int argc, char argv[][]); | ||||||||||||||||||||||||||||||||||||||||||||||||||
3) Strange i=i++; |
Seemingly logical behaviour of i=i++; is i incremented by one at the end.
But is it that simple or logical for compiler too ? Answer is No !
"i++" supposed to "use i value" and "then increment the same". In statement i=i++;, value of i is being used to assign to i itself and that is sort of an anamoly .... i gets modified twice in i=i++; one due to assignment and other due to side-effect of post-increment. As per C standards, this is not correct !! So the behaviour of i=i++; statement (and any of the following statements) is considered to be "undefined" (even if syntactically correct). i=++i; Variations of (i++)*(i++) a[i]=i++; Please refer C Programming FAQs by Steve Summit (section 3) for detailed discussion on the topic. | ||||||||||||||||||||||||||||||||||||||||||||||||||
4) x=y=z=3 |
The statement is valid and it does what we expect i.e. it makes value of x, y, and z 3.
The reason it works is because "=" operator has right-to-left associativity.
So the statement assigns 3 to z, then assigns z to y, and then assigns y to x.
| ||||||||||||||||||||||||||||||||||||||||||||||||||
5) 3["0123456789abcdef"] works ? |
Yes, it does work. Compiler interprets both 3["0123456789abcdef"] and
"0123456789abcdef"[3] same way !
The reason seems to be in the way C thinks of an array (as an constant pointer).
array[index] is as good as *(array + index) or *(index + array)
(addition operator works in the same way irrespective of order of operands).
| ||||||||||||||||||||||||||||||||||||||||||||||||||
6) \r and \n |
\r moves the cursor to the left-most position of *current* line.
\n moved the cursor to the left-most position of *next* line. | ||||||||||||||||||||||||||||||||||||||||||||||||||
7) Declaring structure having an element pointing to structure of its own type. Useful for implementing linked list. |
There are number of ways to do it:
typedef struct struct_type_state state ; struct struct_type_state { int int_state_id; state * ptr_next_state; }; or typedef struct struct_type_state { int int_state_id; struct struct_type_state * ptr_next_state; } state; or struct struct_type_state { int int_state_id; struct struct_type_state * ptr_next_state; }; typedef struct struct_type_state state; | ||||||||||||||||||||||||||||||||||||||||||||||||||
8) -1>>1 |
-1 is (stored as 2's complement) 0xFFFFFFFF in (32 bit) machine.
Right-shifted any number of times) will have the same value i.e. -1>>1 = -1.
So following statement will increment i by one. i = i - (-1>>7); | ||||||||||||||||||||||||||||||||||||||||||||||||||
9) Reading or writing complex declarations |
Example: Declare an array of function pointers returning pointer to integer.
1st method (left-to-right or in-to-out): array - a[] of function pointers - (* (a[]) )() (braces around a[] are optional) returning pointer - *(* a[])() to integer - int *(* a[])() 2nd method (right-to-left or out-to-in): integer - int pointer to - int * function pointers returning - int *(*)() array of - int *(* a[])() | ||||||||||||||||||||||||||||||||||||||||||||||||||
10) printf/scanf format characters |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
11) Reading command line arguments |
Below example illustrate reading of number and string from command line arguments.
#include <stdio.h> #include <stdlib.h> /* for atoi */ int main(int argc, char** argv) { unsigned int integer = 0; char* string = NULL; printf("argc=%d\n", argc); /* number of arguments */ printf("argv[0]=\"%s\"\n", argv[0]); /* name of executable */ printf("argv[1]=\"%s\"\n", argv[1]); /* integer */ printf("argv[2]=\"%s\"\n", argv[2]); /* string */ integer = atoi(argv[1]); string = argv[2]; printf("integer = %d\n", integer); printf("string = \"%s\"\n", string); printf("----\n"); return 0; }
| ||||||||||||||||||||||||||||||||||||||||||||||||||
12) Variable number of arguments |
Example:
#include <stdio.h> #include <stdarg.h> int algorithm( char* parameters, ... ); int main(void) { printf( "algorithm: %d\n", algorithm( "ioioi", 5, '+', 2, '-', 3 ) ); } int algorithm( char* parameters, ... ) { va_list argp; int result = 0; char *pp; char operation = '+'; va_start( argp, parameters ); for( pp = parameters; *pp != '\0'; ++pp ) { switch( *pp ) { case 'i': switch( operation ) { case '+': result += va_arg( argp, int ); break; case '-': result -= va_arg( argp, int ); break; default : /* ERROR */ break; } break; case 'o': operation = (char)va_arg( argp, int ); break; default: /* ERROR */ break; } } va_end( argp ); return result; } Functions using variable number of arguments must have at least one argument before "...". Typically this would be char* having information about type of (variable) arguments that would be following. | ||||||||||||||||||||||||||||||||||||||||||||||||||
13) Precedence and associativity of operators |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
14) Array initialisation and assignment |
Array can be initialised, but can not be assigned values as illustrated in an example below.
char c_array[5] = "test"; /* Initialisation */ char c_array2[5]; int i_array[2] = { 1, 2 }; /* Initialisation */ int i_array2[2]; c_array2 = "test"; /* Assignment - compiler error */ i_array2[] = { 1, 2 }; /* Assignment - compiler error */ i_array2 = i_array; /* Assignment - compiler error */ But here is something that does work ! char *c_ptr; c_ptr = "test ok"; Even following works ! void array_assign( char c_array[] ); int main(void) { char c_array2[5]; array_assign( c_array2 ); } void array_assign( char c_array[] ) { c_array = "test"; } This works because of special treatment to string constants and because array is passed as pointers to function. | ||||||||||||||||||||||||||||||||||||||||||||||||||
15) Big-endian or Little-endian ? |
Following is simple test to determine endianess of machine.
int i=1; if( *(char*)&i == 1 ) { printf( "Little-endian machine\n" ); } else { printf( "Big-endian machine\n" ); } Graphically, 0 <--- Big-endian pointer 0 0 1 <--- Little-endian pointer | ||||||||||||||||||||||||||||||||||||||||||||||||||
16) string.h |
Commonly used ones:
| ||||||||||||||||||||||||||||||||||||||||||||||||||
17) sizeof character constant is 1 ? |
No. sizeof( 'c' ) is 4 (32 bit machine).
Character constant is stored as an integer (4 bytes) rather than a character (1 byte) ! | ||||||||||||||||||||||||||||||||||||||||||||||||||
18) Definition and declaration |
Declaration indicate type of variable.
Definition mean variable is "created" i.e. storage (as per declaration of variable) is allocated. For example int i; is declaration and definition whereas extern int i; is just a declartion. | ||||||||||||||||||||||||||||||||||||||||||||||||||
19) static effect |
We know that if used in function, static allows to have permanent variable inside function accessible only to function.
Another (often forgotten) effect of static is:
if used in file, the variable would be accessible inside the file, but not outside.
| ||||||||||||||||||||||||||||||||||||||||||||||||||
20) bitmap implementation |
Below is a simple bitmap implementation:
#define BITMAP_SIZE 64 #define BYTE_SIZE 8 void set_bit( unsigned int n ); void reset_bit( unsigned int n ); int is_set( unsigned int n ); static unsigned char bit_map[ BITMAP_SIZE/BYTE_SIZE ]; void set_bit( unsigned int n ) { int byte_number = (n-1) / BYTE_SIZE; int bit_number = (n-1) % BYTE_SIZE; if( byte_number < (BITMAP_SIZE/BYTE_SIZE) ) bit_map[byte_number] = bit_map[byte_number] | (1<<bit_number); } void reset_bit( unsigned int n ) { int byte_number = (n-1) / BYTE_SIZE; int bit_number = (n-1) % BYTE_SIZE; bit_map[byte_number] = bit_map[byte_number] & ~(1<<bit_number); } int is_set( unsigned int n ) { int byte_number = (n-1) / BYTE_SIZE; int bit_number = (n-1) % BYTE_SIZE; if( bit_map[byte_number] & (1<<bit_number) ) return 1; return 0; } void display_bitmap( void ) { int ctr; int bctr; for( ctr=(BITMAP_SIZE/BYTE_SIZE - 1); ctr>=0; --ctr ) { for( bctr=(BYTE_SIZE-1);bctr>=0;--bctr ) { if( bit_map[ ctr ] & (1<<bctr) ) { printf( "%1d", 1 ); } else { printf( "%1d", 0 ); } } printf( " " ); } printf( "\n" ); } | ||||||||||||||||||||||||||||||||||||||||||||||||||
21) Generic Stack implementation |
Below is a simple implementation generic stack that can store elements of any type.
void push( void *element ); void *pop(); typedef struct struct_type_item item ; struct struct_type_item { void *element; item *next; }; static item *root = NULL; void push( void *element ) { item *to_put = (item*) malloc( sizeof(item) ); to_put->element = element; to_put->next = NULL; if( root ) { to_put->next = root; root = to_put; } else { root = to_put; } } void *pop() { if( root ) { void *tmp = root; void *element; element = root->element; root = root->next; free( tmp ); return element; } else { return NULL; } } void display_stack( void ) { item* tmp = root; while( tmp ) { printf( "-->%d<---\n", *(int*)tmp->element ); tmp = tmp->next; } } | ||||||||||||||||||||||||||||||||||||||||||||||||||
22) What is void pointer anyway ? |
void pointers are ways to write code that can work on generic data types;
this is illustrated in generic stack implementation.
Following example shows how C treats void pointers: #include <stdio.h> struct test_struct { int x; int y; int z; }; int main(void) { struct test_struct t_struct[3]; void *void_p1=(void *)&(t_struct[0]); void *void_p2=(void *)&(t_struct[1]); struct test_struct *ts_p1=(void *)&(t_struct[0]); struct test_struct *ts_p2=(void *)&(t_struct[1]); printf( "Size of test_struct \n" ); printf( "\t-using void pointers is %d and\n", (void_p2 - void_p1) ); printf( "\t-using structure pointers is %d.\n", (ts_p2 - ts_p1) ); printf( "\t-using sizeof operator is %d.\n", sizeof(struct test_struct) ); }
| ||||||||||||||||||||||||||||||||||||||||||||||||||
23) Reading a file |
Below program takes file name from the command line and output the file content on console
(similar to Linux/Unix command "cat").
#include <stdio.h> #define MAX_LINE_BUFFER_LENGTH 255 int main(int argc, char** argv) { char* file_name = NULL; FILE* file_handle = NULL; char line_buffer[MAX_LINE_BUFFER_LENGTH]; file_name = argv[1]; printf("file_name = \"%s\"\n", file_name); file_handle = fopen(file_name, "r"); /* "r" => reading */ if(file_handle == NULL) return 1; /* file open error */ while(fgets(line_buffer, sizeof(line_buffer), file_handle)) { printf("%s", line_buffer); } fclose(file_handle); printf("----\n"); return 0; }
| ||||||||||||||||||||||||||||||||||||||||||||||||||
24) math.h library |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
25) stdio.h |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
26) Predefined macros (or names as K&R calls it) |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
27) Storage classes |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
28) ctype.h library |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
29) stdlib.h library |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
30) Max and minimum values for int, float, etc. limits.h library |
| ||||||||||||||||||||||||||||||||||||||||||||||||||
31) Glossary |
|
© Copyright Samir Amberkar 2023-24