- The most generic way to initialise an automatic variable to zero (as it would be if in static scope) is to use: type_t variable = {0,}; This operation is defined by C90, and works whether the variable is a scalar or aggregate type as demonstrated below. This can replace memset(&var,0,sizeof(var)) which assumes that initialised variables are always represented by 0 in static scope (Not a terrible assumption TBH). Note the code below along with the assembly generated by gcc-4.1.2 shows that if any element values are not specified, they're initialised as is any unused padding space between elements of aggregate types. However if all elements are explicitly initialised the padding is left untouched. Unfortunately the gcc -Wmissing-field-initializers option (which is included in -Wextra) issues a warning for this useful construct, even when you specify a trailing ',' which should be enough to indicate one hasn't inadvertantly forgotten some initialisers. I've requested that this warning should be suppressed when a trailing ',' is specified. void f(void) { typedef int opaque_t; opaque_t o = { 0, }; /* can init scalar types like this */ typedef struct { char c; int a; int b; } s_t; s_t s1 = { 0, }; /* movl $0, -16(%ebp) movl $0, -12(%ebp) movl $0, -8(%ebp) */ s_t s2 = { 0, 0, 0 }; /* movb $0, -28(%ebp) movl $0, -24(%ebp) movl $0, -20(%ebp) */ s_t s3 = { 1, 0, }; /* movl $0, -52(%ebp) movl $0, -48(%ebp) movl $0, -44(%ebp) movb $1, -52(%ebp) */ }
- Beware of signed integer overflow handling: Later gcc versions may ignore code that assumes signed integers will wrap around on overflow. Even later gcc versions added the -Wstrict-overflow option (which -Wall includes) to warn when it was doing this, so one can find and change problematic code to more portable contructs. For details see: Paul Eggert's Autoconf documentation Felix von Leitner's summary Ian Lance Taylor's info You can search for "OFF_T_MAX" in the new coreutils truncate utility, to see how I handled overflow for various integer operations.
- One can check gcc supported options and code using: echo "void f(int i) {i++;int j;}" | ${CC:-gcc} -xc -c -o /dev/null - 2>/dev/null && echo c99 Note one can instruct gcc to act like a c89 (ansi c) compiler for testing like: echo "void f(int i) {i++;int j;}" | gcc -std=c89 -pedantic -Werror -xc -c -o /dev/null - 2>/dev/null || echo c89 See also the Autoconf AC_PROG_CC and AC_PROG_CC_STDC etc. macros.
- The printf man page is more like a specification than a quick reference. So I created a quick reference for the format specifications, and some more detailed notes on C99 integer types. Note also, the latest linux man pages for the kernel syscall and glibc function interfaces are browsable online as of Dec 2007.
- snprintf & strncpy should specify the full size of the destination buffer, because snprintf will NUL terminate, and with strncpy there is no way to determine if the string was truncated without checking the last character of the buffer. Using memset to zero the buffer before using these functions is redundant and inefficient. Here is an example of correct usage: #include <stdio.h> #include <string.h> int main(void) { char dest[5]; char src[5]="hello"; //Not NUL terminated dest[sizeof(dest)-1]='\0'; //for later check (void) strncpy(dest, src, sizeof(dest)); if (dest[sizeof(dest)-1]!='\0'); //truncated dest[sizeof(dest)-1]='\0'; //should always do this int required = snprintf(dest, sizeof(dest), "%s", src); if (required >= sizeof(dest)); //truncated }
- A common requirement is to get the length of an array at compile time, for which I find the following macro (which is analogous to sizeof) useful: #define lengthof(x) (sizeof(x)/sizeof(x[0])) Also note that C99 supports variable length arrays.
- It can be quite confusing sometimes to know exactly what is #defined in your program, and this is especially true for glibc's "feature" macros. These macros are documented (info libc "feature test macros"), but it's much easier to see what's defined directly using "cpp -dD" like: $ echo "#include <features.h>" | cpp -dN | grep "#define __USE_" #define __USE_ANSI #define __USE_POSIX #define __USE_POSIX2 #define __USE_POSIX199309 #define __USE_POSIX199506 #define __USE_MISC #define __USE_BSD #define __USE_SVID $ echo "#include <features.h>" | cpp -dN -std=c99 | grep "#define __USE_" #define __USE_ANSI #define __USE_ISOC99 Also to see all the predefined macros one can do: $ cpp -dM /dev/null See also my strerror script which uses the same technique.
- Determining the optimum gcc settings for a particular CPU can be challenging. To that end I created a script that gives the answer automatically for 32 bit x86 processors on linux at least.
- Starting with GCC 3.0 one can give branch prediction hints to the compiler. This can help immensely for performance critical code. Note one should only use these hints where absolutely necessary because they make the code more complicated to read/maintain. For details search for builtin_expect in `info gcc` #if __GNUC__ < 3 #define __builtin_expect(foo,bar) (foo) #define expect(foo,bar) (foo) #else #define expect(foo,bar) __builtin_expect((long)(foo),bar) #endif /* idea for these macros taken from Linux kernel */ /* Note tests for NULL (!something) don't need an unlikely as gcc does that by default itself */ #define __likely(foo) expect((foo),1) #define __unlikely(foo) expect((foo),0) An example use is: for(;;) { //hot loop ret = op1(); if (__unlikely(ret == error)) { //handle error } ... }
- Setting CPU affinity for a process/thread on linux requires calling the following code in userspace: (Note the 2.4 vanilla kernels require a simple patch to support this) /* As can be seen here the sched_setaffinity() interface has changed 3 times: rh9 = glibc-2.3.2 = int sched_setaffinity(__pid_t __pid, unsigned int __len, unsigned long int *__mask); fc1 = glibc-2.3.3 = int sched_setaffinity(__pid_t __pid, __const cpu_set_t *__mask); fc2 = glibc-cvs = int sched_setaffinity(__pid_t __pid, size_t __cpusetsize, __const cpu_set_t *__cpuset); therefore call the syscall directly. */ #undef __NR_sched_setaffinity #define __NR_sched_setaffinity 241 //2.4 specific #include <unistd.h> #include <linux/unistd.h> _syscall3(int, sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr) //The above equivalent to: int sched_setaffinity(pid_t pid, unsigned int len, unsigned long *mask); //This function sets affinity to a particular CPU. Pass in 0,1,2,3,... static void set_affinity(unsigned long cpu) { cpu = (1 << cpu); sched_setaffinity(0, sizeof(cpu), &cpu); }
- There are 2 methods of supporting 64 bit files on linux (glibc). I.E. supporting large files > 2GiB. 1. You can do it explicitly in the source by defining _LARGEFILE64_SOURCE and then calling open(,O_LARGEFILE), stat64(), lseek64() etc. 2. You can also get everything working implicitly by just defining _FILE_OFFSET_BITS=64 and using the standard open(), stat(), lseek() etc. calls.
- The following options to gcc can be used to get more info about a specified c file. -E #preprocessor output to stdout -S #assembly to cfile.s -MM #make dependencies to stdout -aux-info file #prototypes to file -H #header files used to stdout
- C++ "Get/Set" methods can use function overloading to expose just one name, which makes things much cleaner. For e.g: class myclass { int priv_int; public: int Priv_int(void) {return priv_int}; void Priv_int(int t) {priv_int = t}; };
- sizeof(type) has more coupling (bad) than sizeof(variable). For e.g.: int var; memset(&var,0,sizeof(int)); /* bad */ memset(&var,0,sizeof(var)); /* good */
- Here are my indent settings: -kr -l78 -lc78 -lps -i4 -br -bbo -bad -bap -cs -nbc -nhnl -nss Some people also like to add: -pcs -psl
- const and volatile declarations are confusing, they modify the nearest item (on their left), so... 1. const int* p_ci /* Pointer to const int */ 2. int const* p_ci /* Pointer to const int */ 3. int *const cp_i /* const pointer to int */ Note 2. gives a parse error in cdecl 2.5
- 071 is an Octal number in C (leading zero) 71 is an decimal number in C (no leading zero)
- beware of sizeof on strings char* string1="12345678"; char string2[]="12345678"; char string3[8]="12345678"; #define string4 "12345678" //sizeof(string1) = 4 (sizeof pointer!) //sizeof(string2) = 9 (NUL char counted!) //sizeof(string3) = 8 (Not NUL terminated!) //sizeof(string4) = 9 (NUL char counted!)
- Another string gotcha: char* stringCONSTANT = "a string"; You can't write into stringCONSTANT like stringCONSTANT[0] = 'b'; as it's implicitly constant. Some compilers/platforms allow you to do this but it's non portable. You will just get weird runtime errors (seg faults) if you do this, due to the string being in a readonly data segment. Note this doesn't apply to the following string declaration as the compiler copies the constant "a string" from the readonly data segment into the string array (every time the string comes into scope). char string[] = "a string";
- #include directive forms: #include "file.h" //or "dirs/file.h" #include <file.h> //or <dirs/file.h> #include "/usr/include/file.h" The "" relative form, instructs the preprocessor to look for include files in the same directory of the file that contains the #include statement (the parent file), and then in the directories of whatever files that include (#include) that file (grandparent files). If no file is found the preprocessor does as if <> were used. For the if <> (relative) form the preprocessor searches along the path specified by the compiler include path, which could be specified using an INCLUDE environment variable or -I options etc. The last absolute address form, doesn't do any path searching. It just looks for the file @ the specified path.
- The C++ equivalent of: pos = sprintf(buff , "%3d %5ld \n",i,lng); is: ostringstream Stm; //Note strstream is old & non standard. string Str; Stm << setw(3) << i << " " << setw(5) << lng; Str = Stm.str();
- /* Example program showing how to parse comma seperated strings. I've seen many times where people write their own (usually flawed) code to parse strings like this. */ #include <stdio.h> int main(void) { char str1[2]; char str2[2]; char str3[2]; char str4[2]; char* t="1,2,3,4"; sscanf(t, "%[^,],%[^,],%[^,],%[^,]", str1, str2, str3, str4); printf("str1=[%s], str2=[%s], str3=[%s], str4=[%s]\n", str1, str2, str3, str4); }
- /* The following will not work generally as if linux is defined as one for e.g., then the following will try and #include <net-snmp/system/1.h> */ #define INC <stdio.h> #define INC2 <net-snmp/system/linux.h> #include INC #include INC2 int main(void) { printf("Hello world\n"); return 1; }
© Oct 16 2006