Here are some GNU toolchain based C- tricks that will help you in reducing unexpected bugs in your Embedded C code.
#include <stdio.h> main() { if (sizeof(int) > -1) printf("True"); else printf("False"); } /* Prints: False. Why? Answer: Interger Promotion... (sizeof(int) > -1) ==> (4u > -1) ==> (4u > -1u) ==> (4u > -1u) ==> (4u > 4294967295uu) because -1u is equal to 4294967295 in unsigned That's why else statement is executed! */
/* why */ #if (sizeof(xyz) > 0) #error "Error" #endif /* fails? */ /* because #if are pre-processor directive while sizeof operator is evaluated at compile time (except in case of VLA). and pre-processors comes first before compiler so at the time of pre-processor, there is no information on sizeof output. */
/* You cannot use a function call when initializing a static or global variable: */ /* Example: */ void fun (void) { static int x = init_ftn(); } /* [Error]: initializer element is not constant */ /* Alternate approach: Error can be avoided by splitting the definition and assignment statments e.g. */ static int x; x = init_ftn();
/* Never do this: */ int *ptr = (int *) malloc (sizeof (int) * XYZ); ptr = realloc (ptr, NEW_SIZE); /* Because if new size memory could not be alloted, the NULL pointer will be return by realloc; thus the address to original allocated memory will be lost. this could lead to memory leak... */
/* sizeof Operator: */ /* sizeof is always computed at compile time in C89. Since C99 and with the introduction of Variable Length Arrays (VLA), it is computed at run time when a variable length array is part of the expression in the sizeof operand. Same for the evaluation of the sizeof operand: it is not evaluated in C89 but in C99 if the operand is of variable length array type it is evaluated. For example: */ int n = 5; int x[n]; sizeof (x[n--]); // n is now 4
/* why free(ptr) doesn't automatically assign NULL to ptr'; This is because pointer here is passed by value not by reference (not as free (**ptr)). so free function has no idea about the actual pointer location. */
/* Retrun of malloc(0) */ /* When malloc is used with an argument of zero, its behavior is "implementation-specific". It may return a pointer to NULL or it may return a pointer to a region with zero bytes allocated. The C standard says: If the space cannot be allocated, a null pointer is returned. If the size of the space requested is zero, the behavior is implementation defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object. malloc(0) will return either "a null pointer or a unique pointer that can be successfully passed to free()". */
int *pi = (int*) malloc(sizeof(int)); /* Before the pointer to void was introduced to C, explicit casts were required with malloc to stop the generation of warnings when assignments were made between incompatible pointer types. Since a pointer to void can be assigned to any other pointer type, explicit casting is no longer required. Some developers consider explicit casts to be a good practice. */
/* calloc vs malloc speed: */ /* The execution of calloc may take longer than using malloc as the memory zeroed out before the call to calloc returns. */
/* To free malloc allocated memory without fucntion free(): */ int *ptr = malloc(10 * size(int)); realloc (ptr,0);
/* free (NULL) as NOP instruction: */ /* The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, noting happens like NOP instruction. */
int *ptr = &x; free (ptr); /* If the pointer passed has been allocated by other than a malloc type function, then the function’s behavior is undefined. Its a runtime error. */
/* An attempt to pass the address of an integer literal as shown below will also generate a syntax error: */ passingAddressOfConstants(&23, &23); /* In this case, the error message will indicate that an lvalue is required as the address-of operator’s operand. */
/* typedef is a language construct that associates a name to a type. You use it the same way you would use the original type, for instance */ typedef int myinteger; typedef char *mystring; typedef void (*myfunc)(); using them like myinteger i; // is equivalent to int i; mystring s; // is the same as char *s; myfunc f; // compile equally as void (*f)();
int a[x]; &a /* pointer to entire array */ a /* pointer to integer (first element) */
int *ptr = malloc (sizeof(int)); ptr++; free (ptr); /* This is a runtime error because Heap Manager has no entry for this address (incremented address) */
/* Passing multidimensional array to function, the column size must be specified. e.g. */ void display2DArray(int arr[][5], int rows) /* or: */ void display2DArray(int (*arr)[5], int rows) /* () is vary important as *arr[5] would be array of pointers not pointer to array. */
/* while one dimension array is passed to funciton like: */ void fun (int *) /* Two dimensional array can't be passed to function like this... */ void fun (int **)
/* When passing array of more than 2-dimension to function all the dimensions except first need to be specified: */ void display3DArray(int (*arr)[2][4], int rows)
/* Array of char vs string array string is sequence of ASCII char ending with ASCII NUL character while array of char may not necessary contain ASCII character (like they may contain numbers < 255) and is not ended with NUL. */
/* Character constants (character literals) are character sequences enclosed in single quotes. In C, they are of type int. This is demonstrated as follows: */ printf("%d\n",sizeof(char)); printf("%d\n",sizeof('a')); /* When executed, the size of char will be 1 while the character literal’s size will be 4. This anomaly is an artifact of the language design. */
/* In C++, */ sizeof('a') == sizeof(char) == 1 /* This makes intuitive sense, since 'a' is a character literal. */ /* In C however, */ sizeof('a') == sizeof(int) /* That is, it appears that C character literals are actually integers. */
/* What is the difference between a String Constant and String Literal in C? */ /* They are one and the same. Merely a preference in which word you use to describe the string. In the C99 Draft from 2007 the term sting literal is used. The term string constant does not appear in the draft at all. Books use the term String Constant, but C99 uses String Literal. Similarly Character literals are character constants. */
/* You cannot assign a string literal to a char array after its been created: */ char a[6]; a = "Hello"; /* why this is an error? This is because a is contant pointer to the first element of array. a = "Hello" means you are assigning `a`(contant pointer to array first elment) the starting address of "Hello" String literal i.e. changing constant pointer a. */ /* Similarly consider the following program: */ typedef struct{ char a[6]; }point; int main (int argc, char *argv[]) { point p; //POINT-X p.a = "onetwo"; //POINT-Y return 0; } /* The compiler wil through an error at POINT-Y because the array a[6] has already been created at POINT-X. same problem described in above. If we replace POINT-X and POINT-Y with: */ point p = {"onetwo"}; //POINT-X /* The error will go away. This is because we are COPING characters from String Literal to character array at the time of creation of array at statement 'point p'. see bellow trick. */
/* What is the difference between char s[] and char *s? */ /* The difference here is that */ char *s = "Hello world"; /* Will place "Hello world" in the read-only parts of the memory, and making s a pointer to that makes any writing operation on this memory illegal. While doing: */ char s[] = "Hello world"; /* puts the literal string in read-only memory and copies the string to newly allocated memory on the stack. Thus making */ s[0] = 'J'; /* legal. */
/* String Pool: */ char *header = (char*) malloc(strlen("Media Player")+1); strcpy(header,"Media Player"); /* Only single copy of string literal will be stored in the String Pool. */
/* Attempting to initialize a pointer to a char with a character literal will not work. Since a character literal is of type int in C, doing so we are actually trying to assign an integer to a character pointer. This will cause the application to terminate when the pointer is dereferenced: */ char* prefix = '+'; // Illegal /* A valid approach using the malloc function follows: */ prefix = (char*)malloc(1); *prefix = '+';
char command[16]; printf("Enter a Command: "); scanf("%s",command); if(command == "Quit") { ... } /* This will evaluate false since we are comparing the address of command array with the string literal’s address. */
char *str = "Hello World"; *(str++) VS *str++ VS (*str)++ /* '*' has lower presedence than ++ so: *(str++) == *str++ ==> ++ operator will apply to str first thus post incrementing str. (*str)++ is actually trying to increment the dereferenced character (*str) of string literal which will be a run time error. */
/* The size of structure is literally equal to sum of data type sizes however the actual size is often larger as padding can occur b/w fields. Consider the following example on 64-bits system: */ typedef struct{ char x; //1-byte int y; //4-bytes int *p; //8-bytes }point; int main () { printf ("%d", sizeof (point)); //16-bytes (3-bytes are padded after char) return 0; }
/* With some (indeed, many) C compilers, you can get away with what's called a 'common' definition of a variable too. 'Common', here, refers to a technique used in Fortran for sharing variables between source files, using a (possibly named) COMMON block. What happens here is that each of a number of files provides a tentative definition of the variable. As long as no more than one file provides an initialized definition, then the various files end up sharing a common single definition of the variable: */ ---------------------------------- @file1.c int i; void inc(void) { i++; } ---------------------------------- @file2.c int i; void dec(void) { i--; } ---------------------------------- @file3.c #include int i = 9; void put(void) { printf("i = %d\n", i); } ---------------------------------- /* This technique does not conform to the letter of the C standard and the 'one definition rule', but the C standard lists it as a common variation on its one definition rule. Because this technique is not always supported, it is best to avoid using it, especially if your code needs to be portable. Using this technique, you can also end up with unintentional type punning. If one of the files declared i as a double instead of as an int, C's type-unsafe linkers probably would not spot the mismatch. If you're on a machine with 64-bit int and double, you'd not even get a warning; on a machine with 32-bit int and 64-bit double, you'd probably get a warning about the different sizes — the linker would use the largest size, exactly as a Fortran program would take the largest size of any common blocks. This is mentioned in the C standard in informative Annex J as a common extension: J.5.11 Multiple external definitions There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2). */
/* Conflicting Error: */ extern static void fun (void); /* A symbol can only be either global or static at a time. */
/* Wild Pointer: An uninitialized pointer is called wild pointer. */
/* Designated Initializers: */ [URL]:= https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Designated-Inits.html /* In a structure initializer, specify the name of a field to initialize with `.fieldname =' before the element value. For example, given the following structure, */ struct point { int x; int y; }; /* The following initialization */ struct point p = { .y = yvalue, .x = xvalue }; /* is equivalent to */ struct point p = { xvalue, yvalue }; /* Another syntax which has the same meaning, obsolete since GCC 2.5, is `fieldname:', as shown here: */ struct point p = { y: yvalue, x: xvalue }; /* The `[index]' or `.fieldname' is known as a designator. You can also use a designator (or the obsolete colon syntax) when initializing a union, to specify which element of the union should be used. For example, */ union foo { int i; double d; }; union foo f = { .d = 4 }; /* will convert 4 to a double to store it in the union using the second element. By contrast, casting 4 to type union foo would store it into the union as the integer i, since it is an integer. */
/* Using incompatible pointer types will result in a warning: */ int (*fptrCompute)(int,int); int addNumbers(int n1, int n2, int n3) { return n1+n2+n3; } fptrCompute = addNumbers; /* The warning follows: warning: assignment from incompatible pointer type */
/* Casting to Union: */ [URL]:= https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Cast-to-Union.html#Cast-to-Union /* A cast to union type is similar to other casts, except that the type specified is a union type. You can specify the type either with union tag or with a typedef name. A cast to union is actually a constructor though, not a cast, and hence does not yield an lvalue like normal casts. The types that may be cast to the union type are those of the members of the union. Thus, given the following union and variables: */ union foo { int i; double d; }; int x; double y; /* both x and y can be cast to type union foo. Using the cast as the right-hand side of an assignment to a variable of union type is equivalent to storing in a member of the union: */ union foo u; /* ... */ u = (union foo) x // ==> u.i = x as x is int u = (union foo) y // ==> u.d = y as y is double
/* By default constant (01234567....) are considered signed integer. */
/* Casting to UNION with incompatible type is compiler error. */ union foo { unsigned int i; double d; }; union foo bar = (union foo)2; /* [Error] cast to union type from type not present in union */ /* Reason: By default 2 (constant) is considered signed integer. union foo doesn't contain any signed int member variable. The compiler doesn't to find suitable member to assign value to it. so it throws error. in order to remove error we can cast constant 2 to unsigned int as: */ union foo bar = (union foo)(unsigned int)2;
/* if you initialize at least one of the members, the rest of the members will get set to zero (initialized as if they had static storage duration). */
typedef struct{ char x; static int y; }point; /* [Error] expected specifier-qualifier-list before 'static' Can't declare static member inside struct in C (You can have a static member in a C++ structure.) because C is not Object Oriented where Objects share member (functions and static variables). I C objects are independent of each other. */
/* Initializing array of structures: */ typedef struct{ char x; int y; }point; point * a[] = { {'a', 2}, {'b', 1}, {'c', 5} };
/* What are qualified and un-qualified types in c language? */ /* In the C, C++, and D programming languages, a type qualifier is a keyword that is applied to a type, resulting in a qualified type. For example, const int is a qualified type representing a constant integer, while int is the corresponding unqualified type, simply an integer. */ /* There are two types of qualifiers available in C language. They are, 1. const 2. volatile */
/* Avoiding compiler warning for unsed values/parameters. ==> cast it to void to tell the compiler that its okay to ignore the parameter at this moment. e.g. */ int ftn (void) { . return xyz; } int main () { (void) ftn(); //suppress compiler warning } /* similarly: */ void ftn (int *xyz) { (void)xyz; // again suppress warning }
/* Q: Why static and global variable can't be initialized with varialbe? */ void main () { int x = 10; static int y = x; } /* Error: initializer element is not a constant */ /* This is because the variables are initialized at run-time. ie. memory location depends on current stack pointer (especially in interrupt based system). At run time variables are assigned an address (relative) and value is loaded in it. On the other hand static and global variables must have an address assign to them on compile time and these variables are initialized before main() by startup (crt0) code. so before main no variable exists in memory by this time so initialization can't be made at run-time with variable name. */
/* what will be the output of the following? */ int i=5; printf("%d %d %d %d %d %d",i++,i--,++i,--i,i); /* The output is compiler dependent as none of the C standards specify in which order the function parameters should be evaluated. so it is left to compiler implementer. so before we can answer this question, we must know the sequence point of compiler function evaluational i.e. right or left etc. */
/* Accessing Linker script symbols: compiler uses symbol tables to store debug/linker symbols. The table can be thought as: _______________________________________________ | | | | SYMBOL | ADDRESS | | | | |-----------------------------------------------| | SYMBOL-1 | ADDRESS-1 | | SYMBOL-2 | ADDRESS-1 | | SYMBOL-3 | ADDRESS-1 | | | | . . . . . . . . . | SYMBOL-N | ADDRESS-N | |___________________________|___________________| when you define a global variable e.g. */ int myVar = 5; /* what compiler does is make an entry in the symbol table for myVar and its address in symbol table and store value 5 in myVar address. when you use the address operator with symbol like: */ int *ptr = &myVar; /* The compiler/linker looks for the symbol myVar in the symbol table, gets the associated address from the address column and store it in the address pointed by ptr. */ /* Symbol in linker script: Symbols in linker script are treated a bit differently. there is no value associated with symbols in linker script. rather the value is treated as an address. e.g. __SECTION_X_START = 12345 instead of storing the value 12345 into the address pointed by __SECTION_X_START, it makes an entry in the symbol table for __SECTION_X_START and place 12345 in address column. Now in order to access __SECTION_X_START value, you need to use the & sign. e.g. */ extern int __SECTION_X_START; printf ("symbol value is: %d", &__SECTION_X_START ); // prints: symbol value is: 12345
/* Integer promotion: consider the following example: */ uint8_t val = 0x5a; /* what will be the result of following expression when the underlaying machine is 32-bits. */ (~val) >> 4; /* expected: 0xa (WRONGE..!!!) why? the actual result will be: 0x0FFFF,FFa because of integer promotion:first the integer will be promoted to underlying machine unsigned int; i.e. val: 0x0000005a (~val) = 0xFFFFFFa5 (~val)>>4 = 0x0FFFFFFa */
/* The expression in sizeof operator is not evaluated: sizeof(i = 420) will give the size of i but the i will not contain 420; */
/* Why we don't need any prototype for main function? The answer is hidden in question why we need prototype? Generally prototypes are needed to tell the compiler what parameters the function is taking and what data type the function returns. For main function the C standard specifies only two formats i.e. */ 1. int main () 2. int main (int argc, char * argv[]) /* as the function prototype are known at advance (as compiler implements C standard) that's why we don't need any function prototype for main to tell the compiler what arguments will the main function take and what will be the return type of the function. */
/* which one is correct? */ float x = 2.0 //or float x = 2.0f /* Technically both are correct but the 2.0 is treated as "double" which is implicitly converted to float by the compiler before assigning it to x. in second case we are explicitly telling the compiler that 2.0 is float type not double. So 2.0f is correct notation. */
/* if you do not like the way compiler numbers the lines in the file, you can restart the line numbering with: */ #line 2000 /* Sneak one of these into a colleague's code just before he starts compiling and watch the fun as all of the syntax errors are reported on the wrong line. */
/* Function Prototype vs declaration It is never required to declare a prototype for a function in C, neither in "old" C (including C89/90) nor in new C (C99). However, there's a significant difference between C89/90 and C99 with regard to function declarations. In C89/90 it was not necessary to declare a function at all. If the function is not declared at the point of the call, the compiler "guesses" (infers) the declaration implicitly from the types of the arguments passed in the call and assumes that the return type is int. For example */ int main() { int i = foo(5); /* No declaration for `foo`, no prototype for `foo`. Will work in C89/90. Assumes `int foo(int)` */ return 0; } int foo(int i) { return i; } /* In C99 every function that you call must be declared before point of the call. However, it is still not necessary to declare it with a prototype specifically. A non-prototype declaration will work as well. This means that in C99 the "implicit int" rule no longer works (for inferred function return types, in this case), but parameter types can still be guessed from the argument types if function is declared without a prototype. The previous example will not compile in C99, since foo is not declared at the point of the call. Yet, you can add a non-prototype declaration */ int foo(); /* Declares `foo`, but still no prototype */ int main() { int i = foo(5); /* No prototype for `foo`, although return type is known. Will work in C99. Assumes `int foo(int)` */ return 0; } /* ... and end up with valid C99 code. Nevertheless, it is always a good practice to declare a prototype for the function before you call it. An additional note: I said above that it is never required to declare a function prototype. In fact, for some functions it is a requirement. In order to properly call a variadic function in C (printf for example) the function must be declared with a prototype before the point of the call. Otherwise, the behavior is undefined. This applies to both C89/90 and C99. */
/* WRONGE... Never do this. */ @file1.c int myarray[5]; @file2.c extern int *myarray;