0% found this document useful (0 votes)
33K views232 pages

DeepC Scandev Mar2013

The document discusses how programming in C is difficult due to the need for a deep understanding of the language. It describes how even professional programmers often do not fully understand C and can write undefined or unspecified code as a result. The talk will examine small code snippets in C to discuss fundamental concepts, limitations, and design philosophies of the language.

Uploaded by

scribmj5796
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
33K views232 pages

DeepC Scandev Mar2013

The document discusses how programming in C is difficult due to the need for a deep understanding of the language. It describes how even professional programmers often do not fully understand C and can write undefined or unspecified code as a result. The talk will examine small code snippets in C to discuss fundamental concepts, limitations, and design philosophies of the language.

Uploaded by

scribmj5796
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 232

Programming is hard. Programming correct C is particularly hard.

Indeed, it is uncommon to see a


screenful containing only well dened and conforming code. Why do professional programmers
write code like this? Because most programmers do not have a deep understanding of the
language they are using. While they sometimes know that certain things are undened or
unspecied, they often do not know why it is so.
In this talk we will study small code snippets in C, and use them to discuss some of the
fundamental building blocks, limitations and underlying design philosophies of this wonderful but
dangerous programming language.
Deep C
http://www.noaanews.noaa.gov/stories2005/images/rov-hercules-titanic.jpg
by Olve Maudal
A 50 minute session at Scandinavian Developer Conference 2013
Tuesday, March 5, 2013
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
$ icc foo.c && ./a.out
Exercise
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
What do you think this code snippet might print if you compile, link and run it
in your development environment?
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
$ gcc foo.c && ./a.out
12
$ clang foo.c && ./a.out
11
$ icc foo.c && ./a.out
13
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
Lets add some ags for better diagnostics.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
$ gcc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
12
$ clang -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
11
$ icc -std=c99 -O -Wall -Wextra -pedantic foo.c && ./a.out
13
On my computer (Mac OS 10.8.2, gcc 4.2.1, clang 4.1, icc 13.0.1):
Lets add some ags for better diagnostics.
It is important to understand that C (and C++) are
not really high-level languages compared to most
other common programming languages.
They are more like just portable assemblers where
you have to appreciate the underlying architecture
to program correctly. This is reected in the
language denition and in how compiler deals with
incorrect code.
Without a deep understanding of the language, its
history, and its design goals, you are doomed to fail.
http://www.slideshare.net/olvemaudal/deep-c
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
4
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
4
4
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
4
4
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
5
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
5
6
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
4
5
6
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
garbage, garbage,
garbage?
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
garbage, garbage,
garbage?
No. Variables with
static storage duration
are initialized to 0
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
garbage, garbage,
garbage?
No. Variables with
static storage duration
are initialized to 0
It is better to
initialize
explicitly.
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
garbage, garbage,
garbage?
No. Variables with
static storage duration
are initialized to 0
It is better to
initialize
explicitly.
I agree, in this case. But, as a
professional programmer, you
sometimes have to read code
written by other people.
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
garbage, garbage,
garbage?
No. Variables with
static storage duration
are initialized to 0
It is better to
initialize
explicitly.
I agree, in this case. But, as a
professional programmer, you
sometimes have to read code
written by other people.
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
garbage, garbage,
garbage?
No. Variables with
static storage duration
are initialized to 0
It is better to
initialize
explicitly.
I agree, in this case. But, as a
professional programmer, you
sometimes have to read code
written by other people.
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
garbage, garbage,
garbage?
No. Variables with
static storage duration
are initialized to 0
It is better to
initialize
explicitly.
I agree, in this case. But, as a
professional programmer, you
sometimes have to read code
written by other people.
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1, 1, 1?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
Yes, in theory that is
correct.
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
In C. Why do you think static variables gets a default
value (usually 0), while auto variables does not get a
default value?
In C. Why do you think static variables gets a default
value (usually 0), while auto variables does not get a
default value?
Because C is a braindead
programming language?
In C. Why do you think static variables gets a default
value (usually 0), while auto variables does not get a
default value?
Because C is a braindead
programming language?
Because C is all about execution
speed. Setting static variables to
default values is a one time cost, while
defaulting auto variables might add a
signcant runtime cost.
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
Yes, in theory that is
correct. Lets try it on
my machine
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
Yes, in theory that is
correct. Lets try it on
my machine
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
Yes, in theory that is
correct. Lets try it on
my machine
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
Ehh...
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
any plausible
explaination for this
behaviour?
Ehh...
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
any plausible
explaination for this
behaviour?
Ehh...
Is it because: The value of an object
with automatic storage duration is used
while it is indeterminate?
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
any plausible
explaination for this
behaviour?
Ehh...
Is it because: The value of an object
with automatic storage duration is used
while it is indeterminate?
That explains why this is undened
behavior, but it does not explain the
phenomenon we just observed: 1,2,3
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
any plausible
explaination for this
behaviour?
Ehh...
Is it because: The value of an object
with automatic storage duration is used
while it is indeterminate?
That explains why this is undened
behavior, but it does not explain the
phenomenon we just observed: 1,2,3
But if it is UB, do I need to care?
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
1
2
3
Yes, in theory that is
correct. Lets try it on
my machine
any plausible
explaination for this
behaviour?
Ehh...
Is it because: The value of an object
with automatic storage duration is used
while it is indeterminate?
That explains why this is undened
behavior, but it does not explain the
phenomenon we just observed: 1,2,3
But if it is UB, do I need to care?
You do, because everything becomes much
easier if you can and are willing to reason
about these things...
1, 1, 1? No, variables with
automatic storage
duration are not
initialized implicitly
Garbage,
garbage,
garbage?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
OK, lets add some
ags
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -Wall -Wextra -pedantic foo.c
OK, lets add some
ags
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -Wall -Wextra -pedantic foo.c
$ ./a.out
OK, lets add some
ags
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -Wall -Wextra -pedantic foo.c
$ ./a.out
1
OK, lets add some
ags
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -Wall -Wextra -pedantic foo.c
$ ./a.out
1
2
OK, lets add some
ags
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -Wall -Wextra -pedantic foo.c
$ ./a.out
1
2
3
OK, lets add some
ags
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -Wall -Wextra -pedantic foo.c
$ ./a.out
1
2
3
OK, lets add some
ags
Lousy compiler!
But seriously, I
dont need to
know, because I
let the compiler
nd bugs like this
Why dont the C standard require that you always
get a warning or error on invalid code?
Why dont the C standard require that you always
get a warning or error on invalid code?
Because C is a braindead
programming language?
Why dont the C standard require that you always
get a warning or error on invalid code?
Because C is a braindead
programming language?
One of the design goals of C is that it
should be relatively easy to write a
compiler. Adding a requirement that
the compilers should refuse or warn
about invalid code would add a huge
burden on the compiler writers.
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
1494495224
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
1494495224
$ cc -O -Wall -Wextra foo.c
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
1494495224
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
1494495224
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1450342656
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
1494495224
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1450342656
1450340344
Pro tip:
Always
compile with
optimization!
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1494497536
1494495224
1494495224
$ cc -O -Wall -Wextra foo.c
foo.c:6: warning: 'a' is used uninitialized in this function
1450342656
1450340344
1450340344
I am now going to show you something cool!
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
I am now going to show you something cool!
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
$ cc foo.c && ./a.out
I am now going to show you something cool!
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
Can you explain this behavior?
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
Can you explain this behavior?
If you can give a plausible explanation for this
behavior, you should feel both good and bad. Bad
because you obviously know something you are
supposed to not know when programming in C.
You make assumptions about the underlying
implementation and architecture. Good because
being able to understand such phenomenons are
essential for troubleshooting C programs and for
avoiding falling into all the traps laid out for you.
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
Can you explain this behavior?
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
eh?
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
Can you explain this behavior?
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
eh?
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
Perhaps this compiler has a pool
of named variables that it reuses.
Eg variable a was used and
released in bar(), then when
foo() needs an integer named a
it will get the same variable for
reuse. If you rename the variable
in bar() to, say b, then I dont
think you will get 42.
Can you explain this behavior?
#include <stdio.h>
void foo(void)
{
int a;
printf("%d\n", a);
}
void bar(void)
{
int a = 42;
}

