A Quick Introduction To C Programming: Lewis Girod CENS Systems Lab July 5, 2005
A Quick Introduction To C Programming: Lewis Girod CENS Systems Lab July 5, 2005
A Quick Introduction To C Programming: Lewis Girod CENS Systems Lab July 5, 2005
Lewis Girod
CENS Systems Lab
July 5, 2005
http://lecs.cs.ucla.edu/~girod/talks/c-tutorial.ppt
1
or,
2
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 with functions, avoid duplicate state variables
3
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
printf(“Hello World\n”);
return 0;
such as emacs, save as file e.g. my_program.c
}
4
C Syntax and Hello World
{
Blocks of code (“lexical
printf(“Hello World\n”); scopes”) are marked by { … }
return 0;
}
Return ‘0’ from this function Print out a message. ‘\n’ means “new line”.
5
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;
} Why ?
__extension__ typedef unsigned long long int __dev_t; In Preprocessing, source code is “expanded” into a
__extension__ typedef
__extension__ typedef
unsigned int
unsigned int
__uid_t;
__gid_t; larger form that is simpler for the compiler to
__extension__ typedef
__extension__ typedef
unsigned long int
unsigned long long int
__ino_t;
__ino64_t;
understand. Any line that starts with ‘#’ is a line
__extension__ typedef
__extension__ typedef
unsigned int
long int
__nlink_t;
__off_t;
that is 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)
{
• Macros are “expanded” (#define)
printf(“Hello World\n”);
return 0;
• Comments are stripped out ( /* */ , // )
}
• Continued lines are joined ( \ )
\?
6
OK, We’re Back.. What is a Function?
Function Arguments
#include <stdio.h>
/* The simplest C Program */
int main(int argc, char **argv)
{ Calling a Function: “printf()” is just
another function, like main(). It’s defined
printf(“Hello World\n”); for you in a “library”, a collection of
return 0; functions you can call from your program.
}
Returning a value
7
What is “Memory”?
8
What is a Variable?
symbol table?
9
Multi-byte Variables
10
Lexical Scoping (Returns nothing)
void p(char x)
Every Variable is Defined within some scope. {
A Variable cannot be referenced by name char y;
/* p,x */
11
Expressions and Evaluation
1 + 2 * 2 1 + 4 5
(1 + 2) * 2 3 * 2 6
13
Assignment Operators
Don’t confuse = and ==! The compiler will warn “suggest parens”.
14
A More Complex Program: pow
#include <stdio.h>
“if” statement #include <inttypes.h>
15
The “Stack”
#include <stdio.h>
Recall lexical scoping. If a variable is valid #include <inttypes.h>
“within the scope of a function”, what float pow(float x, uint32_t exp)
happens when you call that function {
/* base case */
recursively? Is there more than one “exp”? if (exp == 0) {
return 1.0; static
}
Yes. Each function call allocates a “stack
/* “recursive” case */ Java?
frame” where Variables within that function’s return x*pow(x, exp – 1);
scope will reside. }
int argc 1
char **argv 0x2342
float p undefined
5.0 Grows
16
Iterative pow(): the “while” loop
Other languages?
float pow(float x, uint exp)
Problem: “recursion” eats stack space (in C). {
int i=0;
Each loop must allocate space for arguments float result=1.0;
and local variables, because each new call while (i < exp) {
result = result * x;
creates a new “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;
}
17
The “for” loop
The “for” loop is just shorthand for this “while” loop structure.
18
Referencing Data from Other Scopes
19
Can a function modify its arguments?
20
NO!
float x 2.0
32.0
uint32_t exp 5
float result 1.0
32.0
21
Passing Addresses
22
“Pointers”
23
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).
There are two general causes for these errors: How should pointers be initialized?
• 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? */
}
24
Answer: Invalid!
char * get_pointer()
{
char x=0;
return &x;
} But now, ptr points to a
{
location that’s no longer in
char * ptr = get_pointer(); use, and will be reused the
*ptr = 12; /* valid? */
other_function(); next time a function is called!
}
101 charaverage
int x 0 101
12
456603
Return
25
More on Types
We’ve seen a few types at this point: char, int, float, char *
26
Structures
Packing?
struct: a way to compose existing types into a structure
27
Arrays
28
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.
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 extra parens to
int (*x)[10]; x is
a pointer to clarify the expression.
an array of
int
29
Function Types
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
/* typedef defining this type: */ is 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);
30
Dynamic Memory Allocation
31
Caveats with Dynamic Memory
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.
32
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!
33
Macros
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); */
34
Macros and Readability
/* often best to define these types of macro right where they are used */
#define CASE(str) if (strncasecmp(arg, str, strlen(str)) == 0)
35
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;
36
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;
}
37
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
38
Addressing Complexity
39
Addressing Complexity
40
Addressing Mutability
struct pkt_hdr {
int source; struct pkt_hdr {
int dest; int source;
int length; int dest; Otherwise when
}; int length;
struct pkt { };
one changes, you
int source; struct pkt { have to find and fix
int dest; struct pkt_hdr hdr;
int length; uint8_t payload[100];
all the other places
uint8_t payload[100]; };
};
41
Solutions to the pow() challenge question
Recursive Iterative
42