The C Programming Language, 2nd Edition

Chapter 1 – A Tutorial Introduction

1.1 Getting Started
  • C program consists of functions and variables
  • Functions contain statements, variables store values
  • #include <stdio.h>  is the standard input/output library
1.2 Variables and Arithmetic Expressions
  • Variables must be declared before being used
  • Individual statements end with a semicolon
  • while loops tests condition, and if true, executes body; continues until condition is false
  • Integer division truncates, any fractional part is discarded… 5/9 truncates to zero
  • printf is not built into the C language, but merely is a useful function from the standard library
  • float data types give more accurate answers when performing arithmetic, 5.0/9.0 won’t be truncated
1.3 The for statement
  • Starts with initialization, fahr = 0, is done once before the loop is executed
  • Next the condition is evaluated, fahr <= 300, if true then executes body
  • Finally the increment, fahr = fahr + 20, and then the condition is re-evaluated
1.4 Symbolic Constraints
  • Use #define to define your constants, #define LOWER 0
1.5 Character Input and Output
  • Text I/O is dealt with as streams of characters
  • c = getchar() contains next character of input
  • putchar(c) prints contents of the integer variable c as a character
1.6 Arrays
  • int ndigit[10]; declares ndigit as an array of 10 integers, from ndigit[0] to ndigit[9]
1.7 Functions
  • Functions provide a convenient way to perform some action
  • Functions include parameters within their parenthesis
1.8 Arguments – Call by Value
  • All function arguments in C are passed “by value”, unlike some other languages that are passed “by reference”
  • Passed by value means that the function is given values of its arguments in temporary variables, not the originals
  • Whatever is done to a variable inside of a function has no effect on the argument outside that the function was originally called with
  • Arrays as arguments get passed the value of the memory location or address
1.10 External Variables and Scope
  • Variables declared within functions are local variables
  • Local variables exist when the function is called, and cease to exist when the function exits
  • An external variable must be defined once, outside of any function to set aside storage space like: int max;
  • The external variable must be declared in each function that wants access, by stating the type: extern int max;
  • If the external variable is within the source file and defined outside a function, extern is not needed
  • If the program has several source files, extern declarations are needed and are usually placed in a header file
  • Defining variables means the place where the variable is created or assigned storage
  • Declaring variables means the place where the variable is stated but no storage is allocated

Chapter 2 – Types, Operators, and Expressions

2.1 Variable Names
  • First character of a variable name must be a letter
  • Prefer lowercase variable names
2.2 Data Types and Sizes
  • char, int, short, long, float, double
  • Unsigned numbers are always positive or zero
  • An 8-bit char unsigned has values 0 to 255, wheres a signed char would have values -128 to 127
2.3 Constants
  • A long constant is written with an ‘L’ at the end, like 123456789L
  • Unsigned constants written with a ‘U’ at the end
  • An integer can be specified as octal with a 0 in front, decimal 31 written as 037
  • Also can be specified as hexadecimal with a 0x in front, decimal 31 written as 0x1f
  • The null character is ‘\0’
  • ‘x’ is an integer used to produce the numeric value of the letter x, while “x” is an array of characters that contains one character and a ‘\0’
  • An enumeration constant is a list of constant integer variables, enum boolean {NO, YES }; starts with value 0 and next 1
  • An enumeration can be explicitly stated, enum months { JAN = 1, FEB }; means FEB = 2
2.4 Declarations
  • All variables must be declared before use, by specifying a type
  • A variable may be initialized during declaration, int i = 0;
  • A ‘const’ declaration can be used to declare a value that won’t be changed, const double pi = 3.14;
2.5 Arithmetic Operators
  • The modulo operator is ‘%’ and gives the remainder of division, so 3 % 2 equals 1
  • You cannot use the modulo with float or double
2.6 Relational and Logical Operators
  • The numeric value of a logical expression is 1 if true and 0 if false
2.7 Type Conversions
  • Casting is as if the expression were assigned to a variable of a specified type, converting to a certain type
  • sqrt function only takes double, so if n is an integer, sqrt((double) n);
2.8 Increment and Decrement Operators
  • ++n increments n before the value is used, while n++ increments after
  • If n = 5, then x = n++ sets x to 5, but x = ++n sets x to 6