int main(void)
{
bar();
foo();
}
eh?
$ cc foo.c && ./a.out
42
I am now going to show you something cool!
Perhaps this compiler has a pool
of named variables that it reuses.
Eg variable a was used and
released in bar(), then when
foo() needs an integer named a
it will get the same variable for
reuse. If you rename the variable
in bar() to, say b, then I dont
think you will get 42.
Yeah, sure...
Can you explain this behavior?
Strange explanations are often symptoms of having an invalid conceptual model!
Text Segment
Data Segment
Execution Stack
Heap
high address
low address
Memory Layout *
(automatic storage)
(allocated storage)
(static storage)
(instructions / read only data)
And sometimes it is useful to
assume that an activation
record is created and
pushed onto the execution
stack every time a function is
called. The activation record
contains local auto variables,
arguments to the functions,
and housekeeping data such
as pointer to the previous
frame and the return
address.
Activation Record
housekeeping data
arguments
local auto variables
(*) The C standard does not dictate any particular memory layout, so what is
presented here is just a useful conceptual example model that is similar to
what some architecture and run-time enviornments look like
It is sometimes useful to assume that a C program uses a memory model
where the instructions are stored in a text segment, and static variables
are stored in a data segment. Automatic variables are allocated when
needed together with housekeeping variables on an execution stack that
is growing towards low address. The remaining memory, the heap is used for
allocated storage.
The stack and the heap is typically not cleaned up in any way at startup, or
during execution, so before objects are explicitly initialized they typically get
garbage values based on whatever is left in memory from discarded objects
and previous executions. In other words, the programmer must do all the
housekeeping on variables with automatic storage and allocated storage.
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
but you might also get
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
437
347
but you might also get
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
437
347
but you might also get
or
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
437
347
but you might also get
437
437
or
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
437
347
but you might also get
437
437
or or
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
437
347
but you might also get
437
437
347
347
or or
#include <stdio.h>
int foo(int a) {
printf("%d", a);
return a;
}
int bar(int a, int b) {
return a + b;
}

