Cool C Programming
sathyamvellal.in
sathyamvellal.in
#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.
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;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.
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.
double array[SIZE][SIZE] = {
#include "float_values.txt"
}
Been there done that.#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.
It's a cute hack.
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.
http://hg.mozilla.org/mozilla-central/file/48dbd532a004/js/s...
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.
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..
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" ;)
b + 1 = a + sizeof(int) = 4
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.
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?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.
#ifndef FOO_H
#include <foo.h>
#endif1) 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 #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;
} #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.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 ?! :)
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)
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.
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.)
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.although i do think __ FUNCSIG __ deserves a mention along with __FILE__ and __LINE__ :)
sure its not standard, but in practice its never caused me a problem.