63 comments

[ 0.88 ms ] story [ 81.4 ms ] thread
I wish this article talked about why people choose to do and not do these things.

So much of this stuff is the kind of thing people new to C should not be doing. Minimizing use of preprocessor directives leads to more maintainable C. Use them where they are needed and not anywhere else.

Also, for the swapping they show:

  a = a ^ b;
  b = a ^ b;
  a = a ^ b;
They fail to point out it's (platform dependent) performance implications. The above code is faster then the below code on PowerPC/POWER for example, but slower on x86/amd64.

  int tmp = a; //or: register int tmp = a;
  a = b;
  b = tmp;
Too many older developers are carrying around biases today that used to be true (like the performance issue you pointed out).
I'm still trying to shake off their influence. :P
I'd be very annoyed if my compiler didn't turn the latter into the former on platforms where it was more efficient.
Neat point, thanks for pointing it out. I love C (even more so after recently learning and using C++), but one minor thing is that frustrating is how hard it is to both (1) get really good at C and (2) the lack of great books on high-level C.

In other languages I use, I can get better by studying the source of programmers better than me. With C this is risky, as many large projects have messy code bases, and through their complexity hide the fact that one of C's great features is that it can be extremely lightweight in some projects. The tricks skilled C programmers use are a mix on unadvised hacks (like xor swap) and truly clever, portable, advised concepts.

There's also no great C book on higher-level C. Deep C Secrets is good, but perhaps a bit old. If you're using C for data work, you're out of luck (with maybe the exception of Ben Klemen's book, Modelling with Data, which is good but almost uses C too much when a bit of R and python would help). I'd love a book of machine learning and/or computational algorithms in C — with direct data examples.

My apologies for not including this! I'll add it in an edit soon! Thank you!
Actually, in real use, the XOR swapping is almost always slower, because in practice the temp turns into:

    Read a into register1
    Read b into register2
    Write register1 to a
    Write register2 to b
Whereas with the xor, you read the values, then do some pointless xoring, then write them back out. Further, if you are using a and b for anything else, the 'tmp' turns into a simple register renaming, for 0 cost!

While ^ swapping makes sense in assembler, it never makes sense in C.

Doesn't even makes sense in assembly, at least on x86, since you can just use the xchg instruction.
Fair point. Macros in particular are frowned upon. Although.... they can be very handy when used sparingly.
You don't even have to use C to create a preprocessor hell. I'm currently working on Unity3d/C# project which relies on preprocessor directives to behave differently on different platforms, and I would trade it for a runtime checks anytime.
Over using the pre-processor like this (creating "debug" type macros) always rubs me up the wrong way because of the hassle it is for me to read and ironically, to debug.

There are so many gotchas with writing good quality, safe macros and they're also not well handled by many tools. E.g. my IDE is pretty hopeless at following complex macros.

We've got better alternatives today. Unit testing is probably the first one everyone thinks of but i reckon switching to a graphical debugger (i used to only use CLI gdb) also reduced my need for these kind of hacks.

I feel like this article should be named "C's naughty bits", e.g. the array indexing in #7.

There are some good ones though, i like the idea of better separating code and data per #15.

This is a pretty basic article, but might be useful for some. One point I take issue with: a pointer in C is not simply a variable containing an address. It also knows (at compile time) how big the object pointed to is and so increments and decrements mean different things depending on the pointer type. In addition, void* is special because it does NOT know it's size.
Hi there, this was actually targeted at beginners and average C programmers. I'll add your comments in an edit soon! Thanks!
(comment deleted)
a pointer to anything, no matter what type it is, is ALWAYS the same size as a variable or embeded in a struct, (8 bytes in 64 arch and 4 bytes in 32 bit) cause it only needs to keep the address in memory to the thing its pointing to.. thats where the 4GB of memory limit for 32 bit architecture came from..cause you could not point to any memory address that would not fit into a 4 byte int

i think what you are saying is the thing the pointer is pointing to, dont have a known size if its a pointer of void.. since its the only untyped type of pointer.. and the programmer would need to check the size of the thing being pointed in memory at runtime.. (so sizeof wouldnt do it)

Also, if you incremment/decrement a pointer , no matter what type it is, you will have a "cursor to the memory" and can even overflow and see memory beyond what you have allocated.. thats where the nasty bugs come from :)

the type in the pointer will only tell (when you increment) by how much this incrementation will go for each iteration..

So if its a integer pointer you will advance 4 by 4 (in 32 bit) and if its a void* 8 by 8.. .. a small int 2 by 2.. a char .. 1 by 1.. etc..

Firstly, a data pointer is allowed to be a different size from a function pointer (although in most platforms they are the same size).

Secondly, what he said is that pointers are not just addresses, but typed addresses that know what type they're pointing at.

Exactly. char * a = 0; int * b = 0; a + 1 = a + sizeof(char) = 1

