Programming is hard. Programming correct C and C++ is particularly hard. Indeed, both in C
and certainly in C++, it is uncommon to see a screenful containing only well defined 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 undefined or unspecified, they often do not know why
it is so. In these slides we will study small code snippets in C and C++, and use them to discuss
the fundamental building blocks, limitations and underlying design philosophies of these
wonderful but dangerous programming languages.
Deep C (and C++)
http://www.noaanews.noaa.gov/stories2005/images/rov-hercules-titanic.jpg
by Olve Maudal and Jon Jagger
October 2011
Suppose you are about to interview a candidate for a position as
C programmer for various embedded platforms.As part of the
interview you might want to check whether the candidate has a
deep understanding of the programming language or not... here
is a great code snippet to get the conversation started:
int main()
{
int a = 42;
printf(“%dn”, a);
}
Suppose you are about to interview a candidate for a position as
C programmer for various embedded platforms.As part of the
interview you might want to check whether the candidate has a
deep understanding of the programming language or not... here
is a great code snippet to get the conversation started:
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to
compile, link and run this program?
Suppose you are about to interview a candidate for a position as
C programmer for various embedded platforms.As part of the
interview you might want to check whether the candidate has a
deep understanding of the programming language or not... here
is a great code snippet to get the conversation started:
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
One candidate might say:
int main()
{
int a = 42;
printf(“%dn”, a);
}
You must #include <stdio.h>, add
a return 0 and then it will compile and
link.When executed it will print the value
42 on the screen.
What will happen if you try to compile, link and run this program?
One candidate might say:
int main()
{
int a = 42;
printf(“%dn”, a);
}
You must #include <stdio.h>, add
a return 0 and then it will compile and
link.When executed it will print the value
42 on the screen.
What will happen if you try to compile, link and run this program?
One candidate might say:
and there is nothing
wrong with that answer...
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
But another candidate might use this as an opportunity to start
demonstrating a deeper understanding. She might say things like:
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
But another candidate might use this as an opportunity to start
demonstrating a deeper understanding. She might say things like:
You probably want to #include <stdio.h>
which has an explicit declaration of printf(). The
program will compile, link and run, and it will write the
number 42 followed by a newline to the standard
output stream.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and then she elaborates
a bit by saying:
What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
and then she elaborates
a bit by saying:
What will happen if you try to compile, link and run this program?
A C++ compiler will refuse to compile this code as the
language requires explicit declaration of all functions.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and then she elaborates
a bit by saying:
What will happen if you try to compile, link and run this program?
A C++ compiler will refuse to compile this code as the
language requires explicit declaration of all functions.
However a proper C compiler will create an implicit
declaration for the function printf(), compile this
code into an object file.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and then she elaborates
a bit by saying:
What will happen if you try to compile, link and run this program?
A C++ compiler will refuse to compile this code as the
language requires explicit declaration of all functions.
However a proper C compiler will create an implicit
declaration for the function printf(), compile this
code into an object file.
And when linked with a standard library, it will find a
definition of printf()that accidentally will match the
implicit declaration.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and then she elaborates
a bit by saying:
What will happen if you try to compile, link and run this program?
A C++ compiler will refuse to compile this code as the
language requires explicit declaration of all functions.
However a proper C compiler will create an implicit
declaration for the function printf(), compile this
code into an object file.
And when linked with a standard library, it will find a
definition of printf()that accidentally will match the
implicit declaration.
So the program above will actually compile, link and run.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and then she elaborates
a bit by saying:
What will happen if you try to compile, link and run this program?
A C++ compiler will refuse to compile this code as the
language requires explicit declaration of all functions.
You might get a warning though.
However a proper C compiler will create an implicit
declaration for the function printf(), compile this
code into an object file.
And when linked with a standard library, it will find a
definition of printf()that accidentally will match the
implicit declaration.
So the program above will actually compile, link and run.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and while she is on the roll, she might continue with:
What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
and while she is on the roll, she might continue with:
What will happen if you try to compile, link and run this program?
If this is C99, the exit value is defined to indicate
success to the runtime environment, just like in
C++98, but for older versions of C, like ANSI C
and K&R, the exit value from this program will
be some undefined garbage value.
int main()
{
int a = 42;
printf(“%dn”, a);
}
and while she is on the roll, she might continue with:
What will happen if you try to compile, link and run this program?
If this is C99, the exit value is defined to indicate
success to the runtime environment, just like in
C++98, but for older versions of C, like ANSI C
and K&R, the exit value from this program will
be some undefined garbage value.
But since return values are often passed in a
register I would not be surprised if the garbage
value happens to be 3... since printf() will
return 3, the number of characters written to
standard out.
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
and while she is on the roll, she might continue with:
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
And talking about C standards... if you want to show
that you care about C programming, you should use
int main(void) as your entry point - since the
standard says so.
Using void to indicate no parameters is essential for
declarations in C, eg a declaration ‘int f();’, says
there is a function f that takes any number of
arguments. While you probably meant to say
‘int f(void);’. Being explicit by using void also
for function definitions does not hurt.
and while she is on the roll, she might continue with:
int main()
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
Also, if you allow me to be a bit
pedantic... the program is not really
compliant, as the standard says that the
source code must end with a newline.
and to really show off...
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
ah, remember to include an explicit
declaration of printf() as well
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
now, this is a darn cute little C program! Isn’t it?
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
$ cc -std=c99 -c foo.c
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
$ cc -std=c99 -c foo.c
$ cc foo.o
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
$ cc -std=c99 -c foo.c
$ cc foo.o
$ ./a.out
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
$ cc -std=c99 -c foo.c
$ cc foo.o
$ ./a.out
42
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
$ cc -std=c99 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
What will happen if you try to compile, link and run this program?
$ cc -std=c89 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
3
and here is what I get when compiling, linking and running the
above program on my machine:
$ cc -std=c99 -c foo.c
$ cc foo.o
$ ./a.out
42
$ echo $?
0
Is there any difference between these two candidates?
Is there any difference between these two candidates?
Not much, yet, but I really like her answers so far.
Now suppose they are not really candidates. Perhaps they
are stereotypes for engineers working in your organization?
Now suppose they are not really candidates. Perhaps they
are stereotypes for engineers working in your organization?
Would it be useful if most of your colleagues have a deep
understanding of the programming language they are using?
Now suppose they are not really candidates. Perhaps they
are stereotypes for engineers working in your organization?
Let’s find out how deep their knowledge of C and C++ is...
Let’s find out how deep their knowledge of C and C++ is...
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
it will print 4, then 4, then 4
it will print 4, then 4, then 4
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
it will print 4, then 5, then 6
it will print 4, then 5, then 6
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
eh, is it undefined? do you get garbage values?
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
eh, is it undefined? do you get garbage values?
No, you get 1, then 2, then 3
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
eh, is it undefined? do you get garbage values?
No, you get 1, then 2, then 3
ok, I see... why?
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
eh, is it undefined? do you get garbage values?
No, you get 1, then 2, then 3
ok, I see... why?
because static variables are set to 0
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
eh, is it undefined? do you get garbage values?
No, you get 1, then 2, then 3
ok, I see... why?
because static variables are set to 0
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
eh, is it undefined? do you get garbage values?
the standard says that static variables are initialized
to 0, so this should print 1, then 2, then 3
No, you get 1, then 2, then 3
ok, I see... why?
because static variables are set to 0
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
Ehm, why do you think that will happen?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
Ehm, why do you think that will happen?
Because you said they where initialized to 0
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
Ehm, why do you think that will happen?
Because you said they where initialized to 0
But this is not a static variable
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
Ehm, why do you think that will happen?
Because you said they where initialized to 0
But this is not a static variable
ah, then you get three garbage values
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
Ehm, why do you think that will happen?
Because you said they where initialized to 0
But this is not a static variable
ah, then you get three garbage values
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
the value of a will be undefinded, so in theory you
get three garbage values. In practice however, since
auto variables are often allocated on an execution
stack, a might get the same memory location each
time and you might get three consecutive values... if
you compile without optimization.
Ehm, why do you think that will happen?
Because you said they where initialized to 0
But this is not a static variable
ah, then you get three garbage values
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
the value of a will be undefinded, so in theory you
get three garbage values. In practice however, since
auto variables are often allocated on an execution
stack, a might get the same memory location each
time and you might get three consecutive values... if
you compile without optimization.
Ehm, why do you think that will happen?
Because you said they where initialized to 0
But this is not a static variable
ah, then you get three garbage values
on my machine I actually get, 1, then 2, then 3
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Now you get 1, then 1, then 1
the value of a will be undefinded, so in theory you
get three garbage values. In practice however, since
auto variables are often allocated on an execution
stack, a might get the same memory location each
time and you might get three consecutive values... if
you compile without optimization.
Ehm, why do you think that will happen?
Because you said they where initialized to 0
But this is not a static variable
ah, then you get three garbage values
on my machine I actually get, 1, then 2, then 3
I am not surprised... if you compile in debug mode the
runtime might try to be helpful and memset your
stack memory to 0
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
eh?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
eh?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
The cost of setting auto variables to 0 would
increase the cost of function calls. C has a very
strong focus on execution speed.
eh?
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
The cost of setting auto variables to 0 would
increase the cost of function calls. C has a very
strong focus on execution speed.
eh?
Memsetting the global data segment to 0
however, is a one time cost that happens at
start up, and that might be the reason why it is
so in C.
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
Why do you think static variables are set to 0,
while auto variables are not initialized?
The cost of setting auto variables to 0 would
increase the cost of function calls. C has a very
strong focus on execution speed.
eh?
Memsetting the global data segment to 0
however, is a one time cost that happens at
start up, and that might be the reason why it is
so in C.
And to be precise, in C++ however, static
variables are not set to 0, they are set to their
default values... which for native types means 0.
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
static int a;
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
1, 2, 3
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
1, 2, 3
ok, why?