Hello for the secondthirdfourth time, Reddit.
This was a 10-minute lightning talk. It is a presentation, not a book. I spoke when presenting it. Not everything that was said is on these slides, as that would have made for a boring talk. Yes, there are counter examples to some of the things mentioned, and they were discussed when it was presented. And it's meant to be amusing, not dry. Get a grip on yourselves.
Lots of love, rjek.
P.S. If you want to share this with somebody you know, just give them the URL you received. Do not click "share", as I get an email every time asking to grant permission. These now just get binned automatically so I don't feel the need to smite the universe.
P.P.S. Yes, this is in Comic Sans just to troll you.
Some dark corners of C
-std=c99 -pedantic -Wall -Warn-me-harder
Stolen from Rob Kendrick <rjek+dark corners@rjek.com>
Rob Kendrick <rob.kendrick@codethink.co.uk>
Dark corner of C
Here it is!
Shadowy escape
int x = 42;�int func() {� int x = 3840;� {� return x;� }�}�
int x = 42;�int func() {� int x = 3840;� {� extern int x;� return x;� }�}
Returns 3840
Returns 42
Bizarre keyword reuse
void foo(int x[static 10]);
void foo(char x[static 1]);
Passed array must be at least 10 entries.�foo.c:8:2: warning: array argument is too small; contains 5 elements, callee requires at least 10 [-Warray-bounds]
Free compile-time non-NULL check!�foo.c:8:2: warning: null passed to a callee which requires a non-null argument [-Wnonnull]
Warning: Don't breathe this
int x = 'FOO!';
Yes, that's right, it makes demons fly out of your nose!
(On my system, 1,179,602,721.)
If you do this, I will find you.
Yes, single quotes.
[ ] is just + in disguise
assert(spong[x] == x[spong]);
assert(spong[2] == 2[spong]);
d = "0123456789abcdef"[n];
d = n["0123456789abcdef"];
Pointer aliasing
void foo1(int *x, int *y, int *z) {� *x += *z;� *y += *z;�}
movl (%rdx), %eax�addl %eax, (%rdi)�movl (%rdx), %eax�addl %eax, (%rsi)�ret�
Compiler doesn't know that *z won't also be *x, so loads it twice :(
Pointer aliasing: Fixed?
void foo2(int *x, int *y, int *z) {
int w = *z;� *x += w;� *y += w;�}
movl (%rdx), %eax�addl %eax, (%rdi)�addl %eax, (%rsi)�ret��
Only one load, at the cost of beauty.
Pointer aliasing: Fixed!
void foo3(int *x, int *y,
int * restrict z) {� *x += *z;� *y += *z;�}
movl (%rdx), %eax�addl %eax, (%rdi)�addl %eax, (%rsi)�ret��
'restrict' means we promise not to mess about.
Counting up ...
int fact1(int n) {� int i, fact = 1;� for (i = 1; i <= n;
i++)� fact *= i;� return fact;�}�
fact1:�movl $1, %eax�testl %edi, %edi�jle .exit�xorl %ecx, %ecx�.loop:�incl %ecx�imull %ecx, %eax�cmpl %ecx, %edi�jne .loop�.exit �ret�
... vs counting down
int fact2(int n) {� int fact = 1;
if (n == 0)
return fact;� do� fact *= n;� while (--n != 0);
return fact;�}��
fact2:�testl %edi, %edi�movl $1, %eax�je .exit�.loop:�imull %edi, %eax�subl $1, %edi�jne .loop�.exit: �ret
const confusion
const int foo = 10;
const int *foop = &foo;
"Constant" integer foo with value 10.
And a "constant" pointer to it?
int i = 20;
foop = &i;
Oops. const binds left. But if there's nothing to its left, it binds to the right.
Munchy munchy.
z = y+++x;
The C specification says that when there is such an ambiguity, munch as much as possible. (The "greedy lexer rule".)
z = y++ + x;
Munchy munchy. Again.
z = y+++++x;
Alas, not
z = y++ + ++x;
But
z = y++ ++ +x;
Parser go boom.
C keywords
auto break case char const continue default do
double else enum extern float for goto if int
long register restrict return short signed sizeof static struct switch typedef union unsigned void volatile while
Wait, what? auto is a bizarreness left over from B. The only place it's valid, it's also the default. And that's to say a variable should be automatically managed, ie, placed on the stack. static is its antonym. Ish.
Smallest C program
What's the smallest C program that will compile and link?
int main;
(Don't run this.)
main;
Alas it produces a warning. So we can do this:
Global variables are filthy
foo1.c:
int x;
foo2.c:
int x;
int main() { printf("%d\n", x); }
$ gcc -o foo foo1.c foo2.c
$ echo $?
0
Global variables are filthy
foo1.c:
int x = 42;
foo2.c:
int x;
int main() { printf("%d\n", x); }
$ gcc -o foo foo1.c foo2.c
$ echo $?
0
Global variables are filthy
foo1.c:
int x = 0;
foo2.c:
int x = 0;
int main() { printf("%d\n", x); }
$ gcc -o foo foo1.c foo2.c
/tmp/ccx4aT9m.o:(.bss+0x0): multiple definition of `x'
// comments are evil
int foo() {� int a = 10, b = 2;� return a //*� //*/ b� ;�} �
C89: 5
C++: 10
C99: 10
Portable lossless floats
printf("%a\n", 1.2345);
0x1.3c083126e978dp+0
printf("%f\n",atof("0x1.3c083126e978dp+0"));�
1.234500
Ruin somebody's day
What would you sneak into somebody's headers to drive them mad?
#define struct union
#define else
Recommended reads
Sadly very few modern books on the subject of C :(
Thanks for ideas/input...
David Thomas
Daniel Silverstone
Clive Jones
Peter Van Der Linden
Erlend Hamberg