A C programmer's rant -- or -- How to get the current year in 8 easy steps

My guess would be that it will work reliably on any platform regardless of compiler–unless you specifically built a compiler that created code which worked in a non-obvious fashion.

The simplest way to compile out to processor instructions is to place local variables on the stack in order of declaration. Officially, C doesn’t require that you have a stack, that you create variables in the order they are declared, nor that you place those variables anywhere near one another in memory. However, I’d still bet that there isn’t a practical C compiler out there which doesn’t do this. So that means that current_year[4] is going to be the top byte of totlen. If we’re on a little endian machine that will be 0x1A (26), which is a control character. If it’s a big endian machine, it will be a zero byte. In either case, since it’s not a numeric character, atoi will consider it’s job done.

I suppose that a non-ASCII character code on a little endian machine has some chance of resulting in an incorrect value while using a regular compiler, but I’d still generally bet that you’re safe.

Not to say that I’d let the coder who wrote that code know any of this. I’d tell him that it was broken, unsafe code and one day it was going to cause an airplane to crash and kill a few hundred people and to fix it NOW.

Sort of an addendum to this post, but I’ll just note that I can’t count the number of times that I’ve seen people create a typedef for uint32 types, sometimes calling it “uint32”, “__uint_32”, “__uint32_t”, sometimes “DWORD”, other times “package_uint32_t”, etc.

stdint.h is part of the C99 standard and since all it is is a header file with some real clear-cut definitions, it’s likely to be supported pretty much everywhere. At any rate, reproducing it on platforms which don’t have a stdint.h file also isn’t hard, so you might as well just follow their standard.

I once worked with a guy who was tasked with implementing an ordinary linked-list in C. To save space at the expense of complexity (which was completely unneeded) he decided to be all uber-leet and make it a XOR-list. The real tragedy is that there were other places where a lot more space could be saved in a much easier fashion; the records that these list nodes were pointing to, for example, had a lot of number values that came directly from input strings which could have been converted to integers.

Anyway, he spent (I am not making this up) two and a half months trying to get his XOR-list functions to work properly. Eventually, I decided I just had to look.

Turned out he implemented the algorithm exactly right – he just hadn’t figured out the difference between bitwise and logical XOR. :smack: You’d think he would have asked the question after an hour of fucking around instead of ten weeks.

I wish I still had the source laying around, it was full of hilarious profane comments expressing his frustration.

Never did get those record structs memory-optimized. Not like it mattered. This shit was running on bigass servers with gigs of RAM and dealt with a whopping 3000 records at a time. Way to save resources, pal.
ETA: I forgot to mention, since C doesn’t actually have a logical XOR operator, he went through several permutations, most of them random guesses to find the right combination of other logic operators to invent it.

Exactly. I allude to this in the OP, but just briefly.

What happens is that ‘atoi’ will call another function named ‘strtol’ — or at least it’s defined to be equivalent to calling ‘strtol’ — with a requested base of 10. ‘Strtol’ stops scanning when it reads the first character that isn’t a valid digit in the requested base.

So a null byte would terminate the string properly, but so would any byte that’s not a decimal digit, in this case. Of course our author doesn’t bother to take either kind of preparation. This means that ‘strtol’ is going to run past the end of the array, reading through God knows what. Possibly the other local variables defined in the function, but whether that’s true will depend on the compiler and the choices it makes about optimization and stack frame layout.

Since this old code is currently running in operations (as we speak), and the program is not seg-faulting every few minutes, or returning ridiculous year values, I assume that the scanner is hitting good, terminal byte value after the end of the array. In other words, the previous guys got lucky.

So, nobody noticed what kind of file I was getting these compile errors from?

Heh. The makefile was somehow picking up all files with an extension beginning with ‘h’?

Oh Lord, I wish it was something that simple. No, they had an HTML file with (and I swear I’m not making this up) C code embedded in it inside of special tags. This HTML file was run through a proprietary, (Windows-only) parser, which output C code. That C code was run through a Perl script that attempted to optimize the C code. The output of that script was another C file. That C file was run through the compiler to produce machine code. The machine code, when run, outputted HTML.

Oh, and the C code wouldn’t compile for x86-64 because of the aforementioned errors. I fixed that by writing a one-line sed script and running it between the perl and compiler stages.

brainmelt Okaaaay…

Did that HTML have C code embedded in it inside of special tags? :slight_smile:

I’m not sure. The html generation subsystem doesn’t actually work, but you can’t use this driver without linking against it.