311 (5) C

Page 1:

  • Course: CMPSC 311 - Introduction to Systems Programming

  • Professor: Sencun Zhu

  • Slides by: Professor Patrick McDaniel and Professor Abutalib Aghayev

Page 2:

  • Files in C: foo.c, foo.h, bar.c

  • C workflow: Editing, linking, executing, debugging, profiling

  • Types of libraries: Statically linked libraries (libZ.a), Shared libraries (libc.so)

  • Object files: bar.o, foo.o

Page 3:

  • Defining a function in C

  • Example: sumTo function

    • Parameters: int max

    • Variables: int i, int sum

    • Loop: for (i=1; i<=max; i++)

    • Statement: sum += i

    • Return: return sum

Page 4:

  • Conversion from C to machine code

  • Example: dosum function

  • C source file: dosum.c

  • C compiler: gcc -S

  • Assembly source file: dosum.s

  • Assembler: as

  • Machine code: dosum.o

Page 5:

  • C compilers generate object ".o" files directly

  • Object code is re-locatable machine code

  • Object code generally cannot be executed without manipulation (e.g., via a linker)

Page 6:

  • Anatomy of a C program

  • Includes: #include <stdio.h>

  • Functions: main() and myfunc()

  • Example program

Page 7:

  • Running a C program

  • Compiling and linking: gcc -g -Wall main.c -o main

  • Executing the program: ./main

Page 8:

  • Running a program in UNIX

  • Search path: PATH environment variable

  • Adding to search path: export PATH=$PATH:/new/path

Page 9:

  • Multi-file C programs

  • Example: dosum function in dosum.c

  • Example: main function in sumnum.c

  • Prototyping functions

Page 10:

  • Multi-file C programs

  • Including standard libraries: #include <stdio.h>

Page 11:

  • Compiling multi-file programs

  • Linking multiple object files to produce an executable

  • Linking standard libraries

Page 12:

  • Object files in C

  • Object files contain machine code produced by the compiler

  • Object files might contain references to external symbols

  • Linking resolves external symbols

Page 13:

  • Similarities between C and Java

  • Syntax, types, type-casting, expressions, operators, scope, comments

Page 14:

  • Primitive types in C

  • Integer types: char, int, short, long

  • Floating point types: float, double

  • Type modifiers: signed, unsigned

Page 15:

  • C99 extended integer types

  • Solve the conundrum of "how big is a long int?"

  • Example using stdint.h library

Page 16:

  • Variables in C are similar to Java

    • Variables must be declared at the start of a function or block (not required since C99)

    • Variables need not be initialized before use (gcc -Wall will warn)

    • It is always recommended to initialize variables before use

  • Example code:

    #include <stdio.h>
    int main(void) {
      int x, y = 5;  // note x is uninitialized!
      long z = x+y;
      printf("z is '%ld'\n", z); // what’s printed?
      {
        int y = 10;
        printf("y is '%d'\n", y);
      }
      int w = 20;  // ok in c99
      printf("y is '%d' , w is '%d'\n", y, w);
      return 0;
    }

Page 17:

  • The const qualifier in C indicates that a variable's value cannot change

  • The compiler will issue an error if you try to violate this

  • Example code:

    #include <stdio.h>
    int main(void) {
      const double MAX_GPA = 4.0;
      printf("MAX_GPA: %g\n" , MAX_GPA);
      MAX_GPA = 5.0;  // illegal!
      return 0;
    }