2.9 Bitwise Operators
  • For bit manipulation: & is AND, | is inclusive OR, ^ is exclusive OR, << is left shift, >> is right shift, – is one’s complement
  • & operator results in a 1 if both operand bits are 1 (are both true?)
  • | operator results in in a 1 if either of the operator bits are 1 (is either true?)
  • ^ operator results in 1 if exactly one of the operator bits are 1 (is only one true?)
  • x << 2 shifts x by two positions, the vacated bits are filled with zero if unsigned, this is like multiplying by 4 (22)
  • x >> 3 shifts x two positions, the vacated bits are filled with zero if unsigned, this is like dividing by 8 (23)
2.10 Assignment Operators and Expressions
  • i = i +2 can be written as i +=2, += is an assignment operator
2.11 Conditional Expressions
  • z = (a > b) ? a : b means z = the maximum when a and b are compared

Chapter 3 – Control Flow

3.1 Statements and Blocks
  • An expression becomes a statement when  followed by a semicolon, like x =0; or i++;
3.4 Switch
  • Switch statement tests whether an expression matches one of a number of constant integer values
  • A default case is executed if none of the other cases are satisfied
  • Break statement causes an immediate exit from the switch
  • A good practice is putting a break after the last case (usually your default case)
3.5 Loops – While and For
  • for loop is preferable when there is an initialization
  • while loop for when there is no initialization
  • A for statement can have multiple expressions in various parts,  using a comma to separate like for (i = 0, j =1;….)
3.6 Loops – Do-While
  • A do-while loop always executes the body at least once, tests expression at the bottom afterward
  • Statement executed, then expression evaluated
3.7 Break and Continue
  • A break statement provides early exit from a loop
  • A break causes the innermost loop to exit
  • A continue statement causes the next iteration of the loop to be performed

Chapter 4 – Functions and Program Structure

4.1 Basics of Functions
  • Appropriate functions hide details of operation from parts of the program that don’t need to know about them
  • Best practice includes splitting multiple actions into different functions
4.2 Functions Returning Non-Integers
  • If a function takes arguments, declare them, otherwise use void
  • The call to the function and the function itself must have the same type
4.3 External Variables
  • External variables have greater scope and lifetime compared to local variables
  • Useful for when two functions share the same data, yet neither calls the other
4.4 Scope Rules
  • A declaration of an external variable announces the properties of the variable, like the type
  • A definition of an external variable also causes storage to be set aside
  • If outside a function, int sp; is declared, then the variable is defined and storage is set aside
  • But if this was extern int sp; then this merely declares the variable but does not create the variable or reserve storage
4.5 Header Files
  • The definitions and declarations shared among source files go into what is known as a header file
  • This is a central place, one file that helps to organize the program
4.6 Static Variables
  • The static declaration limits the scope of an object to the rest of the source file being compiled
  • If a function is declared static, then the name is not visible to other files
  • Internal static variables are static variables within a function, they remain in existence rather than coming and going as the function runs
4.7 Register Variables
  • A register declaration lets the compiler know the variable will be heavily used
  • May result in smaller and faster programs
4.9 Initialization
  • Without explicit initialization, external and static variables get initialized to zero
  • Initialization is done before program begins execution
  • External and static variables must initialize to a constant expression
  • Automatic and register variables are not restricted to being a constant
  • Initializers in declarations are harder to see and are generally further away from the point of use
  • int low = 0; int high = 2; are initialized in their declaration, compared to: int low, high; low = 0; high = 2;
4.10 Recursion
  • When a function may call itself directly or indirectly
  • Each invocation of the function gets a fresh set of all local variables, independent of the previous set
  • Recursion usually does not provide any reduction in storage space, nor will recursion be any faster, but recursive code is more compact
4.11 The C Preprocessor
  • The preprocessor is a separate first step in compilation, like #include and #define
  • #include is for including contents of a file during compilation
  • #define is to replace a token by an arbitrary name, like #define AGE 10, where a constant named AGE contains the value 10

Chapter 5 – Pointers and Arrays

5.1 Pointers and Addresses
  • A pointer is a variable that contains the address of a variable
  • If c is a char and p is a pointer that points to it, the address of the object can be accessed with &, p = &c
  • The * operator accesses the object the pointer points to, y = *p makes y equal to the char c because p points to c
  • Declare a pointer: char *p
  • y = *p + 1 takes whatever p points at, adds 1, and assigns the result to y
