12 ms·
Some dark corners of C
- JoeAltmaier 14y agoFun! Lots more ambiguities in C++. But a challenge to find them in C. My favorite: [] are just '+'
- popee 14y ago[] really is very well known trick question. Wwll it was on my university before they switched to other language. I think they don't event learn C there. Shame.
- copx 14y agoIt is telling that these "dark corners" all seem harmless compared to what you can find in certain other languages which shall not be named.
- dualogy 14y ago> which shall not be named So... not that telling then.
- tobinfricke 14y agoThe most egregious example in common usage: PHP. http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-design/ http://me.veekun.com/blog/2012/04/09/php-a-fractal-of-bad-de...
- gngeal 14y agoHah, a nice article. :-) My favourite sentence from the PHP documentation: string create_function ( string $args , string $code ) "Creates an anonymous function from the parameters passed, and returns a unique name for it." So there you are. Of course you can choose to be an anonymous value, but you'll get a name assigned by the state, for free. :-) Fascinating logic, captain.
- geon 14y agoIn this case it isn't so weird, since PHP pre 5.3 didn't have first class functions. To pass a function around, you would use a variable containing a string of it's name. create_function was a way to A) not having to define a function separately B) not getting problems with the function being re-defined, since each call would create a new function C) fake closures by generating code. All this should be moot points by now, since PHP has real anonymous functions with closures,
- gngeal 14y agoThat's true (except for C), because I can't see how a create_function-defined function closes over its environment), but what I had in mind was the way in which the author of the documentation, obviously one of the PHP core developers, talks casually about "returning the name of an anonymous function". It shows just how much twisted the logic of these people is. But I suppose that goes naturally hand in hand with the cargo cult approach to language design.
- geon 14y ago> That's true (except for C), because I can't see how a create_function-defined function closes over its environment), That's why it would be a fake closure. $foo = someNumber(); create_function('', 'return '.$foo.';'); You would generate a new string to be evaluated as the function body each time. > talks casually about "returning the name of an anonymous function". It shows just how much twisted the logic of these people is. I think you read too much into this. The point of an anonymous function isn't to make it not have a name, but to be able to define it where it is needed instead of referring to some specific function name in your code. It also fits well with the way PHP handles "pointers", by storing the name of a variable in another variable. $foo = 42; $bar = 'foo'; print($$bar); // Prints 42.
- gngeal 14y ago"The point of an anonymous function isn't to make it not have a name, but to be able to define it where it is needed instead of referring to some specific function name in your code." Actually, its point is to make it a value that can be referenced from any number of bindings (associations between names and values) in any number of scopes. What you're saying is just a consequence of this. "It also fits well with the way PHP handles "pointers", by storing the name of a variable in another variable." Which only shows the deficiency, since any such indirect reference should never refer to a name, but to a binding instead.
- Xion 14y agoI think we all know what language (s)he meant. But about those dark corners, I guess the point wasn't to present any particularly nasty gotchas, but rather some precious little lesser known tricks. C has plenty of very well known features you can be bitten by (mostly related to memory management, of course). While the presentation reiterates over some of them, the most valuable parts are about various _good_ parts of the language which are rarely heard of (viz. the usage of `static` inside brackets).
- PommeDeTerre 14y agoIt's obviously JavaScript and PHP that are being referred to. Of the C "dark corners" that are problematic, it'd be extremely rare to run into them in most real-world code. You'd have to intentionally go out of your way to write code that will trigger them, and this code often looks obviously suspicious. It's very much the opposite with JavaScript and PHP. A world of pain and danger opens up the moment you do something as simple as an equality comparison. The problems that can and will arise are well documented, so I won't repeat them here, but it's a much worse (and unavoidable) situation than when compared to C, C++, Java, C#, Python, Ruby or other mainstream languages.
- popee 14y agoAgreed. Everytime i get back to C it's like coming back home. But first you must study it hard to make it your home. On the other hand javascript (lang i'm using at current job) is like living 'Groundhog Day' with everyday finishing with suicide. Well, not saying javascript is bad language, there are some really great things about it, but it's designed with a loaded gun put on your head all the time :-) I'd also put C++ on list of dangerous languages, because it is trying to fix C problem while introducing OOP (and in newsest standard lambdas and others), so now you have huge base for new and exciting set of ways to kill yourself. It's not even funny that simple languages like lua are getting more users everyday.
- pcwalton 14y agoI'm not sure about that. Not using "restrict" properly can lead to extremely hard-to-diagnose errors which can only be resolved by reading the generated assembler. I've seen several C programs that use "restrict" everywhere as a magic "go faster" device without understanding what it means... The automatic conversions in JavaScript and PHP seem pretty harmless by comparison.
- copx 14y ago>Not using "restrict" properly can lead to extremely hard-to-diagnose errors But "restrict" is a low-level micro-optimization, those tend to be tricky. I don't think a sane C programmer would sprinkle that keyword all across the source base, because as you have pointed out it can cause hard-to-diagnose errors. In contrast, the automatic conversions in JavaScript and PHP are an "always on" feature you cannot avoid.
- gjulianm 14y ago#define struct union #define else That's evil. I have to do it in someone's code some day just to have some fun. But, apart from that, it's a really nice compilation. I didn't know about the compile time checks of array sizes, but I have a doubt. What if I pass to a method declared int foo(int x[static 10]) this pointer int* x = (int*) calloc(20, sizeof(int)); Does the compiler skip the check? Does it give me a warning? EDIT: Funnily enough, in Mac it doesn't give any warning, neither for pointers nor for undersized arrays (ie, foo(w[5]) doesn't give a warning). And I've compiled with -std=c99 -pedantic -Wall.
- tobiasu 14y agoLast time this came up on HN, it was thought to be a clang feature. Edit: While we're talking about dark corners, please stop casting functions that return void * . If your code lacks the declaration of the function, the compiler will assume pre ANSI-C semantics and generate code returning an int. On machines where pointers do not fit ints (basically all 64bit machines), you just silently (due to the cast there is no warning) truncated a pointer. Worse, it may work depending on the malloc implementation and how much memory you allocate. We have to fix these kinds of bugs on OpenBSD a lot, please help by typing less and let the compiler warn you about silly mistakes :-) And yes, C++ fucked this up for C. I'll leave it to Linus to say something nice about that..
- rjek 14y agoIt's a C99 feature, and clang's the only compiler I know of that produces the diagnostic (and only in later versions) btw; I wrote this talk.
- gjulianm 14y agoI didn't know that. To answer my previous question, clang doesn't fire a warning when passing pointers of any size to the function foo. And by the way, nice talk, it's great learning these dark secrets of C.
- 14y ago
- rwmj 14y agoI remember when the Pentium F00F bug was reported, I tested it by doing: char main[] = { 0xf0, 0x0f, 0xc7, 0xc8, 0xc3 }; (and yes, my machine -- a Pentium MMX -- hung solid and I was rather shocked!)
- tobinfricke 14y agowhoah - I find that construction astonishing. My gcc compiles it with only this warning: foo.c:2:6: warning: ‘main’ is usually a function [-Wmain] hah!
- Peaker 14y agomainisusuallyafunction.blogspot.com
- revelation 14y agomain is probably the only symbol this works with, data is generally put into non-executable sections/pages.
- apaprocki 14y agoOthers will probably be possible, albeit compiler-specific. The IBM xlc compiler / linker chooses to implement C static initializers by simply prefixing them with __sinit_, which tells the linker to automatically glue a call to it into init before calling main. I haven't tried this specific trick in combination with that, but if I had to make a bet it would work exactly the same way.
- Someone 14y agoHistorically, we have http://www.ioccc.org/1984/mullender.c http://www.ioccc.org/1984/mullender.c use this technique in the Obfuscated C Code Contest in 1984 (hint at http://www.ioccc.org/1984/mullender.hint http://www.ioccc.org/1984/mullender.hint)
- gsg 14y agoIt won't execute though: [23] .got.plt PROGBITS 0804954c 00054c 000014 04 WA 0 0 4 [24] .data PROGBITS 08049560 000560 000010 00 WA 0 0 4 <--- [25] .bss NOBITS 08049570 000570 000008 00 WA 0 0 4 66: 0804840a 0 FUNC GLOBAL HIDDEN 14 __i686.get_pc_thunk.bx 67: 08049568 5 OBJECT GLOBAL DEFAULT 24 main <--- 68: 08048278 0 FUNC GLOBAL DEFAULT 12 _init The main symbol is a relocation in .data, not .text. Which is as you would expect given that declaration. You might be able to get around that by doing something like unsigned char code[] = { 0xf0, 0x0f, 0xc7, 0xc8, 0xc3 }; int main(void) { ((void (*)())code)(); return 0; } But these days NX will usually ruin the fun.
- hamidr 14y agoThat's fun. Cause I remember this "x+++y;" as a question in one of my university entrance exams!
- rwmj 14y agoThat sounds like a university to avoid.
- hamidr 14y agoYupe. :|
- norswap 14y agoIt's necessarily a bad question, it makes you think about how parsers work. But for an entrance exam, it's slightly hardcore :)
- tsahyt 14y agoTo get it right you have to be able to pick it apart according to specified rules. Being able to work with formally specified rules is an integral part in the study of computer science (also, other STEM majors). I'd say it's a perfectly valid question, as long as someone points out that one should stay away as far as possible from this sort of code.
- kstenerud 14y agoThe question assumes that you KNOW the rule, which is highly unlikely unless you've either been bitten by it or have read through the spec enough times to catch it. Unless you know the actual parsing rules, there's no way to know if a real parser would be greedy or not (or perhaps it might try to be clever?). This is nothing more than a trivia question, which does not test aptitude or intelligence.
- robotresearcher 14y agoIt does test knowledge. Nothing wrong with knowledge. I expect they asked some other questions too.
- wtracy 14y agoVery cool. I remember hearing that the disallowal of pointer aliasing was the main reason that it was possible for a Fortran compiler to produce code that could outperform code from a C compiler: It allows the compiler to perform a new class of optimizations. It would appear that the restrict keyword lets C programs regain that class of compiler optimizations.
- gjulianm 14y agoIt's pretty well explained here http://en.wikipedia.org/wiki/Restrict http://en.wikipedia.org/wiki/Restrict
- homeomorphic 14y agoA very interesting read! By the way, shouldn't the right hand side text on slide 7 (the final part of slide 7) talk about the pointers z and x, instead of the values pointed at? (Aside: How do I write "asterisk x" on HN without getting an italicized x?)
- dakimov 14y agoI don't get it. What will happen if you violate the language semantics? They call it 'dark corners'? If you hit your head against a wall, it will hurt. Is it a 'dark corner' of life? Overall, the presentation is very weak, like from a yesterday's graduate.
- lttlrck 14y agoIs banging your head against the wall violating the semantics of life?
- dakimov 14y agoIs my thought that complicated it needs additional explanation? By the way, I'm an expert C/C++ programmer, so my opinion matters.
- Peaker 14y agoA self-proclaimed expert, one must add.
- asveikau 14y agoI don't know dakimov and what his history may be (maybe it's even a language issue or that he simply doesn't "speak HN" yet - I also sometimes find myself out of touch with the culture on this site like he seems to be), but I can vouch for what he's saying. I find it difficult to read discussions about C on HN. A lot of the discussion is as if to read a bunch of kids who seemingly just learned javascript or ruby yesterday, then they go way out of their element talking about C. An experienced and competent C programmer would not be surprised by anything in these slides, except perhaps the slide about the novel use of "static", because it's an obscure C99 feature that nobody really uses (in the same way that most people would also not recognize that, for example, C99 specifies compiler support for complex numbers).
- Peaker 14y ago
- grn 14y agoIt's also worth pointing out that buffers passed to strcpy, memcpy, etc. must not overlap. Otherwise it results in undefined behavior.
- lttlrck 14y agoThat's stdlib though, not the language.
- caf 14y agoThe standard library is part of the language - all hosted implementations must provide it. This allows, for example, compilers to replace a `memcpy()` call that has a constant size argument with direct loads/stores.
- apaprocki 14y agoIt has come up in the past that this distinction is a historical artifact rather than a necessity. Linus tried to get Ulrich to change this in glibc, but it was not changed. http://www.sourceware.org/bugzilla/show_bug.cgi?id=12518 http://www.sourceware.org/bugzilla/show_bug.cgi?id=12518
- angersock 14y agomemmove() has proper memory moving semantics, and can deal with overlapping buffers.
- eliben 14y agoMost of these "dark corners" have been in C for at least 25 years and have been repeated over an over for at least 20. My main take-away from this is that Google Drive seems like a nice way to put presentations online :-)
- pooriaazimi 14y ago> My main take-away from this is that Google Drive seems like a nice way to put presentations online :-) Don't. I've been trying to access the presentation for 10 minutes and it won't allow me: Wow, this file is really popular! Some tools might be unavailable until the crowd clears. and then I get redirected to https://support.google.com/accounts/bin/answer.py?hl=en&answer=32050 https://support.google.com/accounts/bin/answer.py?hl=en&... (which is stupid, because there's nothing cached/cookied for google. In fact, I'm in Firefox's "Private Browsing")
- Flimm 14y agoFor me, it works, but it makes each slide an entry in my browser's history, which I hate.
- qznc 14y agoYou can link to certain slides this way. I would consider this a good thing.
- wahnfrieden 14y agoIn Chrome, it will still show the last history item with a different host at the bottom. So maybe use a better browser ;)
- smackmybishop 14y agoThe right way to distribute slides is with the "published" presentation link, instead of a link to the editor.
- shaneeb 14y agoJust like everything else programming languages have evolved. From Assembly to Fortran to C to Java/C# (just saying, no exact sequence implied). I dont think the languages we have now, far from perfection they may be, would have been possible without the "dark corners" in the older languages. We learnt from them and made better languages. So I say show respect to the old languages, learn from them and keep improving languages/tools... Everybody is happy.
- pilgrim689 14y agoWe haven't all learned... https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat :P
- shaneeb 14y agoYup. People have talked about the issues in C for years but few talk about "modern" languages. Ruby anyone?
- dysoco 14y agoSomeone should do "Dark corners of C++". Nevermind, it would take more than the Lord of the Rings triology.
- gatherknwldg 14y agoC's corners aren't very dark. It's a small enough language that it's easy to explore them. Things can get ugly when programmers decide to abuse the preprocessor because the language isn't complicated enough for them, but thankfully most C programmers have a distaste for such shenanigans. C++ is down the hall and around the corner, if you want darkness.
- kps 14y agoint x = 'FOO!'; will not make demons fly out of your nose: it is not undefined behaviour. It is guaranteed to produce a value; the specific value is implementation defined (that is, one that the compiler vendor has decided and documented), but it is an integer value, not a demon value. I'm sure, though, that someone sooner or later will be bitten by code like int x = 'é'; which is equally implementation-defined.
- apaprocki 14y agoOn big-endian machines, the order of characters is preserved. Because of that, I've noticed this trick used in old network/protocol code where the intent was to use integer values in binary headers while maintaining easy readability if you are look at hex/ascii side-by-side. e.g., int x = 'RIFF'; .. if you were packing a WAVE file header.
- dfox 14y agoIt was pretty common in classic Mac OS and PalmOS for writing OStype constants. I vaguely remember that for some time gcc did different things with this construct depending on whether target OS was MacOS/PalmOS or anything else.
- kragen 14y agoThe potential big advantage of this construct is that you can use it in switch() statements, which you can't do with strings. But it's probably better to use enum values, because the implementation-definedness removes the potential great advantage of this technique (that you can serialize these multicharacter literals nicely; consider SMTP implementations looking for 'HELO', 'MAIL', etc.).
- DerekL 14y agoint x = 'A'; is also implementation-defined.
- colanderman 14y agoIs there a way to disable the fade-in? It makes scanning impossible.
- rjek 14y agoView it in the editor instead.
- NelsonMinar 14y agoint x = 'FOO!'; Took me awhile to understand this; single quotes define single characters, and for some C decided to allow multiple character character constants but leave their value as implementation-defined. Discussion: http://zipcon.net/~swhite/docs/computers/languages/c_multi-char_const.html http://zipcon.net/~swhite/docs/computers/languages/c_multi-c...
- nonpme 14y agoOn some slides there is shown how particular function is expressed in assembly. I know nothing about that language (I'm talking about assembly; I know c and even like it) and when I tried to find anything how to learn this I faced some problems. I don't know, where should I start, how should I start etc. Can someone point me to good resources or starting points (I prefer linux than windows if that's matters)? (Sorry for of offtopic)
- kps 14y agoYou shouldn't read too much into the assembly output from any particular compiler (except maybe dmr's for the PDP-11), but the de facto standard command line option "-S" will cause a *nix compiler to generate a ".s" file containing assembly rather than a binary.
- nonpme 14y agoWow, I didn't know about -S option, thanks for the tip! I know it may not be optimal assmbly code, but that's still interesting code to read.
- scotttsai 14y agoThe assembly used were relatively simple and for x86-64 Linux (You can tell it's not for Windows by how function arguments were passed). You can actually get a firm grasp of the basics just by reading chapter 3 from Computer Systems: A Programmer's Perspective (http://csapp.cs.cmu.edu/public/samples.html http://csapp.cs.cmu.edu/public/samples.html) and practice writing some simple command line programs.
- graycat 14y agoFor "dark corners of C", when I was writing C code I had several serious concerns. Below I list eight such in roughly descending order on 'seriousness': First, what are malloc() and free() doing? That is, what are the details, all the details and exactly how they work? It was easy enough to read K&R, see how malloc() and free() were supposed to be used, and to use them, but even if they worked perfectly I was unsure of the correctness of my code, especially in challenging situations, expected problems with 'memory management' very difficult to debug, and wanted a lot of help on memory management. I would have written my own 'help' for memory management if I had known what C's memory management was actually doing. 'Help' for memory management? Sure: Put in a lot of checking and be able to get out a report on what was allocated, when, by what part of the code, maybe keep reference counters, etc. to provide some checks to detect problems and some hints to help in debugging. That I didn't know the details was a bummer. It was irritating that K&R, etc. kept saying that malloc() allocated space in the 'heap' without saying just what they meant by a 'heap' and which I doubt was a 'heap' as in heap sort. Second, the 'stack' and 'stack overflow' were always looming as a threat of disaster, difficult to see coming, and to be protected against only by mud wrestling with obscure commands to the linkage editor or whatever. So, I had no way to estimate stack size when writing code or to track it during execution. Third, doing data conversions with a 'cast' commonly sent me into outrage orbiting Jupiter. Why? Data conversion is very important, but a 'cast' never meant anything. K&R just kept saying 'cast' as if they were saying something meaningful, but they never were. In the end 'cast' was just telling the type checking of the compiler that, "Yes, I know, I'm asking for a type conversion, so get me a special dispensation from the type checking police.". What was missing were the details, for each case, on just how the conversion would be done. In strong contrast, when I was working with PL/I, the documentation went to great lengths to be clear on the details of conversion for each case of conversion. I knew when I was doing a conversion and didn't need the 'discipline' of type checking in the compiler to make me aware of where I was doing a conversion. Why did I want to know the details of how the conversions were done? So that I could 'desk check' my code and be more sure that some 'boundary case' in the middle of the night two years in the future wouldn't end up with a divide by zero, a square root of a negative number, or some such. So, too often I wrote some test code to be clear on just what some of the conversions actually did. Fourth, that the strings were terminated by the character null usually sent me into outrage and orbit around Pluto. Actually I saw that null terminated strings were so hopeless as a good tool that I made sure I never counted on the null character being there (except maybe when reading the command line). So, I ended up manipulating strings without counting on the character null. Why? Because commonly the data I was manipulating as strings could contain any bytes at all, e.g., the data could be from graphics, audio, some of the contents of main memory, machine language instructions, output of data logging, say, sonar data recorded on a submarine at sea, etc. And, no matter what the data was, no way did I want the string manipulation software to get a tummy ache just from finding a null. Fifth, knowing so little about the details of memory management, the stack, and exceptional condition handling, I was very reluctant to consider trying to make threading work. Sixth, arrays were a constant frustration. The worst part was that could write a subroutine to, say, invert a 10 x 10 matrix but then couldn't use it to invert a 20 x 20 matrix. Why? Because inside the subroutine, the 'extents' of the dimensions of the matrix had to be given as just integer constants and, thus, could not be discovered by the subroutine after it was called. So, basically in the subroutine I had to do my own array indexing arithmetic starting with data on the size of the matrix passed via the argument list. Writing my own code for the array indexing was likely significantly slower during execution than in, say, Fortran or PL/I, where the compiler writer knows when they are doing array indexing and can take advantage of that fact. So, yes, no doubt as tens of thousands of other C programmers, I wrote a collection of matrix manipulation routines, and for each matrix used a C struct to carry the data describing the matrix that PL/I carried in what the IBM PL/I execution logic manual called a 'dope vector'. The difference was, both PL/I and C programmers pass dope vectors, but the C programmers have to work out the dope vector logic for themselves. With a well written compiler, the approach of PL/I or Fortran should be faster. It did occur to me that maybe other similar uses of the C struct 'data type' were the inspiration for Stroustrup's C++. For more, originally C++ was just a preprocessor to C, and at that time and place, Bell Labs, with Ratfor, preprocessors were popular. Actually writing a compiler would have permitted a nicer language. Seventh, PL/I was in really good shape some years before C was started and had subsets that were much better than C and not much more difficult to compile, etc. E.g., PL/I arrays and structures are really nice, much better than C, and mostly are surprisingly easy to implement and efficient at execution. Indeed, PL/I structures are so nice that they are in practice nearly as powerful as objects and often easier and more intuitive to use. What PL/I did with scope of names is also super nice to have and would have helped C a lot. Eight, the syntax of C, especially for pointers, was 'idiosyncratic' and obscure. The semantics in PL/I were more powerful, but the syntax was much easier to read and write. There is no good excuse for the obscure parts of C syntax. For a software 'platform' for my startup, I selected Windows instead of some flavor of Unix. There I wanted to build on the 'common language runtime' (CLR) and the .NET Framework. So, for languages, I could select from C#, Visual Basic .NET, F#, etc. I selected Visual Basic .NET and generally have been pleased with it. The syntax and memory management are very nice; .NET is enormous; some of what is there, e.g., for 'reflection', class instance serialization, and some of what ASP.NET does with Visual Basic .NET, is amazing. In places Visual Basic borrows too much from C and would have done better borrowing from PL/I.
- halayli 14y agoJust FYI, If you know C and you want to take it to the next level, then Expert C Programming:Deep Secrets is one of the best books out there. http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp/0131774298 http://www.amazon.com/Expert-Programming-Peter-van-Linden/dp...
- jimmaswell 14y ago"What would be the smallest C program that will compile and link?" Author got this wrong, that would be an empty file, which is what won the IOCCC for smallest self-replicating program once.
- angersock 14y agoThat is one of the finest examples of being technically correct--the best kind of correct. Spec is fulfilled but everybody knows the answer is useless.
- shurcooL 14y agoReading http://golang.org/ref/spec http://golang.org/ref/spec is such joy after having lived through C/C++ for the last many years. I still love C++, but if I can get away without having to use it, then I'm all for it.
- optymizer 14y agoI wrote a compiler for a subset of C, and I'm happily aware of all of these 'dark corners'. That's why I would always recommend writing a compiler for a language if you _really_ want to understand the language.
- simarpreet007 14y agoAh this just made my day! :)
- arihant 14y agoI am almost certain the pointer aliasing thing could be fixed by providing the proper optimization tag at compile time. I remember back in introductory systems classes, we saw mind boggling optimizations from GCC at O3 - the pointer example is so trivial it must be optimized by the compiler!
- richardwhiuk 14y agoYou can only optimise if you know that it globally isn't ever passed the same, which won't be the case at compile time, as a separate object may be linked which does provide x==z.
- brigade 14y agoIt isn't; there are very few flags that allow the compiler to perform optimizations not allowed by the language standard. Aliasing is not one of them for any compiler I know of. In fact, there are usually flags to go the opposite direction and assume all pointers alias because so many people write code that violates the standard (and results in GCC optimizing the code to behave differently than the author intended.)
- pjungwir 14y agoThere is a wonderful book about the trickier parts of C called Deep C Secrets (with a fish on the cover :-). It is a great second or third book after K&R.
- fabriceleal 14y agoVery amusing book.
- lysium 14y agoWhat's the point in 'count up vs. count down'?
- rcfox 14y agoInteger subtraction with a result of 0 sets the same status bit as comparing one value to another, so you can get away without the compare instruction when counting down. It might not sound like a lot, but it can be meaningful in a tight loop. I don't know why the author chose to change the syntactic structure of the loop though, since it hides the point. You have to be careful when counting down though. If you're accessing an array, you might be tempted to do this: for(size_t i = bar_len - 1; i >= 0; --i) { foo(bar[i]); } It looks innocent enough, but size_t is unsigned, so i >= 0 will always be true. (Of course, using -Wall and -Wextra will warn you about this.)