Cover
Start nu gratis DDCArv_AppC.pdf
Summary
# C programming basics
This section introduces the C programming language, covering its history, core characteristics, and the fundamental workflow for developing C programs [3](#page=3).
### 1.1 Introduction to C programming
The C programming language was developed at Bell Labs around 1973. It is known for its ability to control a computer for nearly any task, including direct interaction with hardware. C is suitable for generating high-performance code, is relatively easy to use, and is available on a wide range of devices from supercomputers to microcontrollers. It is also closely related to other significant languages such as C++, C#, Objective C, Java, and Arduino [3](#page=3).
### 1.2 Characteristics of C
C is often described as "libertarian" because it allows programmers to perform almost any operation and interact directly with the hardware. However, this freedom means C does not protect programmers from errors and assumes the programmer understands variable and array sizes. Without specific safeguards, C programs can write to any location in memory [4](#page=4).
> **Tip:** Understanding C's "libertarian" nature is crucial for appreciating its power and potential pitfalls. You are responsible for managing memory and ensuring data integrity.
### 1.3 The C programming workflow
The process of creating and running a C program involves several key steps [6](#page=6):
1. **Write code**: This is where the programmer creates the source code for the program [6](#page=6).
2. **Compile code**: The source code is translated into machine code by a compiler [6](#page=6).
3. **Execute code**: The compiled machine code is run by the computer [6](#page=6).
4. **Debug code**: Errors and issues are identified and fixed [6](#page=6).
### 1.4 Comments in C
Comments are used to explain code and can be written in two ways [8](#page=8):
* **Single-line comments**: These begin with `//` and continue to the end of the line.
```c
x += 2; //This is a single-line comment.
```
* **Multi-line comments**: These start with `/*` and end with `*/`, allowing for comments that span multiple lines.
```c
/* You can hide or disable a section of code such as this block
with a multi-line comment
x = bob ? x : y;
y -= 5;
*/
```
It is good practice to start C code files with the filename, programmer's name, email, and date to establish copyright ownership and facilitate tracking [8](#page=8).
### 1.5 Constants, defines, and macros
Constants in C are typically defined using the `#define` directive, which is processed by the preprocessor before compilation. The preprocessor replaces all occurrences of the defined identifier with its corresponding value. By convention, these definitions are placed at the top of the file, and the identifiers are written in all capital letters [9](#page=9).
**Example:**
```c
#define MAXGUESSES 5
#define PI 3.14159
```
### 1.6 Variables in C
Variables in C can be declared as either global or local [10](#page=10).
* **Global variables**: These are declared outside of any function and are accessible from all functions within the program. However, their use can lead to code that is difficult to debug and should generally be avoided, especially in larger projects [10](#page=10).
* **Local variables**: These are declared inside a function and are only accessible within that specific function. Local variables are the preferred choice for most situations [10](#page=10).
### 1.7 Primitive data types
C supports various primitive data types, which define the type of data a variable can hold [11](#page=11).
### 1.8 Integer sizes
The size of integer data types in C can vary depending on the machine architecture. For example, `int` can be 16 or 32 bits, and `long` can be 32 or 64 bits. If precise integer sizes are critical, it is recommended to use sized integer types found in the `` header file. These include [12](#page=12):
* Signed types: `int16_t`, `int32_t`, `int64_t`
* Unsigned types: `uint16_t`, `uint32_t`, `uint64_t`
### 1.9 ASCII table
The ASCII (American Standard Code for Information Interchange) table is a character encoding standard that maps characters to numerical values. It is often referenced when dealing with character data in C [13](#page=13).
---
# Functions and includes in C
This section details the fundamental concepts of functions in C, including their structure, parameter passing, return values, the necessity of prototypes, and the use of the `#include` directive for library integration [14](#page=14) [15](#page=15).
### 2.1 Functions
Functions in C are reusable blocks of code that perform specific tasks. They can accept zero or more inputs (parameters) and can return at most one output value to the caller [15](#page=15).
#### 2.1.1 Function Declaration and Definition
A function definition includes the return type, function name, parameter list, and the function body enclosed in curly braces `{}`. The parameter types are declared in the function declaration [15](#page=15).
* **Return Type:** Specifies the data type of the value the function will return [15](#page=15).
* **Parameters:** Inputs to the function, with their types declared. Variables are passed *by value*, meaning the function receives a copy of the argument, and any modifications within the function do not affect the original variable in the caller [15](#page=15).
* **Function Body:** Contains the statements that execute when the function is called. It can have zero or more statements [15](#page=15).
* **Return Statement:** The `return` statement specifies the value to be returned to the caller [15](#page=15).
> **Example:** A function that calculates the sum of three integers.
>
> ```c
> // Return the sum of the three input variables
> int sum3(int a, int b, int c) {
> int result = a + b + c;
> return result;
> }
> ```
> [16](#page=16).
#### 2.1.2 Function Prototypes
A function prototype is a declaration of a function that specifies its name, return type, and parameter types, without including the function body. Prototypes are essential to inform the compiler about a function's signature before it is used, especially if the function is defined after its first call [15](#page=15) [17](#page=17).
* **Necessity:** A function must be either declared before its first use or have a prototype declared before its first use [15](#page=15).
* **Syntax:** `return_type function_name(type1, type2, ...);`
The parameter names can be omitted in the prototype.
> **Example:** Demonstrating the need for a function prototype when `sum3` is called before its definition.
>
> ```c
> // sum3example.c
> // David_Harris@hmc.edu 22 October 2019
> ////////////////////////////////
> // Prototypes
> ////////////////////////////////
> int sum3(int, int, int); // needed because sum3 is called before declared
> ////////////////////////////////
> // main
> ////////////////////////////////
> void main(void) {
> int answer;
> answer = sum3(6, 7, 8);
> }
>
> ////////////////////////////////
> // other functions
> // prototype not needed if these were moved before main
> ////////////////////////////////
> int sum3(int a, int b, int c) {
> int result = a + b + c;
> return result;
> }
> ```
> [17](#page=17).
> **Tip:** If all function definitions appear *before* they are called in your source file, you might not need explicit prototypes for those functions. However, prototypes are often crucial for organizing code into multiple files or when dealing with mutually recursive functions [17](#page=17).
#### 2.1.3 Prototypes for Mutually Recursive Functions
When functions call each other (e.g., in mutual recursion), prototypes are unavoidable because each function needs to be declared before it can be called by another function that might appear earlier in the source file [18](#page=18).
> **Example:** `f1` calls `f2`, and `f2` calls `f1`. Prototypes for both `f1` and `f2` are required.
>
> ```c
> // Prototypes needed for f1 and/or f2 because they
> // can’t both be declared before each other
> int f1(int);
> int f2(int);
>
> int f1(int n) {
> return f2(n-1) + 1;
> }
>
> int f2(int n) {
> return f1(n-1)*2;
> }
>
> void main(void) {
> int answer;
> answer = f1 [5](#page=5);
> }
> ```
> [18](#page=18).
### 2.2 Includes
The `#include` directive is a preprocessor command used to incorporate the content of other files into the current source file. This is primarily used to access function prototypes for standard library functions or for custom utility functions defined in separate header files [19](#page=19).
* **Standard Library Includes:** For functions provided by the C standard library (e.g., input/output, mathematical functions), header files are included using angle brackets `< >`. This tells the preprocessor to look for these files in the standard system directories.
* `#include ` for standard input/output functions [19](#page=19).
* `#include ` for mathematical functions [19](#page=19).
* **User-Defined Includes:** For your own function prototypes or custom code stored in header files, you use double quotes `" "` instead of angle brackets. This directs the preprocessor to search for the file first in the current directory, and then potentially in other specified include paths.
* `#include "other/myFuncs.h"` [19](#page=19).
---
# Operators and control flow
This section delves into C programming's operators and control flow statements, crucial for directing program execution and data manipulation.
## 3. Operators and control flow
### 3.1 Operators
C provides a rich set of operators for performing various operations on data. These include arithmetic, logical, bitwise, and relational operators, each with specific precedence rules that dictate the order of evaluation in complex expressions [20](#page=20) [22](#page=22).
#### 3.1.1 Boolean logic in C
In C, a value is considered FALSE if it is exactly 0. Any other value, including positive numbers, negative numbers, or non-zero integers like 1, 42, or -1, is treated as TRUE. Logical operators consistently interpret FALSE as 0 and TRUE as 1 [21](#page=21).
#### 3.1.2 Types of operators
* **Arithmetic Operators**: These perform mathematical calculations. Examples include `+` (addition), `-` (subtraction), `*` (multiplication), `/` (division), and `%` (modulo) [23](#page=23).
* **Logical Operators**: Used for Boolean operations, they evaluate conditions. The common ones are `&&` (logical AND), `||` (logical OR), and `!` (logical NOT) [23](#page=23).
* **Bitwise Operators**: These operate on individual bits of their operands. They include `&` (bitwise AND), `|` (bitwise OR), `^` (bitwise XOR), `~` (bitwise NOT), `<<` (left shift), and `>>` (right shift) [23](#page=23).
* **Relational Operators**: Used for comparisons, these operators determine the relationship between two operands. They are `<`, `>`, `<=`, `>=`, `==` (equal to), and `!=` (not equal to) [23](#page=23).
* **Assignment Operators**: Used to assign values to variables. The basic assignment operator is `=`. Compound assignment operators combine an arithmetic or bitwise operation with assignment, such as `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, and `>>=` [23](#page=23).
* **Increment and Decrement Operators**: `++` (increment) and `--` (decrement) increase or decrease the value of a variable by one, respectively. These can be used in prefix or postfix form, affecting the order of operations [23](#page=23).
* **Conditional (Ternary) Operator**: The `?:` operator provides a concise way to write simple if-else statements. The syntax is `condition? value_if_true: value_if_false` [23](#page=23).
* **`sizeof` Operator**: This operator returns the size in bytes of a data type or a variable [23](#page=23).
#### 3.1.3 Operator precedence
Operator precedence determines the order in which operations are performed when multiple operators are present in an expression. Operators with higher precedence are evaluated before operators with lower precedence. Parentheses `()` can be used to override the default precedence [22](#page=22).
> **Tip:** Understanding operator precedence is crucial to avoid unexpected results in complex expressions. Always refer to precedence tables or use parentheses for clarity.
#### 3.1.4 Operator examples
Consider the following C code snippet for illustration [26](#page=26):
```c
int a = 42;
int b = 0x15; // hexadecimal; = 21 in decimal
char c = 0b00001010; // binary; = 10 in decimal
char d = !c; // 0, because c was nonzero
char e = ~c; // 0b11110101 bitwise NOT
char f = e | c; // 0b11111111 bitwise OR
char g = c << 2; // 0b00101000 shift left by 2
int h = (a > b); // 1 because a is greater than b
int i = (a > b) && (c != e); // 1 because both are TRUE
int j = (a > b) ? a : b; // 42 because a > b
int k = sizeof(a); // 4 on most computers
g &= c; // 0b00001000 bitwise AND
```
In this example:
* `d` becomes 0 because `!c` (logical NOT of a non-zero `c`) is FALSE [26](#page=26).
* `e` is the bitwise NOT of `c`, resulting in all bits being flipped [26](#page=26).
* `f` is the bitwise OR of `e` and `c` [26](#page=26).
* `g` is `c` shifted left by 2 positions [26](#page=26).
* `h` is 1 because the expression `a > b` (42 > 21) is TRUE [26](#page=26).
* `i` evaluates to 1 because both conditions `(a > b)` and `(c!= e)` are TRUE [26](#page=26).
* `j` assigns `a` to `j` because `a > b` is TRUE [26](#page=26).
* `k` stores the size of an integer in bytes on the specific system, typically 4 [26](#page=26).
* `g &= c` performs a bitwise AND operation between the current `g` and `c`, storing the result back into `g` [26](#page=26).
### 3.2 Control flow statements
Control flow statements alter the sequential execution of a program, allowing for decision-making and repetition [27](#page=27).
#### 3.2.1 `if` statement
The `if` statement executes a block of code only if a specified condition (expression) evaluates to TRUE [28](#page=28).
**Syntax:**
```c
if (expression)
statement;
```
**Example:**
```c
if (n <= 1) return 1;
```
This code returns 1 if the value of `n` is less than or equal to 1 [29](#page=29).
#### 3.2.2 `if/else` statement
The `if/else` statement provides two alternative execution paths. If the condition is TRUE, the code in the `if` block is executed; otherwise, the code in the `else` block is executed [28](#page=28).
**Syntax:**
```c
if (expression)
statement1;
else
statement2;
```
**Example:**
```c
if (n <= 1) return 1;
else return fact(n-1);
```
This example returns 1 if `n` is less than or equal to 1, otherwise it calls a function `fact` with `n-1` [31](#page=31).
#### 3.2.3 Compound statements
When a control flow statement needs to execute multiple statements, these statements must be enclosed within curly braces `{}`. This forms a compound statement or a block of code [30](#page=30).
**Example:**
```c
if (answer == 42) {
ultimateQuestion = 1;
hitchhikersGuide = 1;
}
```
In this case, if `answer` is 42, both `ultimateQuestion` and `hitchhikersGuide` are set to 1 [30](#page=30).
#### 3.2.4 `switch/case` statement
The `switch/case` statement is used for multi-way branching based on the value of a single variable or expression. It compares the value of the `switch` expression against multiple `case` labels. If a match is found, the code following that `case` is executed. The `break` statement is typically used to exit the `switch` block after a match. A `default` case can be included to handle values that do not match any of the specified `case` labels [28](#page=28).
**Syntax:**
```c
switch (variable) {
case (expression1): statement1; break;
case (expression2): statement2; break;
case (expression3): statement3; break;
default: statement4;
}
```
> **Tip:** Always remember to use `break` statements within `case` blocks to prevent "fall-through" to the next case, which can lead to unintended execution of code. The `default` case is optional but good practice for handling unexpected values.
**Example:**
```c
switch (state) {
case: if (ta) state = 0; else state = 1; break ;
case: state = 2; break [1](#page=1);
case: if (tb) state = 2; else state = 3; break [2](#page=2);
case: state = 0; break [3](#page=3);
default: state = 0;
}
```
This example demonstrates a state machine where the `state` variable is updated based on its current value and potentially other conditions like `ta` or `tb` [32](#page=32).
---
# Loops, arrays, strings, and structures
This section covers fundamental C programming constructs for iteration, data aggregation, and organization, including various loop types, arrays, strings, and structures [33](#page=33) [38](#page=38) [44](#page=44).
### 4.1 Loops
Loops are used to repeatedly execute a block of code until a specified condition is met [34](#page=34).
#### 4.1.1 While loop
The `while` loop checks a condition *before* each iteration. If the condition is true, the loop body executes; otherwise, the loop terminates [34](#page=34).
**Syntax:**
```c
while (condition)
statement;
```
**Example:** Calculating factorial.
> **Example:**
> ```c
> int fact(int n) {
> int result = 1;
> while (n > 1) {
> result = result * n; // or result *= n;
> n = n – 1; // or n--
> }
> return result;
> }
> ```
> [35](#page=35).
A more compact, though less readable, version of the `while` loop is also possible [35](#page=35).
#### 4.1.2 Do/while loop
The `do/while` loop executes the loop body *at least once* before checking the condition. If the condition is true after the execution, the loop continues; otherwise, it terminates [34](#page=34) [36](#page=36).
**Syntax:**
```c
do {
statement;
} while (condition);
```
**Example:**
> **Example:**
> ```c
> int fact(int n) {
> int result = 1;
> do {
> result *= n;
> } while (n-- > 1);
> return result;
> }
> ```
> [36](#page=36).
>
> The `do/while` loop is generally not preferred for this specific factorial example due to its slightly longer and less intuitive structure compared to other loop types [36](#page=36).
#### 4.1.3 For loop
The `for` loop is ideal for situations where the number of iterations is known or can be determined beforehand. It combines initialization, condition checking, and loop operation into a single line [34](#page=34) [37](#page=37).
**Syntax:**
```c
for (initialization; condition; loop operation)
statement;
```
**Execution flow:**
1. **Initialization:** Executed once before the loop begins [37](#page=37).
2. **Condition Check:** Evaluated before each iteration. If true, the loop body executes [37](#page=37).
3. **Loop Body:** The statements within the loop are executed [37](#page=37).
4. **Loop Operation:** Executed after each iteration of the loop body [37](#page=37).
5. The process repeats from step 2.
**Example:**
> **Example:**
> ```c
> int fact(int n) {
> int result = 1;
> int i;
> for (i=1; i <= n; i++)
> result *= i;
> return result;
> }
> ```
> [37](#page=37).
### 4.2 Arrays and strings
Arrays are used to store collections of data of the same type, while strings are a specific type of array used to store text [38](#page=38) [39](#page=39) [41](#page=41).
#### 4.2.1 Arrays
An array is a data structure that holds multiple elements of the same data type. Elements are accessed using an index, starting from 0 up to N-1, where N is the total number of elements in the array [39](#page=39).
* **Initialization:** It is crucial to initialize arrays before use, as uninitialized arrays can contain unpredictable data [39](#page=39).
* **Multidimensional Arrays:** Arrays can have multiple dimensions, allowing for the representation of grids or tables of data [39](#page=39).
**Example:** A 3-element array of floats to store acceleration values.
> **Example:**
> ```c
> float accel [3](#page=3);
> ```
> [39](#page=39).
**Example:** A 2D array to store grades for students across multiple labs.
> **Example:**
> ```c
> #define NUMSTUDENTS 120
> #define NUMLABS 11
> int grades[NUMSTUDENTS][NUMLABS;
> ```
> [39](#page=39).
**Example:** Calculating the magnitude of a 3D vector represented by an array.
> **Example:**
> ```c
> #include
> double mag(double v ) { [3](#page=3).
> return sqrt(v *v + v *v + v *v ) [1](#page=1) [2](#page=2);
> }
> ```
> [40](#page=40).
#### 4.2.2 Strings
A string in C is fundamentally an array of characters terminated by a null character (`\0`). This null terminator signifies the end of the string [41](#page=41).
**Representation:**
If `char name = "BOB";`, it is stored in memory as [20](#page=20):
* `name ` = ASCII value for 'B' .
* `name ` = ASCII value for 'O' [1](#page=1) .
* `name ` = ASCII value for 'B' [2](#page=2) .
* `name ` = 0 (NULL termination) [3](#page=3).
Any subsequent elements in the array (up to `name `) are ignored by string functions [19](#page=19) [41](#page=41).
**String Handling Functions:**
Common string operations include calculating length and copying strings [42](#page=42).
* **`strlen`:** Calculates the length of a string by counting characters until the null terminator is encountered, with a safeguard against exceeding a maximum length [42](#page=42).
* **`strcpy`:** Copies characters from a source string to a destination string, including the null terminator, and also includes a maximum length check [42](#page=42).
**Example:** Using string functions.
> **Example:**
> ```c
> #include
> #define MAXLEN 80
> void main(void) {
> char name ;
> int len;
> char c;
> strcpy(name, "BOB"); // Copies "BOB" into the name array
> len = strlen(name); // len will be 3
> c = name; // c will hold the character 'O' (ASCII 79) [1](#page=1).
> }
> ```
> [43](#page=43).
### 4.3 Structures
Structures allow you to group together related variables of different data types under a single name, creating a user-defined type [45](#page=45).
**Definition:**
The general format for defining a structure is:
```c
struct structure_name {
type1 element1;
type2 element2;
// ... more elements
};
```
**Usage:**
To use a structure, you first declare a variable of that structure type. Individual elements within the structure are accessed using the dot operator (`.`) [46](#page=46).
**Example:** Defining and using a `contact` structure.
> **Example:**
> ```c
> struct contact {
> char name [30](#page=30);
> int age;
> float height; // in meters
> };
>
> struct contact c1;
> strcpy(c1.name, "Ben Bitdiddle");
> c1.age = 20;
> c1.height = 1.82;
> ```
> [46](#page=46).
#### 4.3.1 Typedef for structures
The `typedef` keyword can be used to create an alias for a structure type, simplifying variable declaration [47](#page=47).
**Syntax:**
```c
typedef existing_type new_type_name;
```
**Example:** Using `typedef` to shorten the `contact` structure declaration.
> **Example:**
> ```c
> typedef struct contact {
> char name [30](#page=30);
> int age;
> float height; // in meters
> } contact; // 'contact' is now an alias for 'struct contact'
>
> contact c1; // Variable 'c1' is of type 'contact'
> ```
> [47](#page=47).
**Example:** Structures for geometric points and rectangles.
> **Example:**
> ```c
> typedef struct point {
> int x;
> int y;
> } point;
>
> point p1;
> p1.x = 42;
> p1.y = 9;
>
> typedef struct rect {
> point ll; // lower-left corner
> point ur; // upper-right corner
> int color;
> } rect;
>
> rect r1;
> r1.color = 1;
> r1.ll = p1; // Assigning one structure to another
> r1.ur.x = r1.ll.x + width; // Accessing nested structure members
> r1.ur.y = r1.ll.y + height;
> ```
> [48](#page=48).
---
# Memory management and pointers
This section details how data is stored in memory, the role of the `sizeof` operator, and the fundamental concepts of pointers in C programming.
### 5.1 Memory organization
Variables and data structures are stored in distinct locations within computer memory. Each fundamental data type occupies a specific amount of memory, measured in bytes [50](#page=50).
* **`char`**: Occupies 1 byte [50](#page=50).
* **`short`**: Occupies at least 2 bytes [50](#page=50).
* **`long`**: Occupies at least 4 bytes, and potentially 8 bytes on 64-bit systems [50](#page=50).
* **`int`**: Occupies at least 2 bytes, commonly 4 bytes on both 32-bit and 64-bit systems [50](#page=50).
* **`float`**: Occupies 4 bytes [50](#page=50).
* **`double`**: Occupies 8 bytes [50](#page=50).
Arrays and structures are stored in contiguous blocks of memory, with their total size determined by the sum of the sizes of their constituent elements [50](#page=50).
> **Tip:** Understanding the size of data types is crucial for efficient memory allocation and avoiding overflow issues.
### 5.2 The `sizeof` operator
The `sizeof` operator is a compile-time operator in C that returns the size, in bytes, of a variable or a data type. It can be used with a variable name or with a type name enclosed in parentheses [51](#page=51).
**Syntax:**
* `sizeof(expression)`
* `sizeof expression`
**Examples:**
* `int s1 = sizeof c;` where `c` is a `char`, `s1` will be 1 [51](#page=51).
* `int s2 = sizeof(d);` where `d` is a `double`, `s2` will be 8 [51](#page=51).
* For a `point` structure (e.g., two integers), `sizeof(p)` would be the sum of the sizes of its members, e.g., 4 bytes + 4 bytes = 8 bytes [51](#page=51).
* For a `rect` structure (e.g., two `point` structures and a color integer), `sizeof(r)` could be calculated as (8 bytes + 8 bytes + 4 bytes) = 20 bytes [51](#page=51).
> **Tip:** The `sizeof` operator is invaluable for determining memory requirements, especially when dealing with arrays and complex data structures.
### 5.3 Memory layout examples
**Array Memory Example:**
An array, such as `int ary;`, will allocate four consecutive memory locations, each capable of holding an integer. If the array starts at address `0x101C`, the elements would be at `0x101C` (index 0), `0x1020` (index 1), `0x1024` (index 2), and `0x1028` (index 3), assuming integers are 4 bytes each [4](#page=4) [52](#page=52) [57](#page=57) [58](#page=58).
**Structure Memory Example:**
A structure can contain various data types. For instance, a `rect` structure might have members like `ll` (lower-left coordinate), `ur` (upper-right coordinate), and `color`. If `ll` and `ur` are themselves structures with `x` and `y` coordinates (integers), and `color` is an integer, the memory layout would be sequential. For example, `r1.ll.x` might be at `0x1008`, `r1.ll.y` at `0x100C`, `r1.ur.x` at `0x1010`, `r1.ur.y` at `0x1014`, and `r1.color` at `0x1018` [53](#page=53) [57](#page=57).
### 5.4 Pointers
A pointer is a variable that stores a memory address. It essentially "points" to another location in memory where data is stored [55](#page=55).
#### 5.4.1 Pointer declaration
Pointer variables are declared using an asterisk (`*`) between the data type and the variable name. This signifies that the variable will hold the memory address of a variable of that specific data type [55](#page=55).
**Example:**
`int *ptr;` declares `ptr` as a pointer to an integer [55](#page=55).
#### 5.4.2 The address-of operator (`&`)
The address-of operator (`&`) is used to get the memory address of a variable. When placed before a variable name, it returns the address where that variable is stored [55](#page=55).
**Example:**
If `salary1` is stored at memory address `100`, then `ptr = &salary1;` will assign the value `100` (the address of `salary1`) to the pointer variable `ptr` [55](#page=55).
#### 5.4.3 Dereferencing a pointer (`*`)
The dereference operator (`*`), when used with a pointer variable, retrieves the value stored at the memory address that the pointer holds. It effectively "follows" the pointer to access the data it points to [55](#page=55).
**Example:**
If `ptr` holds the address `100`, and `salary1` at address `100` has the value `98500`, then `salary2 = *ptr + 1000;` will first get the value `98500` from `*ptr`, add `1000`, and assign the result `99500` to `salary2` [55](#page=55).
> **Tip:** It is crucial to initialize pointers before dereferencing them to avoid accessing invalid memory locations, which can lead to program crashes or unpredictable behavior.
### 5.5 Arrays and pointers
In C, an array name is often treated as a pointer to its first element (the element at index 0). This means that pointer arithmetic can be used to access array elements [56](#page=56).
**Example:**
If `ary` is an array, `ary` itself refers to the address of `ary `. Therefore, `*(ary + i)` is equivalent to `ary[i]`, accessing the element at index `i` [69](#page=69).
> **Tip:** Understanding the relationship between arrays and pointers is fundamental for efficient array manipulation and dynamic memory allocation in C.
### 5.6 Pointer arithmetic
Pointer arithmetic involves adding or subtracting integers from a pointer. When an integer `n` is added to a pointer of type `T*`, the resulting address is advanced by `n * sizeof(T)` bytes. This means that pointer arithmetic operates in terms of the number of elements of the data type the pointer points to, not in raw bytes [69](#page=69).
**Example:**
If `ptr` points to the beginning of an integer array (`0x101C`) and we use `*(ptr + 2)`, this accesses the third element of the array (at index 2). If integers are 4 bytes, `ptr + 2` would result in an address `0x101C + 2 * 4` bytes = `0x101C + 8` bytes = `0x1024`, which is the correct address for `ary ` [2](#page=2) [69](#page=69).
### 5.7 Pointer examples with arrays and assignment
The following example illustrates various pointer operations with an integer array `ary` and variables `a` and `b` [62-71](#page=62-71).
**Initialization:**
* `int ary;` declares an array of 4 integers [4](#page=4).
* `int a = 37, b;` declares two integer variables.
* `int *ptr;` declares a pointer to an integer.
**Execution Trace:**
1. `for (i=0; i<3; i++) ary[i = i*i;`: Initializes the first three elements of `ary` to `0`, `1`, and `4` respectively. `ary ` remains uninitialized in this loop [3](#page=3) [63](#page=63).
2. `ptr = &a;`: The pointer `ptr` now holds the address of `a` (e.g., `0x102C`) [64](#page=64).
3. `b = *ptr;`: The value pointed to by `ptr` (which is `a`'s value, `37`) is assigned to `b`. `b` becomes `37` [65](#page=65).
4. `*ptr = 3;`: The value at the address pointed to by `ptr` (i.e., `a`) is changed to `3`. So, `a` becomes `3` [66](#page=66).
5. `ptr = ary;`: The pointer `ptr` is reassigned to point to the beginning of the array `ary` (e.g., `0x101C`) [67](#page=67).
6. `ptr = b;`: This is equivalent to `*(ptr + 1) = b;`. It assigns the value of `b` (`37`) to `ary ` [1](#page=1) [68](#page=68).
7. `*(ptr+2) = 7;`: This assigns the value `7` to the element at `ptr + 2`, which is `ary ` [2](#page=2) [69](#page=69).
8. `ary = 1;`: **This is an out-of-bounds write.** It attempts to write to memory beyond the allocated size of `ary`. In this specific memory layout, it overwrites the variable `a` with the value `1` [4](#page=4) [70](#page=70).
9. `*(ptr+5) = 2;`: This is also an out-of-bounds write. `ptr + 5` would be `0x101C + 5 * 4` bytes = `0x101C + 20` bytes = `0x1030`. This memory location happens to be where `b` is stored, so `b` is overwritten with the value `2` [71](#page=71).
> **Caution:** Accessing memory outside the bounds of an array (buffer overflow) is a serious programming error that can lead to data corruption, crashes, and security vulnerabilities.
#### 5.7.1 `char` pointer example
An example with a `char` variable `age` and a `char` pointer `p` demonstrates fundamental pointer usage [72-73](#page=72-73).
* `char age = 30;`: A character variable `age` is initialized to 30.
* `char *p;`: A pointer `p` to a character is declared.
* `p = &age;`: `p` stores the memory address of `age`.
* `printf("*p = %d\n", *p);`: Dereferencing `p` prints the value of `age` [30](#page=30).
* `printf("sizeof(age) = %ld\n", sizeof(age));`: Prints the size of `age` (1 byte).
* `printf("sizeof(p) = %ld\n", sizeof(p));`: Prints the size of the pointer `p` (typically 8 bytes on a 64-bit system).
* `*p = 40;`: The value at the address `p` points to (i.e., `age`) is updated to 40.
* The program output shows that both `*p` and `age` reflect the updated value of 40.
### 5.8 Pointers and structures
Pointers can be used to point to structures, allowing indirect access and modification of structure members [74](#page=74).
* **Declaration:** `rect *rptr;` declares `rptr` as a pointer to a `rect` structure.
* **Assignment:** `rptr = &r1;` makes `rptr` point to the structure variable `r1`.
* **Accessing members:**
* Using the dot operator with the structure name: `(*rptr).color = 3;` This first dereferences `rptr` to get the structure, then accesses its `color` member. The parentheses around `*rptr` are necessary due to operator precedence.
* Using the arrow operator (member access operator): `rptr->color = 4;` This is the preferred and more concise way to access structure members via a pointer. The arrow `->` combines dereferencing and member access.
> **Tip:** The arrow operator `->` is specifically designed for convenience when working with pointers to structures, making code cleaner and more readable.
---
# Dynamic memory allocation and advanced topics
This section explores dynamic memory allocation in C using `malloc` and `free`, and delves into managing complex memory layouts for multidimensional arrays and structures, including passing structures to functions by address [75](#page=75) [79](#page=79).
### 6.1 Dynamic memory allocation
Dynamic memory allocation allows for memory to be requested and released during program execution, rather than being fixed at compile time [79](#page=79) [80](#page=80).
#### 6.1.1 `malloc` and `free`
* `malloc` is a function that allocates a specified number of bytes of memory and returns a pointer to the beginning of this allocated block [80](#page=80).
* `free` is used to deallocate memory that was previously allocated by `malloc`, making it available for reuse [80](#page=80).
* Both `malloc` and `free` are declared in the `stdlib.h` header file [80](#page=80).
**Example of `malloc` usage:**
To allocate memory for an array of 10 integers, you would use:
`int *ary = (int*)malloc(10*sizeof(int));` [80](#page=80).
> **Tip:** When allocating memory with `malloc`, it's good practice to cast the returned `void` pointer to the appropriate data type for clarity and type safety.
### 6.2 Variable-sized arrays
In standard C, the dimensions of multidimensional arrays must be known at compile time. For arrays whose dimensions are not known until runtime (variable-sized arrays), a common approach is to treat them as one-dimensional arrays and manage the indexing manually [81](#page=81).
#### 6.2.1 Matrix implementation with dynamic allocation
A variable-sized matrix can be implemented by allocating a contiguous block of memory for all its elements and then using a formula to access individual elements.
**Function to create a new matrix:**
The `newMatrix` function allocates memory for an `m` row by `n` column matrix of doubles. It treats the matrix as a single-dimensional array of `m*n` elements [82](#page=82).
```c
double* newMatrix(int m, int n) {
double *mat;
mat = (double*)malloc(m*n*sizeof(double));
return mat;
}
```
**Function to create an identity matrix:**
The `newIdentityMatrix` function creates an `n x n` identity matrix using `newMatrix` and then populates it. The element at row `i` and column `j` is set to 1 if `i == j`, and 0 otherwise. The indexing formula `mat[j+i*n]` is used to map the 2D coordinates to the 1D memory layout [82](#page=82).
```c
double* newIdentityMatrix(int n) {
double *mat = newMatrix(n, n);
int i, j;
for (i=0; i` operator is used to access members of the structure through a pointer [76](#page=76).
```c
void createRect(int xl, int yl, int width, int height, int color, rect *r) {
r->ll.x = xl; r->ll.y = yl;
r->ur.x = xl + width; r->ur.y = yl + height;
r->color = color;
}
```
**Example `main` function call:**
In `main`, a `rect` variable `r1` is declared, and its address (`&r1`) is passed to `createRect`, allowing the function to modify `r1` directly [76](#page=76).
```c
int main(void) {
rect r1;
createRect(3, 5, 10, 20, 1, &r1);
}
```
---
## Common mistakes to avoid
- Review all topics thoroughly before exams
- Pay attention to formulas and key definitions
- Practice with examples provided in each section
- Don't memorize without understanding the underlying concepts
Glossary
| Term | Definition |
|------|------------|
| C programming language | A general-purpose, procedural computer programming language developed in the early 1970s that is known for its efficiency and ability to interact directly with hardware. |
| Preprocessor | A program that processes input text for another program; in C, it handles directives like #define and #include before compilation. |
| #define directive | A preprocessor directive used to create symbolic constants or macros, where an identifier is replaced with a specific value or code snippet. |
| Global variable | A variable declared outside of any function, making it accessible from any part of the program. It is generally advisable to avoid excessive use of global variables. |
| Local variable | A variable declared inside a function, with its scope limited to that function. Local variables are the preferred choice for modular programming. |
| Primitive data types | Fundamental data types in C, such as int, char, float, and double, that represent basic values. |
| Integer sizes | The number of bits used to store an integer, which can vary depending on the architecture (e.g., int can be 16 or 32 bits). `stdint.h` provides fixed-size integer types like `int16_t`. |
| ASCII Table | A character encoding standard where each character is assigned a unique numerical value, used for representing text in computers. |
| Function | A self-contained block of code that performs a specific task. A function can take inputs (arguments) and return at most one output value. |
| Function prototype | A declaration of a function that specifies its name, return type, and the types of its parameters. It allows the compiler to know about a function before its full definition. |
| Pass by value | A mechanism where a function receives a copy of the argument's value, so modifications within the function do not affect the original variable. |
| #include directive | A preprocessor directive used to incorporate the contents of another file, typically header files containing function prototypes and macro definitions, into the current file. |
| Operator | A symbol that performs an operation on one or more operands. C has various operators including arithmetic, relational, logical, and bitwise operators. |
| Precedence | The set of rules that determines the order in which operators are evaluated in an expression. |
| Boolean (True/False) in C | In C, a value is considered FALSE if it is 0, and TRUE if it is any non-zero value. Logical operators often return 0 for FALSE and 1 for TRUE. |
| Control flow statements | Statements that alter the order in which instructions are executed, such as `if`, `else`, and `switch`, allowing for conditional execution and decision-making. |
| Loops | Constructs that allow a block of code to be executed repeatedly based on a condition. Common loop types include `while`, `do-while`, and `for`. |
| Arrays | Data structures that store a collection of elements of the same data type in contiguous memory locations. Elements are accessed using an index starting from 0. |
| Strings | In C, strings are arrays of characters terminated by a null character (`\0`) to signify the end of the string. |
| Structures | User-defined data types that group together variables of different data types under a single name, allowing for the organization of related data. |
| Typedef | A keyword used to create an alias or a shorthand name for an existing data type, simplifying the declaration of complex types like structures. |
| Memory | The physical or virtual storage space where data and program instructions are held. Variables and data structures occupy specific locations in memory. |
| Sizeof operator | An operator that returns the size, in bytes, of a data type or a variable. |
| Pointers | Variables that store memory addresses. They are used to indirectly access and manipulate data stored at those addresses. |
| Address-of operator (&) | An operator that returns the memory address of a variable. |
| Dereference operator (*) | An operator used with pointers to access the value stored at the memory address the pointer points to. |
| Member access operator (.) | Used to access members of a structure when you have the structure variable itself. |
| Arrow operator (->) | Used to access members of a structure when you have a pointer to the structure. |
| Dynamic memory allocation | The process of assigning memory to variables or data structures during program execution, rather than at compile time. |
| `malloc()` function | A standard library function that allocates a block of memory of a specified size and returns a pointer to the beginning of that block. |
| `free()` function | A standard library function used to deallocate memory that was previously allocated by `malloc()`, returning it to the system for reuse. |