2. An Example
• What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
2
You must #include , add a return 0 and then it
will compile and link. When executed it will
print the value 42 on the screen.
3. An Example
• What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
3
You probably want to #include 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.
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.
4. An Example
• What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
4
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.
5. An Example
• What will happen if you try to compile, link and run this program?
int main()
{
int a = 42;
printf(“%dn”, a);
}
5
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.
6. An Example
• Here is what I get when compiling, linking and running the above
program on my machine
6
$ gcc -std=c89 hello1.c
$ ./a.out
42
$ echo $?
3
$ gcc -std=c99 hello1.c
$ ./a.out
42
$ echo $?
0
#include <stdio.h>
int main(void)
{
int a = 42;
printf(“%dn”, a);
}
The default mode for C is now -
std=gnu11 instead of -std=gnu89 in GCC-5
7. Would it be useful if most of your colleagues
have a deep understanding of the C
programming language they are using?
7
https://algoritmaveprogramlama.files.wordpress.co
m/2013/10/c_pic.png
8. More C examples
• What is the result of following C code?
8
#include <stdio.h>
void foo(void)
{
int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
9. More C examples
• What is the result of following C code?
9
#include <stdio.h>
void foo(void)
{
static int a = 3;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
10. More C examples
• What is the result of following C code?
10
#include <stdio.h>
void foo(void)
{
static int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
11. More C examples
• What is the result of following C code?
11
#include <stdio.h>
void foo(void)
{
int a;
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
12. More C examples
• What is the result of following C code?
12
#include <stdio.h>
static int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
13. More C examples
• What is the result of following C code?
13
#include <stdio.h>
int a;
void foo(void)
{
++a;
printf("%dn", a);
}
int main(void)
{
foo();
foo();
foo();
}
14. More C examples
• Can you explain this behavior?
14
#include <stdio.h>
void foo(void)
{
int a;
printf("%dn", a);
}
void bar(void)
{
int a = 42;
}
int main(void)
{
bar();
foo();
}
$ gcc hello3.c && ./a.out
42
$ gcc –O2 hello3.c && ./a.out
0
15. More C examples
• What about this code snippet?
15
#include <stdio.h>
void foo(void)
{
int a = 41;
a = a++;
printf("%dn", a);
}
int main(void)
{
foo();
}
16. More C examples
• What about this code snippet?
16
#include <stdio.h>
int b(void) { puts(“3”); return 3; }
int c(void) { puts(“4”); return 4; }
int main(void)
{
int a = b() + c();
printf(“%dn”, a);
}
17. More C examples
• What do these code snippets print?
17
int a=41; a++; printf("%dn", a);
int a=41; a++ & printf("%dn", a);
int a=41; a++ && printf("%dn", a);
int a=41; if (a++ < 42) printf("%dn", a);
int a=41; a = a++; printf("%dn", a);
19. Sequence point
• Sequence point
– https://en.wikipedia.org/wiki/Sequence_point
– 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
– A lot of developers think C has many sequence points
– The reality is that C has very few sequence points
– This helps to maximize optimization opportunities for the compiler
19
20. Sequence point
– Between the previous and next sequence point an object shall
have its stored value modified at most once by the evaluation of
an expression.
– Furthermore, the prior value shall be read only to determine the
value to be stored.
20
21. Sequence point
• In C and C++, sequence points occur in the
following places
– Between evaluation of the left and right operands of the &&
(logical AND), || (logical OR) (as part of short-circuit evaluation),
and comma operators.
• For example, in the expression *p++ != 0 && *q++ != 0, all side effects of the
sub-expression *p++ != 0 are completed before any attempt to access q.
– Between the evaluation of the first operand of the ternary
"question-mark" operator and the second or third operand.
• For example, in the expression a = (*p++) ? (*p++) : 0 there is a sequence
point after the first *p++, meaning it has already been incremented by the time
the second instance is executed.
– At the end of a full expression.
• This category includes expression statements (such as the assignment a=b;),
return statements, the controlling expressions of if, switch, while, or do-while
statements, and all three expressions in a for statement.
21
22. Sequence point
• In C and C++, sequence points occur in the
following places
– Before a function is entered in a function call.
• The order in which the arguments are evaluated is not specified, but this
sequence point means that all of their side effects are complete before the
function is entered.
• In the expression f(i++) + g(j++) + h(k++), f is called with a parameter of the
original value of i, but i is incremented before entering the body of f. Similarly, j
and k are updated before entering g and h respectively.
• However, it is not specified in which order f(), g(), h() are executed, nor in
which order i, j, k are incremented. Variables j and k in the body of f may or
may not have been already incremented. Note that a function call f(a,b,c) is not
a use of the comma operator and the order of evaluation for a, b, and c is
unspecified.
22
23. Sequence point
• In C and C++, sequence points occur in the following
places
– At a function return, after the return value is copied into the calling
context. (This sequence point is only specified in the C++
standard; it is present only implicitly in C)
– At the end of an initializer
• for example, after the evaluation of 5 in the declaration int a = 5;.
– Between each declarator in each declarator sequence
• for example, between the two evaluations of a++ in int x = a++, y = a++.
– Aftei the action associated with input/output conversion format
specifier
• For example, in the expression printf(“foo %n %d”, &a, 42), there is a
sequence point after the %n is evaluated before printing 42
23
25. Pointers vs. Arrays
• What is the output of following code?
25
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "hello";
char q[] = "hello";
printf("%zun", sizeof(p));
printf("%zun", sizeof(q));
printf("%zun", strlen(p));
printf("%zun", strlen(q));
return 0;
}
26. Pointers vs. Arrays
• What is the output of following code?
26
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "hello";
char q[] = "hello";
printf("%zun", sizeof(p));
printf("%zun", sizeof(q));
printf("%zun", strlen(p));
printf("%zun", strlen(q));
return 0;
}
• the sizeof operator
• sizeof(array) returns the amount of
memory used by all elements in array
• sizeof(pointer) only returns the amount of
memory used by the pointer variable itself
• the & operator
• &array is an alias for &array[0] and
returns the address of the first element in
array
• &pointer returns the address of pointer
27. Pointers vs. Arrays
• What is the output of following code?
27
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "hello";
char q[] = "hello";
printf("%zun", sizeof(p));
printf("%zun", sizeof(q));
printf("%zun", strlen(p));
printf("%zun", strlen(q));
return 0;
}
• a string literal initialization of a character array
• char array[] = “abc” sets the first four
elements in array to ‘a’, ‘b’, ‘c’, and ‘0′
• char *pointer = “abc” sets pointer to the
address of the “abc” string (which may be
stored in read-only memory and thus
unchangeable)
28. Pointers vs. Arrays
• What is the output of following code?
28
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "hello";
char q[] = "hello";
printf("%zun", sizeof(p));
printf("%zun", sizeof(q));
printf("%zun", strlen(p));
printf("%zun", strlen(q));
return 0;
}
• pointer variable can be
assigned a value whereas
array variable cannot be
• arithmetic on pointer
variable is allowed whereas
array variable is not
29. Pointers vs. Arrays
• What is the output of following code?
29
#include <stdio.h>
#include <string.h>
void foo(char *p) {
printf("%zun", sizeof(p));
printf("%zun", strlen(p));
}
void bar(char q[]) {
printf("%zun", sizeof(q));
printf("%zun", strlen(q));
}
int main() {
foo("hello");
bar("hello");
return 0;
}
* arrays passed to functions are converted to
pointers
31. Example of sizeof
• What about this code snippet?
31
#include <stdio.h>
struct X { int a; char b; int c; };
int main(void)
{
printf("%zun", sizeof(int));
printf("%zun", sizeof(char));
printf("%zun", sizeof(struct X));
}
It all depends on the platform and the compile time options
provided. The only thing we know for sure is that sizeof
char is 1
4
1
12
with -fpack-struct option
4
1
9
It is very expensive to work on subword data types,
so the compiler will optimize the code by making
sure that c is on a word boundary by adding some
padding.
Suppose the program is on a 64-bit machine running in 32-
bit compatibility mode
32. Example of sizeof
• What about this code snippet?
32
#include <stdio.h>
struct X { int a; char b; int c; char d; };
int main(void)
{
printf("%zun", sizeof(int));
printf("%zun", sizeof(char));
printf("%zun", sizeof(struct X));
}
4
1
16
with -fpack-struct option
4
1
10
33. Example of sizeof
• What about this code snippet?
33
#include <stdio.h>
struct X { int a; char b; int c; char *d; };
int main(void)
{
printf("%zun", sizeof(int));
printf("%zun", sizeof(char));
printf("%zun", sizeof(struct X));
}
4
1
24
with -fpack-struct option
4
1
17
Suppose the program is on a 64-bit
machine running in 64-bit mode
c
b
a
d
24
16
12
8
4
0
35. Alignment issue
35
The legacy code snip is working well in X86 CISC machine
where instructions are available to directly access unaligned
data in memory.
When porting this legacy code from a CISC architecture to
ARM RISC machine, it will have some issues.
• The most common
unaligned access code
#include <stdio.h>
struct X {
int a;
char b;
int c;
char *d;
} __attribute((packed));
int main(void) {
struct X ss;
ss.c = 10;
return ss.c;
}
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472h/BABFDBCJ.html
36. Alignment issue
36
The ARM compiler generates assembly language code that
reads the word using an LDR instruction.
If the address is not a multiple of four, the LDR instruction
returns a rotated result rather than performing a true
unaligned word load.
#include <stdio.h>
struct X {
int a;
char b;
int c;
char *d;
} __attribute((packed));
int main(void) {
struct X ss;
ss.c = 10;
return ss.c;
}
0 1 2 3 4 5 6 7 8
Generally, this rotation is not what the programmer
expects.
• The most common
unaligned access code
37. Alignment issue
37
X86 asm:
.loc 1 11 0
movl $10, -27(%rbp)
.loc 1 12 0
movl -27(%rbp),
%eax
ARMv5 asm:
.loc 1 11 0
ldr r3, [fp, #-16]
and r3, r3, #255
orr r3, r3, #2560
str r3, [fp, #-16]
mov r3, #0
strb r3, [fp, #-12]
.loc 1 12 0
ldr r3, [fp, #-16]
mov r3, r3, lsr #8
ldrb r2, [fp, #-12] @
zero_extendqisi2
mov r2, r2, asl #24
orr r3, r2, r3
mov r0, r3
#include <stdio.h>
struct X {
int a;
char b;
int c;
char *d;
} __attribute((packed));
int main(void) {
struct X ss;
ss.c = 10;
return ss.c;
}
b
a
d
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
c
• The most common
unaligned access code
38. Alignment issue
38
ARMv7 asm:
.loc 1 11 0
ldr r3, [fp, #-16]
uxtb r3, r3
orr r3, r3, #2560
str r3, [fp, #-16]
mov r3, #0
strb r3, [fp, #-12]
.loc 1 12 0
ldr r3, [fp, #-15] @
unaligned
mov r0, r3
ARMv5 asm:
.loc 1 11 0
ldr r3, [fp, #-16]
and r3, r3, #255
orr r3, r3, #2560
str r3, [fp, #-16]
mov r3, #0
strb r3, [fp, #-12]
.loc 1 12 0
ldr r3, [fp, #-16]
mov r3, r3, lsr #8
ldrb r2, [fp, #-12] @
zero_extendqisi2
mov r2, r2, asl #24
orr r3, r2, r3
mov r0, r3
#include <stdio.h>
struct X {
int a;
char b;
int c;
char *d;
} __attribute((packed));
int main(void) {
struct X ss;
ss.c = 10;
return ss.c;
}
On ARMv6 and later
architectures, unaligned access
is fully supported.
-munaligned-access
-mno-unaligned-access
Enables (or disables) reading and writing of 16-
and 32- bit values from addresses that are not
16- or 32- bit aligned. By default unaligned
access is disabled for all pre-ARMv6 and all
ARMv6-M architectures, and enabled for all other
architectures.
https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html
b
a
d
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
c
• The most common
unaligned access code
39. Alignment issue
39
#include <stdio.h>
struct X {
int a;
char b;
int c;
char *d;
} __attribute((packed));
int main(void) {
struct X ss;
ss.c = 10;
return ss.c;
}
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0333h/Cdfcgggi.h
tml
• A==1:
• unaligned access causes an Alignment fault Data Abort
exception
• U==0 and A==0:
• if unaligned access, the LDR instruction returns a rotated
result rather than performing a true unaligned word load
• U==1 and A==0:
• unaligned access support for loads and stores of single 16-bit
half-words and 32-bit words
Table L-1 SCTLR.U bit values for different architecture versions in ARM architecture reference
manual
• The most common
unaligned access code
41. Inline function
• gcc inlinea.c inlineb.c
– inlineb.c: multiple definition of 'max'
• gcc inlinea.c inlineb.c -O1
– inlineb.c: multiple definition of 'max'
• gcc inlinea.c inlineb.c -std=c99
– inlinea.c: undefined reference to 'max'
– inlineb.c: undefined reference to 'max'
• gcc inlinea.c inlineb.c -std=c99 -O1
– compiled successfully
41
/* inline.h header file */
inline int max(int a, int b)
{
return a > b ? a : b;
}
/* inlinea.c source file */
#include "inline.h“
extern int foo(int a, int
b);
int main(void) {
int a;
a = foo(10,20);
a = max(a,30);
return a;
}
/* inlineb.c source file
*/
#include "inline.h“
int foo(int a, int b) {
int c;
c = max(a,b);
return c;
}
42. Inline function
• gcc inlinea.c inlineb.c
– inlinea.c: undefined reference to 'max'
– inlineb.c: undefined reference to 'max'
• gcc inlinea.c inlineb.c -O1
– compiled successfully
• gcc inlinea.c inlineb.c -std=c99
– inlineb.c: multiple definition of 'max'
• gcc inlinea.c inlineb.c -std=c99 -O1
– inlineb.c: multiple definition of 'max'
42
/* inline.h header file */
extern inline int max(int a, int b)
{
return a > b ? a : b;
}
/* inlinea.c source file */
#include "inline.h“
extern int foo(int a, int
b);
int main(void) {
int a;
a = foo(10,20);
a = max(a,30);
return a;
}
/* inlineb.c source file
*/
#include "inline.h“
int foo(int a, int b) {
int c;
c = max(a,b);
return c;
}
43. Inline function
• static inline
– The definition is equal in GNU89 and C99.
– The function will be used in the same translation units. If the function is inlined, the
stand-alone object code is never emitted.
– If the function is not inlined, the stand-alone object code will be emitted, but it can
not be used by other translation units.
• Inline
– In GNU89, no matter the function is inlined, the stand-alone object code will be
emitted, and can be used by other translation units.
– In C99, no matter the function is inlined, the stand-alone object code will not be
emitted.
• extern inline
– In GNU89, no matter the function is inlined, the stand-alone object code will not be
emitted.
– In C99, no matter the function is inlined, the stand-alone object code will be
emitted, and can be used by other translation units.
43
44. Inline function – Portable model
• gcc inlinea.c inlineb.c
– compiled successfully
• gcc inlinea.c inlineb.c -std=c99
– compiled successfully
• gcc inlinea.c inlineb.c -O1
– compiled successfully
• gcc inlinea.c inlineb.c -std=c99 -O1
– compiled successfully
44
/* inline.h header file */
static inline int max(int a, int b)
{
return a > b ? a : b;
}
/* inlinea.c source file */
#include "inline.h"
extern int foo(int a, int b);
int main(void) {
int a;
a = foo(10,20);
a = max(a,30);
return a;
}
/* inlineb.c source file */
#include "inline.h"
int foo(int a, int b) {
int c;
c = max(a,b);
return c;
}
inlinea.o:
U foo
T main
t max
inlineb.o:
T foo
t max
inlinea.o:
U foo
T main
inlineb.o:
T foo
45. Inline function –GNU89 model
• gcc inlinea.c inlineb.c
– compiled successfully
• gcc inlinea.c inlineb.c -O1
– compiled successfully
45
/* inline.h header file */
extern inline int max(int a, int b)
{
return a > b ? a : b;
}
/* inlinea.c source file */
#include "inline.h"
extern int foo(int a, int
b);
int main(void) {
int a;
a = foo(10,20);
a = max(a,30);
return a;
}
/* inlineb.c source file
*/
#include "inline.h"
int max(int a, int b);
int foo(int a, int b) {
int c;
c = max(a,b);
return c;
}
inlinea.o:
U foo
T main
inlinea.o:
U foo
T main
U max
inlineb.o:
T foo
T max
inlineb.o:
T foo
T max
46. Inline function –C99 model
• gcc inlinea.c inlineb.c -std=c99
– compiled successfully
• gcc inlinea.c inlineb.c -std=c99 -O1
– compiled successfully
46
/* inline.h header file */
inline int max(int a, int b) {
return a > b ? a : b;
}
/* inlinea.c source file */
#include "inline.h"
extern int foo(int a, int b);
int main(void) {
int a;
a = foo(10,20);
a = max(a,30);
return a;
}
/* inlineb.c source file */
#include "inline.h"
extern int max(int a, int b);
int foo(int a, int b) {
int c;
c = max(a,b);
return c;
}
inlinea.o:
U foo
T main
U max
inlinea.o:
U foo
T main
inlineb.o:
T foo
T max
inlineb.o:
T foo
T max
References:
http://www.greenend.org.uk/rjk/tech/inline.html
https://gustedt.wordpress.com/2010/11/29/myth-and-reality-about-inline-in-c99/
http://www.drdobbs.com/the-new-c-inline-functions/184401540
https://gcc.gnu.org/onlinedocs/gcc/Inline.html
48. Volatile type qualifier
• Two functions each executed in a separate thread with the same
pointer as a parameter
48
void add( int *i) {
while (1) {
*i = 0;
for (int a=0; a<10; a++)
(*i)++;
}
}
void check( int *i) {
while (*i != 5) ;
}
void add(volatile int *i) {
while (1) {
*i = 0;
for (int a=0; a<10; a++)
(*i)++;
}
}
void check( int *i) {
while (*i != 5) ;
}
C99 support
References
https://en.wikipedia.org/wiki/Volatile_%28computer_programming%29
https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html
49. Variable length argument
• http://blog.aaronballman.com/2012/06/how-variable-argument-lists-
work-in-c/
49
#include <stdarg.h>
void foo( int bar, ... )
{
va_list args;
va_start( args, bar );
for (;;) {
some_type t = va_arg( args,
some_type );
/* Do something with t */
}
va_end( args );
}
50. Nested function
50
#include <stdio.h>
typedef int (*func_t)(int);
static func_t foo(int arg)
{
int nested(int nested_arg)
{
return (arg + nested_arg);
}
return &nested;
}
int main()
{
func_t g = foo(100);
printf("%dn", (*g)(20));
return 0;
}
nested:
access foo frame by ip register
mov r0, #result
bx lr
foo:
load .LTRAMP0 into stack
store the @frame to TRAMP0[2]
store the @nested to TRAMP0[3]
flush cache
mov r0, @TRAMP0
bx lr
main:
bl foo
blx r0
bl printf
bx lr
.word TRAMP0
.word nested
TRAMP0:
ldr ip, [pc, #0]
ldr pc, [pc, #0]
.word 0
.word 0
trampoline
References
https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html
https://gcc.gnu.org/onlinedocs/gccint/Trampolines.html
http://blog.linux.org.tw/~jserv/archives/2010/07/gcc_nested_func.html
51. Arrays of length zero
• Arrays of Length Zero
51
#include <stdio.h>
struct X {
int a;
char b;
int c;
char d[0];
};
int main(void) {
printf("%zun", sizeof(struct X));
return 0;
}
#include <stdio.h>
struct X {
int a;
char b;
int c;
char d[];
};
int main(void) {
printf("%zun", sizeof(struct X));
return 0;
}
A real case in linux kernel:
http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/
tree/include/uapi/linux/romfs_fs.h
C99: flexible array memberGNU C support
C90 should have length 1
C99: array of a variable size
d[length], length is runtime define
https://gcc.gnu.org/onlinedocs/gcc/Variable-
Length.html
52. Restricted pointers (C99)
• What is the issue of the following code?
• Solution:
52
void copy( char *s1, char *s2, int n)
{
while (n--)
*s1++ = *s2++;
}
void copy ( char restrict *s1, char restrict *s2, int
n)
{
while (n--)
*s1++ = *s2++;
}
54. More about new standard
• C99
– https://en.wikipedia.org/wiki/C99
• C11
– https://en.wikipedia.org/wiki/C11_%28C_standard_revision%29
– http://blog.smartbear.com/codereviewer/c11-a-new-c-standard-
aiming-at-safer-programming/
54
55. References
• 「你所不知道的 C 語言」系列講座
• Deep C
• Are pointers and arrays equivalent in C?
• Lesser known C features
55
Editor's Notes
The instruction set of most processors are optimized for moving a word of data between
memory and CPU. Suppose you want to change a value crossing a word boundary, you would
need to read two words, mask out the value, change the value, mask and write back two words.
Perhaps 10 times slower. Remember, C is focused on execution speed.