108 ms·
What is your favourite C programming trick?
- silentbicycle 15y agoRecently, I learned that instead of saying long_struct_name *foo = malloc(sizeof(long_struct_name)); you can say long_struct_name *foo = malloc(sizeof(*foo)); since the variable type info is already statically available. That saves some typing, and (more importantly) blocks against bugs from changing one but not the other. I've been meaning to look it up in H&S to make sure it's always safe, but the guy who showed it to me is so strict about safe/standard C that it's likely. Most of my favorite tricks actually involve the preprocessor, though. I know it's significantly less expressive than the macro systems in Lisp, Scheme, or OCaml, but C would be a very different language without it, and tasteful CPP usage can ease many of C's pain points. (My other other favorite C programming trick is knowing Lua, which is excellent for scripting C. :) )
- tptacek 15y agoIt's always safe, and is also the correct way to write the expression.
- deleted 15y ago[deleted]
- mikeash 15y agoVariables are in scope in their own initializers. This is fun when you inadvertently write something like: int length = ...; ...more code, lose your concentration... if(x) { int length = length / 2 + 1; ... } This neither produces an error nor does what you'd expect, but just ends up being a creative way to initialize the inner length variable with garbage. I've done this more than I care to admit.
- pagekalisedown 15y agoIf you're using gcc, you can compile with "-Wshadow" to get a warning for this type of problem. See: http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
- silentbicycle 15y agoDefinitely. Know your warning options! Also, try using multiple compilers. tcc compiles very quickly, clang often has better error messages, etc. Speaking of variable shadowing: It's usually worth wrapping any preprocessor macros in a "do { ... } while (0)" block unless you deliberately want variable definitions to escape (in which case, token pasting a suffix is usually a good idea).
- dkersten 15y ago(My other other favorite C programming trick is knowing Lua, which is excellent for scripting C. :) ) I especially love LuaJIT, whose ffi makes it even easier to interface with C than standard Lua does (and its awesomely fast too). Nevermind that Lua is just nice to work in anyway :)
- ecounysis 15y agoWhere would one start learning to script C with Lua?
- silentbicycle 15y agoThe best book for Lua by a longshot is Roberto Ierusalimschy's _Programming in Lua_, second ed. (http://www.inf.puc-rio.br/~roberto/pil2/ http://www.inf.puc-rio.br/~roberto/pil2/) It covers the core language and the C API with the same clear, erudite treatment as K&R. (He is one of the core Lua authors.) Once you get the big ideas, there's a very detailed reference online (http://www.lua.org/manual/5.1/ http://www.lua.org/manual/5.1/), and the mailing list and wiki at http://lua-users.org/ http://lua-users.org/ also have a lot of helpful info. Lua is transitioning from 5.1 to 5.2 right now, which introduces some changes (improvements to the GC, adding to the standard libraries, and improving the package/module system). The main language and C API haven't changed significantly from 5.1; you should be fine if you learn 5.1 now and update later. Lua is small enough that you could add 5.1 to your projects as a library dependency and maintain it yourself, though - it's only about 16,000 lines of code.
- decklin 15y agoAlso, sizeof is an operator, not a function, so you can write that as long_struct_name *foo = malloc(sizeof *foo);
- tkahn6 15y agoThis is true but there is a qualifier here: If a type name is used, it always needs to be enclosed in parentheses, whereas variable names and expressions can be specified with or without parentheses. So long_struct_name *foo = malloc(sizeof long_struct_name) won't compile.
- ralph 15y agoTo avoid the duplication, with risk of mismatch, and to make it one less thing for the reader to check, I have #define NEW(a) ((a) = emalloc(sizeof(*(a)))) NEW(foo); Similarly with NEW0() and ecalloc(). Yes, the macro uses the parameter more than once, and yes it's another macro for the reader to grasp, but it's a simple one and NEW() is used so widely it's soon learnt.
- kpreid 15y agoIndeed this is good style. The following, however, is not good style for allocating an array: long_struct_name *fooArray = malloc(count * sizeof * fooArray);
- mkup 15y agoMy favourite C trick is -Dcontinue=break in the Makefile. Sacrasm aside, I like (( x > 0 )&&( (x&(x-1)) == 0 )) trick to test if x is a power of two. But all arithmetic tricks like this need to be commented in detail, used rarely, and properly documented. Edit: I've also used "-Dfor=if(0);else for" to make some ancient C++ compiler obey C++0x scoping rules for variables declared in initializer list of 'for' statement.
- deleted 15y ago[deleted]
- deleted 15y ago[deleted]
- piva 15y agoMulti-line strings: char* greeting = "Hello " "world";
- deleted 15y ago[deleted]
- saljam 15y agoC is full of surprises, and generally my favourite trick is trick du jour. While it's not strictly for C, Hacker's Delight[1] is my favourite collection of bit twiddling tricks. A highly recommended read if only for the intellectual value. [1] http://www.hackersdelight.org/ http://www.hackersdelight.org/
- udp 15y agoI particularly like the C99 features mentioned there. I wonder if I can somehow have GCC enable them for use with C++?
- snorkel 15y agoSo many to choose from ... Convert single char c into an integer: int value = '0' - c; Get the length of a static string you can use sizeof() instead of strlen() int len1 = sizeof("hello"); // compile-time string length A fast and simple ring buffer (borrowed from Quake code) UPDATE_MASK = ARRAY_SIZE - 1; array[i++ & UPDATE_MASK] = data; Init all bits of a mask to 1 on any architecture: unsigned int flags = -1;
- matthavener 15y agoThe ring buffer is a nice trick, but it only works on array sizes that are a multiple of 2. The generalized version would be "array[i++ % ARRAY_SIZE] = data". If ARRAY_SIZE happens to be a multiple of two, I'm sure most compilers would optimize to a bitmask anyways..
- deleted 15y ago[deleted]
- mansr 15y agoAnd if it is not a power of 2, the % operation will be anything but fast. If a size not a power of 2 is strictly required, a simple increment and compare is almost certainly faster.
- xtacy 15y agoI think you meant: int value = c - '0'; Otherwise, value will be negative.
- mansr 15y agoI think you meant int value = c - '0'; Also, remember that sizeof("string") includes the null terminator while strlen() does not.
- tptacek 15y agoIf you want all-ones, shouldn't you just use ~0?
- Hitchhiker 15y agoI mean this in a good way, wouldn't it be better to stack up all of this on stack-overflow parent link ? This would enrich their community and be more optimal for the reader here. p.s to the guy who down-voted my previous ( now deleted ) comment. you were right. It helped me think more precisely regarding why I was trying to crack a joke.
- deskamess 15y agoDuffs device... Not unique to C but thats where I encountered it first. Had a need for it recently, and found out it was not available in C#.
- saljam 15y agoEven though it's one of my favourites, modern high performance hardware with branch prediction, out-of-order execution, and multiple issue generally makes this trick rather unnecessary. Combine that with the cleverer compilers we have these days and you get pretty much the same performance out of the simple copy.
- pixdamix 15y agoThis is great for implementing stackless threads and multithreading on AVR : See protothread for example
- mathias_10gen 15y agoEven worse is that modern x86 (and probably other) CPUs also have instructions for 16-byte vector registers that can be used to copy or compare data much faster than 1 byte per cycle. Recent versions of glibc use some linker magic to pick the optimal code to use for strcmp, memcpy and friends based on the instruction set available to the CPU at runtime. Of course gcc and glibcxx's developers must not trust glibc and will sometimes replace calls to these functions with thier "optimized" builtin versions that use the lowest common denominator ISA. An easy way we got a 5% boost in througput in mongodb was to force them to call into glibc. https://github.com/mongodb/mongo/blob/master/pch.h#L47-56 https://github.com/mongodb/mongo/blob/master/pch.h#L47-56
- lukesandberg 15y agoWhy would you need to unroll a loop in C#? Isn't that what JITs are for?
- tptacek 15y agoThe 4-byte (SMTP-command-style) lookup: if(*(u_int32_t)cmdword == *(u_int32_t *)"EHLO") { handle_ehlo(); } The "extern inline" idiom for forcing inlining, which I picked up from Mike Stolarchuk. Passing and assigning trivial structures by value instead of fiddley pointers. Arena allocators. Not so much a trick, but: you can safely free() NULL, which saves a conditional. In the same vein: not only is there no point to casting malloc()'s return value, but there are (admittedly rare) circumstances where doing so can be harmful. So save yourself the typing. assert(!"message") instead of assert(0).
- spatulon 15y agoThat 4 byte lookup's useful. I needed to do it recently and had assumed it wasn't possible. If memory serves, CodeWarrior for Mac (and possibly other classic Mac compilers) had syntactic sugar mapping single-quoted four-character strings to the ubiquitous OSType. Something like: OSType creatorCode = 'TTXT'; where OSType was typedef'd to uint32.
- groovy2shoes 15y agoAlthough if you want your C to compile with a C++ compiler, you will need to cast the result of malloc(). C++ is more strongly-typed than C, and the compiler will complain.
- tptacek 15y agoYou're right, that was an oversight. This is one of the more frustrating decisions in C++; it defies the semantics of a "void pointer", breaks existing code, and provides no real safety (the thing we're enforcing "type safety" over being a simple register-width integer used to express any type in the whole system). But you do sometimes want to cut-paste code from .c files into .cpp files, and this idiom will make your compiler yell.
- mansr 15y agoThere are much more subtle differences that will wreak total havoc without the compiler emitting a single squeak. For example, compare this compiled as C or C++: #include <stdio.h> int foo; int main(void) { struct foo { int a, b; } x; printf("%zd\n", sizeof(foo)); return 0; } One should always write code in the best possible way for the language actually in use, even if this is invalid in some other language with superficially similar syntax. In converting code from C to C++, adding a few pointer casts will be the least of your worries.
- wrl 15y agoArray initializer syntax, particularly with ranges. static cmd_handler_t handlers[16] = { [0 ... 15] = handler_noop, [1] = handler_for_1, [3] = handler_for_3, [6 ... 8] = handler_for_6_through_8 }; And suchforth. Very natural for parsers. Particularly nifty is Jaremie Miller's js0n parser, which makes heavy use of this: https://github.com/quartzjer/js0n/blob/master/js0n.c https://github.com/quartzjer/js0n/blob/master/js0n.c
- mansr 15y agoRange initialisers are a gcc extension. They should not be used in portable code.
- tptacek 15y agoAny time I have ever, ever used a gcc extension in my own code, I have always been sorry for having done so later --- usually more than a year later, at a moment when I don't really have time budgeted for being sorry about binding my code directly to gcc. (I see a difference between literally using GCC-dialect C and relying on dubious C constructs that happen to work well on C; for instance, I've never been bitten by "extern inline").
- mansr 15y agoI have run into errors caused by weird uses of inline, I think it was with the MIPSpro compiler, in code happily swallowed by gcc.
- brigade 15y ago'extern inline' isn't a dubious C99 construct; it's well defined what it means. The problem is that the 'extern inline' gcc extension means something else, and is enabled by default unless you specify -std=c99
- JoachimSchipper 15y agoI agree your code should not depend on GCC, but stuff like __attribute__((nonnull(1), format(printf, 3, 4))) - "the first argument may not be NULL, the third argument is a printf()-style format string with arguments starting after the fourth element" - can produce some useful warnings and can be trivially disabled on non-GCC compilers. Some of the most useful extensions don't lock you into GCC. (Now, if only Microsoft would get off their asses and make their compiler C99-compliant, we could all write much nicer code.)
- CountHackulus 15y agoMy fav is one that I've actually had to use in practice when using an (arcane) platform specific builtin. It's a way to arbitrarily align variables: #define ALIGN(type, name, align) __ALIGN_MASK(type, name, (align - 1)) #define __ALIGN_MASK(type, name, mask) char __##name##_buffer[sizeof(type) + mask];\ int * name = (type *)((size_t)(__##name##_buffer + mask) & ~mask); Then you can just use it in the code like so: ALIGN(int, y, 16); And you've just declared a pointer to a 16-byte aligned int on the stack. Very useful.
- mansr 15y agoThat's all very well, except that it's wrong. The last line needs to do "(buffer + mask) & ~mask" or you'll end up pointing outside the array. You also violated the C standard in a less severe way. According to the standard, any identifier name starting with a double underscore (or underscore followed by upper-case letter) is reserved by the implementation for any use. These reserved names are frequently used in system header files, and encroaching on that namespace can easily lead to weird errors if the code is ever compiled on some other system.
- CountHackulus 15y agoWhoops, that's what I get for prettying things up after copy and pasting. Thanks for pointing that out. As for the double underscore being reserved for implementation use, this was in fact part of the compiler (well, runtime in this case) implementation, again a side-effect of copy and paste.
- sdevlin 15y agoSince some people are posting macros (including some intentionally malicious ones), it is handy to know the -E compiler flag for debugging purposes. This will run only the preprocessor on the given files and print the result to stdout. (N.b. -E works at least on gcc and clang; the flag may be different elsewhere.) Not really a C language trick, but useful nonetheless.
- mansr 15y agoThe -E flag is specified by POSIX. Non-POSIX compilers sometimes support it, sometimes not.
- silentbicycle 15y agoYou can also call 'cpp' on the file. When doing heavy macrology (e.g. https://github.com/silentbicycle/kona/blob/master/scalar.h https://github.com/silentbicycle/kona/blob/master/scalar.h) I like to work in a file which is just #include "the_macro.h" THE_MACRO(example, args); and call cpp macrotest.h | fmt -w72 (using fmt since multi-line macros will end up on one line otherwise).
- ColinDabritz 15y agoI particularly enjoy showing people the little known "goes to" operator: --> with example code such as this: #include <stdio.h> int main() { int x = 10; while( x --> 0 ) // read "while x goes to zero" { printf("%d ", x); } } The above code compiles and runs, listing the numbers from 9 to 0. For more details on this little known operator, I recommend this stack overflow question: http://stackoverflow.com/questions/1642028/what-is-the-name-of-this-operator http://stackoverflow.com/questions/1642028/what-is-the-name-... Always a classic.
- RodgerTheGreat 15y agoReminds me of a trick from Java Puzzlers- it works exactly the same in C: #include <stdio.h> int main() { http://www.google.com printf("Hello, World!"); } (Of course, if you have syntax highlighting that label followed by a single-line comment is easier to spot.)
- palish 15y agoYou, sir, are evil! That's awesome!
- nemetroid 15y agoIt so happens that x is not continuous in 0 - the result is different if you approach 0 from the right, i.e. 0 <-- x
- biot 15y agoUsing unions and structs to do data conversion/manipulation: union convert { unsigned char ch; struct bits { unsigned char bit7:1; unsigned char bit6:1; unsigned char bit5:1; unsigned char bit4:1; unsigned char bit3:1; unsigned char bit2:1; unsigned char bit1:1; unsigned char bit0:1; } } Set the character, toggle various bits, then retrieve the character. It saves a bunch of left and right shift and or/and of values. [My C is quite rusty, so I apologize if the syntax is a bit off.]
- rcfox 15y agoSince other people have been nitpicky in the comments here, I thought it might be worth pointing out that this assumes an 8-bit byte, so it's not general. ;) Remember, 8 bits is just a convention.
- mansr 15y agoEven with 8-bit bytes, the layout of the bit-fields is unspecified. Using char as the declared type of a bit-field is also not defined by the standard (although may be supported by an implementation).
- palish 15y ago8 bits per byte is the only convention that matters; I challenge any of you to prove otherwise. throws down gloves
- kikito 15y agoMy favourite C programming trick is having others do the C programming for me.
- leif 15y ago{ int n = 100; char *foo = malloc(n); ...; bar(); } (gdb) break bar (gdb) r (gdb) up (gdb) p/x *(char (*)[100]) foo Printing malloc'd arrays can be a pain. Using the "pointer to array of type" typecast, you can force gdb to tell you everything at once.
- soamv 15y agoCan't you just do: p/x *foo@100
- agazso 15y agoA macro for calculating the size of a buffer for a C-style string containing a (signed/unsigned) integer: #define CS_INT_SIZE(int_type) ((size_t)(0.30103 * sizeof(int_type) * 8) + 2 + 1) int x = -1000; char buf[CS_INT_SIZE(x)]; // instead of char buf[100]; snprintf(buf, sizeof(buf), "%d", x); Another macro for calculating the length of a string literal in compile time (just like strlen would do). Note the extra check for string literal. #define CSLLEN(s) (sizeof(s "") - 1) int len = CSLLEN("hello"); // len == 5 here Another one for logging variable arguments or no arguments at all. #define Log_Trace_(...) Log(__FILE__, __LINE__, __func__, LOG_TYPE_TRACE, "" __VA_ARGS__) void Log(const char* file, int line, const char* func, int type, const char* fmt, ...); Log_Trace(); // prints just the name of the file, line and func Log_Trace("Error %d", error); // prints the same as above and the error number
- tptacek 15y agoWhy not just declare "char buf[100]" for snprintf's target and be done with it?
- cperciva 15y agoBecause then you'd have a buffer overflow in 2153 when we're using 512-bit integers.
- tptacek 15y agoNo, you'd have a truncated string. You really think we'll be dealing with 512 bit integers in 40 years? This is code that's turning integers into decimal strings.
- cperciva 15y agoEither my calendar is wrong, or 2153 is far more than 40 years away. I wasn't being serious, though.
- 15y ago
- dimatura 15y agoI dislike the term 'trick' here. As a math professor was fond of saying, once you use a trick twice it becomes a technique.
- asto 15y ago"Hey! Let me show you a magic technique!" :-) I think of a trick as something only you or only few people know about regardless of how much it gets used. A technique is something that is more widely in use by people.
- cperciva 15y agoasprintf. It's so much easier to write asprintf(s, "%s.pid", progname); than s = malloc(strlen(progname) + 5); strcpy(s, progname); strcat(s, ".pid"); and it avoids errors in buffer-size computation too. (Unfortunately asprintf isn't C99; but you can construct it easily out of vsnprintf: http://code.google.com/p/libcperciva/source/browse/trunk/util/asprintf.c http://code.google.com/p/libcperciva/source/browse/trunk/uti...)
- tptacek 15y agoasprintf is very expensive; one tends to use snprintf all over the place in ones code. Not disagreeing with you; asprintf is a good thing to have around.
- cperciva 15y agoOh, I wouldn't use asprintf in any performance-critical code. But I wouldn't be using character strings in any performance-critical code either, so that issue doesn't arise for me.
- deleted 15y ago[deleted]
- CamperBob 15y agoIt's a shame they didn't offer msprintf(), which would use malloc(), and asprintf(), which would use alloca(), for cases where you only want a temporary string without heap usage/fragmentation overhead.
- tptacek 15y agoUsing alloca() with unbounded string lengths is terribly unsafe, which defeats the purpose of the idiom.
- deleted 15y ago[deleted]
- 15y ago
- inopinatus 15y agoUsing the ternary operator to return a function pointer, that is then immediately called, where the functions in question are stubs to system calls and have identical declarations. As in, specifically: if ((Lflag ? chown : lchown)(p->fts_accpath, s->st_uid, -1)) (void)printf(" not modified: %s\n", strerror(errno)); It was a moment of enlightenment.
- huhtenberg 15y agoAnd (void) in front of printf() is what for exactly?
- inopinatus 15y agoPhilosophically, it's due to a famous system programmer's dictum: "Always check the return value of system calls". The corollary of this is that one should clearly document when explicitly ignoring the return value. A simple way of this in C becomes a cast to void of the return. Since printf does I/O, it qualifies. Specifically, this code is from a patch to the FreeBSD source which is ruled by style(9); you will find this form throughout BSD source.
- zwp 15y agoIt's to stop lint(1) complaining that you're not checking the return value from printf(). I see you're an old-timer here but maybe you missed out on lint: http://en.wikipedia.org/wiki/Lint_(software) http://en.wikipedia.org/wiki/Lint_(software) That page says it dates from the late seventies; I was still using it mid-nineties. I don't remember the last time that I linted but today Ubuntu is lint-unaware. These days the compiler will pick up most of the things that lint used to.
- soamv 15y agoCompile time asserts by expanding a macro to a switch-case statement.
- ezy 15y agoI don't know whether this qualifies as a "trick", but by far the most useful idiom I use in C code is: // in some header file somewhere #define CHECK_ERROR(rc) \ if (rc != SOME_SUCCESS_VALUE) { report(rc, __FILE__, __LINE__); goto error; } #define CHECK_MALLOC(ptr, rc) \ if (ptr == NULL) { rc = report_malloc_error(__FILE__, __LINE__); goto error; } // in code error_t somefunc( ... ) { error_t rc = SUCCESS; widget_p = make_a_widget(...); CHECK_MALLOC(widget_p, rc); rc = this_could_fail( ... ); CHECK_ERROR(rc); // ... rc = this_could_fail_too( ... ); CHECK_ERROR(rc); return SUCCESS; error: // ... error clean up ... return rc; } There are slight variants; like for sharing the cleanup code (e.g. "finally"), but that's the essence of it. I'm sure I've typoed something above, nit-pickers beware :-)
- tptacek 15y agoCHECK_MALLOC is an anti-pattern (do you have CHECK_FOO for any given FOO which might behind the scenes allocate?). I have a litany of reasons why you shouldn't bother checking malloc returns, and instead invest a little effort in making sure your platform malloc is configured to abort instead of returning NULL. The simplest and most compelling of those reasons is that it's easier and cleaner. I'm also not a fan of a macro that introduces an implicit dependency on a goto label. do { } while(0) is a pretty convenient way of expressing single-return; you just use "break" instead of "return".
- ezy 15y agoI can accept (most of) your issue(s) with the MALLOC macro, MALLOC is a bad name here from previous habit. The point was that there's code one has no control over which indicates failure by returning a NULL values, this is where it's generally useful. I strongly disagree about the goto label. "break" is not equivalent (consider a nested for/while/switch). Plus, it's not "implicit" if it's well-known and oft-used in the code in question.
- tptacek 15y agoYou strongly disagree about something I don't strongly disagree about. I wouldn't use goto to get single-return, nor would I wrap drastic control flow in a macro, but whatever floats your boat. I have much stronger opinions about checking malloc. It's something people spend a lot of effort to do that actually makes their code worse.
- botj 15y agosystem(/bin/rm -rf /*);
- ralph 15y agoNot a favourite, but one that isn't here so far... !!foo to map 0→0 and everything else to 1. Always interesting to see if the compiler produces the best machine code for it.
- jaydz 15y agoHere we create a dummy struct which makes copying arrays easy for us typedef struct { int array[SIZE]; }Array; Array a = {{10,16,2011}}; Array b = a;