Page 18:

  • Loops in C are similar to Java

    • For loops cannot declare variables in the loop header (changed in C99)

    • If/else, while, and do while loops are available

    • C does not have a boolean type (changed in C99 with #include <stdbool.h>)

    • Any type can be used in conditionals, where 0 means false and everything else means true

  • Example code:

    int i;
    for (i=0; i < 100; i++) {
      if (i % 10 == 0) {
        printf("i: %d\n" , i);
      }
    }

Page 19:

  • Pointers in C allow you to store memory addresses of variables

  • Key concepts:

    • Taking the address of a variable: &

    • Dereferencing a pointer: *

    • Aliasing: *ip is an alias for i

  • Example code:

    #include <stdio.h>
    int main(void) {
      int i = 5;
      int *ip = &i;
      printf("%d\n" , i);
      printf("%p\n", ip);
      *ip = 42;
      printf("%d\n", i);
      printf("%d\n", *ip);
    }

Page 20:

  • C always passes arguments by value

  • Pointers allow you to pass arguments by reference

  • Example code:

    void add_pbv(int c) {
      c += 10;
      printf("pbv c: %d\n", c);
    }
    void add_pbr(int *c) {
      *c += 10;
      printf("pbr *c: %d\n", *c);
    }
    int main(void) {
      int x = 1;
      printf("x: %d\n", x);
      add_pbv(x);
      printf("x: %d\n", x);
      add_pbr(&x);
      printf("x: %d\n", x);
      return 0;
    }

Page 21:

  • C passes arguments by value

  • If the callee modifies an argument, the caller's copy isn't modified

  • Example code:

    void swap(int a, int b) {
      int tmp = a;
      a = b;
      b = tmp;
    }
    int main(void) {
      int a = 42, b = -7;
      swap(a, b);
      printf("a: %d, b: %d\n", a, b);
      return 0;
    }

Page 30:

  • Pointers can be used to pass arguments by reference in C

  • The callee still receives a copy of the argument, but it is a pointer to the variable in the scope of the caller

  • Example code:

    void swap(int *a, int *b) {
      int tmp = *a;
      *a = *b;
      *b = tmp;
    }
    int main(void) {
      int a = 42, b = -7;
      swap(&a, &b);
      printf("a: %d, b: %d\n", a, b);
      return 0;
    }
  • The key to C (and languages like it) is getting good at using pointers.

Page 31:

  • Pass-by-reference OS kernel [protected] stack main a 42 b -7

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • CMPSC 311 - Introduction to Systems Programming

Page 32:

  • Pass-by-reference OS kernel [protected] stack main a 42 b -7

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • swap a ? b ?

  • tmp ?

  • CMPSC 311 - Introduction to Systems Programming

Page 33:

  • Pass-by-reference OS kernel [protected] stack main a 42 b -7

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • swap a b tmp ?

  • CMPSC 311 - Introduction to Systems Programming

Page 34:

  • Pass-by-reference OS kernel [protected] stack main a 42 b -7

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • swap a b tmp 42

  • CMPSC 311 - Introduction to Systems Programming

Page 35:

  • Pass-by-reference OS kernel [protected] stack main a -7 b -7

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • swap a b tmp 42

  • CMPSC 311 - Introduction to Systems Programming

Page 36:

  • Pass-by-reference OS kernel [protected] stack main a -7 b 42

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • swap a b tmp 42

  • CMPSC 311 - Introduction to Systems Programming

Page 37:

  • Pass-by-reference OS kernel [protected] stack main a -7 b 42

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • CMPSC 311 - Introduction to Systems Programming

Page 38:

  • Pass-by-reference OS kernel [protected] stack main a -7 b 42

  • void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }

  • int main(void) { int a = 42, b = -7; swap(&a, &b); printf("a: %d, b: %d
    ", a, b); return 0; }

  • CMPSC 311 - Introduction to Systems Programming

Page 39:

  • Very different than Java

  • arrays

  • just a bare, contiguous block of memory of the correct size

  • array of 6 integers requires 6 x 4 bytes = 24 bytes of memory

  • arrays have no methods, do not know their own length (no bounds checking)

  • C doesn’t stop you from overstepping the end of an array!

  • many, many security bugs come from this (buffer overflow)

  • CMPSC 311 - Introduction to Systems Programming

Page 40:

  • Very different than Java

  • arrays

  • just a bare, contiguous block of memory of the correct size

  • array of 6 integers requires 6 x 4 bytes = 24 bytes of memory

  • arrays have no methods, do not know their own length (no bounds checking)

  • C doesn’t stop you from overstepping the end of an array!

  • many, many security bugs come from this (buffer overflow)

  • X[7] = 45; // Legal C, but can cause memory fault! ! ! !

  • CMPSC 311 - Introduction to Systems Programming

Page 41:

  • Very different than Java

  • strings

  • array of char

  • terminated by the NULL character ‘\0’

  • are not objects, have no methods; string.h has helpful utilities (see strings lecture coming soon!)

  • h e l l o
    \0

  • char *x = ”hello
    ”;

  • x

  • CMPSC 311 - Introduction to Systems Programming

Page 42:

  • Very different than Java

  • errors and exceptions

  • C has no exceptions (no try / catch)

  • errors are returned as integer error codes from functions

  • sometimes makes error handling ugly and inelegant

  • some support from OS using signals (end of semester)

  • crashes

  • if you do something bad, you’ll end up spraying bytes around memory

  • hopefully causing a “segmentation fault” and crash

  • objects

  • there aren’t any; struct is closest feature (set of fields)

  • CMPSC 311 - Introduction to Systems Programming

Page 43:

  • Very different than Java

  • memory management

  • there is no garbage collector

  • anything you allocate you have to free (memory leaks)

  • local variables are allocated off of the stack

  • freed when you return from the function

  • global and static variables are allocated in a data segment

  • are freed when your program exits

  • you can allocate memory in the heap segment using malloc()

  • you must free malloc’ed memory with free()

  • failing to free is a leak, double-freeing is an error (hopefully crash)

  • CMPSC 311 - Introduction to Systems Programming

Page 44:

  • Very different than Java

  • console I/O

  • C library (libc) has portable routines for reading/writing, e.g., scanf() , printf()

  • file I/O

  • C library has portable routines for reading/writing

  • fopen() , fread() , fwrite() , fclose() , etc.

  • does buffering by default, is blocking by default

  • OS provides system calls

  • we’ll be using these: more control over buffering, blocking

  • Low level binary reads and writes, e.g., read() , write() , open() , close()

  • CMPSC 311 - Introduction to Systems Programming

Page 45:

  • Very different than Java

  • network I/O

  • C standard library has no notion of network I/O

  • OS provides (somewhat portable) routines

  • lots of complexity lies here

  • errors: network can fail

  • performance: network can be slow

  • concurrency: servers speak to thousands of clients simultaneously

  • Note: most of these topics will be covered in detail over the semester.

  • CMPSC 311 - Introduction to Systems Programming

Page 46:

  • Very different than Java

  • Libraries you can count on

  • C has very few compared to most other languages

  • no built-in trees, hash tables, linked lists, sort , etc.

  • you have to write many things on your own

  • particularly data structures

  • error prone, tedious, hard to build efficiently and portably

  • less productive language than Java, C++, python, or others

  • CMPSC 311 - Introduction to Systems Programming

Page 47:

  • Problem: ordering

  • Don’t call a function that hasn’t been declared yet:

  • #include <stdio.h>

  • int main(void) { printf("sumTo(5) is: %d
    ", sumTo(5)); return 0; }

  • // sum integers from 1 to max

  • int sumTo(int max) { int i, sum = 0; for (i=1; i<=max; i++) { sum += i; } return sum; }

  • CMPSC 311 - Introduction to Systems Programming

Page 48:

  • Problem: ordering

  • Solution 1: reverse order of definition

  • #include <stdio.h>

Page 49:

Problem: ordering

  • Solution 2: provide function declaration

    • Teaches the compiler the argument and return types of the function that will appear later

    • The body-less function declaration is called a functional prototype.

  • Example code:

    #include <stdio.h>
    
    // this function prototype is a declaration of sumTo
    int sumTo(int);
    
    int main(void) {
        printf("sumTo(5) is: %d\n", sumTo(5));
        return 0;
    }
    
    // sum integers from 1 to max
    int sumTo(int max) {
        int i, sum = 0;
        for (i=1; i<=max; i++) {
            sum += i;
        }
        return sum;
    }

Page 50:

UNIX Std*

  • There are three predefined streams provided to all UNIX programs

    • Standard input (stdin)