5.2 Pointers and Function Arguments
  • For functions to directly alter a variable passed through their arguments, use pointers
  • swap(&a, &b); is how you would call a function that has arguments as void swap(int *px, int *py)
  • Pointer arguments allow a function to access and change objects passed through
5.3 Pointers and Arrays
  • pa = &a[0]; sets pa to point to element zero of the array, pa contains the address of a[0]
  • x = *pa; copies the  contents of a[0] to x
  • *(pa+1) refers to the contents of a[1], *(pa+2) refers to a[2], and so on
  • pa = a is  the same as pa =&a[0], and the reference to a[i] can be written as *(a+i)
  • &a[i] and a+i are the same, as a+i is the address of the i-th element
  • Keep in mind a pointer is a variable, so you can do pa++ operations, but the array name is not, so you cannot do a++
5.4 Address Arithmetic
  • One of C’s best strengths is the ease of address arithmetic, the integration of pointers and arrays
  • If a is an array, static int *p = a defines an integer pointer to the beginning of array a, which is position a[0]
  • If p and q are pointers, arithmetic like p < q is true if p points to an earlier element of an array than q does
5.5 Character Pointers and Functions
  • Any string , i.e. “Hello, World” is an array of characters that ends with the null character ‘\0’
  • char amessage[] = “hello”; is an array while char *pmessage = “hello”; is a pointer
  • The array has a fixed amount of storage, and you cannot add more characters, while the pointer is a string constant that cannot be modified
  • Copying a string t to a string s is written as: void strcopy(char *s, char*t) {while (*s++ = *t++) {;} }
  • The function strcopy takes two strings and sets the pointers to the beginning char, and sets the chars of s to the chars of t before incrementing onto the next chars
5.6 Pointer Arrays; Pointers to Pointers
  • Pointers are variables, so they can be stored in arrays
  • char *lineptr[MAXLINES] says that lineptr is an array containing MAXLINES elements, each element being a pointer to a char
  • lineptr[i] is a character pointer, and *lineptr[i] is the character it points to
5.7 Multi-dimensional Arrays
  • A two-dimensional array is really a one-dimensional array whose elements are an array
  • Declare like, int array[2][3]; which is an array of 2 rows (index 0 to 1) each having 3 columns (index 0 to 2)
5.9 Pointers vs. Multi-dimensional Arrays
  • There is a difference between a two-dimensional array and an array of pointers
  • If you have int a[10][20]; and int *b[10] then a[3][4] and b[3][4] are both valid operations that refer to an integer element
  • Array a is a true two-dimensional array that can hold 200 elements, while array b allocates 10 pointers and does not initialize them
  • If each element of b points to a 20 element array, then there will be 200 elements set aside, with ten cells for the pointers
  • The advantage of the pointer array is that the rows can be of different lengths
  • If you have an array of pointers like char *months[] = { “Jan”, “Feb” }; then months[1] refers to Feb while months[1][0] refers to F
  • A two-dimensional array like char months[][4] = { “Jan”, “Feb” }; means months[1] refers to Feb and months[1][0] refers to F

Chapter 6 – Structures

6.1 Basics of Structures
  • A structure is a collection of one or more variables, possibly with different types
  • They help organize complex data in large programs, since related variables can be treated as one unit
  • An example structure for a point on a graph would be struct point { int x; int y; };
  • struct point pt; defines a variable pt that is a structure of type struct point, can set pt.x = 5; and pt.y = 10;
  • Structures can be nested, like struct rectangle { struct point pt1; struct point pt2; };
6.2 Structures and Functions
  • struct point *pp; means that pp is a pointer to a structure of type struct point
  • Can by used by doing pp = &origin; and then using (*pp).x and (*pp).y to get values
  • Can also write pp->x and pp->y
6.5 Self-referential Structures
  • If you wanted to make a binary search tree, you can use structures to define each node
  • The struct tnode would have a pointer to the text, number of occurrences, left child, and right child
  • This would be char *word; int count; struct tnode *left; and struct tnode *right;
  • Structures can refer  to themselves, like struct t { struct s *p; ); where p points to an s
6.7 Typedef
  • Typedef allows for creations of new data type names
  • typedef int Length; makes the name Length a synonym for int
  • Can declare variables as Length minLength, maxLength;
6.8 Unions
  • Union is a variable that can hold objects of different types and sizes
  • Declared as union u_tag { int ival; float fval; } u; where any of the types defined can be assigned to the variable u