int main(void) {
int i = foo(3) + foo(4);
printf("%d\n", i);

int j = bar(foo(3), foo(4));
printf("%d\n", j);
}
$ cc foo.c && ./a.out
347
437
437
347
C and C++ are among the few
programming languages where evaluation
order is mostly unspecied. This is an
example of unspecied behaviour.
but you might also get
437
437
347
347
or or
In C. Why is the evaluation order mostly
unspecied?
In C. Why is the evaluation order mostly
unspecied?
In C. Why is the evaluation order mostly
unspecied?
Because C is a braindead
programming language?
In C. Why is the evaluation order mostly
unspecied?
Because C is a braindead
programming language?
Because there is a design goal to
allow optimal execution speed on a
wide range of architectures. In C the
compiler can choose to evaluate
expressions in the order that is most
optimal for a particular platform. This
allows for great optimization
opportunities.
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
What? Inconceivable!
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
What? Inconceivable!
This is a classic example of undened
behaviour. Anything can happen! Nasal
demons can start ying out of your nose!
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
What? Inconceivable!
This is a classic example of undened
behaviour. Anything can happen! Nasal
demons can start ying out of your nose!
I agree this is crap
code, but why is it
wrong?
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
What? Inconceivable!
$ cc foo.cpp && ./a.out
42
This is a classic example of undened
behaviour. Anything can happen! Nasal
demons can start ying out of your nose!
I agree this is crap
code, but why is it
wrong?
In this case? Line 6. What is i*3? Is it 2*3 or 3*3 or something else?
In C you can not assume anything about a variable with side-effects
(here i++) before there is a sequence point.
$ cc foo.cpp && ./a.out
42
I dont care, I never
write code like that.
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
Good for you. But bugs like this can easily
happen if you dont understand the rules of
sequencing. And very often, the compiler is
not able to help you...
I dont care, I never
write code like that.
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
Good for you. But bugs like this can easily
happen if you dont understand the rules of
sequencing. And very often, the compiler is
not able to help you...
I dont care, I never
write code like that.
But why do we
not get warning
on this by default?
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
$ cc foo.cpp && ./a.out
42
Good for you. But bugs like this can easily
happen if you dont understand the rules of
sequencing. And very often, the compiler is
not able to help you...
I dont care, I never
write code like that.
But why do we
not get warning
on this by default?
At least two reasons. First of all it is sometimes
very difcult to detect such sequencing violations.
Secondly, there is so much existing code out there
that breaks these rules, so issuing warnings here
might cause other problems.
#include <stdio.h>
int main(void) {
int v[6] = {4,6,2,9};
int i = 2;
int j = i * 3 + v[i++];
printf(%d\n, j);
}
What do these code snippets print?
What do these code snippets print?
int a=41; a++; printf("%d\n", a);
1
What do these code snippets print?
int a=41; a++ & printf("%d\n", a);
2
int a=41; a++; printf("%d\n", a);
1
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; a++ & printf("%d\n", a);
2
int a=41; a++; printf("%d\n", a);
1
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2
int a=41; a++; printf("%d\n", a);
1
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1 42
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2 undened
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1 42
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2 undened
42
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1 42
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2 undened
42
42
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1 42
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2 undened
42
42
undened
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1 42
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2 undened
42
42
undened
int a=41; a = a++; printf("%d\n", a);
5
int a=41; a++; printf("%d\n", a);
1 42
?
int a=41; a = foo(a++); printf("42\n");
6
What do these code snippets print?
int a=41; a++ && printf("%d\n", a);
3
int a=41; if (a++ < 42) printf("%d\n", a);
4
int a=41; a++ & printf("%d\n", a);
2 undened
42
42
undened
int a=41; a = a++; printf("%d\n", a);
5
When exactly do side-effects take place in C and C++?
int a=41; a++; printf("%d\n", a);
1 42
?
int a=41; a = foo(a++); printf("42\n");
6
A sequence point is a point in the program's
execution sequence where all previous side-
effects shall have taken place and where all
subsequent side-effects shall not have taken place
Sequence Points
Between the previous and next sequence point an
object shall have its stored value modified at most
once by the evaluation of an expression.
Sequence Points - Rule 1
a = a++
this is undefined!
Furthermore, the prior value shall be read only to
determine the value to be stored.
Sequence Points - Rule 2
a + a++
this is undefined!!
Sequence Points
A lot of developers think C has many sequence points
Sequence Points
The reality is that C has very few sequence points.
This helps to maximize optimization opportunities
for the compiler.
Sequence points in C
1) At the end of a full expression there is a sequence point.
a = i++;
++i;
if (++i == 42) { ... }
2) In a function call, there is a sequence point after the evaluation of the arguments, but before the
actual call.
foo(++i)
3) The logical and (&&) and logical or (||) guarantees a left-to-right evaluation, and if the second
operand is evaluated, there is a sequence point between the evaluation of the rst and second
operands.
if (p && *p++ == 42) { ... }
4) The comma operator (,) guarantees left-to-right evaluation and there is a sequence point
between evaluating the left operand and the right operand.
i = 39; a = (i++, i++, ++i);
5) For the conditional operator (?:), the rst operand is evaluated; there is a sequence point
between its evaluation and the evaluation of the second or third operand (whichever is evaluated)
a++ > 42 ? --a : ++a;
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
4
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
4
4
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
4
4
Believe it or not, I
have met several
programmers who
thought this snippet
would print 3,3,3.
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
4
4
They are all
morons!
Believe it or not, I
have met several
programmers who
thought this snippet
would print 3,3,3.
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
4
4
They are all
morons!
Believe it or not, I
have met several
programmers who
thought this snippet
would print 3,3,3.
Did you know about
sequence points? Do
you have a deep
understanding of when
side-effects really take
place in C?
#include <stdio.h>
void foo(void)
{
int a = 3;
a++;
printf("%d\n", a);
}

