
Cool C Programming - intull
http://sathyamvellal.in/blog/cool-c-programming/?d=5
======
codezero
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.

~~~
intull
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

~~~
codezero
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.

~~~
intull
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.

------
voidlogic
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;

~~~
dicroce
Too many older developers are carrying around biases today that used to be
true (like the performance issue you pointed out).

~~~
vonmoltke
I'm still trying to shake off their influence. :P

------
angersock
Hah, that's a fun one:

    
    
      double array[SIZE][SIZE] = {
          #include "float_values.txt"
      }
    

Been there done that.

~~~
chubot
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.

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

It's a cute hack.

~~~
chubot
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.

~~~
apaprocki
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...](http://hg.mozilla.org/mozilla-
central/file/48dbd532a004/js/src/jsopcode.cpp#l60)

~~~
Someone
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](http://www.drdobbs.com/the-new-c-x-macros/184401387) gives
nice examples.

------
dicroce
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.

~~~
oscargrouch
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..

~~~
Peaker
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.

~~~
oscargrouch
> 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" ;)

------
CraigJPerry
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.

------
intull
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](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!

------
huhtenberg

      Feature/Trick #4 :
    
      #ifndef FILE_H
      #define FILE_H
       ...
      #endif
    

How is this "Cool C Programming"? What am I missing?

~~~
apaprocki
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.

~~~
ben0x539
I assumed that was common practice.

~~~
apaprocki
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

------
kbart
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

------
catfood
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;
      }

------
imslavko
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.

~~~
oscargrouch
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 ?! :)

~~~
imslavko
We did Topcoder in school, so C was the best option (java wasn't available on
local competitions)

------
peterashford
...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)

------
joshguthrie
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.

------
shurcooL

      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.)

~~~
intull
Yeah sorry for the mistake! Will change it soon!

~~~
shurcooL
It's not your fault, just a good example of the kinds of programming errors
this language design makes more likely.

------
skaevola
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.

~~~
intull
Haha, I started with talking about the standard buffers and have completely
missed using them! Thanks for reminding this!

------
NAFV_P
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?

------
AlexanderDhoore
Never seen curly brackets called flower brackets before :) That's new.

~~~
prakashk
You'd have if you learned programming in India :)

------
jheriko
some good tips

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

~~~
npsimons
Ah, except __FUNCSIG__ isn't portable/standard.

~~~
jheriko
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.

~~~
npsimons
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.

------
shrikrishna
Great post! Really loved it!

