8 ms·
Cool C Programming
- voidlogic 13y agoI 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 13y agoToo many older developers are carrying around biases today that used to be true (like the performance issue you pointed out).
- vonmoltke 13y agoI'm still trying to shake off their influence. :P
- yoklov 13y agoI'd be very annoyed if my compiler didn't turn the latter into the former on platforms where it was more efficient.
- vowelless 13y agoRelevant: http://en.wikipedia.org/wiki/XOR_swap_algorithm#Reasons_for_avoidance_in_practice http://en.wikipedia.org/wiki/XOR_swap_algorithm#Reasons_for_...
- vsbuffalo 13y agoNeat 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.
- intull 13y agoMy apologies for not including this! I'll add it in an edit soon! Thank you!
- CJefferson 13y agoActually, 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.
- dima55 13y agoDoesn't even makes sense in assembly, at least on x86, since you can just use the xchg instruction.
- peterashford 13y agoFair point. Macros in particular are frowned upon. Although.... they can be very handy when used sparingly.
- golergka 13y agoYou 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.
- CraigJPerry 13y agoOver 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.
- dicroce 13y agoThis 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.
- intull 13y agoHi there, this was actually targeted at beginners and average C programmers. I'll add your comments in an edit soon! Thanks!
- deleted 13y ago[deleted]
- oscargrouch 13y agoa 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 13y agoFirstly, 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.
- angersock 13y agoHah, that's a fun one: double array[SIZE][SIZE] = { #include "float_values.txt" } Been there done that.
- deleted 13y ago[deleted]
- chubot 13y agoMeh, 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 13y agoHere, "float_values.txt" could just be a pile of comma-separated numbers, suitable for editing by non-programmers. It's a cute hack.
- chubot 13y agoI'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 13y agoTaking 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/src/jsopcode.cpp#l60 http://hg.mozilla.org/mozilla-central/file/48dbd532a004/js/s...
- skaevola 13y agoCool 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 13y agoHaha, I started with talking about the standard buffers and have completely missed using them! Thanks for reminding this!
- jheriko 13y agosome good tips although i do think __ FUNCSIG __ deserves a mention along with __FILE__ and __LINE__ :)
- npsimons 13y agoAh, except __FUNCSIG__ isn't portable/standard.
- jheriko 13y agoi 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 13y agoFunny 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.
- intull 13y agoI did mention this during the talk, but missed writing about it. Will add it in my next edit! :)
- shrikrishna 13y agoGreat post! Really loved it!
- codezero 13y agoI 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.
- deletes 13y agoYes, your version is correct. For example using bitwise or comparison operators with parameters would break OPs CHECK.
- intull 13y agoYes, 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 13y agoThis 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 13y agoYes, 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.
- breadbox 13y agoIn 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.
- intull 13y agoHi 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 13y agoFeature/Trick #4 : #ifndef FILE_H #define FILE_H ... #endif How is this "Cool C Programming"? What am I missing?
- apaprocki 13y agoIt 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.
- dfbrown 13y agogcc 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.
- ben0x539 13y agoI assumed that was common practice.
- apaprocki 13y agoNope, 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
- ExpiredLink 13y agoYou don't miss anything. Cool C Preprocessor Programming isn't Cool C Programming.
- intull 13y agoThis 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).
- imslavko 13y agoWhen 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 13y agoDid 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 13y agoWe did Topcoder in school, so C was the best option (java wasn't available on local competitions)
- AlexanderDhoore 13y agoNever seen curly brackets called flower brackets before :) That's new.
- prakashk 13y agoYou'd have if you learned programming in India :)
- deleted 13y ago[deleted]
- catfood 13y agoMight 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; }
- shurcooL 13y agochar* 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.)
- peterashford 13y ago...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)
- NAFV_P 13y agoI 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?
- kbart 13y agoNot 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
- joshguthrie 13y agoAnything 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.