b + 1 = a + sizeof(int) = 4

> Secondly, what he said is that pointers are not just addresses, but typed addresses that know what type they're pointing at.

Hum Ok! At first i thought he was disagreeing about pointer representing addresses; now with the way you have put it, and by reading again, i see he is adding more information to it.. thats not only address but a typed address (unless void of course)..

Sorry for misunderstanding.. i want to reiterate that i agree with you "dicroce" ;)

Hah, that's a fun one:

  double array[SIZE][SIZE] = {
      #include "float_values.txt"
  }
Been there done that.
(comment deleted)
Meh, there is already an idiom for that. It's

#include "data.h"

Where data.h has "extern double array[SIZE][SIZE];" and data.c has the data. I don't see what's clever or useful about this.

Here, "float_values.txt" could just be a pile of comma-separated numbers, suitable for editing by non-programmers.

It's a cute hack.

I've worked in a lot of situations with C/C++ code and non-programmers, and that simply wouldn't work. Non-programmers will break your build with such a mechanism. If they are capable of editing comma-separated lists of numbers in a text editor without breaking it, they are capable of editing a C file with a float[][] declaration at the top.

It would cause a problem after about 2 days, and then you would end up replacing this in 5 minutes with a 5 line Python script that does a minimal amount of error checking (or a higher level tool to generate the data, e.g. a game logic editor).

EDIT: A cute hack is one that lets you take a shortcut and saves you time. This is one that saves a tiny amount of typing (or writing 5 lines of Python), in exchange for wasting time with broken builds.

Taking that pattern to the next level is Mozilla Spidermonkey, which defines a macro and then includes a static data file which calls the macro with its data contents. The same table is included multiple times with different macro definitions to output different code. Pre-processor code generation :) Example:

http://hg.mozilla.org/mozilla-central/file/48dbd532a004/js/s...

That's pretty normal, if used in moderation.

For example, it is a way to define overloads on functions on vectors with floats and double coordinates without having to copy-paste thousands of lines (using sed and make is another)

http://www.drdobbs.com/the-new-c-x-macros/184401387 gives nice examples.

Cool tricks!

About the debugging macros: printf prints messages to stdout, not stderr. You want to use fprintf instead:

  fprintf(stderr ,"error message");
This has the advantage of being unbuffered, and still printing to the console even if stdout has been directed to a file.
Haha, I started with talking about the standard buffers and have completely missed using them! Thanks for reminding this!
some good tips

although i do think __ FUNCSIG __ deserves a mention along with __FILE__ and __LINE__ :)

Ah, except __FUNCSIG__ isn't portable/standard.
i believe it works with the gcc, clang and msvs compilers across ios, android, windows phone, linux, windows desktop, windows metro, mac os and the game console compilers.

sure its not standard, but in practice its never caused me a problem.

Funny thing is, I tried using __FUNCSIG__ with gcc and clang before I posted. Doesn't work; gcc 4.8.0 on Linux and clang 3.1 on Cygwin. Looks like a Microsoftism to me.
I did mention this during the talk, but missed writing about it. Will add it in my next edit! :)
I am a bit rusty, but isn't it always better to individually parenthesize macro variables in the expression?

  #define CHECK(X, Y) \
  do { printf("Performing Assertion\n"); assert((X - Y) != 0); \
  printf("Assertion passed\n"); } while (0)
Here, assert((X - Y) != 0) should be assert(((X) - (Y)) != 0)

It's OK when you are doing subtraction, but if you were doing something like division or multiplication, you could screw with the order of operations if you don't do this.

Yes, your version is correct. For example using bitwise or comparison operators with parameters would break OPs CHECK.
Yes, macros are to be fully enclosed but just for a better readability sake I've avoided them here. As I said in my comment above, I mainly intended to convey the idea. I did explain every aspect during the talk though. I'll make a better effort from next time! Thanks
This seems like a little detail that there's no harm in including, especially since you focus so much on macros, and this is kind of macro 101.
Yes, I get your point :) The post doesn't focus on the preprocessor actually, its just that there's more to explain in it which people usually neglect. So I've somehow written more on the preprocessor which looks like I've focused on it.
In this particular case, resist the temptation to simplify. Macro parenthesization is critical, and should be second nature for anyone who wants to play around with them.
"Simplicity" is not a good thing when it introduces confusion and errors.
Hi all! I never expected this to reach the front page! Thank you all! This was a quick writeup of a talk I delivered in our college's Open Source Community (http://pesos.pes.edu). This was meant for beginners and average C programmers and summarized it through a blog post. I can understand few things do seem a bit different, but the post intends to mainly convey the idea than the exact implementation aspects!

Hope you enjoyed it! Thanks!

  Feature/Trick #4 :

  #ifndef FILE_H
  #define FILE_H
   ...
  #endif
