CSC 357 Lecture Notes Week 2, Part 1
C Program Structure Pointers, Arrays, and Structs in C Memory Management



  1. An initial example -- a simple linked list in C.
    1. See the attached listings for
    2. We'll start with a tour through the listings.
    3. Then we'll cover the concepts and features that appear in the example, with particular emphasis on chapters 5 and 6 of K&R.

  2. Review of overall C program structure.
    1. C programs are defined as collections of .c and .h files.
      1. The .h files are "header" files that contain data and function declarations.
      2. The .c files contain the function definitions, i.e., the implementations.
    2. C programs also include preprocessor directives #include and #define.

  3. Constants and parameterized macros with #define.
    1. As explained in K&R Section 2.3, #define is used to define constant data values, as in
      #define MAXLINE 1000
      
    2. By convention, data constant names are spelled as all uppercase letters.
    3. #define can also be used to define parameterized macros, as exemplified in std-macros.h.
    4. The general form of a macro is:
      #define name optional-parameters body
    5. E.g.,
      #define new(t) (t*) malloc(sizeof(t))
      
    6. Macros are invoked strictly by in-place textual substitution.
      1. In the case of new, for example, the invocation
        ListNode* node = new(ListNode);
        
        expands to
        ListNode* node = (ListNode*) malloc(sizeof(ListNode));
        
      2. This expansion is done by the C preprocessor.
      3. You can inspect the preprocessor output explicitly using the -E switch to gcc.

  4. The distinction between C function declaration and definition.
    1. A declaration is the signature only of a function, e.g.,
      void insert(LinkedList* list, ListNode* node, int i);
      
      1. Such declarations typically appear in .h files.
      2. Strictly speaking, a function declaration does not need parameter names, since it defines just the type signature.
      3. E.g., the following declaration has the same semantics as the preceding
        void insert(LinkedList*, ListNode*, int);
        
        though it is typical to include names for clarity.
    2. The definition of a function defines its body, i.e., the code between the curly braces.

  5. Declare-before-use policy in C programs.
    1. Before a function is called in a C program, the compiler must have seen at least its declaration.
    2. The best way to make this happen is to put function declarations in the .h file that is included by the .c files that use the functions.
    3. Declare-before-use can also be achieved without function declarations, by ordering the function definitions appropriately, i.e., by defining a function lexically before all other functions that call it.
    4. When a function is used before it is declared (or defined) the compiler will assume that the function return type and all parameters types are int.
      1. These assumptions are very often wrong.
      2. If the function call violates these assumptions, the compiler will issue warnings to that effect, often confusingly so.
      3. E.g., suppose the following function declaration in list-node.h was missing:
          ListNode* newListNode(int value);
        
      4. When the linked-list Makefile is run, the missing declaration results in nine warnings of the following form:
          linked-list-test.c:31: warning:
              passing argument 2 of 'insert' makes pointer from integer without a cast
        
      5. Be sure you understand why this is the case.

  6. C's memory model.
    1. Computer memory in a C program is directly accessible to the programmer.
    2. When you declare an array of 10 charss, for example, the compiler allocates a block of memory that is exactly 10 bytes of contiguous characters.
    3. There is no "veil" over memory in C, as there is in Java and other high-level languages.
      1. Metaphorically, a veil obscures certain features, leaving them to the viewer's imagination.
      2. This is the case in Java's memory model, where the programmer cannot see exactly what's going on in JVM memory.
      3. A string of 10 characters in Java is definitely not a simple memory block of 10 bytes.
    4. In C, there is no veil, and the programmer is often keenly aware of the machine-level structure of memory.



index | lectures | lectures | programs | handouts | solutions | examples | documentation | bin