int main(void)
{
foo();
foo();
foo();
}
$ cc foo.c
$ ./a.out
4
4
4
They are all
morons!
Believe it or not, I
have met several
programmers who
thought this snippet
would print 3,3,3.
Did you know about
sequence points? Do
you have a deep
understanding of when
side-effects really take
place in C?
ehh...
Strange explanations are often symptoms of having an invalid conceptual model!
Behavior
implementation-dened behavior:
the construct is not incorrect; the code must
compile; the compiler must document the
behavior
unspecied behavior: the same as
implementation-dened except the behavior
need not be documented
undened behavior: the standard
imposes no requirements ; anything at all can
happen, all bets are off, nasal demons might y
out of your nose.
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
int main()
{
// implementation-defined
int i = ~0;
i >>= 1;
printf("%d\n", i);
// unspecified
printf("4") + printf("2");
printf("\n");
// undefined
int k = INT_MAX;
k += 1;
printf("%d\n", k);
}
Note that many compilers will not give you any warnings when compiling this code, and due to the
undened behavior caused by signed integer overow above, the whole program is in theory undened.
Behavior
implementation-dened behavior:
the construct is not incorrect; the code must
compile; the compiler must document the
behavior
unspecied behavior: the same as
implementation-dened except the behavior
need not be documented
undened behavior: the standard
imposes no requirements ; anything at all can
happen, all bets are off, nasal demons might y
out of your nose.
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
int main()
{
// implementation-defined
int i = ~0;
i >>= 1;
printf("%d\n", i);
// unspecified
printf("4") + printf("2");
printf("\n");
// undefined
int k = INT_MAX;
k += 1;
printf("%d\n", k);
}
Note that many compilers will not give you any warnings when compiling this code, and due to the
undened behavior caused by signed integer overow above, the whole program is in theory undened.
... and, locale-specic behavior
the C standard denes the expected behavior, but says very little
about how it should be implemented.
the C standard denes the expected behavior, but says very little
about how it should be implemented.
this is a key feature of C, and one of the
reason why C is such a successful
programming language on a wide range
of hardware!
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is:
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is:
3.1415926535897932384626433832795028841971
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is:
3.1415926535897932384626433832795028841971
Inconceivable!
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is:
3.1415926535897932384626433832795028841971
Inconceivable!
Remember... when you have undened
behavior, anything can happen!
You keep using that word. I do not think it
means what you think it means.
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
int the_answer(int seed)
{
int answer = seed + 42;
return answer - seed;
}
$ cc main.c deep_thought.c && ./a.out
The anwser is:
3.1415926535897932384626433832795028841971
Inconceivable!
Remember... when you have undened
behavior, anything can happen!
You keep using that word. I do not think it
means what you think it means.
#include <stdio.h>
#include <limits.h>
int the_answer(int);
int main(void)
{
printf("The answer is:\n");
int a = the_answer(INT_MAX);
printf("%d\n", a);
}
main.c deep_thought.c
Integer overow gives undened behavior. If you want to
prevent this to happen you must write the logic yourself. This is
the spirit of C, you dont get code you have not asked for.
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
Exercise
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
gcc version 4.7.2 (GCC)
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
gcc version 4.7.2 (GCC)
$ gcc foo.c bar.c main.c
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
gcc version 4.7.2 (GCC)
$ gcc foo.c bar.c main.c
$ ./a.out
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
gcc version 4.7.2 (GCC)
$ gcc foo.c bar.c main.c
$ ./a.out
true
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
gcc version 4.7.2 (GCC)
$ gcc foo.c bar.c main.c
$ ./a.out
true
false
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
Exercise
$ gcc -v
gcc version 4.7.2 (GCC)
$ gcc foo.c bar.c main.c
$ ./a.out
true
false
$
#include <stdio.h>
#include <stdbool.h>
void foo(void)
{
bool b;
if (b)
printf("true\n");
if (!b)
printf("false\n");
}
foo.c
This program is UB because b is used without being initialized. But in
practice, what do you think might happen when this function is called?
void bar(void);
void foo(void);
int main(void)
{
bar();
foo();
}
main.c
void bar(void)
{
char c = 2;
(void)c;
}
bar.c
This is what I get on my computer (Mac OS 10.8.2, gcc 4.7.2)
bool b;
if (b)
printf("b is true\n");
if (!b)
printf("b is false\n");
A real story of anything can happen
bool b;
if (b)
printf("b is true\n");
if (!b)
printf("b is false\n");
; the following code assumes that $b is either 0 or 1
load_reg_a $b
compare_reg_a 0
jump_equal label1
call_proc print_b_is_true
label1:
load_reg_a $b
xor_reg_a 1
compare_reg_a 0
jump_equal label2
call_proc print_b_is_false
label2:
A real story of anything can happen
bool b;
if (b)
printf("b is true\n");
if (!b)
printf("b is false\n");
; the following code assumes that $b is either 0 or 1
load_reg_a $b
compare_reg_a 0
jump_equal label1
call_proc print_b_is_true
label1:
load_reg_a $b
xor_reg_a 1
compare_reg_a 0
jump_equal label2
call_proc print_b_is_false
label2:
A real story of anything can happen
this is approximately the code generated by
one actual version of gcc, try to imagine what
will happen if the garbage value of b is 2
bool b;
if (b)
printf("b is true\n");
if (!b)
printf("b is false\n");
; the following code assumes that $b is either 0 or 1
load_reg_a $b
compare_reg_a 0
jump_equal label1
call_proc print_b_is_true
label1:
load_reg_a $b
xor_reg_a 1
compare_reg_a 0
jump_equal label2
call_proc print_b_is_false
label2:
b is true
b is false
A real story of anything can happen
this is approximately the code generated by
one actual version of gcc, try to imagine what
will happen if the garbage value of b is 2
#include <stdio.h>
#include <string.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
struct X a = {42,'a',1337};
struct X b = {42,'a',1337};
if (memcmp(&a, &b, sizeof a) == 0)
printf("equal\n");
else
printf("not equal\n");
}
a few words about memory
#include <stdio.h>
#include <string.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
struct X a = {42,'a',1337};
struct X b = {42,'a',1337};
if (memcmp(&a, &b, sizeof a) == 0)
printf("equal\n");
else
printf("not equal\n");
}
This might happen:
$ cc -O2 foo.c && ./a.out
equal
$ cc -O3 foo.c && ./a.out
not equal
$
a few words about memory
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
12
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
12
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
$ cc -fpack-struct foo.c
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
12
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
$ cc -fpack-struct foo.c
$ ./a.out
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
12
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
$ cc -fpack-struct foo.c
$ ./a.out
4
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
12
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
$ cc -fpack-struct foo.c
$ ./a.out
4
1
#include <stdio.h>
struct X {
int a;
char b;
int c;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
12
a few words about memory
On my machine (Mac OS 10.8.2 x86_64):
$ cc -fpack-struct foo.c
$ ./a.out
4
1
9
a a a a
b c c c
c
a a a a
b . . .
c c c c
packed struct memory aligned
struct X {
int a;
char b;
int c;
};
sizeof(struct X) == 9 sizeof(struct X) == 12
a a a a
b c c c
c
a a a a
b . . .
c c c c
packed struct memory aligned
struct X {
int a;
char b;
int c;
};
sizeof(struct X) == 9 sizeof(struct X) == 12
Imagine how the assembly code for this snippet would look like:
void foo(struct X * x) {
x->c += 42;
}
a a a a
b c c c
c
a a a a
b . . .
c c c c
packed struct memory aligned
struct X {
int a;
char b;
int c;
};
sizeof(struct X) == 9 sizeof(struct X) == 12
?
Imagine how the assembly code for this snippet would look like:
void foo(struct X * x) {
x->c += 42;
}
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
$ cc -fpack-struct foo.c
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
8
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
struct X {
int a;
char b;
int c;
char * p;
};
int main(void)
{
printf("%zu\n", sizeof(int));
printf("%zu\n", sizeof(char));
printf("%zu\n", sizeof(char *));
printf("%zu\n", sizeof(struct X));
}
$ cc foo.c
$ ./a.out
4
1
8
24
a few words about memory
$ cc -fpack-struct foo.c
$ ./a.out
4
1
8
17
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
In C (and C++), unlike most other languages, the order in which
subexpressions are evaluated and the order in which side effects
take place, except as specied for the function-call (), &&, ||, ?:, and
comma operators, is unspecied. Therefore the expression
i + v[++i] + v[++i]
does not make sense.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
In C (and C++), unlike most other languages, the order in which
subexpressions are evaluated and the order in which side effects
take place, except as specied for the function-call (), &&, ||, ?:, and
comma operators, is unspecied. Therefore the expression
i + v[++i] + v[++i]
does not make sense.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
In C (and C++), unlike most other languages, the order in which
subexpressions are evaluated and the order in which side effects
take place, except as specied for the function-call (), &&, ||, ?:, and
comma operators, is unspecied. Therefore the expression
i + v[++i] + v[++i]
does not make sense.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
In C (and C++), unlike most other languages, the order in which
subexpressions are evaluated and the order in which side effects
take place, except as specied for the function-call (), &&, ||, ?:, and
comma operators, is unspecied. Therefore the expression
i + v[++i] + v[++i]
does not make sense.
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
In C (and C++), unlike most other languages, the order in which
subexpressions are evaluated and the order in which side effects
take place, except as specied for the function-call (), &&, ||, ?:, and
comma operators, is unspecied. Therefore the expression
i + v[++i] + v[++i]
does not make sense.
?
#include <stdio.h>
int main(void)
{
int v[] = {0,2,4,6,8};
int i = 1;
int n = i + v[++i] + v[++i];
printf("%d\n", n);
}
foo.c
So whats wrong with this code?
It is crap code
The standard says that
this is invalid code
Update a variable
multiple times between
two semicolons
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
In C (and C++), unlike most other languages, the order in which
subexpressions are evaluated and the order in which side effects
take place, except as specied for the function-call (), &&, ||, ?:, and
comma operators, is unspecied. Therefore the expression
i + v[++i] + v[++i]
does not make sense.
?
But, seriously, who is releasing code with undened behavior?
But, seriously, who is releasing code with undened behavior?
But, seriously, who is releasing code with undened behavior?
But, seriously, who is releasing code with undened behavior?
But, seriously, who is releasing code with undened behavior?
But, seriously, who is releasing code with undened behavior?
But, seriously, who is releasing code with undened behavior?
...
/* if both are imag, store value, otherwise store 0.0 */
if (!(li && ri)) {
tfree(r);
r = bcon(0);
}
p = buildtree(ASSIGN, l, r);
p->n_type = p->n_type += (FIMAG-FLOAT);
....
snippet from pftn.c in pcc 1.0.0.RELEASE 20110221
But, seriously, who is releasing code with undened behavior?
...
/* if both are imag, store value, otherwise store 0.0 */
if (!(li && ri)) {
tfree(r);
r = bcon(0);
}
p = buildtree(ASSIGN, l, r);
p->n_type = p->n_type += (FIMAG-FLOAT);
....
snippet from pftn.c in pcc 1.0.0.RELEASE 20110221
But, seriously, who is releasing code with undened behavior?
Its undened behavior because: Between two sequence points, an
object is modied more than once, or is modied and the prior value
is read other than to determine the value to be stored
the you modify and use the value of a variable twice between
sequence points
...
/* if both are imag, store value, otherwise store 0.0 */
if (!(li && ri)) {
tfree(r);
r = bcon(0);
}
p = buildtree(ASSIGN, l, r);
p->n_type = p->n_type += (FIMAG-FLOAT);
....
snippet from pftn.c in pcc 1.0.0.RELEASE 20110221
C and C++ are not really high level languages,
they are more like portable assemblers. When
programming in C and C++ you must have a
understanding of what happens under the hood!
And if you dont have a decent understanding of
it, then you are doomed to create lots of bugs...
C and C++ are not really high level languages,
they are more like portable assemblers. When
programming in C and C++ you must have a
understanding of what happens under the hood!
And if you dont have a decent understanding of
it, then you are doomed to create lots of bugs...
But if you do have a useful mental model of
what happens under the hood, then...
http://www.sharpshirter.com/assets/images/sharkpunchashgrey1.jpg
!
The spirit of C
trust the programmer

let them do what needs to be done

the programmer is in charge not the compiler


keep the language small and simple

small amount of code ! small amount of assembler

provide only one way to do an operation

new inventions are not entertained


make it fast, even if its not portable

target efcient code generation

int preference, int promotion rules

sequence points, maximum leeway to compiler


rich expression support

lots of operators

expressions combine into larger expressions

You might also like