How is this "Cool C Programming"? What am I missing?
It might as well talk about #pragma once if it is explaining include guards.

Fun fact: Oracle (Sun) Studio doesn't support #pragma once. Instead, it actually looks for proper include guards in the header and if they exist, it optimizes away re-includes of the file if the condition is false.

I assumed that was common practice.
Nope, and to make matters worse -- IBM xlC doesn't optimize at the include sites. So adding include-site guards actually speeds up compilation times in a non-insignificant way. e.g.,

  #ifndef FOO_H
  #include <foo.h>
  #endif
gcc does this as well (though it also supports #pragma once). I believe the current convention is to prefer #ifndef include guards as it is more portable and most compilers can detect and avoid re-including files with them.
You don't miss anything. Cool C Preprocessor Programming isn't Cool C Programming.
This talk was actually meant for beginners and for those who were acquainted with C to some extent. Furthermore, many never learn about the preprocessor features (at least here in India) and I ended up explaining things in detail which made up a majority of the post. I know it feels as if there was a prime focus on the preprocessor, but that wasn't the idea. It just took longer to explain those features (at least for beginners).
When I was in highschool we had this debugging macro:

    #define dbg(...) fprintf(stderr, __VA_ARGS__)
So the dbg function is just a shortcut to "printf to stderr". It also was easy to disable with #ifdef.
Did you learn this in high school?

Im curious because kids starting to get by using C is such a nice thing.. cause its a very thin layer for how machines works..

Alienation from the machine inner working(if learning by JS or Python for instance) is such a bad thing, and can take years to the grown up to recover from it :)

Im not against learning the "relaxed" languages, but its better when you do not start with them.. when you only learn them as more tools to do all sort of jobs we have to face

I was lucky to be a teenager at the nineties when programming were more sane and would make us learn how the real machine works.. (before the java madness)

Programmers cannot be lazy, they need to know everything in deep detail, to make the best decisions.. Lazy programmers, very weak programs..

The kids from where you grow up, should be very good when they are adults and decide to work with technology ?! :)

We did Topcoder in school, so C was the best option (java wasn't available on local competitions)
Never seen curly brackets called flower brackets before :) That's new.
You'd have if you learned programming in India :)
(comment deleted)
Might be obvious to some of you, but I use this to check what my macros expand out into to make sure the syntax is valid.

  #include <stdio.h>

  #define atoa(x) #x

  #define mdb(x) printf("%s\n", atoa(x))

  #define swap(type, x, y) do {type temp = *(x); *(x) = *(y); *(y) = temp;} while(0)

  int main(int argc, char *argv[])

  {

    int cat[2] = {33, 44};

    printf("atoa(cat): %s\n", atoa(cat)); // "cat" 
	
    mdb(swap(int, cat, cat+1)); // expands the macro inside to text 
	
    swap(int, cat, cat+1); // the macro itself 
	
    printf("cat[0]: %d\n", cat[0]); // 44 
	
    return 0;
  }

  char* first_name; int age;

  fscanf(fptr, "%s %d", first_name, &age);
  printf("Name: %s\nAge: %d\n\n", first_name, age);
Cool usage of uninitialized char* pointer bro.

(Sorry about the snarky tone, I just wanted to use that meme because it's appropriate.)

Yeah sorry for the mistake! Will change it soon!
It's not your fault, just a good example of the kinds of programming errors this language design makes more likely.
...erm... more 'C for newbs'.

Although, it does point out some interesting idioms of the language and I agree that scanf is bloody cool. IMO every language should have printf/scanf equivalents :o)

I was intrigued by the bit regarding array indexing, but I have yet to break out vim and mod that C source. How would this apply to multidimensional arrays?
Not bad reading for a beginner/intermediate C programmer. However, if you are targeting such, try to emphasize more on possible pitfalls, things to avoid and best practices. What first comes to my mind:

1) Name variables in macro differently (e.g. _X instead of just X) and always use them in parentheses.

2) Always initialize pointers, especially in examples for beginners, as segmentation fault while stepping on uninitialized or dangling pointer is one of the most common errors I have seen.

3) Using -Wall compiler directive is also a good idea to see many possible problems you could miss otherwise.

4) Trick #15 is a really bad practice, it's way better to include header file that declares and defines array, something like Gimp or OpenSSL do when you choose to export output as a header file.

5) Just a suggestion: there is a very handy macro for debugging worth mentioning:

    #ifdef DEBUG
    #define DBG(fmt, ...) \
        printf("%s: " fmt, __func__, ## __VA_ARGS__)
    #else
    #define DBG(fmt, ...)
    #endif
Anything involving the preprocessor ranks high in my favorite C trick (I once got a "clever abuse of the C preprocessor" notation after writing a C+ library for school written entirely using macros).

Actually, anything involving meta-programming is "cool". Knowing a language is "easy" once you get the logic, but manipulating the language to do the work for you is even more satisfying.