First Time in C Programming
First Time in C Programming
Dediary Prasetya
Writing and Running Programs
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv) 1. Write text of program (source code) using an editor such
{
printf(“Hello World\n”); as emacs, save as file e.g. my_program.c
return 0;
}
#include <stdio.h>
The main() function is always
/* The simplest C Program */ where your program starts
int main(int argc, char **argv) running.
{
printf(“Hello World\n”);
Blocks of code (“lexical scopes”)
are marked by { … }
return 0;
}
Return ‘0’ from this function Print out a message. ‘\n’ means “new line”.
3
A Quick Digression About the Compiler
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
Preprocess Compilation occurs in two steps:
{
printf(“Hello World\n”); “Preprocessing” and “Compiling”
return 0;
}
__extension__ typedef unsigned long long int __dev_t; In Preprocessing, source code is “expanded” into a
__extension__ typedef unsigned int __uid_t;
__extension__ typedef unsigned int __gid_t; larger form that is simpler for the compiler to
__extension__ typedef unsigned long int __ino_t;
__extension__ typedef unsigned long long int __ino64_t; understand. Any line that starts with ‘#’ is a line that is
__extension__ typedef unsigned int __nlink_t;
__extension__ typedef long int __off_t; interpreted by the Preprocessor.
__extension__ typedef long long int __off64_t;
extern void flockfile (FILE *__stream) ;
extern int ftrylockfile (FILE *__stream) ;
extern void funlockfile (FILE *__stream) ; • Include files are “pasted in” (#include)
int main(int argc, char **argv)
{
printf(“Hello World\n”);
• Macros are “expanded” (#define)
}
return 0; • Comments are stripped out ( /* */ , // )
• Continued lines are joined ( \ )
4
What is a Function?
A Function is a series of instructions to run. You pass
Arguments to a function and it returns a Value.
Function Arguments
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
{ Calling a Function: “printf()” is just another
printf(“Hello World\n”); function, like main(). It’s defined for you in a
“library”, a collection of functions you can call
return 0;
from your program.
}
Returning a value
5
What is “Memory”?
Addr Value
Memory is like a big table of numbered slots
where bytes can be stored. 0
1
The number of a slot is its Address. 2
One byte Value can be stored in each slot. 3
72?
4 ‘H’ (72)
Some “logical” data values span more than one 5 ‘e’ (101)
slot, like the character string “Hello\n” 6 ‘l’ (108)
7 ‘l’ (108)
A Type names a logical meaning to a span of 8 ‘o’ (111)
memory. Some simple types are:
9 ‘\n’ (10)
char a single character (1 slot) 10 ‘\0’ (0)
char [10] an array of 10 characters
11
int signed 4 byte integer
float 4 byte floating point 12
int64_t signed 8 byte integer
6
What is a Variable?
Symbol Addr Value
A Variable names a place in memory where you
store a Value of a certain Type. 0
1
You first Define a variable by giving it a name 2
and specifying the type, and optionally an 3
initial value
x 4 ?
y 5 ‘e’ (101)
char x; Initial value of x is undefined
char y=‘e’; 6
7
The compiler puts them
Initial value
somewhere in memory. 8
9
Name
10
11
Type is single character (char)
12
7
Multi-byte Variables
Different types consume different amounts of Symbol Addr Value
memory. Most architectures store data on 0
“word boundaries”, or even multiples of the 1
size of a primitive data type (int, char) 2
3
char x;
char y=‘e’; x 4 ?
int z = 0x01020304; y 5 ‘e’ (101)
6
0x means the constant is padding
written in hex 7
z 8 4
9 3
An int consumes 4 bytes
10 2
11 1
12
8
ASCII is an abbreviation for American Standard Code for Information Interchange. ASCII was
developed in the 1960s and was based on earlier codes used by telegraph systems
ASCII does not have diacritics (marks that are added to a letter, like the dots (umlauts) above vowels in
German, or the tilde (~) above the 'n' for the 'ñ' used in Spanish).
This led to some systems using 8 bits (a full byte) instead of 7 bits. The proper name for systems that use 8
bits is called extended ASCII. Eight bits allows for 256 characters. The first 128 characters must be the same
as for ASCII and the rest are usually used for alphabetic letters with accents, for example like É, È, Î and Ü.
And some systems like those using Chinese characters still do not work, as they use thousands of characters.
So unicode was created to have one common system for all languages.
Start
Get Name
Get City
Get date of
birth
Stop
#include <stdio.h>
int main()
{
int age, dd,mm,mmm,yy,dy;
char name[20], city[20];
15
Expressions and Evaluation
Expressions combine Values using Operators, according to precedence.
1 + 2 * 2 1 + 4 5
(1 + 2) * 2 3 * 2 6
Don’t confuse = and ==! The compiler will warn “suggest parens”.
18
A More Complex Program: pow
#include <stdio.h>
“if” statement #include <inttypes.h>
19
The “Stack”
#include <stdio.h>
Recall lexical scoping. If a variable is valid #include <inttypes.h>
“within the scope of a function”, what happens float pow(float x, uint32_t exp)
when you call that function recursively? Is there {
/* base case */
more than one “exp”? if (exp == 0) {
return 1.0; static
}
Yes. Each function call allocates a “stack frame”
/* “recursive” case */ Java?
where Variables within that function’s scope will return x*pow(x, exp – 1);
}
reside.
int main(int argc, char **argv)
float x 5.0 {
float p;
uint32_t exp 0 Return 1.0 p = pow(5.0, 1);
printf(“p = %f\n”, p);
float x 5.0 return 0;
}
uint32_t exp 1 Return 5.0
int argc 1
char **argv 0x2342
float p undefined
5.0 Grows
20
Iterative pow(): the “while” loop
float pow(float x, uint exp)
Problem: “recursion” eats stack space (in C). Each {
int i=0;
loop must allocate space for arguments and local float result=1.0;
variables, because each new call creates a new while (i < exp) {
result = result * x;
“scope”. i++;
}
return result;
}
Solution: “while” loop.
int main(int argc, char **argv)
loop: {
if (condition) { while (condition) {
float p;
statements; statements;
p = pow(10.0, 5);
goto loop; }
printf(“p = %f\n”, p);
} return 0;
}
21
The “for” loop
The “for” loop is just shorthand for this “while” loop structure.
22
Referencing Data from Other Scopes
So far, all of our examples all of the data values we have used have
been defined in our lexical scope
23
Can a function modify its arguments?
What if we wanted to implement a function pow_assign() that
modified its argument, so that these are equivalent:
float p = 2.0; float p = 2.0;
/* p is 2.0 here */ /* p is 2.0 here */
p = pow(p, 5); pow_assign(p, 5);
/* p is 32.0 here */ /* p is 32.0 here */
24
NO!
Remember the stack!
float x 2.0
32.0
uint32_t exp 5
float result 1.0
32.0
25
Passing Addresses
Symbol Addr Value
Recall our model for variables stored in 0
memory 1
2
What if we had a way to find out the 3
address of a symbol, and a way to
char x 4 ‘H’ (72)
reference that memory location by
address? char y 5 ‘e’ (101)
6
address_of(y) == 5
memory_at[5] == 101 7
void f(address_of_char p)
8
{
memory_at[p] = memory_at[p] - 32;
9
} 10
char y = 101; /* y is 101 */ 11
f(address_of(y)); /* i.e. f(5) */
/* y is now 101-32 = 69 */ 12
26
“Pointers”
This is exactly how “pointers” work.
A “pointer type”: pointer to char
“address of” or reference operator: &
“memory_at” or dereference operator: *
27
Pointer Validity
A Valid pointer is one that points to memory that your program controls.
Using invalid pointers will cause non-deterministic behavior, and will often
cause Linux to kill your process (SEGV or Segmentation Fault).
How should pointers be initialized?
There are two general causes for these errors:
• Program errors that set the pointer value to a strange number
• Use of a pointer that was at one time valid, but later became invalid
char * get_pointer()
{
char x=0;
return &x;
}
{
char * ptr = get_pointer();
*ptr = 12; /* valid? */
}
28
Answer: Invalid!
A pointer to a variable allocated on the stack becomes invalid when that
variable goes out of scope and the stack frame is “popped”. The pointer will
point to an area of the memory that may later get reused and rewritten.
char * get_pointer()
{
char x=0;
return &x;
} But now, ptr points to a
{
location that’s no longer in use,
char * ptr = get_pointer(); and will be reused the next time
*ptr = 12; /* valid? */
other_function(); a function is called!
}
101 charaverage
int x 0 101
12
456603
Return
30
Structures Packing?
struct: a way to compose existing types into a structure
31
Arrays
Arrays in C are composed of a particular type, laid out in memory in a
repeating pattern. Array elements are accessed by stepping forward in
memory from the base of the array by a multiple of the element size.
32
How to Parse and Define C Types
At this point we have seen a few basic types, arrays, pointer types, and
structures. So far we’ve glossed over how types are named.
int x; /* int; */ typedef int T;
int *x; /* pointer to int; */ typedef int *T;
int x[10]; /* array of ints; */ typedef int T[10];
typedef defines a
int *x[10]; /* array of pointers to int; */ typedef int *T[10]; new type
int (*x)[10]; /* pointer to array of ints; */ typedef int (*T)[10];
C type names are parsed by starting at the type name and working outwards
according to the rules of precedence:
x is
an array of
pointers to
int *x[10]; int Arrays are the primary source of
confusion. When in doubt, use
x is extra parens to clarify the
int (*x)[10];
a pointer to expression.
an array of
int
33
Function Types
The other confusing form is the function type.
For example, qsort: (a sort function in the standard library)
void qsort(void *base, size_t nmemb, size_t size, The last argument is a
int (*compar)(const void *, const void *));
comparison function
/* function matching this type: */
int cmp_function(const void *x, const void *y);
const means the function is
/* typedef defining this type: */ not allowed to modify
typedef int (*cmp_type) (const void *, const void *); memory via this pointer.
/* rewrite qsort prototype using our typedef */
void qsort(void *base, size_t nmemb, size_t size, cmp_type compar);
34
Dynamic Memory Allocation
So far all of our examples have allocated variables statically by defining them
in our program. This allocates them in the stack.
35
Caveats with Dynamic Memory
Dynamic memory is useful. But it has several caveats:
Whereas the compiler enforces that reclaimed stack space can no longer be
reached, it is easy to accidentally keep a pointer to dynamic memory that has
been freed. Whenever you free memory you must be certain that you will not try
to use it again. It is safest to erase any pointers to it.
Because dynamic memory always uses pointers, there is generally no way for the
compiler to statically verify usage of dynamic memory. This means that errors
that are detectable with static allocation are not with dynamic
36
Some Common Errors and Hints
sizeof() can take a variable reference in place of a type name. This gurantees the right allocation,
but don’t accidentally allocate the sizeof() the pointer instead of the object!
Macros and static inline functions must be included in any file that uses
them, usually via a header file. Common uses for macros:
More on C
/* Macros are used to define constants */ constants?
#define FUDGE_FACTOR 45.6 Float constants must have a decimal
#define MSEC_PER_SEC 1000 point, else they are type int
#define INPUT_FILENAME “my_input_file”
enums
/* Macros are used to do constant arithmetic */
Why?
#define TIMER_VAL (2*MSEC_PER_SEC) Put expressions in parens.
/* Macros are used to capture information from the compiler */
#define DBG(args...) \
do { \ Multi-line macros need \
fprintf(stderr, “%s:%s:%d: “, \
__FUNCTION__, __FILE__, __LINENO__); \ args… grabs rest of args
fprintf(stderr, args...); \
} while (0) Why?
Enclose multi-statement macros in do{}while(0)
/* ex. DBG(“error: %d”, errno); */
38
Macros and Readability
Sometimes macros can be used to improve code readability… but make sure
what’s going on is obvious.
/* often best to define these types of macro right where they are used */
#define CASE(str) if (strncasecmp(arg, str, strlen(str)) == 0)
Macros can be used to generate static inline functions. This is like a C version
of a C++ template. See emstar/libmisc/include/queue.h for an example of
this technique.
39
Using “goto”
Some schools of thought frown upon goto, but goto has its place. A good
philosophy is, always write code in the most expressive and clear way
possible. If that involves using goto, then goto is not bad.
goto try_again;
goto fail;
40
Unrolling a Failed Initialization
{
using goto
state_t *initialize()
state_t *initialize()
/* allocate state struct */ {
state_t *s = g_new0(state_t, 1); /* allocate state struct */
if (s) { state_t *s = g_new0(state_t, 1);
/* allocate sub-structure */ if (s == NULL) goto free0;
s->sub = g_new0(sub_t, 1);
if (s->sub) { /* allocate sub-structure */
/* open file */ s->sub = g_new0(sub_t, 1);
s->sub->fd = if (s->sub == NULL) goto free1;
open(“/dev/null”, O_RDONLY);
if (s->sub->fd >= 0) { /* open file */
/* success! */ s->sub->fd =
} open(“/dev/null”, O_RDONLY);
else { if (s->sub->fd < 0) goto free2;
free(s->sub);
free(s); /* success! */
s = NULL; return s;
}
} free2:
else { free(s->sub);
/* failed! */ free1:
free(s); free(s);
s = NULL; free0:
} return NULL;
} }
return s;
}
41
High Level Question: Why is
Software Hard?
Answer(s):
• Complexity: Every conditional (“if”) doubles
number of paths through your code, every
bit of state doubles possible states
– Solution: reuse code paths, avoid duplicate state variables
43
Addressing Complexity
• Complexity: Every conditional (“if”) doubles number of paths through
your code, every bit of state doubles possible states
– Solution: reuse code paths, avoid duplicate state variables
44
Addressing Mutability
• Mutability: Software is easy to change.. Great for rapid fixes .. And
rapid breakage .. always one character away from a bug
– Solution: tidy, readable code, easy to understand by inspection.
Avoid code duplication; physically the same logically the same
struct pkt_hdr {
int source; struct pkt_hdr {
int dest; int source;
int length; int dest; Otherwise when one
}; int length;
struct pkt { };
changes, you have to
int source; struct pkt { find and fix all the
int dest; struct pkt_hdr hdr;
int length; uint8_t payload[100];
other places
uint8_t payload[100]; };
};
45
Solutions to the pow() challenge
Recursive
questionIterative
46