0% found this document useful (0 votes)
40 views752 pages

Guide To C Programming

Uploaded by

scribd2024
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
40 views752 pages

Guide To C Programming

Uploaded by

scribd2024
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Beej’s Guide to C Programming

Brian “Beej Jorgensen” Hall

v0.7.28, Copyright © June 6, 2022

[Link]
Contents

1 Foreword 2
1.1 Audience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 How to Read This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 Platform and Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.4 Official Homepage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.5 Email Policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 Mirroring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.7 Note for Translators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.8 Copyright and Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.9 Dedication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Hello, World! 6
2.1 What to Expect from C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 Hello, World! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Compilation Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.4 Building with gcc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.5 Building with clang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.6 Building from IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.7 C Versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

3 Variables and Statements 12


3.1 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.1.1 Variable Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.1.2 Variable Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.1.3 Boolean Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.2 Operators and Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.1 Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.2 Ternary Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.3 Pre-and-Post Increment-and-Decrement . . . . . . . . . . . . . . . . . . . . . . 16
3.2.4 The Comma Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2.5 Conditional Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2.6 Boolean Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.2.7 The sizeof Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.3 Flow Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3.1 The if-else statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3.2 The while statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.3 The do-while statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.3.4 The for statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.3.5 The switch Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4 Functions 26
4.1 Passing by Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.2 Function Prototypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.3 Empty Parameter Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

i
CONTENTS ii

5 Pointers—Cower In Fear! 30
5.1 Memory and Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2 Pointer Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
5.3 Dereferencing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
5.4 Passing Pointers as Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.5 The NULL Pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5.6 A Note on Declaring Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
5.7 sizeof and Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

6 Arrays 37
6.1 Easy Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
6.2 Getting the Length of an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
6.3 Array Initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
6.4 Out of Bounds! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
6.5 Multidimensional Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
6.6 Arrays and Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.6.1 Getting a Pointer to an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.6.2 Passing Single Dimensional Arrays to Functions . . . . . . . . . . . . . . . . . . 42
6.6.3 Changing Arrays in Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
6.6.4 Passing Multidimensional Arrays to Functions . . . . . . . . . . . . . . . . . . . . 44

7 Strings 46
7.1 String Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.2 String Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
7.3 String Variables as Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7.4 String Initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
7.5 Getting String Length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.6 String Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
7.7 Copying a String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

8 Structs 51
8.1 Declaring a Struct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
8.2 Struct Initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
8.3 Passing Structs to Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
8.4 The Arrow Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.5 Copying and Returning structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.6 Comparing structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

9 File Input/Output 55
9.1 The FILE* Data Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
9.2 Reading Text Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9.3 End of File: EOF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
9.3.1 Reading a Line at a Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
9.4 Formatted Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
9.5 Writing Text Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
9.6 Binary File I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
9.6.1 struct and Number Caveats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

10 typedef: Making New Types 63


10.1 typedef in Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
10.1.1 Scoping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
10.2 typedef in Practice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
10.2.1 typedef and structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
10.2.2 typedef and Other Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
10.2.3 typedef and Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

[Link]
CONTENTS iii

10.2.4 typedef and Capitalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65


10.3 Arrays and typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

11 Pointers II: Arithmetic 67


11.1 Pointer Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
11.1.1 Adding to Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
11.1.2 Changing Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
11.1.3 Subtracting Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
11.2 Array/Pointer Equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
11.2.1 Array/Pointer Equivalence in Function Calls . . . . . . . . . . . . . . . . . . . . . 71
11.3 void Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

12 Manual Memory Allocation 76


12.1 Allocating and Deallocating, malloc() and free() . . . . . . . . . . . . . . . . . . . 76
12.2 Error Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
12.3 Allocating Space for an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
12.4 An Alternative: calloc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
12.5 Changing Allocated Size with realloc() . . . . . . . . . . . . . . . . . . . . . . . . 79
12.5.1 Reading in Lines of Arbitrary Length . . . . . . . . . . . . . . . . . . . . . . . 80
12.5.2 realloc() with NULL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
12.6 Aligned Allocations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

13 Scope 85
13.1 Block Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
13.1.1 Where To Define Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
13.1.2 Variable Hiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
13.2 File Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
13.3 for-loop Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
13.4 A Note on Function Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

14 Types II: Way More Types! 89


14.1 Signed and Unsigned Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
14.2 Character Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
14.3 More Integer Types: short, long, long long . . . . . . . . . . . . . . . . . . . . . . . 91
14.4 More Float: double and long double . . . . . . . . . . . . . . . . . . . . . . . . . 93
14.4.1 How Many Decimal Digits? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
14.4.2 Converting to Decimal and Back . . . . . . . . . . . . . . . . . . . . . . . . . . 96
14.5 Constant Numeric Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
14.5.1 Hexadecimal and Octal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
14.5.2 Integer Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
14.5.3 Floating Point Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

15 Types III: Conversions 101


15.1 String Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
15.1.1 Numeric Value to String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
15.1.2 String to Numeric Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
15.2 char Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
15.3 Numeric Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
15.3.1 Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
15.3.2 Integer to Integer Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
15.3.3 Integer and Floating Point Conversions . . . . . . . . . . . . . . . . . . . . . . 106
15.4 Implicit Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
15.4.1 The Integer Promotions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
15.4.2 The Usual Arithmetic Conversions . . . . . . . . . . . . . . . . . . . . . . . . . 106
15.4.3 void* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
CONTENTS iv

15.5 Explicit Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107


15.5.1 Casting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

16 Types IV: Qualifiers and Specifiers 109


16.1 Type Qualifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
16.1.1 const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
16.1.2 restrict . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
16.1.3 volatile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
16.1.4 _Atomic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
16.2 Storage-Class Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
16.2.1 auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
16.2.2 static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
16.2.3 extern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
16.2.4 register . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
16.2.5 _Thread_local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

17 Multifile Projects 117


17.1 Includes and Function Prototypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
17.2 Dealing with Repeated Includes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
17.3 static and extern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
17.4 Compiling with Object Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

18 The Outside Environment 121


18.1 Command Line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
18.1.1 The Last argv is NULL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
18.1.2 The Alternate: char **argv . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
18.1.3 Fun Facts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
18.2 Exit Status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
18.2.1 Other Exit Status Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
18.3 Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
18.3.1 Setting Environment Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
18.3.2 Unix-like Alternative Environment Variables . . . . . . . . . . . . . . . . . . . . 128

19 The C Preprocessor 130


19.1 #include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
19.2 Simple Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
19.3 Conditional Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
19.3.1 If Defined, #ifdef and #endif . . . . . . . . . . . . . . . . . . . . . . . . . . 132
19.3.2 If Not Defined, #ifndef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
19.3.3 #else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
19.3.4 General Conditional: #if, #elif . . . . . . . . . . . . . . . . . . . . . . . . . 133
19.3.5 Losing a Macro: #undef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
19.4 Built-in Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
19.4.1 Mandatory Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
19.4.2 Optional Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
19.5 Macros with Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
19.5.1 Macros with One Argument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
19.5.2 Macros with More than One Argument . . . . . . . . . . . . . . . . . . . . . . . 138
19.5.3 Macros with Variable Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . 139
19.5.4 Stringification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.5.5 Concatenation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.6 Multiline Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
19.7 Example: An Assert Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
19.8 The #error Directive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
19.9 The #pragma Directive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

[Link]
CONTENTS v

19.9.1 Non-Standard Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143


19.9.2 Standard Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
19.9.3 _Pragma Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
19.10 The #line Directive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
19.11 The Null Directive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145

20 structs II: More Fun with structs 146


20.1 Initializers of Nested structs and Arrays . . . . . . . . . . . . . . . . . . . . . . . . 146
20.2 Anonymous structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
20.3 Self-Referential structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
20.4 Flexible Array Members . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
20.5 Padding Bytes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
20.6 offsetof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
20.7 Bit-Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
20.7.1 Non-Adjacent Bit-Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
20.7.2 Signed or Unsigned ints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
20.7.3 Unnamed Bit-Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
20.7.4 Zero-Width Unnamed Bit-Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
20.8 Unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
20.8.1 Unions and Type Punning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
20.8.2 Pointers to unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
20.8.3 Common Initial Sequences in Unions . . . . . . . . . . . . . . . . . . . . . . . 156
20.9 Unions and Unnamed Structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
20.10 Passing and Returning structs and unions . . . . . . . . . . . . . . . . . . . . . . . 160

21 Characters and Strings II 161


21.1 Escape Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
21.1.1 Frequently-used Escapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
21.1.2 Rarely-used Escapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
21.1.3 Numeric Escapes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

22 Enumerated Types: enum 165


22.1 Behavior of enum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
22.1.1 Numbering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
22.1.2 Trailing Commas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
22.1.3 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
22.1.4 Style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
22.2 Your enum is a Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

23 Pointers III: Pointers to Pointers and More 169


23.1 Pointers to Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
23.1.1 Pointer Pointers and const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
23.2 Multibyte Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
23.3 The NULL Pointer and Zero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
23.4 Pointers as Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
23.5 Casting Pointers to other Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
23.6 Pointer Differences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
23.7 Pointers to Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

24 Bitwise Operations 181


24.1 Bitwise AND, OR, XOR, and NOT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
24.2 Bitwise Shift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

25 Variadic Functions 183


25.1 Ellipses in Function Signatures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
CONTENTS vi

25.2 Getting the Extra Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184


25.3 va_list Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
25.4 Library Functions That Use va_lists . . . . . . . . . . . . . . . . . . . . . . . . . . 186

26 Locale and Internationalization 187


26.1 Setting the Localization, Quick and Dirty . . . . . . . . . . . . . . . . . . . . . . . . . . 187
26.2 Getting the Monetary Locale Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
26.2.1 Monetary Digit Grouping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
26.2.2 Separators and Sign Position . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
26.2.3 Example Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
26.3 Localization Specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

27 Unicode, Wide Characters, and All That 192


27.1 What is Unicode? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
27.2 Code Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
27.3 Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
27.4 Source and Execution Character Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
27.5 Unicode in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
27.6 A Quick Note on UTF-8 Before We Swerve into the Weeds . . . . . . . . . . . . . . . 196
27.7 Different Character Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
27.7.1 Multibyte Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
27.7.2 Wide Characters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
27.8 Using Wide Characters and wchar_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
27.8.1 Multibyte to wchar_t Conversions . . . . . . . . . . . . . . . . . . . . . . . . . 198
27.9 Wide Character Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
27.9.1 wint_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
27.9.2 I/O Stream Orientation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
27.9.3 I/O Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
27.9.4 Type Conversion Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
27.9.5 String and Memory Copying Functions . . . . . . . . . . . . . . . . . . . . . . . 201
27.9.6 String and Memory Comparing Functions . . . . . . . . . . . . . . . . . . . . . . 201
27.9.7 String Searching Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
27.9.8 Length/Miscellaneous Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 202
27.9.9 Character Classification Functions . . . . . . . . . . . . . . . . . . . . . . . . . 202
27.10 Parse State, Restartable Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
27.11 Unicode Encodings and C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
27.11.1 UTF-8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
27.11.2 UTF-16, UTF-32, char16_t, and char32_t . . . . . . . . . . . . . . . . . . . . 205
27.11.3 Multibyte Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
27.11.4 Third-Party Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

28 Exiting a Program 207


28.1 Normal Exits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
28.1.1 Returning From main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
28.1.2 exit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
28.1.3 Setting Up Exit Handlers with atexit() . . . . . . . . . . . . . . . . . . . . . 208
28.2 Quicker Exits with quick_exit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
28.3 Nuke it from Orbit: _Exit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
28.4 Exiting Sometimes: assert() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
28.5 Abnormal Exit: abort() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210

29 Signal Handling 211


29.1 What Are Signals? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
29.2 Handling Signals with signal() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
29.3 Writing Signal Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212

[Link]
CONTENTS vii

29.4 What Can We Actually Do? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214


29.5 Friends Don’t Let Friends signal() . . . . . . . . . . . . . . . . . . . . . . . . . . . 216

30 Variable-Length Arrays (VLAs) 217


30.1 The Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
30.2 sizeof and VLAs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
30.3 Multidimensional VLAs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
30.4 Passing One-Dimensional VLAs to Functions . . . . . . . . . . . . . . . . . . . . . . 219
30.5 Passing Multi-Dimensional VLAs to Functions . . . . . . . . . . . . . . . . . . . . . . 220
30.5.1 Partial Multidimensional VLAs . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
30.6 Compatibility with Regular Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
30.7 typedef and VLAs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
30.8 Jumping Pitfalls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
30.9 General Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

31 goto 224
31.1 A Simple Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
31.2 Labeled continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
31.3 Bailing Out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
31.4 Labeled break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
31.5 Multi-level Cleanup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
31.6 Tail Call Optimization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
31.7 Restarting Interrupted System Calls . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
31.8 goto and Variable Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
31.9 goto and Variable-Length Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

32 Types Part V: Compound Literals and Generic Selections 232


32.1 Compound Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
32.1.1 Passing Unnamed Objects to Functions . . . . . . . . . . . . . . . . . . . . . . 233
32.1.2 Unnamed structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
32.1.3 Pointers to Unnamed Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
32.1.4 Unnamed Objects and Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
32.1.5 Silly Unnamed Object Example . . . . . . . . . . . . . . . . . . . . . . . . . . 235
32.2 Generic Selections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235

33 Arrays Part II 239


33.1 Type Qualifiers for Arrays in Parameter Lists . . . . . . . . . . . . . . . . . . . . . . . 239
33.2 static for Arrays in Parameter Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
33.3 Equivalent Initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240

34 Long Jumps with setjmp, longjmp 243


34.1 Using setjmp and longjmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
34.2 Pitfalls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
34.2.1 The Values of Local Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
34.2.2 How Much State is Saved? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
34.2.3 You Can’t Name Anything setjmp . . . . . . . . . . . . . . . . . . . . . . . . . 245
34.2.4 You Can’t setjmp() in a Larger Expression . . . . . . . . . . . . . . . . . . . . 245
34.2.5 When Can’t You longjmp()? . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
34.2.6 You Can’t Pass 0 to longjmp() . . . . . . . . . . . . . . . . . . . . . . . . . . 246
34.2.7 longjmp() and Variable Length Arrays . . . . . . . . . . . . . . . . . . . . . . 246

35 Incomplete Types 247


35.1 Use Case: Self-Referential Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
35.2 Incomplete Type Error Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
35.3 Other Incomplete Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
CONTENTS viii

35.4 Use Case: Arrays in Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249


35.5 Completing Incomplete Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249

36 Complex Numbers 251


36.1 Complex Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
36.2 Assigning Complex Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
36.3 Constructing, Deconstructing, and Printing . . . . . . . . . . . . . . . . . . . . . . . . 252
36.4 Complex Arithmetic and Comparisons . . . . . . . . . . . . . . . . . . . . . . . . . . 253
36.5 Complex Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
36.5.1 Trigonometry Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
36.5.2 Exponential and Logarithmic Functions . . . . . . . . . . . . . . . . . . . . . . 255
36.5.3 Power and Absolute Value Functions . . . . . . . . . . . . . . . . . . . . . . . . 255
36.5.4 Manipulation Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255

37 Fixed Width Integer Types 256


37.1 The Bit-Sized Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
37.2 Maximum Integer Size Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
37.3 Using Fixed Size Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
37.4 Limits of Fixed Size Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
37.5 Format Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258

38 Date and Time Functionality 260


38.1 Quick Terminology and Information . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
38.2 Date Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
38.3 Initialization and Conversion Between Types . . . . . . . . . . . . . . . . . . . . . . . . 261
38.3.1 Converting time_t to struct tm . . . . . . . . . . . . . . . . . . . . . . . . . 262
38.3.2 Converting struct tm to time_t . . . . . . . . . . . . . . . . . . . . . . . . . 262
38.4 Formatted Date Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
38.5 More Resolution with timespec_get() . . . . . . . . . . . . . . . . . . . . . . . . . . 264
38.6 Differences Between Times . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264

39 Multithreading 266
39.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
39.2 Things You Can Do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
39.3 Data Races and the Standard Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
39.4 Creating and Waiting for Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
39.5 Detaching Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
39.6 Thread Local Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
39.6.1 _Thread_local Storage-Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
39.6.2 Another Option: Thread-Specific Storage . . . . . . . . . . . . . . . . . . . . . . 274
39.7 Mutexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
39.7.1 Different Mutex Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
39.8 Condition Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
39.8.1 Timed Condition Wait . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
39.8.2 Broadcast: Wake Up All Waiting Threads . . . . . . . . . . . . . . . . . . . . . . 284
39.9 Running a Function One Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284

40 Atomics 285
40.1 Testing for Atomic Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
40.2 Atomic Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
40.3 Synchronization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
40.4 Acquire and Release . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
40.5 Sequential Consistency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
40.6 Atomic Assignments and Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
40.7 Library Functions that Automatically Synchronize . . . . . . . . . . . . . . . . . . . . 292

[Link]
CONTENTS ix

40.8 Atomic Type Specifier, Qualifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292


40.9 Lock-Free Atomic Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
40.9.1 Signal Handlers and Lock-Free Atomics . . . . . . . . . . . . . . . . . . . . . . 295
40.10 Atomic Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
40.11 Atomic structs and unions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
40.12 Atomic Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
40.13 Memory Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
40.13.1 Sequential Consistency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
40.13.2 Acquire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
40.13.3 Release . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
40.13.4 Consume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
40.13.5 Acquire/Release . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
40.13.6 Relaxed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
40.14 Fences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
40.15 References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299

41 Function Specifiers, Alignment Specifiers/Operators 301


41.1 Function Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
41.1.1 inline for Speed—Maybe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
41.1.2 noreturn and _Noreturn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
41.2 Alignment Specifiers and Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
41.2.1 alignas and _Alignas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
41.2.2 alignof and _Alignof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305

42 <assert.h> Runtime and Compile-time Diagnostics 307


42.1 Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
42.2 assert() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
42.3 static_assert() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309

43 <complex.h> Complex Number Functionality 311


43.1 cacos() cacosf() cacosl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
43.2 casin() casinf() casinl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
43.3 catan() catanf() catanl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
43.4 ccos() ccosf() ccosl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
43.5 csin() csinf() csinl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
43.6 ctan() ctanf() ctanl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
43.7 cacosh() cacoshf() cacoshl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
43.8 casinh() casinhf() casinhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
43.9 catanh() catanhf() catanhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
43.10 ccosh() ccoshf() ccoshl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
43.11 csinh() csinhf() csinhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
43.12 ctanh() ctanhf() ctanhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
43.13 cexp() cexpf() cexpl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
43.14 clog() clogf() clogl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
43.15 cabs() cabsf() cabsl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
43.16 cpow() cpowf() cpowl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
43.17 csqrt() csqrtf() csqrtl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
43.18 carg() cargf() cargl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
43.19 cimag() cimagf() cimagl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
43.20 CMPLX() CMPLXF() CMPLXL() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
43.21 conj() conjf() conjl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
43.22 cproj() cproj() cproj() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
43.23 creal() crealf() creall() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333

44 <ctype.h> Character Classification and Conversion 335


CONTENTS x

44.1 isalnum() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336


44.2 isalpha() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
44.3 isblank() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
44.4 iscntrl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
44.5 isdigit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
44.6 isgraph() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
44.7 islower() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
44.8 isprint() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
44.9 ispunct() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
44.10 isspace() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
44.11 isupper() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
44.12 isxdigit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
44.13 tolower() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
44.14 toupper() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346

45 <errno.h> Error Information 348


45.1 errno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348

46 <fenv.h> Floating Point Exceptions and Environment 351


46.1 Types and Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
46.2 Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
46.3 feclearexcept() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
46.4 fegetexceptflag() fesetexceptflag() . . . . . . . . . . . . . . . . . . . . . . . 353
46.5 feraiseexcept() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
46.6 fetestexcept() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
46.7 fegetround() fesetround() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
46.8 fegetenv() fesetenv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
46.9 feholdexcept() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
46.10 feupdateenv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360

47 <float.h> Floating Point Limits 363


47.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
47.2 FLT_ROUNDS Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
47.3 FLT_EVAL_METHOD Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
47.4 Subnormal Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
47.5 How Many Decimal Places Can I Use? . . . . . . . . . . . . . . . . . . . . . . . . . . 366
47.6 Comprehensive Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367

48 <inttypes.h> More Integer Conversions 370


48.1 Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
48.2 imaxabs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
48.3 imaxdiv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
48.4 strtoimax() strtoumax() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
48.5 wcstoimax() wcstoumax() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374

49 <iso646.h> Alternative Operator Spellings 376

50 <limits.h> Numeric Limits 377


50.1 CHAR_MIN and CHAR_MAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
50.2 Choosing the Correct Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
50.3 Whither Two’s Complement? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
50.4 Demo Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378

51 <locale.h> locale handling 380


51.1 setlocale() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380

[Link]
CONTENTS xi

51.2 localeconv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382

52 <math.h> Mathematics 386


52.1 Math Function Idioms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
52.2 Math Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
52.3 Math Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
52.4 Math Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
52.5 Math Pragmas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
52.6 fpclassify() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
52.7 isfinite(), isinf(), isnan(), isnormal() . . . . . . . . . . . . . . . . . . . . . . 391
52.8 signbit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
52.9 acos(), acosf(), acosl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
52.10 asin(), asinf(), asinl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
52.11 atan(), atanf(), atanl(), atan2(), atan2f(), atan2l() . . . . . . . . . . . . . . 395
52.12 cos(), cosf(), cosl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
52.13 sin(), sinf(), sinl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
52.14 tan(), tanf(), tanl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
52.15 acosh(), acoshf(), acoshl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
52.16 asinh(), asinhf(), asinhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
52.17 atanh(), atanhf(), atanhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
52.18 cosh(), coshf(), coshl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
52.19 sinh(), sinhf(), sinhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
52.20 tanh(), tanhf(), tanhl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
52.21 exp(), expf(), expl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
52.22 exp2(), exp2f(), exp2l() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
52.23 expm1(), expm1f(), expm1l() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
52.24 frexp(), frexpf(), frexpl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
52.25 ilogb(), ilogbf(), ilogbl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
52.26 ldexp(), ldexpf(), ldexpl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
52.27 log(), logf(), logl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
52.28 log10(), log10f(), log10l() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
52.29 log1p(), log1pf(), log1pl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
52.30 log2(), log2f(), log2l() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
52.31 logb(), logbf(), logbl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
52.32 modf(), modff(), modfl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
52.33 scalbn(), scalbnf(), scalbnl() scalbln(), scalblnf(), scalblnl() . . . . . . 415
52.34 cbrt(), cbrtf(), cbrtl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416
52.35 fabs(), fabsf(), fabsl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
52.36 hypot(), hypotf(), hypotl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
52.37 pow(), powf(), powl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
52.38 sqrt() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
52.39 erf(), erff(), erfl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
52.40 erfc(), erfcf(), erfcl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
52.41 lgamma(), lgammaf(), lgammal() . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
52.42 tgamma(), tgammaf(), tgammal() . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
52.43 ceil(), ceilf(), ceill() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
52.44 floor(), floorf(), floorl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
52.45 nearbyint(), nearbyintf(), nearbyintl() . . . . . . . . . . . . . . . . . . . . . 426
52.46 rint(), rintf(), rintl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
52.47 lrint(), lrintf(), lrintl(), llrint(), llrintf(), llrintl() . . . . . . . . . . . 427
52.48 round(), roundf(), roundl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
52.49 lround(), lroundf(), lroundl() llround(), llroundf(), llroundl() . . . . . . 429
52.50 trunc(), truncf(), truncl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
52.51 fmod(), fmodf(), fmodl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
CONTENTS xii

52.52 remainder(), remainderf(), remainderl() . . . . . . . . . . . . . . . . . . . . . 432


52.53 remquo(), remquof(), remquol() . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
52.54 copysign(), copysignf(), copysignl() . . . . . . . . . . . . . . . . . . . . . . . 435
52.55 nan(), nanf(), nanl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
52.56 nextafter(), nextafterf(), nextafterl() . . . . . . . . . . . . . . . . . . . . . . 437
52.57 nexttoward(), nexttowardf(), nexttowardl() . . . . . . . . . . . . . . . . . . . 438
52.58 fdim(), fdimf(), fdiml() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
52.59 fmax(), fmaxf(), fmaxl(), fmin(), fminf(), fminl() . . . . . . . . . . . . . . . . 439
52.60 fma(), fmaf(), fmal() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
52.61 isgreater(), isgreaterequal(), isless(), islessequal() . . . . . . . . . . . . . 441
52.62 islessgreater() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
52.63 isunordered() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443

53 <setjmp.h> Non-local Goto 444


53.1 setjmp() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
53.2 longjmp() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446

54 <signal.h> signal handling 449


54.1 signal() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
54.2 raise() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453

55 <stdalign.h> Macros for Alignment 455


55.1 alignas() _Alignas() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
55.2 alignof() _Alignof() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457

56 <stdarg.h> Variable Arguments 459


56.1 va_arg() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
56.2 va_copy() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
56.3 va_end() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462
56.4 va_start() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463

57 <stdatomic.h> Atomic-Related Functions 466


57.1 Atomic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
57.2 Lock-free Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
57.3 Atomic Flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
57.4 Memory Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 468
57.5 ATOMIC_VAR_INIT() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
57.6 atomic_init() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
57.7 kill_dependency() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
57.8 atomic_thread_fence() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
57.9 atomic_signal_fence() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
57.10 atomic_is_lock_free() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
57.11 atomic_store() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
57.12 atomic_load() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
57.13 atomic_exchange() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
57.14 atomic_compare_exchange_*() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
57.15 atomic_fetch_*() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 480
57.16 atomic_flag_test_and_set() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482
57.17 atomic_flag_clear() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484

58 <stdbool.h> Boolean Types 486


58.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
58.2 _Bool? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487

59 <stddef.h> A Few Standard Definitions 488

[Link]
CONTENTS xiii

59.1 ptrdiff_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488


59.2 size_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
59.3 max_align_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
59.4 wchar_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
59.5 offsetof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490

60 <stdint.h> More Integer Types 491


60.1 Specific-Width Integers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
60.2 Other Integer Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
60.3 Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
60.4 Other Limits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
60.5 Macros for Declaring Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493

61 <stdio.h> Standard I/O Library 494


61.1 remove() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
61.2 rename() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
61.3 tmpfile() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498
61.4 tmpnam() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
61.5 fclose() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 500
61.6 fflush() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
61.7 fopen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
61.8 freopen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
61.9 setbuf(), setvbuf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
61.10 printf(), fprintf(), sprintf(), snprintf() . . . . . . . . . . . . . . . . . . . . . 507
61.11 scanf(), fscanf(), sscanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513
61.12 vprintf(), vfprintf(), vsprintf(), vsnprintf() . . . . . . . . . . . . . . . . . 519
61.13 vscanf(), vfscanf(), vsscanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 521
61.14 getc(), fgetc(), getchar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 522
61.15 gets(), fgets() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
61.16 putc(), fputc(), putchar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525
61.17 puts(), fputs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
61.18 ungetc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
61.19 fread() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
61.20 fwrite() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529
61.21 fgetpos(), fsetpos() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
61.22 fseek(), rewind() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
61.23 ftell() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 533
61.24 feof(), ferror(), clearerr() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534
61.25 perror() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536

62 <stdlib.h> Standard Library Functions 538


62.1 <stdlib.h> Types and Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
62.2 atof() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 539
62.3 atoi(), atol(), atoll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
62.4 strtod(), strtof(), strtold() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 541
62.5 strtol(), strtoll(), strtoul(), strtoull() . . . . . . . . . . . . . . . . . . . . 543
62.6 rand() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545
62.7 srand() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
62.8 aligned_alloc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
62.9 calloc(), malloc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
62.10 free() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
62.11 realloc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
62.12 abort() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
62.13 atexit(), at_quick_exit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
CONTENTS xiv

62.14 exit(), quick_exit(), _Exit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556


62.15 getenv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557
62.16 system() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
62.17 bsearch() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
62.18 qsort() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561
62.19 abs(), labs(), llabs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 562
62.20 div(), ldiv(), lldiv() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
62.21 mblen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
62.22 mbtowc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
62.23 wctomb() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 567
62.24 mbstowcs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
62.25 wcstombs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 570

63 <stdnoreturn.h> Macros for Non-Returning Functions 572

64 <string.h> String Manipulation 573


64.1 memcpy(), memmove() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
64.2 strcpy(), strncpy() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574
64.3 strcat(), strncat() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
64.4 strcmp(), strncmp(), memcmp() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
64.5 strcoll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578
64.6 strxfrm() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 579
64.7 strchr(), strrchr(), memchr() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582
64.8 strspn(), strcspn() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
64.9 strpbrk() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584
64.10 strstr() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
64.11 strtok() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 586
64.12 memset() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
64.13 strerror() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588
64.14 strlen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589

65 <tgmath.h> Type-Generic Math Functions 591


65.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 592

66 <threads.h> Multithreading Functions 594


66.1 call_once() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
66.2 cnd_broadcast() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596
66.3 cnd_destroy() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
66.4 cnd_init() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
66.5 cnd_signal() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 602
66.6 cnd_timedwait() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
66.7 cnd_wait() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
66.8 mtx_destroy() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 607
66.9 mtx_init() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
66.10 mtx_lock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
66.11 mtx_timedlock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
66.12 mtx_trylock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614
66.13 mtx_unlock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
66.14 thrd_create() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 617
66.15 thrd_current() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
66.16 thrd_detach() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 620
66.17 thrd_equal() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
66.18 thrd_exit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
66.19 thrd_join() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
66.20 thrd_sleep() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625

[Link]
CONTENTS xv

66.21 thrd_yield() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626


66.22 tss_create() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 628
66.23 tss_delete() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
66.24 tss_get() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
66.25 tss_set() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 634

67 <time.h> Date and Time Functions 637


67.1 Thread Safety Warning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
67.2 clock() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
67.3 difftime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639
67.4 mktime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
67.5 time() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
67.6 timespec_get() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643
67.7 asctime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 645
67.8 ctime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
67.9 gmtime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
67.10 localtime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 647
67.11 strftime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648

68 <uchar.h> Unicode utility functions 653


68.1 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653
68.2 OS X issue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
68.3 mbrtoc16() mbrtoc32() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
68.4 c16rtomb() c32rtomb() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657

69 <wchar.h> Wide Character Handling 660


69.1 Restartable Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
69.2 wprintf(), fwprintf(), swprintf() . . . . . . . . . . . . . . . . . . . . . . . . . 662
69.3 wscanf() fwscanf() swscanf() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
69.4 vwprintf() vfwprintf() vswprintf() . . . . . . . . . . . . . . . . . . . . . . . . . 664
69.5 vwscanf(), vfwscanf(), vswscanf() . . . . . . . . . . . . . . . . . . . . . . . . . 665
69.6 getwc() fgetwc() getwchar() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
69.7 fgetws() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 667
69.8 putwchar() putwc() fputwc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
69.9 fputws() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
69.10 fwide() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 670
69.11 ungetwc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
69.12 wcstod() wcstof() wcstold() . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
69.13 wcstol() wcstoll() wcstoul() wcstoull() . . . . . . . . . . . . . . . . . . . . . 675
69.14 wcscpy() wcsncpy() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
69.15 wmemcpy() wmemmove() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 677
69.16 wcscat() wcsncat() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
69.17 wcscmp(), wcsncmp(), wmemcmp() . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
69.18 wcscoll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 680
69.19 wcsxfrm() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
69.20 wcschr() wcsrchr() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 683
69.21 wcsspn() wcscspn() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
69.22 wcspbrk() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685
69.23 wcsstr() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
69.24 wcstok() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
69.25 wcslen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
69.26 wcsftime() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
69.27 btowc() wctob() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 690
69.28 mbsinit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
CONTENTS xvi

69.29 mbrlen() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692


69.30 mbrtowc() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
69.31 wcrtomb() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
69.32 mbsrtowcs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 696
69.33 wcsrtombs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 698

70 <wctype.h> Wide Character Classification and Transformation 701


70.1 iswalnum() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
70.2 iswalpha() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 702
70.3 iswblank() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
70.4 iswcntrl() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
70.5 iswdigit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
70.6 iswgraph() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
70.7 iswlower() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706
70.8 iswprint() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
70.9 iswpunct() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 708
70.10 iswspace() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
70.11 iswupper() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 710
70.12 iswxdigit() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
70.13 iswctype() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
70.14 wctype() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 713
70.15 towlower() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
70.16 towupper() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
70.17 towctrans() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
70.18 wctrans() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718

71 Exercises 720
71.1 Intro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
71.2 Variables and Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 720
71.3 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
71.4 Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
71.5 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
71.6 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
71.7 Structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
71.8 I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
71.9 Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
71.10 Pointers II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 723
71.11 Manual Memory Allocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
71.12 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
71.13 Types II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
71.14 Types III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
71.15 Types IV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
71.16 Multifile Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
71.17 The Outside Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
71.18 The C Preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727

[Link]
CONTENTS 1

\index{Modulus|see {\texttt{%} modulus operator}}


Chapter 1

Foreword

No point in wasting words here, folks, let’s jump straight into the C code:
E((ck?main((z?(stat(M,&t)?P+=a+'{'?[Link]
execv(M,k),a=G,i=P,y=G&255,
sprintf(Q,y/'@'-3?A(*L(V(%d+%d)+%d,0)

And they lived happily ever after. The End.


What’s this? You say something’s still not clear about this whole C programming language thing?
Well, to be quite honest, I’m not even sure what the above code does. It’s a snippet from one of the entries in
the 2001 International Obfuscated C Code Contest1 , a wonderful competition wherein the entrants attempt
to write the most unreadable C code possible, with often surprising results.
The bad news is that if you’re a beginner in this whole thing, all C code you see probably looks obfuscated!
The good news is, it’s not going to be that way for long.
What we’ll try to do over the course of this guide is lead you from complete and utter sheer lost confusion
on to the sort of enlightened bliss that can only be obtained though pure C programming. Right on.

1.1 Audience
This guide assumes that you’ve already got some programming knowledge under your belt from another
language, such as Python2 , JavaScript3 , Java4 , Rust5 , Go6 , Swift7 , etc. (Objective-C8 devs will have a par-
ticularly easy time of it!)
We’re going to assume you know what variables are, what loops do, how functions work, and so on.
If that’s not you for whatever reason the best I can hope to provide is some honest entertainment for your
reading pleasure. The only thing I can reasonably promise is that this guide won’t end on a cliffhanger… or
will it?
1
[Link]
2
[Link]
3
[Link]
4
[Link]
5
[Link]
6
[Link]
7
[Link]
8
[Link]

[Link]
Chapter 1. Foreword 3

1.2 How to Read This Book


The book is in two parts: the tutorial part and the reference part.
If you’re new, go through the tutorial part in order, generally. The higher you get in chapters, the less
important it is to go in order.
And no matter your skill level, the reference part is there with complete examples of the standard library
function calls to help refresh your memory whenever needed. Good for reading over a bowl of cereal or
other time.

1.3 Platform and Compiler


I’ll try to stick to Plain Ol’-Fashioned ISO-standard C9 . Well, for the most part. Here and there I might go
crazy and start talking about POSIX10 or something, but we’ll see.
Unix users (e.g. Linux, BSD, etc.) try running cc or gcc from the command line–you might already have a
compiler installed. If you don’t, search your distribution for installing gcc or clang.
Windows users should check out Visual Studio Community11 . Or, if you’re looking for a more Unix-like
experience (recommended!), install WSL12 and gcc.
Mac users will want to install XCode13 , and in particular the command line tools.
There are a lot of compilers out there, and virtually all of them will work for this book. And a C++ compiler
will compile a lot of (but not all!) C code. Best use a proper C compiler if you can.

1.4 Official Homepage


This official location of this document is [Link] . Maybe this’ll change in the future, but
it’s more likely that all the other guides are migrated off Chico State computers.

1.5 Email Policy


I’m generally available to help out with email questions so feel free to write in, but I can’t guarantee a
response. I lead a pretty busy life and there are times when I just can’t answer a question you have. When
that’s the case, I usually just delete the message. It’s nothing personal; I just won’t ever have the time to give
the detailed answer you require.
As a rule, the more complex the question, the less likely I am to respond. If you can narrow down your
question before mailing it and be sure to include any pertinent information (like platform, compiler, error
messages you’re getting, and anything else you think might help me troubleshoot), you’re much more likely
to get a response.
If you don’t get a response, hack on it some more, try to find the answer, and if it’s still elusive, then write
me again with the information you’ve found and hopefully it will be enough for me to help out.
Now that I’ve badgered you about how to write and not write me, I’d just like to let you know that I fully
appreciate all the praise the guide has received over the years. It’s a real morale boost, and it gladdens me to
hear that it is being used for good! :-) Thank you!
9
[Link]
10
[Link]
11
[Link]
12
[Link]
13
[Link]
14
[Link]
Chapter 1. Foreword 4

1.6 Mirroring
You are more than welcome to mirror this site, whether publicly or privately. If you publicly mirror the site
and want me to link to it from the main page, drop me a line at beej@[Link].

1.7 Note for Translators


If you want to translate the guide into another language, write me at beej@[Link] and I’ll link to your
translation from the main page. Feel free to add your name and contact info to the translation.
Please note the license restrictions in the Copyright and Distribution section, below.

1.8 Copyright and Distribution


Beej’s Guide to C is Copyright © 2021 Brian “Beej Jorgensen” Hall.
With specific exceptions for source code and translations, below, this work is licensed under the Creative
Commons Attribution-Noncommercial-No Derivative Works 3.0 License. To view a copy of this license,
visit [Link] or send a letter to Creative Commons,
171 Second Street, Suite 300, San Francisco, California, 94105, USA.
One specific exception to the “No Derivative Works” portion of the license is as follows: this guide may
be freely translated into any language, provided the translation is accurate, and the guide is reprinted in its
entirety. The same license restrictions apply to the translation as to the original guide. The translation may
also include the name and contact information for the translator.
The C source code presented in this document is hereby granted to the public domain, and is completely free
of any license restriction.
Educators are freely encouraged to recommend or supply copies of this guide to their students.
Contact beej@[Link] for more information.

1.9 Dedication
The hardest things about writing these guides are:
• Learning the material in enough detail to be able to explain it
• Figuring out the best way to explain it clearly, a seemingly-endless iterative process
• Putting myself out there as a so-called authority, when really I’m just a regular human trying to make
sense of it all, just like everyone else
• Keeping at it when so many other things draw my attention
A lot of people have helped me through this process, and I want to acknowledge those who have made this
book possible.
• Everyone on the Internet who decided to help share their knowledge in one form or another. The free
sharing of instructive information is what makes the Internet the great place that it is.
• The volunteers at cppreference.com15 who provide the bridge that leads from the spec to the real world.
• The helpful and knowledgeable folks on [Link].c16 and r/C_Programming17 who got me through
the tougher parts of the language.
• Everyone who submitted corrections and pull-requests on everything from misleading instructions to
typos.
15
[Link]
16
[Link]
17
[Link]

[Link]
Chapter 1. Foreword 5

Thank you! ♥
Chapter 2

Hello, World!

2.1 What to Expect from C


“Where do these stairs go?”
“They go up.”
—Ray Stantz and Peter Venkman, Ghostbusters
C is a low-level language.
It didn’t used to be. Back in the day when people carved punch cards out of granite, C was an incredible way
to be free of the drudgery of lower-level languages like assembly1 .
But now in these modern times, current-generation languages offer all kinds of features that didn’t exist in
1972 when C was invented. This means C is a pretty basic language with not a lot of features. It can do
anything, but it can make you work for it.
So why would we even use it today?
• As a learning tool: not only is C a venerable piece of computing history, but it is connected to the bare
metal2 in a way that present-day languages are not. When you learn C, you learn about how software
interfaces with computer memory at a low level. There are no seatbelts. You’ll write software that
crashes, I assure you. And that’s all part of the fun!
• As a useful tool: C still is used for certain applications, such as building operating systems3 or in
embedded systems4 . (Though the Rust5 programming language is eyeing both these fields!)
If you’re familiar with another language, a lot of things about C are easy. C inspired many other languages,
and you’ll see bits of it in Go, Rust, Swift, Python, JavaScript, Java, and all kinds of other languages. Those
parts will be familiar.
The one thing about C that hangs people up is pointers. Virtually everything else is familiar, but pointers are
the weird one. The concept behind pointers is likely one you already know, but C forces you to be explicit
about it, using operators you’ve likely never seen before.
It’s especially insidious because once you grok6 pointers, they’re suddenly easy. But up until that moment,
they’re slippery eels.
1
[Link]
2
[Link]
3
[Link]
4
[Link]
5
[Link]
6
[Link]

[Link]
Chapter 2. Hello, World! 7

Everything else in C is just memorizing another way (or sometimes the same way!) of doing something
you’ve done already. Pointers are the weird bit. And, arguably, even pointers are variations on a theme
you’re probably familiar with.
So get ready for a rollicking adventure as close to the core of the computer as you can get without assembly,
in the most influential computer language of all time7 . Hang on!

2.2 Hello, World!


This is the canonical example of a C program. Everyone uses it. (Note that the numbers to the left are for
reader reference only, and are not part of the source code.)
1 /* Hello world program */
2

3 #include <stdio.h>
4

5 int main(void)
6 {
7 printf("Hello, World!\n"); // Actually do the work here
8 }

We’re going to don our long-sleeved heavy-duty rubber gloves, grab a scalpel, and rip into this thing to see
what makes it tick. So, scrub up, because here we go. Cutting very gently…
Let’s get the easy thing out of the way: anything between the digraphs /* and */ is a comment and will be
completely ignored by the compiler. Same goes for anything on a line after a //. This allows you to leave
messages to yourself and others, so that when you come back and read your code in the distant future, you’ll
know what the heck it was you were trying to do. Believe me, you will forget; it happens.
Now, what is this #include? GROSS! Well, it tells the C Preprocessor to pull the contents of another file
and insert it into the code right there.
Wait—what’s a C Preprocessor? Good question. There are two stages8 to compilation: the preprocessor
and the compiler. Anything that starts with pound sign, or “octothorpe”, (#) is something the preprocessor
operates on before the compiler even gets started. Common preprocessor directives, as they’re called, are
#include and #define. More on that later.

Before we go on, why would I even begin to bother pointing out that a pound sign is called an octothorpe?
The answer is simple: I think the word octothorpe is so excellently funny, I have to gratuitously spread its
name around whenever I get the opportunity. Octothorpe. Octothorpe, octothorpe, octothorpe.
So anyway. After the C preprocessor has finished preprocessing everything, the results are ready for the
compiler to take them and produce assembly code9 , machine code10 , or whatever it’s about to do. Machine
code is the “language” the CPU understands, and it can understand it very rapidly. This is one of the reasons
C programs tend to be quick.
Don’t worry about the technical details of compilation for now; just know that your source runs through the
preprocessor, then the output of that runs through the compiler, then that produces an executable for you to
run.
What about the rest of the line? What’s <stdio.h>? That is what is known as a header file. It’s the dot-h at
the end that gives it away. In fact it’s the “Standard I/O” (stdio) header file that you will grow to know and
love. It gives us access to a bunch of I/O functionality11 . For our demo program, we’re outputting the string
7
I know someone will fight me on that, but it’s gotta be at least in the top three, right?
8
Well, technically there are more than two, but hey, let’s pretend there are two—ignorance is bliss, right?
9
[Link]
10
[Link]
11
Technically, it contains preprocessor directives and function prototypes (more on that later) for common input and output needs.
Chapter 2. Hello, World! 8

“Hello, World!”, so we in particular need access to the printf() function to do this. The <stdio.h> file
gives us this access. Basically, if we tried to use without #include <stdio.h>, the compiler would have
complained to us about it.
How did I know I needed to #include <stdio.h> for printf()? Answer: it’s in the documentation. If
you’re on a Unix system, man 3 printf and it’ll tell you right at the top of the man page what header files
are required. Or see the reference section in this book. :-)
Holy moly. That was all to cover the first line! But, let’s face it, it has been completely dissected. No mystery
shall remain!
So take a breather…look back over the sample code. Only a couple easy lines to go.
Welcome back from your break! I know you didn’t really take a break; I was just humoring you.
The next line is main(). This is the definition of the function main(); everything between the squirrelly
braces ({ and }) is part of the function definition.
(How do you call a different function, anyway? The answer lies in the printf() line, but we’ll get to that
in a minute.)
Now, the main function is a special one in many ways, but one way stands above the rest: it is the function
that will be called automatically when your program starts executing. Nothing of yours gets called before
main(). In the case of our example, this works fine since all we want to do is print a line and exit.

Oh, that’s another thing: once the program executes past the end of main(), down there at the closing
squirrelly brace, the program will exit, and you’ll be back at your command prompt.
So now we know that that program has brought in a header file, stdio.h, and declared a main() function
that will execute when the program is started. What are the goodies in main()?
I am so happy you asked. Really! We only have the one goodie: a call to the function printf(). You can
tell this is a function call and not a function definition in a number of ways, but one indicator is the lack of
squirrelly braces after it. And you end the function call with a semicolon so the compiler knows it’s the end
of the expression. You’ll be putting semicolons after almost everything, as you’ll see.
You’re passing one argument to the function printf(): a string to be printed when you call it. Oh, yeah—
we’re calling a function! We rock! Wait, wait—don’t get cocky. What’s that crazy \n at the end of the string?
Well, most characters in the string will print out just like they are stored. But there are certain characters that
you can’t print on screen well that are embedded as two-character backslash codes. One of the most popular
is \n (read “backslash-N”) that corresponds to the newline character. This is the character that causes further
printing to continue at the beginning of the next line instead of the current. It’s like hitting return at the end
of the line.
So copy that code into a file called hello.c and build it. On a Unix-like platform (e.g. Linux, BSD, Mac,
or WSL), from the command line you’ll build with a command like so:
gcc -o hello hello.c

(This means “compile hello.c, and output an executable called hello”.)


After that’s done, you should have a file called hello that you can run with this command:
./hello

(The leading ./ tells the shell to “run from the current directory”.)
And see what happens:
Hello, World!

It’s done and tested! Ship it!

[Link]
Chapter 2. Hello, World! 9

2.3 Compilation Details


Let’s talk a bit more about how to build C programs, and what happens behind the scenes there.
Like other languages, C has source code. But, depending on what language you’re coming from, you might
never have had to compile your source code into an executable.
Compilation is the process of taking your C source code and turning it into a program that your operating
system can execute.
JavaScript and Python devs aren’t used to a separate compilation step at all–though behind the scenes it’s
happening! Python compiles your source code into something called bytecode that the Python virtual machine
can execute. Java devs are used to compilation, but that produces bytecode for the Java Virtual Machine.
When compiling C, machine code is generated. This is the 1s and 0s that can be executed directly and speedily
by the CPU.
Languages that typically aren’t compiled are called interpreted languages. But as we mentioned
with Java and Python, they also have a compilation step. And there’s no rule saying that C can’t
be interpreted. (There are C interpreters out there!) In short, it’s a bunch of gray areas. Com-
pilation in general is just taking source code and turning it into another, more easily-executed
form.
The C compiler is the program that does the compilation.
As we’ve already said, gcc is a compiler that’s installed on a lot of Unix-like operating systems12 . And it’s
commonly run from the command line in a terminal, but not always. You can run it from your IDE, as well.
So how do we do command line builds?

2.4 Building with gcc


If you have a source file called hello.c in the current directory, you can build that into a program called
hello with this command typed in a terminal:
gcc -o hello hello.c

The -o means “output to this file”13 . And there’s hello.c at the end, the name of the file we want to compile.
If your source is broken up into multiple files, you can compile them all together (almost as if they were one
file, but the rules are actually more complex than that) by putting all the .c files on the command line:
gcc -o awesomegame ui.c characters.c npc.c items.c

and they’ll all get built together into a big executable.


That’s enough to get started—later we’ll talk details about multiple source files, object files, and all kinds of
fun stuff.

2.5 Building with clang


On Macs, the compiler isn’t gcc—it’s clang[t[clang]T]. But a wrapper is also installed so you can run gcc
and have it still work.
12
[Link]
13
If you don’t give it an output filename, it will export to a file called [Link] by default—this filename has its roots deep in Unix
history.
Chapter 2. Hello, World! 10

2.6 Building from IDEs


If you’re using an Integrated Development Environment (IDE), you probably don’t have to build from the
command line.
With Visual Studio, CTRL-F7 will build, and CTRL-F5 will run.
With VS Code, things are more complex, but you can hit F5 to run via the debugger. (You’ll have to install
the C/C++ Extension.)
With XCode, you can build with COMMAND-B and run with COMMAND-R. To get the command line tools, Google
for “XCode command line tools” and you’ll find instructions for installing them.
For getting started, I encourage you to also try to build from the command line—it’s history!

2.7 C Versions
C has come a long way over the years, and it had many named version numbers to describe which dialect of
the language you’re using.
These generally refer to the year of the specification.
The most famous are C89, C99, C11, and C2x. We’ll focus on the latter in this book.
But here’s a more complete table:

Version Description
K&R C 1978, the original. Named after Brian Kernighan and Dennis Ritchie.
Ritchie designed and coded the language, and Kernighan co-authored the
book on it. You rarely see original K&R code today. If you do, it’ll look odd,
like Middle English looks odd to modern English readers.
C89, ANSI C, C90 In 1989, the American National Standards Institute (ANSI) produced a C
language specification that set the tone for C that persists to this day. A year
later, the reins were handed to the International Organization for
Standardization (ISO) that produced the identical C90.
C95 A rarely-mentioned addition to C89 that included wide character support.
C99 The first big overhaul with lots of language additions. The thing most people
remember is the addition of //-style comments. This is the most popular
version of C in use as of this writing.
C11 This major version update includes Unicode support and multi-threading. Be
advised that if you start using these language features, you might be
sacrificing portability with places that are stuck in C99 land. But, honestly,
1999 is getting to be a while back now.
C17, C18 Bugfix update to C11. C17 seems to be the official name, but the publication
was delayed until 2018. As far as I can tell, these two are interchangeable,
with C17 being preferred.
C2x What’s coming next! Expected to eventually become C21.

You can force GCC to use one of these standards with the -std= command line argument. If you want it to
be picky about the standard, add -pedantic.
For example:
gcc -std=c11 -pedantic foo.c

For this book, I compile programs for C2x with all warnings set:

[Link]
Chapter 2. Hello, World! 11

gcc -Wall -Wextra -std=c2x -pedantic foo.c


Chapter 3

Variables and Statements

“It takes all kinds to make a world, does it not, Padre?”


“So it does, my son, so it does.”
—Pirate Captain Thomas Bartholomew Red to the Padre, Pirates
There sure can be lotsa stuff in a C program.
Yup.
And for various reasons, it’ll be easier for all of us if we classify some of the types of things you can find in
a program, so we can be clear what we’re talking about.

3.1 Variables
It’s said that “variables hold values”. But another way to think about it is that a variable is a human-readable
name that refers to some data in memory.
We’re going to take a second here and take a peek down the rabbit hole that is pointers. Don’t worry about
it.
You can think of memory as a big array of bytes1 . Data is stored in this “array”2 . If a number is larger than
a single byte, it is stored in multiple bytes. Because memory is like an array, each byte of memory can be
referred to by its index. This index into memory is also called an address, or a location, or a pointer.
When you have a variable in C, the value of that variable is in memory somewhere, at some address. Of
course. After all, where else would it be? But it’s a pain to refer to a value by its numeric address, so we
make a name for it instead, and that’s what the variable is.
The reason I’m bringing all this up is twofold:
1. It’s going to make it easier to understand pointer variables later—they’re variables that hold the address
of other variables!
2. Also, it’s going to make it easier to understand pointers later.
So a variable is a name for some data that’s stored in memory at some address.
1
A “byte” is typically an 8-bit binary number. Think of it as an integer that can only hold the values from 0 to 255, inclusive.
Technically, C allows bytes to be any number of bits and if you want to unambiguously refer to an 8-bit number, you should use the
term octet. But programmers are going assume you mean 8-bits when you say “byte” unless you specify otherwise.
2
I’m seriously oversimplifying how modern memory works, here. But the mental model works, so please forgive me.

12

[Link]
Chapter 3. Variables and Statements 13

3.1.1 Variable Names


You can use any characters in the range 0-9, A-Z, a-z, and underscore for variable names, with the following
rules:
• You can’t start a variable with a digit 0-9.
• You can’t start a variable name with two underscores.
• You can’t start a variable name with an underscore followed by a capital A-Z.
For Unicode, just try it. There are some rules in the spec in §D.2 that talk about which Unicode codepoint
ranges are allowed in which parts of identifiers, but that’s too much to write about here and is probably
something you’ll never have to think about anyway.

3.1.2 Variable Types


Depending on which languages you already have in your toolkit, you might or might not be familiar with the
idea of types. But C’s kinda picky about them, so we should do a refresher.
Some example types, some of the most basic:

Type Example C Type


Integer 3490 int
Floating point 3.14159 float
Character (single) 'c' char
String "Hello, world!" char *3

C makes an effort to convert automatically between most numeric types when you ask it to. But other than
that, all conversions are manual, notably between string and numeric.
Almost all of the types in C are variants on these types.
Before you can use a variable, you have to declare that variable and tell C what type the variable holds. Once
declared, the type of variable cannot be changed later at runtime. What you set it to is what it is until it falls
out of scope and is reabsorbed into the universe.
Let’s take our previous “Hello, world” code and add a couple variables to it:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i; // Holds signed integers, e.g. -3, -2, 0, 1, 10
6 float f; // Holds signed floating point numbers, e.g. -3.1416
7

8 printf("Hello, World!\n"); // Ah, blessed familiarity


9 }

There! We’ve declared a couple of variables. We haven’t used them yet, and they’re both uninitialized. One
holds an integer number, and the other holds a floating point number (a real number, basically, if you have a
math background).
Uninitialized variables have indeterminate value4 . They have to be initialized or else you must assume they
contain some nonsense number.
3
Read this as “pointer to a char” or “char pointer”. “Char” for character. Though I can’t find a study, it seems anecdotally most
people pronounce this as “char”, a minority say “car”, and a handful say “care”. We’ll talk more about pointers later.
4
Colloquially, we say they have “random” values, but they aren’t truly—or even pseudo-truly—random numbers.
Chapter 3. Variables and Statements 14

This is one of the places C can “get you”. Much of the time, in my experience, the indeterminate
value is zero… but it can vary from run to run! Never assume the value will be zero, even if you
see it is. Always explicitly initialize variables to some value before you use them5 .
What’s this? You want to store some numbers in those variables? Insanity!
Let’s go ahead and do that:
1 int main(void)
2 {
3 int i;
4

5 i = 2; // Assign the value 2 into the variable i


6

7 printf("Hello, World!\n");
8 }

Killer. We’ve stored a value. Let’s print it.


We’re going to do that by passing two amazing arguments to the printf() function. The first argument is
a string that describes what to print and how to print it (called the format string), and the second is the value
to print, namely whatever is in the variable i.
printf() hunts through the format string for a variety of special sequences which start with a percent sign
(%) that tell it what to print. For example, if it finds a %d, it looks to the next parameter that was passed, and
prints it out as an integer. If it finds a %f, it prints the value out as a float. If it finds a %s, it prints a string.
As such, we can print out the value of various types like so:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i = 2;
6 float f = 3.14;
7 char *s = "Hello, world!"; // char * ("char pointer") is the string type
8

9 printf("%s i = %d and f = %f!\n", s, i, f);


10 }

And the output will be:


Hello, world! i = 2 and f = 3.14!

In this way, printf() might be similar to various types of format strings or parameterized strings in other
languages you’re familiar with.

3.1.3 Boolean Types


C has Boolean types, true or false?
1!

Historically, C didn’t have a Boolean type, and some might argue it still doesn’t.
In C, 0 means “false”, and non-zero means “true”.
So 1 is true. And -37 is true. And 0 is false.
5
This isn’t strictly 100% true. When we get to learning about static storage duration, you’ll find the some variables are initialized to
zero automatically. But the safe thing to do is always initialize them.

[Link]
Chapter 3. Variables and Statements 15

You can just declare Boolean types as ints:


int x = 1;

if (x) {
printf("x is true!\n");
}

If you #include <stdbool.h>, you also get access to some symbolic names that might make things look
more familiar, namely a bool type and true and false values:
1 #include <stdio.h>
2 #include <stdbool.h>
3

4 int main(void) {
5 bool x = true;
6

7 if (x) {
8 printf("x is true!\n");
9 }
10 }

But these are identical to using integer values for true and false. They’re just a facade to make things look
nice.

3.2 Operators and Expressions


C operators should be familiar to you from other languages. Let’s blast through some of them here.
(There are a bunch more details than this, but we’re going to do enough in this section to get started.)

3.2.1 Arithmetic
Hopefully these are familiar:
i = i + 3; // Addition (+) and assignment (=) operators, add 3 to i
i = i - 8; // Subtraction, subtract 8 from i
i = i * 9; // Multiplication
i = i / 2; // Division
i = i % 5; // Modulo (division remainder)

There are shorthand variants for all of the above. Each of those lines could more tersely be written as:
i += 3; // Same as "i = i + 3", add 3 to i
i -= 8; // Same as "i = i - 8"
i *= 9; // Same as "i = i * 9"
i /= 2; // Same as "i = i / 2"
i %= 5; // Same as "i = i % 5"

There is no exponentiation. You’ll have to use one of the pow() function variants from math.h.
Let’s get into some of the weirder stuff you might not have in your other languages!

3.2.2 Ternary Operator


C also includes the ternary operator. This is an expression whose value depends on the result of a conditional
embedded in it.
Chapter 3. Variables and Statements 16

// If x > 10, add 17 to y. Otherwise add 37 to y.

y += x > 10? 17: 37;

What a mess! You’ll get used to it the more you read it. To help out a bit, I’ll rewrite the above expression
using if statements:
// This expression:

y += x > 10? 17: 37;

// is equivalent to this non-expression:

if (x > 10)
y += 17;
else
y += 37;

Compare those two until you see each of the components of the ternary operator.
Or, another example that prints if a number stored in x is odd or even:
printf("The number %d is %s.\n", x, x % 2 == 0? "even": "odd")

The %s format specifier in printf() means print a string. If the expression x % 2 evaluates to 0, the value
of the entire ternary expression evaluates to the string "even". Otherwise it evaluates to the string "odd".
Pretty cool!
It’s important to note that the ternary operator isn’t flow control like the if statement is. It’s just an expression
that evaluates to a value.

3.2.3 Pre-and-Post Increment-and-Decrement


Now, let’s mess with another thing that you might not have seen.
These are the legendary post-increment and post-decrement operators:
i++; // Add one to i (post-increment)
i--; // Subtract one from i (post-decrement)

Very commonly, these are just used as shorter versions of:


i += 1; // Add one to i
i -= 1; // Subtract one from i

but they’re more subtly different than that, the clever scoundrels.
Let’s take a look at this variant, pre-increment and pre-decrement:
++i; // Add one to i (pre-increment)
--i; // Subtract one from i (pre-decrement)

With pre-increment and pre-decrement, the value of the variable is incremented or decremented before the
expression is evaluated. Then the expression is evaluated with the new value.
With post-increment and post-decrement, the value of the expression is first computed with the value as-is,
and then the value is incremented or decremented after the value of the expression has been determined.
You can actually embed them in expressions, like this:

[Link]
Chapter 3. Variables and Statements 17

i = 10;
j = 5 + i++; // Compute 5 + i, _then_ increment i

printf("%d, %d\n", i, j); // Prints 11, 15

Let’s compare this to the pre-increment operator:


i = 10;
j = 5 + ++i; // Increment i, _then_ compute 5 + i

printf("%d, %d\n", i, j); // Prints 11, 16

This technique is used frequently with array and pointer access and manipulation. It gives you a way to use
the value in a variable, and also increment or decrement that value before or after it is used.
But by far the most common place you’ll see this is in a for loop:
for (i = 0; i < 10; i++)
printf("i is %d\n", i);

But more on that later.

3.2.4 The Comma Operator


This is an uncommonly-used way to separate expressions that will run left to right:
x = 10, y = 20; // First assign 10 to x, then 20 to y

Seems a bit silly, since you could just replace the comma with a semicolon, right?
x = 10; y = 20; // First assign 10 to x, then 20 to y

But that’s a little different. The latter is two separate expressions, while the former is a single expression!
With the comma operator, the value of the comma expression is the value of the rightmost expression:
x = 1, 2, 3;

printf("x is %d\n", x); // Prints 3, because 3 is rightmost in the comma list

But even that’s pretty contrived. One common place the comma operator is used is in for loops to do multiple
things in each section of the statement:
for (i = 0, j = 10; i < 100; i++, j++)
printf("%d, %d\n", i, j);

We’ll revisit that later.

3.2.5 Conditional Operators


For Boolean values, we have a raft of standard operators:
a == b; // True if a is equivalent to b
a != b; // True if a is not equivalent to b
a < b; // True if a is less than b
a > b; // True if a is greater than b
a <= b; // True if a is less than or equal to b
a >= b; // True if a is greater than or equal to b

Don’t mix up assignment = with comparison ==! Use two equals to compare, one to assign.
Chapter 3. Variables and Statements 18

We can use the comparison expressions with if statements:


if (a <= 10)
printf("Success!\n");

3.2.6 Boolean Operators


We can chain together or alter conditional expressions with Boolean operators for and, or, and not.

Operator Boolean meaning


&& and
|| or
! not

An example of Boolean “and”:


// Do something if x less than 10 and y greater than 20:

if (x < 10 && y > 20)


printf("Doing something!\n");

An example of Boolean “not”:


if (!(x < 12))
printf("x is not less than 12\n");

! has higher precedence than the other Boolean operators, so we have to use parentheses in that case.

Of course, that’s just the same as:


if (x >= 12)
printf("x is not less than 12\n");

but I needed the example!

3.2.7 The sizeof Operator


This operator tells you the size (in bytes) that a particular variable or data type uses in memory.
More particularly, it tells you the size (in bytes) that the type of a particular expression (which might be just
a single variable) uses in memory.
This can be different on different systems, except for char and its variants (which are always 1 byte).
And this might not seem very useful now, but we’ll be making references to it here and there, so it’s worth
covering.
Since this computes the number of bytes needed to store a type, you might think it would return an int. Or…
since the size can’t be negative, maybe an unsigned?
But it turns out C has a special type to represent the return value from sizeof. It’s size_t, pronounced
“size tee”6 . All we know is that it’s an unsigned integer type that can hold the size in bytes of anything you
can give to sizeof.
size_t shows up a lot of different places where counts of things are passed or returned. Think of it as a
value that represents a count.
You can take the sizeof a variable or expression:
6
The _t is short for type.

[Link]
Chapter 3. Variables and Statements 19

int a = 999;

// %zu is the format specifier for type size_t

printf("%zu\n", sizeof a); // Prints 4 on my system


printf("%zu\n", sizeof(2 + 7)); // Prints 4 on my system
printf("%zu\n", sizeof 3.14); // Prints 8 on my system

// If you need to print out negative size_t values, use %zd

Remember: it’s the size in bytes of the type of the expression, not the size of the expression itself. That’s
why the size of 2+7 is the same as the size of a—they’re both type int. We’ll revisit this number 4 in the
very next block of code…
…Where we’ll see you can take the sizeof a type (note the parentheses are required around a type name,
unlike an expression):
printf("%zu\n", sizeof(int)); // Prints 4 on my system
printf("%zu\n", sizeof(char)); // Prints 1 on all systems

It’s important to note that sizeof is a compile-time operation7 . The result of the expression is determined
entirely at compile-time, not at runtime.
We’ll make use of this later on.

3.3 Flow Control


Booleans are all good, but of course we’re nowhere if we can’t control program flow. Let’s take a look at a
number of constructs: if, for, while, and do-while.
First, a general forward-looking note about statements and blocks of statements brought to you by your local
friendly C developer:
After something like an if or while statement, you can either put a single statement to be executed, or a
block of statements to all be executed in sequence.
Let’s start with a single statement:
if (x == 10) printf("x is 10\n");

This is also sometimes written on a separate line. (Whitespace is largely irrelevant in C—it’s not like Python.)
if (x == 10)
printf("x is 10\n");

But what if you want multiple things to happen due to the conditional? You can use squirrelly braces to mark
a block or compound statement.
if (x == 10) {
printf("x is 10\n");
printf("And also this happens when x is 10\n");
}

It’s a really common style to always use squirrelly braces even if they aren’t necessary:
if (x == 10) {
printf("x is 10\n");
}

7
Except for with variable length arrays—but that’s a story for another time.
Chapter 3. Variables and Statements 20

Some devs feel the code is easier to read and avoids errors like this where things visually look like they’re
in the if block, but actually they aren’t.
// BAD ERROR EXAMPLE

if (x == 10)
printf("This happens if x is 10\n");
printf("This happens ALWAYS\n"); // Surprise!! Unconditional!

while and for and the other looping constructs work the same way as the examples above. If you want to
do multiple things in a loop or after an if, wrap them up in squirrelly braces.
In other words, the if is going to run the one thing after the if. And that one thing can be a single statement
or a block of statements.

3.3.1 The if-else statement


We’ve already been using if for multiple examples, since it’s likely you’ve seen it in a language before, but
here’s another:
int i = 10;

if (i > 10) {
printf("Yes, i is greater than 10.\n");
printf("And this will also print if i is greater than 10.\n");
}

if (i <= 10) printf("i is less than or equal to 10.\n");

In the example code, the message will print if i is greater than 10, otherwise execution continues to the next
line. Notice the squirrley braces after the if statement; if the condition is true, either the first statement or
expression right after the if will be executed, or else the collection of code in the squirlley braces after the
if will be executed. This sort of code block behavior is common to all statements.

Of course, because C is fun this way, you can also do something if the condition is false with an else clause
on your if:
int i = 99;

if (i == 10)
printf("i is 10!\n");
else {
printf("i is decidedly not 10.\n");
printf("Which irritates me a little, frankly.\n");
}

And you can even cascade these to test a variety of conditions, like this:
int i = 99;

if (i == 10)
printf("i is 10!\n");

else if (i == 20)
printf("i is 20!\n");

else if (i == 99) {
printf("i is 99! My favorite\n");

[Link]
Chapter 3. Variables and Statements 21

printf("I can't tell you how happy I am.\n");


printf("Really.\n");
}

else
printf("i is some crazy number I've never heard of.\n");

Though if you’re going that route, be sure to check out the switch statement for a potentially better solution.
The catch is switch only works with equality comparisons with constant numbers. The above if-else
cascade could check inequality, ranges, variables, or anything else you can craft in a conditional expression.

3.3.2 The while statement


while is your average run-of-the-mill looping construct. Do a thing while a condition expression is true.

Let’s do one!
// Print the following output:
//
// i is now 0!
// i is now 1!
// [ more of the same between 2 and 7 ]
// i is now 8!
// i is now 9!

i = 0;

while (i < 10) {


printf("i is now %d!\n", i);
i++;
}

printf("All done!\n");

That gets you a basic loop. C also has a for loop which would have been cleaner for that example.
A not-uncommon use of while is for infinite loops where you repeat while true:
while (1) {
printf("1 is always true, so this repeats forever.\n");
}

3.3.3 The do-while statement


So now that we’ve gotten the while statement under control, let’s take a look at its closely related cousin,
do-while.

They are basically the same, except if the loop condition is false on the first pass, do-while will execute
once, but while won’t execute at all. In other words, the test to see whether or not to execute the block
happens at the end of the block with do-while. It happens at the beginning of the block with while.
Let’s see by example:
// Using a while statement:

i = 10;
Chapter 3. Variables and Statements 22

// this is not executed because i is not less than 10:


while(i < 10) {
printf("while: i is %d\n", i);
i++;
}

// Using a do-while statement:

i = 10;

// this is executed once, because the loop condition is not checked until
// after the body of the loop runs:

do {
printf("do-while: i is %d\n", i);
i++;
} while (i < 10);

printf("All done!\n");

Notice that in both cases, the loop condition is false right away. So in the while, the loop fails, and the
following block of code is never executed. With the do-while, however, the condition is checked after the
block of code executes, so it always executes at least once. In this case, it prints the message, increments i,
then fails the condition, and continues to the “All done!” output.
The moral of the story is this: if you want the loop to execute at least once, no matter what the loop condition,
use do-while.
All these examples might have been better done with a for loop. Let’s do something less deterministic—
repeat until a certain random number comes up!
1 #include <stdio.h> // For printf
2 #include <stdlib.h> // For rand
3

4 int main(void)
5 {
6 int r;
7

8 do {
9 r = rand() % 100; // Get a random number between 0 and 99
10 printf("%d\n", r);
11 } while (r != 37); // Repeat until 37 comes up
12 }

Side note: did you run that more than once? If you did, did you notice the same sequence of numbers came
up again. And again. And again? This is because rand() is a pseudorandom number generator that must be
seeded with a different number in order to generate a different sequence. Look up the srand() function for
more details.

3.3.4 The for statement


Welcome to one of the most popular loops in the world! The for loop!
This is a great loop if you know the number of times you want to loop in advance.
You could do the same thing using just a while loop, but the for loop can help keep the code cleaner.

[Link]
Chapter 3. Variables and Statements 23

Here are two pieces of equivalent code—note how the for loop is just a more compact representation:
// Print numbers between 0 and 9, inclusive...

// Using a while statement:

i = 0;
while (i < 10) {
printf("i is %d\n", i);
i++;
}

// Do the exact same thing with a for-loop:

for (i = 0; i < 10; i++) {


printf("i is %d\n", i);
}

That’s right, folks—they do exactly the same thing. But you can see how the for statement is a little more
compact and easy on the eyes. (JavaScript users will fully appreciate its C origins at this point.)
It’s split into three parts, separated by semicolons. The first is the initialization, the second is the loop
condition, and the third is what should happen at the end of the block if the loop condition is true. All three
of these parts are optional.
for (initialize things; loop if this is true; do this after each loop)

Note that the loop will not execute even a single time if the loop condition starts off false.
for-loop fun fact!

You can use the comma operator to do multiple things in each clause of the for loop!
for (i = 0, j = 999; i < 10; i++, j--) {
printf("%d, %d\n", i, j);
}

An empty for will run forever:


for(;;) { // "forever"
printf("I will print this again and again and again\n" );
printf("for all eternity until the heat-death of the universe.\n");

printf("Or until you hit CTRL-C.\n");


}

3.3.5 The switch Statement


Depending on what languages you’re coming from, you might or might not be familiar with switch, or C’s
version might even be more restrictive than you’re used to. This is a statement that allows you to take a
variety of actions depending on the value of an integer expression.
Basically, it evaluates an expression to an integer value, jumps to the case that corresponds to that value.
Execution resumes from that point. If a break statement is encountered, then execution jumps out of the
switch.

Let’s do an example where the user enters a number of goats and we print out a gut-feel of how many goats
that is.
Chapter 3. Variables and Statements 24

1 #include <stdio.h>
2

3 int main(void)
4 {
5 int goat_count;
6

7 printf("Enter a goat count: ");


8 scanf("%d", &goat_count); // Read an integer from the keyboard
9

10 switch (goat_count) {
11 case 0:
12 printf("You have no goats.\n");
13 break;
14

15 case 1:
16 printf("You have a singular goat.\n");
17 break;
18

19 case 2:
20 printf("You have a brace of goats.\n");
21 break;
22

23 default:
24 printf("You have a bona fide plethora of goats!\n");
25 break;
26 }
27 }

In that example, if the user enters, say, 2, the switch will jump to the case 2 and execute from there. When
(if) it hits a break, it jumps out of the switch.
Also, you might see that default label there at the bottom. This is what happens when no cases match.
Every case, including default, is optional. And they can occur in any order, but it’s really typical for
default, if any, to be listed last.

So the whole thing acts like an if-else cascade:


if (goat_count == 0)
printf("You have no goats.\n");
else if (goat_count == 1)
printf("You have a singular goat.\n");
else if (goat_count == 2)
printf("You have a brace of goats.\n");
else
printf("You have a bona fide plethora of goats!\n");

With some key differences:


• switch is often faster to jump to the correct code (though the spec makes no such guarantee).
• if-else can do things like relational conditionals like < and >= and floating point and other types,
while switch cannot.
There’s one more neat thing about switch that you sometimes see that is quite interesting: fall through.
Remember how break causes us to jump out of the switch?
Well, what happens if we don’t break?

[Link]
Chapter 3. Variables and Statements 25

Turns out we just keep on going into the next case! Demo!
switch (x) {
case 1:
printf("1\n");
// Fall through!
case 2:
printf("2\n");
break;
case 3:
printf("3\n");
break;
}

If x == 1, this switch will first hit case 1, it’ll print the 1, but then it just continues on to the next line of
code… which prints 2!
And then, at last, we hit a break so we jump out of the switch.
if x == 2, then we just hit the case 2, print 2, and break as normal.
Not having a break is called fall through.
ProTip: ALWAYS put a comment in the code where you intend to fall through, like I did above. It will save
other programmers from wondering if you meant to do that.
In fact, this is one of the common places to introduce bugs in C programs: forgetting to put a break in your
case. You gotta do it if you don’t want to just roll into the next case8 .

Earlier I said that switch works with integer types—keep it that way. Don’t use floating point or string types
in there. One loophole-ish thing here is that you can use character types because those are secretly integers
themselves. So this is perfectly acceptable:
char c = 'b';

switch (c) {
case 'a':
printf("It's 'a'!\n");
break;

case 'b':
printf("It's 'b'!\n");
break;

case 'c':
printf("It's 'c'!\n");
break;
}

Finally, you can use enums in switch since they are also integer types. But more on that in the enum chapter.

8
This was considered such a hazard that the designers of the Go Programming Language made break the default; you have to
explicitly use Go’s fallthrough statement if you want to fall into the next case.
Chapter 4

Functions

“Sir, not in an environment such as this. That’s why I’ve also been programmed for over thirty
secondary functions that—”
—C3PO, before being rudely interrupted, reporting a now-unimpressive number of additional
functions, Star Wars script
Very much like other languages you’re used to, C has the concept of functions.
Functions can accept a variety of arguments and return a value. One important thing, though: the arguments
and return value types are predeclared—because that’s how C likes it!
Let’s take a look at a function. This is a function that takes an int as an argument, and returns an int.
1 #include <stdio.h>
2

3 int plus_one(int n) // The "definition"


4 {
5 return n + 1;
6 }
7

The int before the plus_one indicates the return type.


The int n indicates that this function takes one int argument, stored in parameter n. A parameter is a
special type of local variable into which the arguments are copied.
I’m going to drive home the point that the arguments are copied into the parameters, here. Lots of things in
C are easier to understand if you know that the parameter is a copy of the argument, not the argument itself.
More on that in a minute.
Continuing the program down into main(), we can see the call to the function, where we assign the return
value into local variable j:
8 int main(void)
9 {
10 int i = 10, j;
11

12 j = plus_one(i); // The "call"


13

14 printf("i + 1 is %d\n", j);


15 }

26

[Link]
Chapter 4. Functions 27

Before I forget, notice that I defined the function before I used it. If I hadn’t done that, the
compiler wouldn’t know about it yet when it compiles main() and it would have given an
unknown function call error. There is a more proper way to do the above code with function
prototypes, but we’ll talk about that later.
Also notice that main() is a function!
It returns an int.
But what’s this void thing? This is a keyword that’s used to indicate that the function accepts no arguments.
You can also return void to indicate that you don’t return a value:
1 #include <stdio.h>
2

3 // This function takes no arguments and returns no value:


4

5 void hello(void)
6 {
7 printf("Hello, world!\n");
8 }
9

10 int main(void)
11 {
12 hello(); // Prints "Hello, world!"
13 }

4.1 Passing by Value


I’d mentioned earlier that when you pass an argument to a function, a copy of that argument gets made and
stored in the corresponding parameter.
If the argument is a variable, a copy of the value of that variable gets made and stored in the parameter.
More generally, the entire argument expression is evaluated and its value determined. That value is copied
to the parameter.
In any case, the value in the parameter is its own thing. It is independent of whatever values or variables you
used as arguments when you made the function call.
So let’s look at an example here. Study it and see if you can determine the output before running it:
1 #include <stdio.h>
2

3 void increment(int a)
4 {
5 a++;
6 }
7

8 int main(void)
9 {
10 int i = 10;
11

12 increment(i);
13

14 printf("i == %d\n", i); // What does this print?


15 }
Chapter 4. Functions 28

At first glance, it looks like i is 10, and we pass it to the function increment(). There the value gets
incremented, so when we print it, it must be 11, right?
“Get used to disappointment.”
—Dread Pirate Roberts, The Princess Bride
But it’s not 11—it prints 10! How?
It’s all about the fact that the expressions you pass to functions get copied onto their corresponding parameters.
The parameter is a copy, not the original.
So i is 10 out in main(). And we pass it to increment(). The corresponding parameter is called a in that
function.
And the copy happens, as if by assignment. Loosely, a = i. So at that point, a is 10. And out in main(), i
is also 10.
Then we increment a to 11. But we’re not touching i at all! It remains 10.
Finally, the function is complete. All its local variables are discarded (bye, a!) and we return to main(),
where i is still 10.
And we print it, getting 10, and we’re done.
This is why in the previous example with the plus_one() function, we returned the locally modified value
so that we could see it again in main().
Seems a little bit restrictive, huh? Like you can only get one piece of data back from a function, is what
you’re thinking. There is, however, another way to get data back; C folks call it passing by reference and
that’s a story we’ll tell another time.
But no fancy-schmancy name will distract you from the fact that EVERYTHING you pass to a function WITH-
OUT EXCEPTION is copied into its corresponding parameter, and the function operates on that local copy,
NO MATTER WHAT. Remember that, even when we’re talking about this so-called passing by reference.

4.2 Function Prototypes


So if you recall back in the ice age a few sections ago, I mentioned that you had to define the function before
you used it, otherwise the compiler wouldn’t know about it ahead of time, and would bomb out with an error.
This isn’t quite strictly true. You can notify the compiler in advance that you’ll be using a function of a certain
type that has a certain parameter list. That way the function can be defined anywhere (even in a different
file), as long as the function prototype has been declared before you call that function.
Fortunately, the function prototype is really quite easy. It’s merely a copy of the first line of the function
definition with a semicolon tacked on the end for good measure. For example, this code calls a function that
is defined later, because a prototype has been declared first:
1 #include <stdio.h>
2

3 int foo(void); // This is the prototype!


4

5 int main(void)
6 {
7 int i;
8

9 // We can call foo() here before it's definition because the


10 // prototype has already been declared, above!
11

12 i = foo();

[Link]
Chapter 4. Functions 29

13

14 printf("%d\n", i); // 3490


15 }
16

17 int foo(void) // This is the definition, just like the prototype!


18 {
19 return 3490;
20 }

If you don’t declare your function before you use it (either with a prototype or its definition), you’re per-
forming something called an implicit declaration. This was allowed in the first C standard (C89), and that
standard has rules about it, but is no longer allowed today. And there is no legitimate reason to rely on it in
new code.
You might notice something about the sample code we’ve been using… That is, we’ve been using the good old
printf() function without defining it or declaring a prototype! How do we get away with this lawlessness?
We don’t, actually. There is a prototype; it’s in that header file stdio.h that we included with #include,
remember? So we’re still legit, officer!

4.3 Empty Parameter Lists


You might see these from time to time in older code, but you shouldn’t ever code one up in new code. Always
use void to indicate that a function takes no parameters. There’s never1 a reason to do this in modern code.
If you’re good just remembering to put void in for empty parameter lists in functions and prototypes, you
can skip the rest of this section.
There are two contexts for this:
• Omitting all parameters where the function is defined
• Omitting all parameters in a prototype
Let’s look at a potential function definition first:
void foo() // Should really have a `void` in there
{
printf("Hello, world!\n");
}

While the spec spells out that the behavior in this instance is as-if you’d indicated void (C11 §[Link]¶14),
the void type is there for a reason. Use it.
But in the case of a function prototype, there is a significant difference between using void and not:
void foo();
void foo(void); // Not the same!

Leaving void out of the prototype indicates to the compiler that there is no additional information about the
parameters to the function. It effectively turns off all that type checking.
With a prototype definitely use void when you have an empty parameter list.

1
Never say “never”.
Chapter 5

Pointers—Cower In Fear!

“How do you get to Carnegie Hall?”


“Practice!”
—20th-century joke of unknown origin
Pointers are one of the most feared things in the C language. In fact, they are the one thing that makes this
language challenging at all. But why?
Because they, quite honestly, can cause electric shocks to come up through the keyboard and physically weld
your arms permanently in place, cursing you to a life at the keyboard in this language from the 70s!
Really? Well, not really. I’m just trying to set you up for success.
Depending on what language you came from, you might already understand the concept of references, where
a variable refers to an object of some type.
This is very much the same, except we have to be more explicit with C about when we’re talking about the
reference or the thing it refers to.

5.1 Memory and Variables


Computer memory holds data of all kinds, right? It’ll hold floats, ints, or whatever you have. To make
memory easy to cope with, each byte of memory is identified by an integer. These integers increase sequen-
tially as you move up through memory1 . You can think of it as a bunch of numbered boxes, where each box
holds a byte2 of data. Or like a big array where each element holds a byte, if you come from a language with
arrays. The number that represents each box is called its address.
Now, not all data types use just a byte. For instance, an int is often four bytes, as is a float, but it really
depends on the system. You can use the sizeof operator to determine how many bytes of memory a certain
type uses.
// %zu is the format specifier for type size_t

printf("an int uses %zu bytes of memory\n", sizeof(int));

// That prints "4" for me, but can vary by system.

1
Typically. I’m sure there are exceptions out there in the dark corridors of computing history.
2
A byte is a number made up of no more than 8 binary digits, or bits for short. This means in decimal digits just like grandma used
to use, it can hold an unsigned number between 0 and 255, inclusive.

30

[Link]
Chapter 5. Pointers—Cower In Fear! 31

Memory Fun Facts: When you have a data type that uses more than a byte of memory, the
bytes that make up the data are always adjacent to one another in memory. Sometimes they’re
in order, and sometimes they’re not3 , but that’s platform-dependent, and often taken care of for
you without you needing to worry about pesky byte orderings.
So anyway, if we can get on with it and get a drum roll and some foreboding music playing for the definition
of a pointer, a pointer is a variable that holds an address. Imagine the classical score from 2001: A Space
Odyssey at this point. Ba bum ba bum ba bum BAAAAH!
Ok, so maybe a bit overwrought here, yes? There’s not a lot of mystery about pointers. They are the address
of data. Just like an int variable can hold the value 12, a pointer variable can hold the address of data.
This means that all these things mean the same thing, i.e. a number that represents a point in memory:
• Index into memory (if you’re thinking of memory like a big array)
• Address
• Location
I’m going to use these interchangeably. And yes, I just threw location in there because you can never have
enough words that mean the same thing.
And a pointer variable holds that address number. Just like a float variable might hold 3.14159.
Imagine you have a bunch of Post-it® notes all numbered in sequence with their address. (The first one is at
index numbered 0, the next at index 1, and so on.)
In addition to the number representing their positions, you can also write another number of your choice on
each. It could be the number of dogs you have. Or the number of moons around Mars…
…Or, it could be the index of another Post-it note!
If you have written the number of dogs you have, that’s just a regular variable. But if you wrote the index of
another Post-it in there, that’s a pointer. It points to the other note!
Another analogy might be with house addresses. You can have a house with certain qualities, yard, metal
roof, solar, etc. Or you could have the address of that house. The address isn’t the same as the house itself.
One’s a full-blown house, and the other is just a few lines of text. But the address of the house is a pointer
to that house. It’s not the house itself, but it tells you where to find it.
And we can do the same thing in the computer with data. You can have a data variable that’s holding some
value. And that value is in memory at some address. And you could have a different pointer variable hold
the address of that data variable.
It’s not the data variable itself, but, like with a house address, it tells us where to find it.
When we have that, we say we have a “pointer to” that data. And we can follow the pointer to access the
data itself.
(Though it doesn’t seem particularly useful yet, this all becomes indispensable when used with function calls.
Bear with me until we get there.)
So if we have an int, say, and we want a pointer to it, what we want is some way to get the address of that
int, right? After all, the pointer just holds the address of the data. What operator do you suppose we’d use
to find the address of the int?
Well, by a shocking surprise that must come as something of a shock to you, gentle reader, we use the
address-of operator (which happens to be an ampersand: “&”)to find the address of the data. Ampersand.

So for a quick example, we’ll introduce a new format specifier for printf() so you can print a pointer. You
know already how %d prints a decimal integer, yes? Well, %p prints a pointer. Now, this pointer is going to
3
The order that bytes come in is referred to as the endianness of the number. Common ones are big endian and little endian. This
usually isn’t something you need to worry about.
Chapter 5. Pointers—Cower In Fear! 32

look like a garbage number (and it might be printed in hexadecimal4 instead of decimal), but it is merely the
index into memory the data is stored in. (Or the index into memory that the first byte of data is stored in,
if the data is multi-byte.) In virtually all circumstances, including this one, the actual value of the number
printed is unimportant to you, and I show it here only for demonstration of the address-of operator.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i = 10;
6

7 printf("The value of i is %d\n", i);


8 printf("And its address is %p\n", (void *)&i);
9

10 // %p expects the argument to be a pointer to void


11 // so we cast it to make the compiler happy.
12 }

On my computer, this prints:


The value of i is 10
And its address is 0x7ffddf7072a4

If you’re curious, that hexadecimal number is 140,727,326,896,068 in decimal (base 10 just like Grandma
used to use). That’s the index into memory where the variable i’s data is stored. It’s the address of i. It’s
the location of i. It’s a pointer to i.
It’s a pointer because it lets you know where i is in memory. Like a home address written on a scrap of paper
tells you where you can find a particular house, this number indicates to us where in memory we can find
the value of i. It points to i.
Again, we don’t really care what the address’s exact number is, generally. We just care that it’s a pointer to
i.

5.2 Pointer Types


So… this is all well and good. You can now successfully take the address of a variable and print it on the
screen. There’s a little something for the ol’ resume, right? Here’s where you grab me by the scruff of the
neck and ask politely what the frick pointers are good for.
Excellent question, and we’ll get to that right after these messages from our sponsor.
ACME ROBOTIC HOUSING UNIT CLEANING SERVICES. YOUR HOMESTEAD WILL BE DRA-
MATICALLY IMPROVED OR YOU WILL BE TERMINATED. MESSAGE ENDS.

Welcome back to another installment of Beej’s Guide. When we met last we were talking about how to make
use of pointers. Well, what we’re going to do is store a pointer off in a variable so that we can use it later.
You can identify the pointer type because there’s an asterisk (*) before the variable name and after its type:
1 int main(void)
2 {
3 int i; // i's type is "int"
4 int *p; // p's type is "pointer to an int", or "int-pointer"
5 }

Hey, so we have here a variable that is a pointer type, and it can point to other ints. That is, it can hold the
address of other ints. We know it points to ints, since it’s of type int* (read “int-pointer”).
4
That is, base 16 with digits 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F.

[Link]
Chapter 5. Pointers—Cower In Fear! 33

When you do an assignment into a pointer variable, the type of the right hand side of the assignment has to
be the same type as the pointer variable. Fortunately for us, when you take the address-of a variable, the
resultant type is a pointer to that variable type, so assignments like the following are perfect:
int i;
int *p; // p is a pointer, but is uninitialized and points to garbage

p = &i; // p is assigned the address of i--p now "points to" i

On the left of the assignment, we have a variable of type pointer-to-int (int*), and on the right side, we
have expression of type pointer-to-int since i is an int (because address-of int gives you a pointer to int).
The address of a thing can be stored in a pointer to that thing.
Get it? I know it still doesn’t quite make much sense since you haven’t seen an actual use for the pointer
variable, but we’re taking small steps here so that no one gets lost. So now, let’s introduce you to the anti-
address-of operator. It’s kind of like what address-of would be like in Bizarro World.

5.3 Dereferencing
A pointer variable can be thought of as referring to another variable by pointing to it. It’s rare you’ll hear
anyone in C land talking about “referring” or “references”, but I bring it up just so that the name of this
operator will make a little more sense.
When you have a pointer to a variable (roughly “a reference to a variable”), you can use the original variable
through the pointer by dereferencing the pointer. (You can think of this as “de-pointering” the pointer, but
no one ever says “de-pointering”.)
Back to our analogy, this is vaguely like looking at a home address and then going to that house.
Now, what do I mean by “get access to the original variable”? Well, if you have a variable called i, and you
have a pointer to i called p, you can use the dereferenced pointer p exactly as if it were the original variable
i!

You almost have enough knowledge to handle an example. The last tidbit you need to know is actually
this: what is the dereference operator? It’s actually called the indirection operator, because you’re accessing
values indirectly via the pointer. And it is the asterisk, again: *. Now, don’t get this confused with the asterisk
you used in the pointer declaration, earlier. They are the same character, but they have different meanings in
different contexts5 .
Here’s a full-blown example:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i;
6 int *p; // this is NOT a dereference--this is a type "int*"
7

8 p = &i; // p now points to i, p holds address of i


9

10 i = 10; // i is now 10
11 *p = 20; // the thing p points to (namely i!) is now 20!!
12

13 printf("i is %d\n", i); // prints "20"

5
That’s not all! It’s used in /*comments*/ and multiplication and in function prototypes with variable length arrays! It’s all the
same *, but the context gives it different meaning.
Chapter 5. Pointers—Cower In Fear! 34

14 printf("i is %d\n", *p); // "20"! dereference-p is the same as i!


15 }

Remember that p holds the address of i, as you can see where we did the assignment to p on line 8. What
the indirection operator does is tells the computer to use the object the pointer points to instead of using the
pointer itself. In this way, we have turned *p into an alias of sorts for i.
Great, but why? Why do any of this?

5.4 Passing Pointers as Arguments


Right about now, you’re thinking that you have an awful lot of knowledge about pointers, but absolutely zero
application, right? I mean, what use is *p if you could just simply say i instead?
Well, my friend, the real power of pointers comes into play when you start passing them to functions. Why
is this a big deal? You might recall from before that you could pass all kinds of arguments to functions and
they’d be dutifully copied into parameters, and then you could manipulate local copies of those variables
from within the function, and then you could return a single value.
What if you wanted to bring back more than one single piece of data from the function? I mean, you can
only return one thing, right? What if I answered that question with another question? …Er, two questions?
What happens when you pass a pointer as an argument to a function? Does a copy of the pointer get put into
its corresponding parameter? You bet your sweet peas it does. Remember how earlier I rambled on and on
about how EVERY SINGLE ARGUMENT gets copied into parameters and the function uses a copy of the
argument? Well, the same is true here. The function will get a copy of the pointer.
But, and this is the clever part: we will have set up the pointer in advance to point at a variable… and then
the function can dereference its copy of the pointer to get back to the original variable! The function can’t
see the variable itself, but it can certainly dereference a pointer to that variable!
This is analogous to writing a home address on a piece of paper, and then copying that onto another piece of
paper. You now have two pointers to that house, and both are equally good at getting you to the house itself.
In the case of a function call. one of the copies is stored in a pointer variable out in the calling scope, and the
other is stored in a pointer variable that is the parameter of the function.
Example! Let’s revisit our old increment() function, but this time let’s make it so that it actually increments
the value out in the caller.
1 #include <stdio.h>
2

3 void increment(int *p) // note that it accepts a pointer to an int


4 {
5 *p = *p + 1; // add one to the thing p points to
6 }
7

8 int main(void)
9 {
10 int i = 10;
11 int *j = &i; // note the address-of; turns it into a pointer to i
12

13 printf("i is %d\n", i); // prints "10"


14 printf("i is also %d\n", *j); // prints "10"
15

16 increment(j); // j is an int*--to i
17

[Link]
Chapter 5. Pointers—Cower In Fear! 35

18 printf("i is %d\n", i); // prints "11"!


19 }

Ok! There are a couple things to see here… not the least of which is that the increment() function takes
an int* as an argument. We pass it an int* in the call by changing the int variable i to an int* using the
address-of operator. (Remember, a pointer holds an address, so we make pointers to variables by running
them through the address-of operator.)
The increment() function gets a copy of the pointer. Both the original pointer j (in main()) and the copy
of that pointer p (the parameter in increment()) point to the same address, namely the one holding the value
i. (Again, by analogy, like two pieces of paper with the same home address written on them.) Dereferencing
either will allow you to modify the original variable i! The function can modify a variable in another scope!
Rock on!
The above example is often more concisely written in the call just by using address-of right in the argument
list:
printf("i is %d\n", i); // prints "10"
increment(&i);
printf("i is %d\n", i); // prints "11"!

Pointer enthusiasts will recall from early on in the guide, we used a function to read from the keyboard,
scanf()… and, although you might not have recognized it at the time, we used the address-of to pass
a pointer to a value to scanf(). We had to pass a pointer, see, because scanf() reads from the keyboard
(typically) and stores the result in a variable. The only way it can see that variable out in the calling function’s
scope is if we pass a pointer to that variable:
int i = 0;

scanf("%d", &i); // pretend you typed "12"


printf("i is %d\n", i); // prints "i is 12"

See, scanf() dereferences the pointer we pass it in order to modify the variable it points to. And now you
know why you have to put that pesky ampersand in there!

5.5 The NULL Pointer


Any pointer variable of any pointer type can be set to a special value called NULL. This indicates that this
pointer doesn’t point to anything.
int *p;

p = NULL;

Since it doesn’t point to a value, dereferencing it is undefined behavior, and probably will result in a crash:
int *p = NULL;

*p = 12; // CRASH or SOMETHING PROBABLY BAD. BEST AVOIDED.

Despite being called the billion dollar mistake by its creator6 , the NULL pointer is a good sentinel value7 and
general indicator that a pointer hasn’t yet been initialized.
(Of course, like other variables, the pointer points to garbage unless you explicitly assign it to point to an
address or NULL.)
6
[Link]
7
[Link]
Chapter 5. Pointers—Cower In Fear! 36

5.6 A Note on Declaring Pointers


The syntax for declaring a pointer can get a little weird. Let’s look at this example:
int a;
int b;

We can condense that into a single line, right?


int a, b; // Same thing

So a and b are both ints. No problem.


But what about this?
int a;
int *p;

Can we make that into one line? We can. But where does the * go?
The rule is that the * goes in front of any variable that is a pointer type. That is. the * is not part of the int
in this example. it’s a part of variable p.
With that in mind, we can write this:
int a, *p; // Same thing

It’s important to note that the following line does not declare two pointers:
int *p, q; // p is a pointer to an int; q is just an int.

This can be particularly insidious-looking if the programmer writes this following (valid) line of code which
is functionally identical to the one above.
int* p, q; // p is a pointer to an int; q is just an int.

So take a look at this and determine which variables are pointers and which are not:
int *a, b, c, *d, e, *f, g, h, *i;

I’ll drop the answer in a footnote8 .

5.7 sizeof and Pointers


Just a little bit of syntax here that might be confusing and you might see from time to time.
Recall that sizeof operates on the type of the expression.
int *p;

sizeof(int); // Returns size of an `int`


sizeof p // p is type int*, so returns size of `int*`
sizeof *p // *p is type int, so returns size of `int`

You might see code with that last sizeof in there. Just remember that sizeof is all about the type of the
expression, not the variables in the expression themselves.

8
The pointer type variables are a, d, f, and i, because those are the ones with * in front of them.

[Link]
Chapter 6

Arrays

“Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought,
proper consideration.”
—Stan Kelly-Bootle, computer scientist
Luckily, C has arrays. I mean, I know it’s considered a low-level language1 but it does at least have the
concept of arrays built-in. And since a great many languages drew inspiration from C’s syntax, you’re
probably already familiar with using [ and ] for declaring and using arrays.
But C only barely has arrays! As we’ll find out later, arrays are just syntactic sugar in C—they’re actually
all pointers and stuff deep down. Freak out! But for now, let’s just use them as arrays. Phew.

6.1 Easy Example


Let’s just crank out an example:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i;
6 float f[4]; // Declare an array of 4 floats
7

8 f[0] = 3.14159; // Indexing starts at 0, of course.


9 f[1] = 1.41421;
10 f[2] = 1.61803;
11 f[3] = 2.71828;
12

13 // Print them all out:


14

15 for (i = 0; i < 4; i++) {


16 printf("%f\n", f[i]);
17 }
18 }

When you declare an array, you have to give it a size. And the size has to be fixed2 .
1
These days, anyway.
2
Again, not really, but variable-length arrays—of which I’m not really a fan—are a story for another time.

37
Chapter 6. Arrays 38

In the above example, we made an array of 4 floats. The value in the square brackets in the declaration lets
us know that.
Later on in subsequent lines, we access the values in the array, setting them or getting them, again with square
brackets.
Hopefully this looks familiar from languages you already know!

6.2 Getting the Length of an Array


You can’t…ish. C doesn’t record this information3 . You have to manage it separately in another variable.
When I say “can’t”, I actually mean there are some circumstances when you can. There is a trick to get the
number of elements in an array in the scope in which an array is declared. But, generally speaking, this won’t
work the way you want if you pass the array to a function4 .
Let’s take a look at this trick. The basic idea is that you take the sizeof the array, and then divide that by
the size of each element to get the length. For example, if an int is 4 bytes, and the array is 32 bytes long,
there must be room for 32 4 or 8 ints in there.
int x[12]; // 12 ints

printf("%zu\n", sizeof x); // 48 total bytes


printf("%zu\n", sizeof(int)); // 4 bytes per int

printf("%zu\n", sizeof x / sizeof(int)); // 48/4 = 12 ints!

If it’s an array of chars, then sizeof the array is the number of elements, since sizeof(char) is defined
to be 1. For anything else, you have to divide by the size of each element.
But this trick only works in the scope in which the array was defined. If you pass the array to a function, it
doesn’t work. Even if you make it “big” in the function signature:
void foo(int x[12])
{
printf("%zu\n", sizeof x); // 8?! What happened to 48?
printf("%zu\n", sizeof(int)); // 4 bytes per int

printf("%zu\n", sizeof x / sizeof(int)); // 8/4 = 2 ints?? WRONG.


}

This is because when you “pass” arrays to functions, you’re only passing a pointer to the first element, and
that’s what sizeof measures. More on this in the Passing Single Dimensional Arrays to Functions section,
below.
One more thing you can do with sizeof and arrays is get the size of an array of a fixed number of elements
without declaring the array. This is like how you can get the size of an int with sizeof(int).
For example, to see how many bytes would be needed for an array of 48 doubles, you can do this:
sizeof(double [48]);

6.3 Array Initializers


You can initialize an array with constants ahead of time:
3
Since arrays are just pointers to the first element of the array under the hood, there’s no additional information recording the length.
4
Because when you pass an array to a function, you’re actually just passing a pointer to the first element of that array, not the “entire”
array.

[Link]
Chapter 6. Arrays 39

1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i;
6 int a[5] = {22, 37, 3490, 18, 95}; // Initialize with these values
7

8 for (i = 0; i < 5; i++) {


9 printf("%d\n", a[i]);
10 }
11 }

Catch: initializer values must be constant terms. Can’t throw variables in there. Sorry, Illinois!
You should never have more items in your initializer than there is room for in the array, or the compiler will
get cranky:
foo.c: In function ‘main’:
foo.[Link] warning: excess elements in array initializer
6 | int a[5] = {22, 37, 3490, 18, 95, 999};
| ^~~
foo.[Link] note: (near initialization for ‘a’)

But (fun fact!) you can have fewer items in your initializer than there is room for in the array. The remaining
elements in the array will be automatically initialized with zero. This is true in general for all types of array
initializers: if you have an initializer, anything not explicitly set to a value will be set to zero.
int a[5] = {22, 37, 3490};

// is the same as:

int a[5] = {22, 37, 3490, 0, 0};

It’s a common shortcut to see this in an initializer when you want to set an entire array to zero:
int a[100] = {0};

Which means, “Make the first element zero, and then automatically make the rest zero, as well.”
You can set specific array elements in the initializer, as well, by specifying an index for the value! When
you do this, C will happily keep initializing subsequent values for you until the initializer runs out, filling
everything else with 0.
To do this, put the index in square brackets with an = after, and then set the value.
Here’s an example where we build an array:
int a[10] = {0, 11, 22, [5]=55, 66, 77};

Because we listed index 5 as the start for 55, the resulting data in the array is:
0 11 22 0 0 55 66 77 0 0

You can put simple constant expressions in there, as well.


#define COUNT 5

int a[COUNT] = {[COUNT-3]=3, 2, 1};

which gives us:


Chapter 6. Arrays 40

0 0 3 2 1

Lastly, you can also have C compute the size of the array from the initializer, just by leaving the size off:
int a[3] = {22, 37, 3490};

// is the same as:

int a[] = {22, 37, 3490}; // Left the size off!

6.4 Out of Bounds!


C doesn’t stop you from accessing arrays out of bounds. It might not even warn you.
Let’s steal the example from above and keep printing off the end of the array. It only has 5 elements, but let’s
try to print 10 and see what happens:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i;
6 int a[5] = {22, 37, 3490, 18, 95};
7

8 for (i = 0; i < 10; i++) { // BAD NEWS: printing too many elements!
9 printf("%d\n", a[i]);
10 }
11 }

Running it on my computer prints:


22
37
3490
18
95
32765
1847052032
1780534144
-56487472
21890

Yikes! What’s that? Well, turns out printing off the end of an array results in what C developers call undefined
behavior. We’ll talk more about this beast later, but for now it means, “You’ve done something bad, and
anything could happen during your program run.”
And by anything, I mean typically things like finding zeroes, finding garbage numbers, or crashing. But
really the C spec says in this circumstance the compiler is allowed to emit code that does anything5 .
Short version: don’t do anything that causes undefined behavior. Ever6 .
5
In the good old MS-DOS days before memory protection was a thing, I was writing some particularly abusive C code that deliber-
ately engaged in all kinds of undefined behavior. But I knew what I was doing, and things were working pretty well. Until I made a
misstep that caused a lockup and, as I found upon reboot, nuked all my BIOS settings. That was fun. (Shout-out to @man for those fun
times.)
6
There are a lot of things that cause undefined behavior, not just out-of-bounds array accesses. This is what makes the C language
so exciting.

[Link]
Chapter 6. Arrays 41

6.5 Multidimensional Arrays


You can add as many dimensions as you want to your arrays.
int a[10];
int b[2][7];
int c[4][5][6];

These are stored in memory in row-major order7 . This means with a 2D array, the first index listed indicates
the row, and the second the column.
You an also use initializers on multidimensional arrays by nesting them:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int row, col;
6

7 int a[2][5] = { // Initialize a 2D array


8 {0, 1, 2, 3, 4},
9 {5, 6, 7, 8, 9}
10 };
11

12 for (row = 0; row < 2; row++) {


13 for (col = 0; col < 5; col++) {
14 printf("(%d,%d) = %d\n", row, col, a[row][col]);
15 }
16 }
17 }

For output of:


(0,0) = 0
(0,1) = 1
(0,2) = 2
(0,3) = 3
(0,4) = 4
(1,0) = 5
(1,1) = 6
(1,2) = 7
(1,3) = 8
(1,4) = 9

And you can initialize with explicit indexes:


// Make a 3x3 identity matrix

int a[3][3] = {[0][0]=1, [1][1]=1, [2][2]=1};

which builds a 2D array like this:


1 0 0
0 1 0
0 0 1
7
[Link]
Chapter 6. Arrays 42

6.6 Arrays and Pointers


[Casually] So… I kinda might have mentioned up there that arrays were pointers, deep down? We should
take a shallow dive into that now so that things aren’t completely confusing. Later on, we’ll look at what the
real relationship between arrays and pointers is, but for now I just want to look at passing arrays to functions.

6.6.1 Getting a Pointer to an Array


I want to tell you a secret. Generally speaking, when a C programmer talks about a pointer to an array, they’re
talking about a pointer to the first element of the array8 .
So let’s get a pointer to the first element of an array.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int a[5] = {11, 22, 33, 44, 55};
6 int *p;
7

8 p = &a[0]; // p points to the array


9 // Well, to the first element, actually
10

11 printf("%d\n", *p); // Prints "11"


12 }

This is so common to do in C that the language allows us a shorthand:


1 p = &a[0]; // p points to the array
2

3 // is the same as:


4

5 p = a; // p points to the array, but much nicer-looking!

Just referring to the array name in isolation is the same as getting a pointer to the first element of the array!
We’re going to use this extensively in the upcoming examples.
But hold on a second—isn’t p an int*? And *p gives us 11, same as a[0]? Yessss. You’re starting to get a
glimpse of how arrays and pointers are related in C.

6.6.2 Passing Single Dimensional Arrays to Functions


Let’s do an example with a single dimensional array. I’m going to write a couple functions that we can pass
the array to that do different things.
Prepare for some mind-blowing function signatures!
1 #include <stdio.h>
2

3 // Passing as a pointer to the first element


4 void times2(int *a, int len)
5 {
6 for (int i = 0; i < len; i++)
7 printf("%d\n", a[i] * 2);
8 }

8
This is technically incorrect, as a pointer to an array and a pointer to the first element of an array have different types. But we can
burn that bridge when we get to it.

[Link]
Chapter 6. Arrays 43

10 // Same thing, but using array notation


11 void times3(int a[], int len)
12 {
13 for (int i = 0; i < len; i++)
14 printf("%d\n", a[i] * 3);
15 }
16

17 // Same thing, but using array notation with size


18 void times4(int a[5], int len)
19 {
20 for (int i = 0; i < len; i++)
21 printf("%d\n", a[i] * 4);
22 }
23

24 int main(void)
25 {
26 int x[5] = {11, 22, 33, 44, 55};
27

28 times2(x, 5);
29 times3(x, 5);
30 times4(x, 5);
31 }

All those methods of listing the array as a parameter in the function are identical.
void times2(int *a, int len)
void times3(int a[], int len)
void times4(int a[5], int len)

In usage by C regulars, the first is the most common, by far.


And, in fact, in the latter situation, the compiler doesn’t even care what number you pass in (other than it has
to be greater than zero9 ). It doesn’t enforce anything at all.
Now that I’ve said that, the size of the array in the function declaration actually does matter when you’re
passing multidimensional arrays into functions, but let’s come back to that.

6.6.3 Changing Arrays in Functions


We’ve said that arrays are just pointers in disguise. This means that if you pass an array to a function, you’re
likely passing a pointer to the first element in the array.
But if the function has a pointer to the data, it is able to manipulate that data! So changes that a function
makes to an array will be visible back out in the caller.
Here’s an example where we pass a pointer to an array to a function, the function manipulates the values in
that array, and those changes are visible out in the caller.
1 #include <stdio.h>
2

3 void double_array(int *a, int len)


4 {

9
C11 §[Link]¶1 requires it be greater than zero. But you might see code out there with arrays declared of zero length at the end of
structs and GCC is particularly lenient about it unless you compile with -pedantic. This zero-length array was a hackish mechanism
for making variable-length structures. Unfortunately, it’s technically undefined behavior to access such an array even though it basically
worked everywhere. C99 codified a well-defined replacement for it called flexible array members, which we’ll chat about later.
Chapter 6. Arrays 44

5 // Multiple each element by 2


6 //
7 // This doubles the values in x in main() since x and a both point
8 // to the same array in memory!
9

10 for (int i = 0; i < len; i++)


11 a[i] *= 2;
12 }
13

14 int main(void)
15 {
16 int x[5] = {1, 2, 3, 4, 5};
17

18 double_array(x, 5);
19

20 for (int i = 0; i < 5; i++)


21 printf("%d\n", x[i]); // 2, 4, 6, 8, 10!
22 }

Even though we passed the array in as parameter a which is type int*, look at how we access it using array
notation with a[i]! Whaaaat. This is totally allowed.
Later when we talk about the equivalence between arrays and pointers, we’ll see how this makes a lot more
sense. For now, it’s enough to know that functions can make changes to arrays that are visible out in the
caller.

6.6.4 Passing Multidimensional Arrays to Functions


The story changes a little when we’re talking about multidimensional arrays. C needs to know all the di-
mensions (except the first one) so it has enough information to know where in memory to look to find a
value.
Here’s an example where we’re explicit with all the dimensions:
1 #include <stdio.h>
2

3 void print_2D_array(int a[2][3])


4 {
5 for (int row = 0; row < 2; row++) {
6 for (int col = 0; col < 3; col++)
7 printf("%d ", a[row][col]);
8 printf("\n");
9 }
10 }
11

12 int main(void)
13 {
14 int x[2][3] = {
15 {1, 2, 3},
16 {4, 5, 6}
17 };
18

19 print_2D_array(x);
20 }

[Link]
Chapter 6. Arrays 45

But in this case, these two10 are equivalent:


void print_2D_array(int a[2][3])
void print_2D_array(int a[][3])

The compiler really only needs the second dimension so it can figure out how far in memory to skip for each
increment of the first dimension. In general, it needs to know all the dimensions except the first one.
Also, remember that the compiler does minimal compile-time bounds checking (if you’re lucky), and C does
zero runtime checking of bounds. No seat belts! Don’t crash by accessing array elements out of bounds!

10
This is also equivalent: void print_2D_array(int (*a)[3]), but that’s more than I want to get into right now.
Chapter 7

Strings

Finally! Strings! What could be simpler?


Well, turns out strings aren’t actually strings in C. That’s right! They’re pointers! Of course they are!
Much like arrays, strings in C barely exist.
But let’s check it out—it’s not really such a big deal.

7.1 String Literals


Before we start, let’s talk about string literals in C. These are sequences of characters in double quotes (").
(Single quotes enclose characters, and are a different animal entirely.)
Examples:
"Hello, world!\n"
"This is a test."
"When asked if this string had quotes in it, she replied, \"It does.\""

The first one has a newline at the end—quite a common thing to see.
The last one has quotes embedded within it, but you see each is preceded by (we say “escaped by”) a backslash
(\) indicating that a literal quote belongs in the string at this point. This is how the C compiler can tell the
difference between printing a double quote and the double quote at the end of the string.

7.2 String Variables


Now that we know how to make a string literal, let’s assign it to a variable so we can do something with it.
char *s = "Hello, world!";

Check out that type: pointer to a char. The string variable s is actually a pointer to the first character in that
string, namely the H.
And we can print it with the %s (for “string”) format specifier:
char *s = "Hello, world!";

printf("%s\n", s); // "Hello, world!"

46

[Link]
Chapter 7. Strings 47

7.3 String Variables as Arrays


Another option is this, nearly equivalent to the above char* usage:
char s[14] = "Hello, world!";

// or, if we were properly lazy and have the compiler


// figure the length for us:

char s[] = "Hello, world!";

This means you can use array notation to access characters in a string. Let’s do exactly that to print all the
characters in a string on the same line:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 char s[] = "Hello, world!";
6

7 for (int i = 0; i < 13; i++)


8 printf("%c\n", s[i]);
9 }

Note that we’re using the format specifier %c to print a single character.
Also, check this out. The program will still work fine if we change the definition of s to be a char* type:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 char *s = "Hello, world!"; // char* here
6

7 for (int i = 0; i < 13; i++)


8 printf("%c\n", s[i]); // But still use arrays here...?
9 }

And we still can use array notation to get the job done when printing it out! This is surprising, but is still
only because we haven’t talked about array/pointer equivalence yet. But this is yet another hint that arrays
and pointers are the same thing, deep down.

7.4 String Initializers


We’ve already seen some examples with initializing string variables with string literals:
char *s = "Hello, world!";
char t[] = "Hello, again!";

But these two are subtly different.


This one is a pointer to a string literal (i.e. a pointer to the first character in a string):
char *s = "Hello, world!";

If you try to mutate that string with this:


Chapter 7. Strings 48

char *s = "Hello, world!";

s[0] = 'z'; // BAD NEWS: tried to mutate a string literal!

The behavior is undefined. Probably, depending on your system, a crash will result.
But declaring it as an array is different. This one is a mutable copy of the string that we can change at will:
char t[] = "Hello, again!"; // t is an array copy of the string
t[0] = 'z'; // No problem

printf("%s\n", t); // "zello, again!"

So remember: if you have a pointer to a string literal, don’t try to change it! And if you use a string in double
quotes to initialize an array, that’s not actually a string literal.

7.5 Getting String Length


You can’t, since C doesn’t track it for you. And when I say “can’t”, I actually mean “can”1 . There’s a function
in <string.h> called strlen() that can be used to compute the length of any string in bytes2 .
1 #include <stdio.h>
2 #include <string.h>
3

4 int main(void)
5 {
6 char *s = "Hello, world!";
7

8 printf("The string is %zu bytes long.\n", strlen(s));


9 }

The strlen() function returns type size_t, which is an integer type so you can use it for integer math. We
print size_t with %zu.
The above program prints:
The string is 13 bytes long.

Great! So it is possible to get the string length!


But… if C doesn’t track the length of the string anywhere, how does it know how long the string is?

7.6 String Termination


C does strings a little differently than many programming languages, and in fact differently than almost every
modern programming language.
When you’re making a new language, you have basically two options for storing a string in memory:
1. Store the bytes of the string along with a number indicating the length of the string.
2. Store the bytes of the string, and mark the end of the string with a special byte called the terminator.
If you want strings longer than 255 characters, option 1 requires at least two bytes to store the length. Whereas
option 2 only requires one byte to terminate the string. So a bit of savings there.
1
Though it is true that C doesn’t track the length of strings.
2
If you’re using the basic character set or an 8-bit character set, you’re used to one character being one byte. This isn’t true in all
character encodings, though.

[Link]
Chapter 7. Strings 49

Of course, these days it seems ridiculous to worry about saving a byte (or 3—lots of languages will happily
let you have strings that are 4 gigabytes in length). But back in the day, it was a bigger deal.
So C took approach #2. In C, a “string” is defined by two basic characteristics:
• A pointer to the first character in the string.
• A zero-valued byte (or NUL character3 ) somewhere in memory after the pointer that indicates the end
of the string.
A NUL character can be written in C code as \0, though you don’t often have to do this.
When you include a string in double quotes in your code, the NUL character is automatically, implicitly
included.
char *s = "Hello!"; // Actually "Hello!\0" behind the scenes

So with this in mind, let’s write our own strlen() function that counts chars in a string until it finds a NUL.
The procedure is to look down the string for a single NUL character, counting as we go4 :
int my_strlen(char *s)
{
int count = 0;

while (s[count] != '\0') // Single quotes for single char


count++;

return count;
}

And that’s basically how the built-in strlen() gets the job done.

7.7 Copying a String


You can’t copy a string through the assignment operator (=). All that does is make a copy of the pointer to
the first character… so you end up with two pointers to the same string:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 char s[] = "Hello, world!";
6 char *t;
7

8 // This makes a copy of the pointer, not a copy of the string!


9 t = s;
10

11 // We modify t
12 t[0] = 'z';
13

14 // But printing s shows the modification!


15 // Because t and s point to the same string!
16

17 printf("%s\n", s); // "zello, world!"


18 }

3
This is different than the NULL pointer, and I’ll abbreviate it NUL when talking about the character versus NULL for the pointer.
4
Later we’ll learn a neater way to do it with pointer arithmetic.
Chapter 7. Strings 50

If you want to make a copy of a string, you have to copy it a byte at a time—but this is made easier with the
strcpy() function5 .

Before you copy the string, make sure you have room to copy it into, i.e. the destination array that’s going
to hold the characters needs to be at least as long as the string you’re copying.
1 #include <stdio.h>
2 #include <string.h>
3

4 int main(void)
5 {
6 char s[] = "Hello, world!";
7 char t[100]; // Each char is one byte, so plenty of room
8

9 // This makes a copy of the string!


10 strcpy(t, s);
11

12 // We modify t
13 t[0] = 'z';
14

15 // And s remains unaffected because it's a different string


16 printf("%s\n", s); // "Hello, world!"
17

18 // But t has been changed


19 printf("%s\n", t); // "zello, world!"
20 }

Notice with strcpy(), the destination pointer is the first argument, and the source pointer is the second. A
mnemonic I use to remember this is that it’s the order you would have put t and s if an assignment = worked
for strings, with the source on the right and the destination on the left.

5
There’s a safer function called strncpy() that you should probably use instead, but we’ll get to that later.

[Link]
Chapter 8

Structs

In C, we have something called a struct, which is a user-definable type that holds multiple pieces of data,
potentially of different types.
It’s a convenient way to bundle multiple variables into a single one. This can be beneficial for passing
variables to functions (so you just have to pass one instead of many), and useful for organizing data and
making code more readable.
If you’ve come from another language, you might be familiar with the idea of classes and objects. These
don’t exist in C, natively1 . You can think of a struct as a class with only data members, and no methods.

8.1 Declaring a Struct


You can declare a struct in your code like so:
struct car {
char *name;
float price;
int speed;
};

This is often done at the global scope outside any functions so that the struct is globally available.
When you do this, you’re making a new type. The full type name is struct car. (Not just car—that won’t
work.)
There aren’t any variables of that type yet, but we can declare some:
struct car saturn; // Variable "saturn" of type "struct car"

And now we have an uninitialized variable saturn2 of type struct car.


We should initialize it! But how do we set the values of those individual fields?
Like in many other languages that stole it from C, we’re going to use the dot operator (.) to access the
individual fields.
[Link] = "Saturn SL/2";
[Link] = 15999.99;

1
Although in C individual items in memory like ints are referred to as “objects”, they’re not objects in an object-oriented program-
ming sense.
2
The Saturn was a popular brand of economy car in the United States until it was put out of business by the 2008 crash, sadly so to
us fans.

51
Chapter 8. Structs 52

[Link] = 175;

printf("Name: %s\n", [Link]);


printf("Price (USD): %f\n", [Link]);
printf("Top Speed (km): %d\n", [Link]);

There on the first lines, we set the values in the struct car, and then in the next bit, we print those values
out.

8.2 Struct Initializers


That example in the previous section was a little unwieldy. There must be a better way to initialize that
struct variable!

You can do it with an initializer by putting values in for the fields in the order they appear in the struct
when you define the variable. (This won’t work after the variable has been defined—it has to happen in the
definition).
struct car {
char *name;
float price;
int speed;
};

// Now with an initializer! Same field order as in the struct declaration:


struct car saturn = {"Saturn SL/2", 16000.99, 175};

printf("Name: %s\n", [Link]);


printf("Price: %f\n", [Link]);
printf("Top Speed: %d km\n", [Link]);

The fact that the fields in the initializer need to be in the same order is a little freaky. If someone changes the
order in struct car, it could break all the other code!
We can be more specific with our initializers:
struct car saturn = {.speed=175, .name="Saturn SL/2"};

Now it’s independent of the order in the struct declaration. Which is safer code, for sure.
Similar to array initializers, any missing field designators are initialized to zero (in this case, that would be
.price, which I’ve omitted).

8.3 Passing Structs to Functions


You can do a couple things to pass a struct to a function.
1. Pass the struct.
2. Pass a pointer to the struct.
Recall that when you pass something to a function, a copy of that thing gets made for the function to operate
on, whether it’s a copy of a pointer, an int, a struct, or anything.
There are basically two cases when you’d want to pass a pointer to the struct:
1. You need the function to be able to make changes to the struct that was passed in, and have those
changes show in the caller.

[Link]
Chapter 8. Structs 53

2. The struct is somewhat large and it’s more expensive to copy that onto the stack than it is to just
copy a pointer3 .
For those two reasons, it’s far more common to pass a pointer to a struct to a function, though its by no
means illegal to pass the struct itself.
Let’s try passing in a pointer, making a function that will allow you to set the .price field of the struct
car:
1 #include <stdio.h>
2

3 struct car {
4 char *name;
5 float price;
6 int speed;
7 };
8

9 int main(void)
10 {
11 struct car saturn = {.speed=175, .name="Saturn SL/2"};
12

13 // Pass a pointer to this struct car, along with a new,


14 // more realistic, price:
15 set_price(&saturn, 799.99);
16

17 printf("Price: %f\n", [Link]);


18 }

You should be able to come up with the function signature for set_price() just by looking at the types of
the arguments we have there.
saturn is a struct car, so &saturn must be the address of the struct car, AKA a pointer to a struct
car, namely a struct car*.

And 799.99 is a float.


So the function declaration must look like this:
void set_price(struct car *c, float new_price)

We just need to write the body. One attempt might be:


void set_price(struct car *c, float new_price) {
[Link] = new_price; // ERROR!!
}

That won’t work because the dot operator only works on structs… it doesn’t work on pointers to structs.
Ok, so we can dereference the struct to de-pointer it to get to the struct itself. Dereferencing a struct
car* results in the struct car that the pointer points to, which we should be able to use the dot operator
on:
void set_price(struct car *c, float new_price) {
(*c).price = new_price; // Works, but is ugly and non-idiomatic :(
}

And that works! But it’s a little clunky to type all those parens and the asterisk. C has some syntactic sugar
called the arrow operator that helps with that.
3
A pointer is likely 8 bytes on a 64-bit system.
Chapter 8. Structs 54

8.4 The Arrow Operator


The arrow operator helps refer to fields in pointers to structs.
void set_price(struct car *c, float new_price) {
// (*c).price = new_price; // Works, but non-idiomatic :(
//
// The line above is 100% equivalent to the one below:

c->price = new_price; // That's the one!


}

So when accessing fields, when do we use dot and when do we use arrow?
• If you have a struct, use dot (.).
• If you have a pointer to a struct, use arrow (->).

8.5 Copying and Returning structs


Here’s an easy one for you!
Just assign from one to the other!
struct car a, b;

b = a; // Copy the struct

And returning a struct (as opposed to a pointer to one) from a function also makes a similar copy to the
receiving variable.
This is not a “deep copy”4 . All fields are copied as-is, including pointers to things.

8.6 Comparing structs


There’s only one safe way to do it: compare each field one at a time.
You might think you could use memcmp(), but that doesn’t handle the case of the possible padding bytes that
might be in there.
If you clear the struct to zero first with memset(), then it might work, though there could be weird elements
that might not compare as you expect5 .

4
A deep copy follows pointer in the struct and copies the data they point to, as well. A shallow copy just copies the pointers, but
not the things they point to. C doesn’t come with any built-in deep copy functionality.
5
[Link]

[Link]
Chapter 9

File Input/Output

We’ve already seen a couple examples of I/O with scanf() and printf() for doing I/O at the console
(screen/keyboard).
But we’ll push those concepts a little farther this chapter.

9.1 The FILE* Data Type


When we do any kind of I/O in C, we do so through a piece of data that you get in the form of a FILE* type.
This FILE* holds all the information needed to communicate with the I/O subsystem about which file you
have open, where you are in the file, and so on.
The spec refers to these as streams, i.e. a stream of data from a file or from any source. I’m going to use
“files” and “streams” interchangeably, but really you should think of a “file” as a special case of a “stream”.
There are other ways to stream data into a program than just reading from a file.
We’ll see in a moment how to go from having a filename to getting an open FILE* for it, but first I want to
mention three streams that are already open for you and ready for use.
|FILE* name|Description| |-|-| |stdin|Standard Input, generally the keyboard by default| |stdout|Standard
Output, generally the screen by default| |stderr|Standard Error, generally the screen by default, as well|
We’ve actually been using these implicitly already, it turns out. For example, these two calls are the same:
printf("Hello, world!\n");
fprintf(stdout, "Hello, world!\n"); // printf to a file

But more on that later.


Also you’ll notice that both stdout and stderr go to the screen. While this seems at first either like an
oversight or redundancy, it actually isn’t. Typical operating systems allow you to redirect the output of either
of those into different files, and it can be convenient to be able to separate error messages from regular
non-error output.
For example, in a POSIX shell (like sh, ksh, bash, zsh, etc.) on a Unix-like system, we could run a program
and send just the non-error (stdout) output to one file, and all the error (stderr) output to another file.
./foo > [Link] 2> [Link] # This command is Unix-specific

For this reason, you should send serious error messages to stderr instead of stdout.
More on how to do that later.

55
Chapter 9. File Input/Output 56

9.2 Reading Text Files


Streams are largely categorized two different ways: text and binary.
Text streams are allowed to do significant translation of the data, most notably translations of newlines to
their different representations1 . Text files are logically a sequence of lines separated by newlines. To be
portable, your input data should always end with a newline.
But the general rule is that if you’re able to edit the file in a regular text editor, it’s a text file. Otherwise, it’s
binary. More on binary later.
So let’s get to work—how do we open a file for reading, and pull data out of it?
Let’s create a file called [Link] that has just this in it:
Hello, world!

And let’s write a program to open the file, read a character out of it, and then close the file when we’re done.
That’s the game plan!
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp; // Variable to represent open file
6

7 fp = fopen("[Link]", "r"); // Open file for reading


8

9 int c = fgetc(fp); // Read a single character


10 printf("%c\n", c); // Print char to stdout
11

12 fclose(fp); // Close the file when done


13 }

See how when we opened the file with fopen(), it returned the FILE* to us so we could use it later.
(I’m leaving it out for brevity, but fopen() will return NULL if something goes wrong, like file-not-found,
so you should really error check it!)
Also notice the "r" that we passed in—this means “open a text stream for reading”. (There are various
strings we can pass to fopen() with additional meaning, like writing, or appending, and so on.)
After that, we used the fgetc() function to get a character from the stream. You might be wondering why
I’ve made c an int instead of a char—hold that thought!
Finally, we close the stream when we’re done with it. All streams are automatically closed when the program
exits, but it’s good form and good housekeeping to explicitly close any files yourself when done with them.
The FILE* keeps track of our position in the file. So subsequent calls to fgetc() would get the next character
in the file, and then the next, until the end.
But that sounds like a pain. Let’s see if we can make it easier.

9.3 End of File: EOF


There is a special character defined as a macro: EOF. This is what fgetc() will return when the end of the
file has been reached and you’ve attempted to read another character.
1
We used to have three different newlines in broad effect: Carriage Return (CR, used on old Macs), Linefeed (LF, used on Unix
systems), and Carriage Return/Linefeed (CRLF, used on Windows systems). Thankfully the introduction of OS X, being Unix-based,
reduced this number to two.

[Link]
Chapter 9. File Input/Output 57

How about I share that Fun Fact™, now. Turns out EOF is the reason why fgetc() and functions like it
return an int instead of a char. EOF isn’t a character proper, and its value likely falls outside the range of
char. Since fgetc() needs to be able to return any byte and EOF, it needs to be a wider type that can hold
more values. so int it is. But unless you’re comparing the returned value against EOF, you can know, deep
down, it’s a char.
All right! Back to reality! We can use this to read the whole file in a loop.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp;
6 int c;
7

8 fp = fopen("[Link]", "r");
9

10 while ((c = fgetc(fp)) != EOF)


11 printf("%c", c);
12

13 fclose(fp);
14 }

(If line 10 is too weird, just break it down starting with the innermost-nested parens. The first thing we do
is assign the result of fgetc() into c, and then we compare that against EOF. We’ve just crammed it into a
single line. This might look hard to read, but study it—it’s idiomatic C.)
And running this, we see:
Hello, world!

But still, we’re operating a character at a time, and lots of text files make more sense at the line level. Let’s
switch to that.

9.3.1 Reading a Line at a Time


So how can we get an entire line at once? fgets() to the rescue! For arguments, it takes a pointer to a
char buffer to hold bytes, a maximum number of bytes to read, and a FILE* to read from. It returns NULL
on end-of-file or error. fgets() is even nice enough to NUL-terminate the string when its done2 .
Let’s do a similar loop as before, except let’s have a multiline file and read it in a line at a time.
Here’s a file [Link]:
A wise man can learn more from
a foolish question than a fool
can learn from a wise answer.
--Bruce Lee

And here’s some code that reads that file a line at a time and prints out a line number before each one:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp;
6 char s[1024]; // Big enough for any line this program will encounter

2
If the buffer’s not big enough to read in an entire line, it’ll just stop reading mid-line, and the next call to fgets() will continue
reading the rest of the line.
Chapter 9. File Input/Output 58

7 int linecount = 0;
8

9 fp = fopen("[Link]", "r");
10

11 while (fgets(s, sizeof s, fp) != NULL)


12 printf("%d: %s", ++linecount, s);
13

14 fclose(fp);
15 }

Which gives the output:


1: A wise man can learn more from
2: a foolish question than a fool
3: can learn from a wise answer.
4: --Bruce Lee

9.4 Formatted Input


You know how you can get formatted output with printf() (and, thus, fprintf() like we’ll see, below)?
You can do the same thing with fscanf().
Let’s have a file with a series of data records in it. In this case, whales, with name, length in meters, and
weight in tonnes. [Link]:
blue 29.9 173
right 20.7 135
gray 14.9 41
humpback 16.0 30

Yes, we could read these with fgets() and then parse the string with sscanf() (and in some ways that’s
more resilient against corrupted files), but in this case, let’s just use fscanf() and pull it in directly.
The fscanf() function skips leading whitespace when reading, and returns EOF on end-of-file or error.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp;
6 char name[1024]; // Big enough for any line this program will encounter
7 float length;
8 int mass;
9

10 fp = fopen("[Link]", "r");
11

12 while (fscanf(fp, "%s %f %d", name, &length, &mass) != EOF)


13 printf("%s whale, %d tonnes, %.1f meters\n", name, mass, length);
14

15 fclose(fp);
16 }

Which gives the result:


blue whale, 173 tonnes, 29.9 meters
right whale, 135 tonnes, 20.7 meters

[Link]
Chapter 9. File Input/Output 59

gray whale, 41 tonnes, 14.9 meters


humpback whale, 30 tonnes, 16.0 meters

9.5 Writing Text Files


In much the same way we can use fgetc(), fgets(), and fscanf() to read text streams, we can use
fputc(), fputs(), and fprintf() to write text streams.

To do so, we have to fopen() the file in write mode by passing "w" as the second argument. Opening an
existing file in "w" mode will instantly truncate that file to 0 bytes for a full overwrite.
We’ll put together a simple program that outputs a file [Link] using a variety of output functions.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp;
6 int x = 32;
7

8 fp = fopen("[Link]", "w");
9

10 fputc('B', fp);
11 fputc('\n', fp); // newline
12 fprintf(fp, "x = %d\n", x);
13 fputs("Hello, world!\n", fp);
14

15 fclose(fp);
16 }

And this produces a file, [Link], with these contents:


B
x = 32
Hello, world!

Fun fact: since stdout is a file, you could replace line 8 with:
fp = stdout;

and the program would have outputted to the console instead of to a file. Try it!

9.6 Binary File I/O


So far we’ve just been talking text files. But there’s that other beast we mentioned early on called binary
files, or binary streams.
These work very similarly to text files, except the I/O subsystem doesn’t perform any translations on the data
like it might with a text file. With binary files, you get a raw stream of bytes, and that’s all.
The big difference in opening the file is that you have to add a "b" to the mode. That is, to read a binary file,
open it in "rb" mode. To write a file, open it in "wb" mode.
Because it’s streams of bytes, and streams of bytes can contain NUL characters, and the NUL character is
the end-of-string marker in C, it’s rare that people use the fprintf()-and-friends functions to operate on
binary files.
Chapter 9. File Input/Output 60

Instead the most common functions are fread() and fwrite(). The functions read and write a specified
number of bytes to the stream.
To demo, we’ll write a couple programs. One will write a sequence of byte values to disk all at once. And
the second program will read a byte at a time and print them out3 .
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp;
6 unsigned char bytes[6] = {5, 37, 0, 88, 255, 12};
7

8 fp = fopen("[Link]", "wb"); // wb mode for "write binary"!


9

10 // In the call to fwrite, the arguments are:


11 //
12 // * Pointer to data to write
13 // * Size of each "piece" of data
14 // * Count of each "piece" of data
15 // * FILE*
16

17 fwrite(bytes, sizeof(char), 6, fp);


18

19 fclose(fp);
20 }

Those two middle arguments to fwrite() are pretty odd. But basically what we want to tell the function is,
“We have items that are this big, and we want to write that many of them.” This makes it convenient if you
have a record of a fixed length, and you have a bunch of them in an array. You can just tell it the size of one
record and how many to write.
In the example above, we tell it each record is the size of a char, and we have 6 of them.
Running the program gives us a file [Link], but opening it in a text editor doesn’t show anything
friendly! It’s binary data—not text. And random binary data I just made up, at that!
If I run it through a hex dump4 program, we can see the output as bytes:
05 25 00 58 ff 0c

And those values in hex do match up to the values (in decimal) that we wrote out.
But now let’s try to read them back in with a different program. This one will open the file for binary reading
("rb" mode) and will read the bytes one at a time in a loop.
fread() has the neat feature where it returns the number of bytes read, or 0 on EOF. So we can loop until
we see that, printing numbers as we go.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 FILE *fp;
6 unsigned char c;
7

3
Normally the second program would read all the bytes at once, and then print them out in a loop. That would be more efficient.
But we’re going for demo value, here.
4
[Link]

[Link]
Chapter 9. File Input/Output 61

8 fp = fopen("[Link]", "rb"); // rb for "read binary"!


9

10 while (fread(&c, sizeof(char), 1, fp) > 0)


11 printf("%d\n", c);
12 }

And, running it, we see our original numbers!


5
37
0
88
255
12

Woo hoo!

9.6.1 struct and Number Caveats


As we saw in the structs section, the compiler is free to add padding to a struct as it sees fit. And different
compilers might do this differently. And the same compiler on different architectures could do it differently.
And the same compiler on the same architectures could do it differently.
What I’m getting at is this: it’s not portable to just fwrite() an entire struct out to a file when you don’t
know where the padding will end up.
How do we fix this? Hold that thought—we’ll look at some ways to do this after looking at another related
problem.
Numbers!
Turns out all architectures don’t represent numbers in memory the same way.
Let’s look at a simple fwrite() of a 2-byte number. We’ll write it in hex so each byte is clear. The most
significant byte will have the value 0x12 and the least significant will have the value 0x34.
unsigned short v = 0x1234; // Two bytes, 0x12 and 0x34

fwrite(&v, sizeof v, 1, fp);

What ends up in the stream?


Well, it seems like it should be 0x12 followed by 0x34, right?
But if I run this on my machine and hex dump the result, I get:
34 12

They’re reversed! What gives?


This has something to do with what’s called the endianess5 of the architecture. Some write the most signifi-
cant bytes first, and some the least significant bytes first.
This means that if you write a multibyte number out straight from memory, you can’t do it in a portable way6 .
A similar problem exists with floating point. Most systems use the same format for their floating point
numbers, but some do not. No guarantees!
So… how can we fix all these problems with numbers and structs to get our data written in a portable way?
5
[Link]
6
And this is why I used individual bytes in my fwrite() and fread() examples, above, shrewdly.
Chapter 9. File Input/Output 62

The summary is to serialize the data, which is a general term that means to take all the data and write it out
in a format that you control, that is well-known, and programmable to work the same way on all platforms.
As you might imagine, this is a solved problem. There are a bunch of serialization libraries you can take
advantage of, such as Google’s protocol buffers7 , out there and ready to use. They will take care of all the
gritty details for you, and even will allow data from your C programs to interoperate with other languages
that support the same serialization methods.
Do yourself and everyone a favor! Serialize your binary data when you write it to a stream! This will keep
things nice and portable, even if you transfer data files from one architecture to another.

7
[Link]

[Link]
Chapter 10

typedef: Making New Types

Well, not so much making new types as getting new names for existing types. Sounds kinda pointless on the
surface, but we can really use this to make our code cleaner.

10.1 typedef in Theory


Basically, you take an existing type and you make an alias for it with typedef.
Like this:
typedef int antelope; // Make "antelope" an alias for "int"

antelope x = 10; // Type "antelope" is the same as type "int"

You can take any existing type and do it. You can even make a number of types with a comma list:
typedef int antelope, bagel, mushroom; // These are all "int"

That’s really useful, right? That you can type mushroom instead of int? You must be super excited about
this feature!
OK, Professor Sarcasm—we’ll get to some more common applications of this in a moment.

10.1.1 Scoping
typedef follows regular scoping rules.

For this reason, it’s quite common to find typedef at file scope (“global”) so that all functions can use the
new types at will.

10.2 typedef in Practice


So renaming int to something else isn’t that exciting. Let’s see where typedef commonly makes an ap-
pearance.

10.2.1 typedef and structs


Sometimes a struct will be typedef’d to a new name so you don’t have to type the word struct over and
over.

63
Chapter 10. typedef: Making New Types 64

struct animal {
char *name;
int leg_count, speed;
};

// original name new name


// | |
// v v
// |-----------| |----|
typedef struct animal animal;

struct animal y; // This works


animal z; // This also works because "animal" is an alias

Personally, I don’t care for this practice. I like the clarity the code has when you add the word struct to the
type; programmers know what they’re getting. But it’s really common so I’m including it here.
Now I want to run the exact same example in a way that you might commonly see. We’re going to put the
struct animal in the typedef. You can mash it all together like this:
// original name
// |
// v
// |-----------|
typedef struct animal {
char *name;
int leg_count, speed;
} animal; // <-- new name

struct animal y; // This works


animal z; // This also works because "animal" is an alias

That’s exactly the same as the previous example, just more concise.
But that’s not all! There’s another common shortcut that you might see in code using what are called anony-
mous structures1 . It turns out you don’t actually need to name the structure in a variety of places, and with
typedef is one of them.

Let’s do the same example with an anonymous structure:


// Anonymous struct! It has no name!
// |
// v
// |----|
typedef struct {
char *name;
int leg_count, speed;
} animal; // <-- new name

//struct animal y; // ERROR: this no longer works--no such struct!


animal z; // This works because "animal" is an alias

As another example, we might find something like this:


typedef struct {
int x, y;

1
We’ll talk more about these later.

[Link]
Chapter 10. typedef: Making New Types 65

} point;

point p = {.x=20, .y=40};

printf("%d, %d\n", p.x, p.y); // 20, 40

10.2.2 typedef and Other Types


It’s not that using typedef with a simple type like int is completely useless… it helps you abstract the types
to make it easier to change them later.
For example, if you have float all over your code in 100 zillion places, it’s going to be painful to change
them all to double if you find you have to do that later for some reason.
But if you prepared a little with:
typedef float app_float;

// and

app_float f1, f2, f3;

Then if later you want to change to another type, like long double, you just need to change the typedef:
// voila!
// |---------|
typedef long double app_float;

// and no need to change this line:

app_float f1, f2, f3; // Now these are all long doubles

10.2.3 typedef and Pointers


You can make a type that is a pointer.
typedef int *intptr;

int a = 10;
intptr x = &a; // "intptr" is type "int*"

I really don’t like this practice. It hides the fact that x is a pointer type because you don’t see a * in the
declaration.
IMHO, it’s better to explicitly show that you’re declaring a pointer type so that other devs can clearly see it
and don’t mistake x for having a non-pointer type.
But at last count, say, 832,007 people had a different opinion.

10.2.4 typedef and Capitalization


I’ve seen all kinds of capitalization on typedef.
typedef struct {
int x, y;
} my_point; // lower snake case

typedef struct {
Chapter 10. typedef: Making New Types 66

int x, y;
} MyPoint; // CamelCase

typedef struct {
int x, y;
} Mypoint; // Leading uppercase

typedef struct {
int x, y;
} MY_POINT; // UPPER SNAKE CASE

The C11 specification doesn’t dictate one way or another, and shows examples in all uppercase and all low-
ercase.
K&R2 uses leading uppercase predominantly, but show some examples in uppercase and snake case (with
_t).

If you have a style guide in use, stick with it. If you don’t, grab one and stick with it.

10.3 Arrays and typedef


The syntax is a little weird, and this is rarely seen in my experience, but you can typedef an array of some
number of items.
// Make type five_ints an array of 5 ints
typedef int five_ints[5];

five_ints x = {11, 22, 33, 44, 55};

I don’t like it because it hides the array nature of the variable, but it’s possible to do.

[Link]
Chapter 11

Pointers II: Arithmetic

Time to get more into it with a number of new pointer topics! If you’re not up to speed with pointers, check
out the first section in the guide on the matter.

11.1 Pointer Arithmetic


Turns out you can do math on pointers, notably addition and subtraction.
But what does it mean when you do that?
In short, if you have a pointer to a type, adding one to the pointer moves to the next item of that type directly
after it in memory.
It’s important to remember that as we move pointers around and look at different places in memory, we
need to make sure that we’re always pointing to a valid place in memory before we dereference. If we’re off
in the weeds and we try to see what’s there, the behavior is undefined and a crash is a common result.
This is a little chicken-and-eggy with Array/Pointer Equivalence, below, but we’re going to give it a shot,
anyway.

11.1.1 Adding to Pointers


First, let’s take an array of numbers.
int a[5] = {11, 22, 33, 44, 55};

Then let’s get a pointer to the first element in that array:


int a[5] = {11, 22, 33, 44, 55};

int *p = &a[0]; // Or "int *p = a;" works just as well

Then let’s print the value there by dereferencing the pointer:


printf("%d\n", *p); // Prints 11

Now let’s use pointer arithmetic to print the next element in the array, the one at index 1:
printf("%d\n", *(p + 1)); // Prints 22!!

What happened there? C knows that p is a pointer to an int. So it knows the sizeof an int1 and it knows
to skip that many bytes to get to the next int after the first one!
1
Recall that the sizeof operator tells you the size in bytes of an object in memory.

67
Chapter 11. Pointers II: Arithmetic 68

In fact, the prior example could be written these two equivalent ways:
printf("%d\n", *p); // Prints 11
printf("%d\n", *(p + 0)); // Prints 11

because adding 0 to a pointer results in the same pointer.


Let’s think of the upshot here. We can iterate over elements of an array this way instead of using an array:
int a[5] = {11, 22, 33, 44, 55};

int *p = &a[0]; // Or "int *p = a;" works just as well

for (int i = 0; i < 5; i++) {


printf("%d\n", *(p + i)); // Same as p[i]!
}

And that works the same as if we used array notation! Oooo! Getting closer to that array/pointer equivalence
thing! More on this later in this chapter.
But what’s actually happening, here? How does it work?
Remember from early on that memory is like a big array, where a byte is stored at each array index?
And the array index into memory has a few names:
• Index into memory
• Location
• Address
• Pointer!
So a point is an index into memory, somewhere.
For a random example, say that a number 3490 was stored at address (“index”) 23,237,489,202. If we have
an int pointer to that 3490, that value of that pointer is 23,237,489,202… because the pointer is the memory
address. Different words for the same thing.
And now let’s say we have another number, 4096, stored right after the 3490 at address 23,237,489,210 (8
higher than the 3490 because each int in this example is 8 bytes long).
If we add 1 to that pointer, it actually jumps ahead sizeof(int) bytes to the next int. It knows to jump
that far ahead because it’s an int pointer. If it were a float pointer, it’d jump sizeof(float) bytes ahead
to get to the next float!
So you can look at the next int, by adding 1 to the pointer, the one after that by adding 2 to the pointer, and
so on.

11.1.2 Changing Pointers


We saw how we could add an integer to a pointer in the previous section. This time, let’s modify the pointer,
itself.
You can just add (or subtract) integer values directly to (or from) any pointer!
Let’s do that example again, except with a couple changes. First, I’m going to add a 999 to the end of our
numbers to act as a sentinel value. This will let us know where the end of the data is.
int a[] = {11, 22, 33, 44, 55, 999}; // Add 999 here as a sentinel

int *p = &a[0]; // p points to the 11

And we also have p pointing to the element at index 0 of a, namely 11, just like before.

[Link]
Chapter 11. Pointers II: Arithmetic 69

Now—let’s starting incrementing p so that it points at subsequent elements of the array. We’ll do this until p
points to the 999; that is, we’ll do it until *p == 999:
while (*p != 999) { // While the thing p points to isn't 999
printf("%d\n", *p); // Print it
p++; // Move p to point to the next int!
}

Pretty crazy, right?


When we give it a run, first p points to 11. Then we increment p, and it points to 22, and then again, it points
to 33. And so on, until it points to 999 and we quit.

11.1.3 Subtracting Pointers


You can subtract a value from a pointer to get to earlier address, as well, just like we were adding to them
before.
But we can also subtract two pointers to find the difference between them, e.g. we can calculate how many
ints there are between two int*s. The catch is that this only works within a single array2 —if the pointers
point to anything else, you get undefined behavior.
Remember how strings are char*s in C? Let’s see if we can use this to write another variant of strlen()
to compute the length of a string that utilizes pointer subtraction.
The idea is that if we have a pointer to the beginning of the string, we can find a pointer to the end of the
string by scanning ahead for the NUL character.
And if we have a pointer to the beginning of the string, and we computed the pointer to the end of the string,
we can just subtract the two pointers to come up with the length!
1 #include <stdio.h>
2

3 int my_strlen(char *s)


4 {
5 // Start scanning from the beginning of the string
6 char *p = s;
7

8 // Scan until we find the NUL character


9 while (*p != '\0')
10 p++;
11

12 // Return the difference in pointers


13 return p - s;
14 }
15

16 int main(void)
17 {
18 printf("%d\n", my_strlen("Hello, world!")); // Prints "13"
19 }

Remember that you can only use pointer subtraction between two pointers that point to the same array!
2
Or string, which is really an array of chars. Somewhat peculiarly, you can also have a pointer that references one past the end of
the array without a problem and still do math on it. You just can’t dereference it when it’s out there.
Chapter 11. Pointers II: Arithmetic 70

11.2 Array/Pointer Equivalence


We’re finally ready to talk about this! We’ve seen plenty of examples of places where we’ve intermixed array
notation, but let’s give out the fundamental formula of array/pointer equivalence:
a[b] == *(a + b)

Study that! Those are equivalent and can be used interchangeably!


I’ve oversimplified a bit, because in my above example a and b can both be expressions, and we might want
a few more parentheses to force order of operations in case the expressions are complex.
The spec is specific, as always, declaring (in C11 §[Link]¶2):
E1[E2] is identical to (*((E1)+(E2)))

but that’s a little harder to grok. Just make sure you include parentheses if the expressions are complicated
so all your math happens in the right order.
This means we can decide if we’re going to use array or pointer notation for any array or pointer (assuming
it points to an element of an array).
Let’s use an array and pointer with both array and pointer notation:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int a[] = {11, 22, 33, 44, 55};
6

7 int *p = a; // p points to the first element of a, 11


8

9 // Print all elements of the array a variety of ways:


10

11 for (int i = 0; i < 5; i++)


12 printf("%d\n", a[i]); // Array notation with a
13

14 for (int i = 0; i < 5; i++)


15 printf("%d\n", p[i]); // Array notation with p
16

17 for (int i = 0; i < 5; i++)


18 printf("%d\n", *(a + i)); // Pointer notation with a
19

20 for (int i = 0; i < 5; i++)


21 printf("%d\n", *(p + i)); // Pointer notation with p
22

23 for (int i = 0; i < 5; i++)


24 printf("%d\n", *(p++)); // Moving pointer p
25 //printf("%d\n", *(a++)); // Moving array variable a--ERROR!
26 }

So you can see that in general, if you have an array variable, you can use pointer or array notion to access
elements. Same with a pointer variable.
The one big difference is that you can modify a pointer to point to a different address, but you can’t do that
with an array variable.

[Link]
Chapter 11. Pointers II: Arithmetic 71

11.2.1 Array/Pointer Equivalence in Function Calls


This is where you’ll encounter this concept the most, for sure.
If you have a function that takes a pointer argument, e.g.:
int my_strlen(char *s)

this means you can pass either an array or a pointer to this function and have it work!
char s[] = "Antelopes";
char *t = "Wombats";

printf("%d\n", my_strlen(s)); // Works!


printf("%d\n", my_strlen(t)); // Works, too!

And it’s also why these two function signatures are equivalent:
int my_strlen(char *s) // Works!
int my_strlen(char s[]) // Works, too!

11.3 void Pointers


You’ve already seen the void keyword used with functions, but this is an entirely separate, unrelated animal.
Sometimes it’s useful to have a pointer to a thing that you don’t know the type of.
I know. Bear with me just a second.
There are basically two use cases for this.
1. A function is going to operate on something byte-by-byte. For example, memcpy() copies bytes of memory
from one pointer to another, but those pointers can point to any type. memcpy() takes advantage of the fact
that if you iterate through char*s, you’re iterating through the bytes of an object no matter what type the
object is. More on this in the Multibyte Values subsection.
2. Another function is calling a function you passed to it (a callback), and it’s passing you data. You know
the type of the data, but the function calling you doesn’t. So it passes you void*s—’cause it doesn’t
know the type—and you convert those to the type you need. The built-in qsort() and bsearch()
use this technique.
Let’s look at an example, the built-in memcpy() function:
void *memcpy(void *s1, void *s2, size_t n);

This function copies n bytes of memory starting from address s1 into the memory starting at address s2.
But look! s1 and s2 are void*s! Why? What does it mean? Let’s run more examples to see.
For instance, we could copy a string with memcpy() (though strcpy() is more appropriate for strings):
1 #include <stdio.h>
2 #include <string.h>
3

4 int main(void)
5 {
6 char s[] = "Goats!";
7 char t[100];
8

9 memcpy(t, s, 7); // Copy 7 bytes--including the NUL terminator!


10
Chapter 11. Pointers II: Arithmetic 72

11 printf("%s\n", t); // "Goats!"


12 }

Or we can copy some ints:


1 #include <stdio.h>
2 #include <string.h>
3

4 int main(void)
5 {
6 int a[] = {11, 22, 33};
7 int b[3];
8

9 memcpy(b, a, 3 * sizeof(int)); // Copy 3 ints of data


10

11 printf("%d\n", b[1]); // 22
12 }

That one’s a little wild—you see what we did there with memcpy()? We copied the data from a to b, but we
had to specify how many bytes to copy, and an int is more than one byte.
OK, then—how many bytes does an int take? Answer: depends on the system. But we can tell how many
bytes any type takes with the sizeof operator.
So there’s the answer: an int takes sizeof(int) bytes of memory to store.
And if we have 3 of them in our array, like we did in that example, the entire space used for the 3 ints must
be 3 * sizeof(int).
(In the string example, earlier, it would have been more technically accurate to copy 7 * sizeof(char)
bytes. But chars are always one byte large, by definition, so that just devolves into 7 * 1.)
We could even copy a float or a struct with memcpy()! (Though this is abusive—we should just use =
for that):
struct antelope my_antelope;
struct antelopy my_clone_antelope;

// ...

memcpy(&my_clone_antelope, &my_antelope, sizeof my_antelope);

Look at how versatile memcpy() is! If you have a pointer to a source and a pointer to a destination, and you
have the number of bytes you want to copy, you can copy any type of data.
Imagine if we didn’t have void*. We’d have to write specialized memcpy() functions for each type:
memcpy_int(int *a, int *b, int count);
memcpy_float(float *a, float *b, int count);
memcpy_double(double *a, double *b, int count);
memcpy_char(char *a, char *b, int count);
memcpy_unsigned_char(unsigned char *a, unsigned char *b, int count);

// etc... blech!

Much better to just use void* and have one function that can do it all.
That’s the power of void*. You can write functions that don’t care about the type and is still able to do things
with it.

[Link]
Chapter 11. Pointers II: Arithmetic 73

But with great power comes great responsibility. Maybe not that great in this case, but there are some limits.
1. You cannot do pointer arithmetic on a void*. 2. You cannot dereference a void*. 3. You cannot use the
arrow operator on a void*, since it’s also a dereference. 4. You cannot use array notation on a void*, since
it’s also a dereference, as well3 .
And if you think about it, these rules make sense. All those operations rely on knowing the sizeof the type
of data pointed to, and with void*, we don’t know the size of the data being pointed to—it could be anything!

But wait—if you can’t dereference a void* what good can it ever do you?
Like with memcpy(), it helps you write generic functions that can handle multiple types of data. But the
secret is that, deep down, you convert the void* to another type before you use it!
And conversion is easy: you can just assign into a variable of the desired type4 .
char a = 'X'; // A single char

void *p = &a; // p points to the 'X'


char *q = p; // q also points to the 'X'

printf("%c\n", *p); // ERROR--cannot dereference void*!


printf("%c\n", *q); // Prints "X"

Let’s write our own memcpy() to try this out. We can copy bytes (chars), and we know the number of bytes
because it’s passed in.
void *my_memcpy(void *dest, void *src, int byte_count)
{
// Convert void*s to char*s
char *s = src, *d = dest;

// Now that we have char*s, we can dereference and copy them


while (byte_count--) {
*d++ = *s++;
}

// Most of these functions return the destination, just in case


// that's useful to the caller.
return dest;
}

Right there at the beginning, we copy the void*s into char*s so that we can use them as char*s. It’s as
easy as that.
Then some fun in a while loop, where we decrement byte_count until it becomes false (0). Remember
that with post-decrement, the value of the expression is computed (for while to use) and then the variable is
decremented.
And some fun in the copy, where we assign *d = *s to copy the byte, but we do it with post-increment so
that both d and s move to the next byte after the assignment is made.
Lastly, most memory and string functions return a copy of a pointer to the destination string just in case the
caller wants to use it.
3
Because remember that array notation is just a dereference and some pointer math, and you can’t dereference a void*!
4
You can also cast the void* to another type, but we haven’t gotten to casts yet.
Chapter 11. Pointers II: Arithmetic 74

Now that we’ve done that, I just want to quickly point out that we can use this technique to iterate over the
bytes of any object in C, floats, structs, or anything!
Let’s run one more real-world example with the built-in qsort() routine that can sort anything thanks to the
magic of void*s.
(In the following example, you can ignore the word const, which we haven’t covered yet.)
1 #include <stdio.h>
2 #include <stdlib.h>
3

4 // The type of structure we're going to sort


5 struct animal {
6 char *name;
7 int leg_count;
8 };
9

10 // This is a comparison function called by qsort() to help it determine


11 // what exactly to sort by. We'll use it to sort an array of struct
12 // animals by leg_count.
13 int compar(const void *elem1, const void *elem2)
14 {
15 // We know we're sorting struct animals, so let's make both
16 // arguments pointers to struct animals
17 const struct animal *animal1 = elem1;
18 const struct animal *animal2 = elem2;
19

20 // Return <0 =0 or >0 depending on whatever we want to sort by.


21

22 // Let's sort ascending by leg_count, so we'll return the difference


23 // in the leg_counts
24 if (animal1->leg_count > animal2->leg_count)
25 return 1;
26

27 if (animal1->leg_count < animal2->leg_count)


28 return -1;
29

30 return 0;
31 }
32

33 int main(void)
34 {
35 // Let's build an array of 4 struct animals with different
36 // characteristics. This array is out of order by leg_count, but
37 // we'll sort it in a second.
38 struct animal a[4] = {
39 {.name="Dog", .leg_count=4},
40 {.name="Monkey", .leg_count=2},
41 {.name="Antelope", .leg_count=4},
42 {.name="Snake", .leg_count=0}
43 };
44

45 // Call qsort() to sort the array. qsort() needs to be told exactly


46 // what to sort this data by, and we'll do that inside the compar()
47 // function.

[Link]
Chapter 11. Pointers II: Arithmetic 75

48 //
49 // This call is saying: qsort array a, which has 4 elements, and
50 // each element is sizeof(struct animal) bytes big, and this is the
51 // function that will compare any two elements.
52 qsort(a, 4, sizeof(struct animal), compar);
53

54 // Print them all out


55 for (int i = 0; i < 4; i++) {
56 printf("%d: %s\n", a[i].leg_count, a[i].name);
57 }
58 }

As long as you give qsort() a function that can compare two items that you have in your array to be sorted, it
can sort anything. And it does this without needing to have the types of the items hardcoded in there anywhere.
qsort() just rearranges blocks of bytes based on the results of the compar() function you passed in.
Chapter 12

Manual Memory Allocation

This is one of the big areas where C likely diverges from languages you already know: manual memory
management.
Other languages uses reference counting, garbage collection, or other means to determine when to allocate
new memory for some data—and when to deallocate it when no variables refer to it.
And that’s nice. It’s nice to be able to not worry about it, to just drop all the references to an item and trust
that at some point the memory associated with it will be freed.
But C’s not like that, entirely.
Of course, in C, some variables are automatically allocated and deallocated when they come into scope and
leave scope. We call these automatic variables. They’re your average run-of-the-mill block scope “local”
variables. No problem.
But what if you want something to persist longer than a particular block? This is where manual memory
management comes into play.
You can tell C explicitly to allocate for you a certain number of bytes that you can use as you please. And
these bytes will remain allocated until you explicitly free that memory1 .
It’s important to free the memory you’re done with! If you don’t, we call that a memory leak and your process
will continue to reserve that memory until it exits.
If you manually allocated it, you have to manually free it when you’re done with it.
So how do we do this? We’re going to learn a couple new functions, and make use of the sizeof operator
to help us learn how many bytes to allocate.
In common C parlance, devs say that automatic local variables are allocated “on the stack”, and manually-
allocated memory is “on the heap”. The spec doesn’t talk about either of those things, but all C devs will
know what you’re talking about if you bring them up.
All functions we’re going to learn in this chapter can be found in <stdlib.h>.

12.1 Allocating and Deallocating, malloc() and free()


The malloc() function accepts a number of bytes to allocate, and returns a void pointer to that block of
newly-allocated memory.
1
Or until the program exits, in which case all the memory allocated by it is freed. Asterisk: some systems allow you to allocate
memory that persists after a program exits, but it’s system dependent, out of scope for this guide, and you’ll certainly never do it on
accident.

76

[Link]
Chapter 12. Manual Memory Allocation 77

Since it’s a void*, you can assign it into whatever pointer type you want… normally this will correspond in
some way to the number of bytes you’re allocating.
So… how many bytes should I allocate? We can use sizeof to help with that. If we want to allocate enough
room for a single int, we can use sizeof(int) and pass that to malloc().
After we’re done with some allocated memory, we can call free() to indicate we’re done with that memory
and it can be used for something else. As an argument, you pass the same pointer you got from malloc()
(or a copy of it). It’s undefined behavior to use a memory region after you free() it.
Let’s try. We’ll allocate enough memory for an int, and then store something there, and the print it.
// Allocate space for a single int (sizeof(int) bytes-worth):

int *p = malloc(sizeof(int));

*p = 12; // Store something there

printf("%d\n", *p); // Print it: 12

free(p); // All done with that memory

//*p = 3490; // ERROR: undefined behavior! Use after free()!

Now, in that contrived example, there’s really no benefit to it. We could have just used an automatic int
and it would have worked. But we’ll see how the ability to allocate memory this way has its advantages,
especially with more complex data structures.
One more thing you’ll commonly see takes advantage of the fact that sizeof can give you the size of the
result type of any constant expression. So you could put a variable name in there, too, and use that. Here’s
an example of that, just like the previous one:
int *p = malloc(sizeof *p); // *p is an int, so same as sizeof(int)

12.2 Error Checking


All the allocation functions return a pointer to the newly-allocated stretch of memory, or NULL if the memory
cannot be allocated for some reason.
Some OSes like Linux can be configured in such a way that malloc() never returns NULL, even if you’re
out of memory. But despite this, you should always code it up with protections in mind.
int *x;

x = malloc(sizeof(int) * 10);

if (x == NULL) {
printf("Error allocating 10 ints\n");
// do something here to handle it
}

Here’s a common pattern that you’ll see, where we do the assignment and the condition on the same line:
int *x;

if ((x = malloc(sizeof(int) * 10)) == NULL)


printf("Error allocating 10 ints\n");
Chapter 12. Manual Memory Allocation 78

// do something here to handle it


}

12.3 Allocating Space for an Array


We’ve seen how to allocate space for a single thing; now what about for a bunch of them in an array?
In C, an array is a bunch of the same thing back-to-back in a contiguous stretch of memory.
We can allocate a contiguous stretch of memory—we’ve seen how to do that. If we wanted 3490 bytes of
memory, we could just ask for it:
char *p = malloc(3490); // Voila

And—indeed!—that’s an array of 3490 chars (AKA a string!) since each char is 1 byte. In other words,
sizeof(char) is 1.

Note: there’s no initialization done on the newly-allocated memory—it’s full of garbage. Clear it with mem-
set() if you want to, or see calloc(), below.

But we can just multiply the size of the thing we want by the number of elements we want, and then access
them using either pointer or array notation. Example!
1 #include <stdio.h>
2 #include <stdlib.h>
3

4 int main(void)
5 {
6 // Allocate space for 10 ints
7 int *p = malloc(sizeof(int) * 10);
8

9 // Assign them values 0-45:


10 for (int i = 0; i < 10; i++)
11 p[i] = i * 5;
12

13 // Print all values 0, 5, 10, 15, ..., 40, 45


14 for (int i = 0; i < 10; i++)
15 printf("%d\n", p[i]);
16

17 // Free the space


18 free(p);
19 }

The key’s in that malloc() line. If we know each int takes sizeof(int) bytes to hold it, and we know
we want 10 of them, we can just allocate exactly that many bytes with:
sizeof(int) * 10

And this trick works for every type. Just pass it to sizeof and multiply by the size of the array.

12.4 An Alternative: calloc()


This is another allocation function that works similarly to malloc(), with two key differences:
• Instead of a single argument, you pass the size of one element, and the number of elements you wish
to allocate. It’s like it’s made for allocating arrays.
• It clears the memory to zero.

[Link]
Chapter 12. Manual Memory Allocation 79

You still use free() to deallocate memory obtained through calloc().


Here’s a comparison of calloc() and malloc().
// Allocate space for 10 ints with calloc(), initialized to 0:
int *p = calloc(sizeof(int), 10);

// Allocate space for 10 ints with malloc(), initialized to 0:


int *q = malloc(sizeof(int) * 10);
memset(q, 0, sizeof(int) * 10); // set to 0

Again, the result is the same for both except malloc() doesn’t zero the memory by default.

12.5 Changing Allocated Size with realloc()


If you’ve already allocated 10 ints, but later you decide you need 20, what can you do?
One option is to allocate some new space, and then memcpy() the memory over… but it turns out that
sometimes you don’t need to move anything. And there’s one function that’s just smart enough to do the
right thing in all the right circumstances: realloc().
It takes a pointer to some previously-allocted memory (by malloc() or calloc()) and a new size for the
memory region to be.
It then grows or shrinks that memory, and returns a pointer to it. Sometimes it might return the same pointer
(if the data didn’t have to be copied elsewhere), or it might return a different one (if the data did have to be
copied).
Be sure when you call realloc(), you specify the number of bytes to allocate, and not just the number of
array elements! That is:
num_floats *= 2;

np = realloc(p, num_floats); // WRONG: need bytes, not number of elements!

np = realloc(p, num_floats * sizeof(float)); // Better!

Let’s allocate an array of 20 floats, and then change our mind and make it an array of 40.
We’re going to assign the return value of realloc() into another pointer just to make sure it’s not NULL. If
it’s not, then we can reassign it into our original pointer. (If we just assigned the return value directly into the
original pointer, we’d lose that pointer if the function returned NULL and we’d have no way to get it back.)
1 #include <stdio.h>
2 #include <stdlib.h>
3

4 int main(void)
5 {
6 // Allocate space for 20 floats
7 float *p = malloc(sizeof *p * 20); // sizeof *p same as sizeof(float)
8

9 // Assign them fractional values 0.0-1.0:


10 for (int i = 0; i < 20; i++)
11 p[i] = i / 20.0;
12

13 // But wait! Let's actually make this an array of 40 elements


14 float *new_p = realloc(p, sizeof *p * 40);
15
Chapter 12. Manual Memory Allocation 80

16 // Check to see if we successfully reallocated


17 if (new_p == NULL) {
18 printf("Error reallocing\n");
19 return 1;
20 }
21

22 // If we did, we can just reassign p


23 p = new_p;
24

25 // And assign the new elements values in the range 1.0-2.0


26 for (int i = 20; i < 40; i++)
27 p[i] = 1.0 + (i - 20) / 20.0;
28

29 // Print all values 0.0-2.0 in the 40 elements:


30 for (int i = 0; i < 40; i++)
31 printf("%f\n", p[i]);
32

33 // Free the space


34 free(p);
35 }

Notice in there how we took the return value from realloc() and reassigned it into the same pointer variable
p that we passed in. That’s pretty common to do.

Also if line 7 is looking weird, with that sizeof *p in there, remember that sizeof works on the size of
the type of the expression. And the type of *p is float, so that line is equivalent to sizeof(float).

12.5.1 Reading in Lines of Arbitrary Length


I want to demonstrate two things with this full-blown example.
1. Use of realloc() to grow a buffer as we read in more data.
2. Use of realloc() to shrink the buffer down to the perfect size after we’ve completed the read.
What we see here is a loop that calls fgetc() over and over to append to a buffer until we see that the last
character is a newline.
Once it finds the newline, it shrinks the buffer to just the right size and returns it.
1 #include <stdio.h>
2 #include <stdlib.h>
3

4 // Read a line of arbitrary size from a file


5 //
6 // Returns a pointer to the line.
7 // Returns NULL on EOF or error.
8 //
9 // It's up to the caller to free() this pointer when done with it.
10 //
11 // Note that this strips the newline from the result. If you need
12 // it in there, probably best to switch this to a do-while.
13

14 char *readline(FILE *fp)


15 {
16 int offset = 0; // Index next char goes in the buffer
17 int bufsize = 4; // Preferably power of 2 initial size

[Link]
Chapter 12. Manual Memory Allocation 81

18 char *buf; // The buffer


19 int c; // The character we've read in
20

21 buf = malloc(bufsize); // Allocate initial buffer


22

23 if (buf == NULL) // Error check


24 return NULL;
25

26 // Main loop--read until newline or EOF


27 while (c = fgetc(fp), c != '\n' && c != EOF) {
28

29 // Check if we're out of room in the buffer accounting


30 // for the extra byte for the NUL terminator
31 if (offset == bufsize - 1) { // -1 for the NUL terminator
32 bufsize *= 2; // 2x the space
33

34 char *new_buf = realloc(buf, bufsize);


35

36 if (new_buf == NULL) {
37 free(buf); // On error, free and bail
38 return NULL;
39 }
40

41 buf = new_buf; // Successful realloc


42 }
43

44 buf[offset++] = c; // Add the byte onto the buffer


45 }
46

47 // We hit newline or EOF...


48

49 // If at EOF and we read no bytes, free the buffer and


50 // return NULL to indicate we're at EOF:
51 if (c == EOF && offset == 0) {
52 free(buf);
53 return NULL;
54 }
55

56 // Shrink to fit
57 if (offset < bufsize - 1) { // If we're short of the end
58 char *new_buf = realloc(buf, offset + 1); // +1 for NUL terminator
59

60 // If successful, point buf to new_buf;


61 // otherwise we'll just leave buf where it is
62 if (new_buf != NULL)
63 buf = new_buf;
64 }
65

66 // Add the NUL terminator


67 buf[offset] = '\0';
68

69 return buf;
70 }
Chapter 12. Manual Memory Allocation 82

71

72 int main(void)
73 {
74 FILE *fp = fopen("[Link]", "r");
75

76 char *line;
77

78 while ((line = readline(fp)) != NULL) {


79 printf("%s\n", line);
80 free(line);
81 }
82

83 fclose(fp);
84 }

When growing memory like this, it’s common (though hardly a law) to double the space needed each step
just to minimize the number of realloc()s that occur.
Finally you might note that readline() returns a pointer to a malloc()d buffer. As such, it’s up to the
caller to explicitly free() that memory when it’s done with it.

12.5.2 realloc() with NULL


Trivia time! These two lines are equivalent:
char *p = malloc(3490);
char *p = realloc(NULL, 3490);

That could be convenient if you have some kind of allocation loop and you don’t want to special-case the
first malloc().
int *p = NULL;
int length = 0;

while (!done) {
// Allocate 10 more ints:
length += 10;
p = realloc(p, sizeof *p * length);

// Do amazing things
// ...
}

In that example, we didn’t need an initial malloc() since p was NULL to start.

12.6 Aligned Allocations


You probably aren’t going to need to use this.
And I don’t want to get too far off in the weeds talking about it right now, but there’s this thing called memory
alignment, which has to do with the memory address (pointer value) being a multiple of a certain number.
For example, a system might require that 16-bit values begin on memory addresses that are multiples of 2.
Or that 64-bit values begin on memory addresses that are multiples of 2, 4, or 8, for example. It depends on
the CPU.
Some systems require this kind of alignment for fast memory access, or some even for memory access at all.

[Link]
Chapter 12. Manual Memory Allocation 83

Now, if you use malloc(), calloc(), or realloc(), C will give you a chunk of memory that’s well-aligned
for any value at all, even structs. Works in all cases.
But there might be times that you know that some data can be aligned at a smaller boundary, or must be aligned
at a larger one for some reason. I imagine this is more common with embedded systems programming.
In those cases, you can specify an alignment with aligned_alloc().
The alignment is an integer power of two greater than zero, so 2, 4, 8, 16, etc. and you give that to
aligned_alloc() before the number of bytes you’re interested in.

The other restriction is that the number of bytes you allocate needs to be a multiple of the alignment. But
this might be changing. See C Defect Report 4602
Let’s do an example, allocating on a 64-byte boundary:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4

5 int main(void)
6 {
7 // Allocate 256 bytes aligned on a 64-byte boundary
8 char *p = aligned_alloc(64, 256); // 256 == 64 * 4
9

10 // Copy a string in there and print it


11 strcpy(p, "Hello, world!");
12 printf("%s\n", p);
13

14 // Free the space


15 free(p);
16 }

I want to throw a note here about realloc() and aligned_alloc(). realloc() doesn’t have any align-
ment guarantees, so if you need to get some aligned reallocated space, you’ll have to do it the hard way with
memcpy().

Here’s a non-standard aligned_realloc() function, if you need it:


void *aligned_realloc(void *ptr, size_t old_size, size_t alignment, size_t size)
{
char *new_ptr = aligned_alloc(alignment, size);

if (new_ptr == NULL)
return NULL;

size_t copy_size = old_size < size? old_size: size; // get min

if (ptr != NULL)
memcpy(new_ptr, ptr, copy_size);

free(ptr);

return new_ptr;
}

2
[Link]
Chapter 12. Manual Memory Allocation 84

Note that it always copies data, taking time, while real realloc() will avoid that if it can. So this is hardly
efficient. Avoid needing to reallocate custom-aligned data.

[Link]
Chapter 13

Scope

Scope is all about what variables are visible in what contexts.

13.1 Block Scope


This is the scope of almost all the variables devs define. It includes what other languages might call “function
scope”, i.e. variables that are declared inside functions.
The basic rule is that if you’ve declared a variable in a block delimited by squirrelly braces, the scope of that
variable is that block.
If there’s a block inside a block, then variables declared in the inner block are local to that block, and cannot
be seen in the outer scope.
Once a variable’s scope ends, that variable can no longer be referenced, and you can consider its value to be
gone into the great bit bucket1 in the sky.
An example with nested scope:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int a = 12; // Local to outer block, but visible in inner block
6

7 if (a == 12) {
8 int b = 99; // Local to inner block, not visible in outer block
9

10 printf("%d %d\n", a, b); // OK: "12 99"


11 }
12

13 printf("%d\n", a); // OK, we're still in a's scope


14

15 printf("%d\n", b); // ILLEGAL, out of b's scope


16 }

1
[Link]

85
Chapter 13. Scope 86

13.1.1 Where To Define Variables


Another fun fact is that you can define variables anywhere in the block, within reason—they have the scope
of that block, but cannot be used before they are defined.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i = 0;
6

7 printf("%d\n", i); // OK: "0"


8

9 //printf("%d\n", j); // ILLEGAL--can't use j before it's defined


10

11 int j = 5;
12

13 printf("%d %d\n", i, j); // OK: "0 5"


14 }

Historically, C required all the variables be defined before any code in the block, but this is no longer the
case in the C99 standard.

13.1.2 Variable Hiding


If you have a variable named the same thing at an inner scope as one at an outer scope, the one at the inner
scope takes precedence at long as you’re running in the inner scope. That is, it hides the one at outer scope
for the duration of its lifetime.
1 #include <stdio.h>
2

3 int main(void)
4 {
5 int i = 10;
6

7 {
8 int i = 20;
9

10 printf("%d\n", i); // Inner scope i, 20 (outer i is hidden)


11 }
12

13 printf("%d\n", i); // Outer scope i, 10


14 }

You might have noticed in that example that I just threw a block in there at line 7, not so much as a for or
if statement to kick it off! This is perfectly legal. Sometimes a dev will want to group a bunch of local
variables together for a quick computation and will do this, but it’s rare to see.

13.2 File Scope


If you define a variable outside of a block, that variable has file scope. It’s visible in all functions in the file
that come after it, and shared between them. (An exception is if a block defines a variable of the same name,
it would hide the one at file scope.)
This is closest to what you would consider to be “global” scope in another language.

[Link]
Chapter 13. Scope 87

For example:
1 #include <stdio.h>
2

3 int shared = 10; // File scope! Visible to the whole file after this!
4

5 void func1(void)
6 {
7 shared += 100; // Now shared holds 110
8 }
9

10 void func2(void)
11 {
12 printf("%d\n", shared); // Prints "110"
13 }
14

15 int main(void)
16 {
17 func1();
18 func2();
19 }

Note that if shared were declared at the bottom of the file, it wouldn’t compile. It has to be declared before
any functions use it.
There are ways to further modify items at file scope, namely with static and extern, but we’ll talk more about
those later.

13.3 for-loop Scope


I really don’t know what to call this, as C11 §[Link]¶1 doesn’t give it a proper name. We’ve done it already
a few times in this guide, as well. It’s when you declare a variable inside the first clause of a for-loop:
for (int i = 0; i < 10; i++)
printf("%d\n", i);

printf("%d\n", i); // ILLEGAL--i is only in scope for the for-loop

In that example, i’s lifetime begins the moment it is defined, and continues for the duration of the loop.
If the loop body is enclosed in a block, the variables defined in the for-loop are visible from that inner scope.
Unless, of course, that inner scope hides them. This crazy example prints 999 five times:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 for (int i = 0; i < 5; i++) {
6 int i = 999; // Hides the i in the for-loop scope
7 printf("%d\n", i);
8 }
9 }
Chapter 13. Scope 88

13.4 A Note on Function Scope


The C spec does refer to function scope, but it’s used exclusively with labels, something we haven’t discussed
yet. More on that another day.

[Link]
Chapter 14

Types II: Way More Types!

We’re used to char, int, and float types, but it’s now time to take that stuff to the next level and see what
else we have out there in the types department!

14.1 Signed and Unsigned Integers


So far we’ve used int as a signed type, that is, a value that can be either negative or positive. But C also has
specific unsigned integer types that can only hold positive numbers.
These types are prefaced by the keyword unsigned.
int a; // signed
signed int a; // signed
signed a; // signed, "shorthand" for "int" or "signed int", rare
unsigned int b; // unsigned
unsigned c; // unsigned, shorthand for "unsigned int"

Why? Why would you decide you only wanted to hold positive numbers?
Answer: you can get larger numbers in an unsigned variable than you can in a signed ones.
But why is that?
You can think of integers being represented by a certain number of bits1 . On my computer, an int is repre-
sented by 64 bits.
And each permutation of bits that are either 1 or 0 represents a number. We can decide how to divvy up these
numbers.
With signed numbers, we use (roughly) half the permutations to represent negative numbers, and the other
half to represent positive numbers.
With unsigned, we use all the permutations to represent positive numbers.
On my computer with 64-bit ints using two’s complement2 to represent unsigned numbers, I have the fol-
lowing limits on integer range:

1
“Bit” is short for binary digit. Binary is just another way of representing numbers. Instead of digits 0-9 like we’re used to, it’s digits
0-1.
2
[Link]

89
Chapter 14. Types II: Way More Types! 90

Type Minimum Maximum


int -9,223,372,036,854,775,808 9,223,372,036,854,775,807
unsigned int 0 18,446,744,073,709,551,615

Notice that the largest positive unsigned int is approximately twice as large as the largest positive int.
So you can get some flexibility there.

14.2 Character Types


Remember char? The type we can use to hold a single character?
char c = 'B';

printf("%c\n", c); // "B"

I have a shocker for you: it’s actually an integer.


char c = 'B';

// Change this from %c to %d:


printf("%d\n", c); // 66 (!!)

Deep down, char is just a small int, namely an integer that uses just a single byte of space, limiting its range
to…
Here the C spec gets just a little funky. It assures us that a char is a single byte, i.e. sizeof(char) == 1.
But then in C11 §3.6¶3 it goes out of its way to say:
A byte is composed of a contiguous sequence of bits, the number of which is implementation-
defined.
Wait—what? Some of you might be used to the notion that a byte is 8 bits, right? I mean, that’s what it
is, right? And the answer is, “Almost certainly.”3 But C is an old language, and machines back in the day
had, shall we say, a more relaxed opinion over how many bits were in a byte. And through the years, C has
retained this flexibility.
But assuming your bytes in C are 8 bits, like they are for virtually all machines in the world that you’ll ever
see, the range of a char is…
—So before I can tell you, it turns out that chars might be signed or unsigned depending on your compiler.
Unless you explicitly specify.
In many cases, just having char is fine because you don’t care about the sign of the data. But if you need
signed or unsigned chars, you must be specific:
char a; // Could be signed or unsigned
signed char b; // Definitely signed
unsigned char c; // Definitely unsigned

OK, now, finally, we can figure out the range of numbers if we assume that a char is 8 bits and your system
uses the virtually universal two’s complement representation for signed and unsigned4 .
So, assuming those constraints, we can finally figure our ranges:

3
The industry term for a sequence of exactly, indisputably 8 bits is an octet.
4
In general, f you have an 𝑛 bit two’s complement number, the signed range is −2𝑛−1 to 2𝑛−1 − 1. And the unsigned range is 0
to 2𝑛 − 1.

[Link]
Chapter 14. Types II: Way More Types! 91

char type Minimum Maximum


signed char -128 127
unsigned char 0 255

And the ranges for char are implementation-defined.


Let me get this straight. char is actually a number, so can we do math on it?
Yup! Just remember to keep things in the range of a char!
1 #include <stdio.h>
2

3 int main(void)
4 {
5 char a = 10, b = 20;
6

7 printf("%d\n", a + b); // 30!


8 }

What about those constant characters in single quotes, like 'B'? How does that have a numeric value?
The spec is also hand-wavey here, since C isn’t designed to run on a single type of underlying system.
But let’s just assume for the moment that your character set is based on ASCII5 for at least the first 128
characters. In that case, the character constant will be converted to a char whose value is the same as the
ASCII value of the character.
That was a mouthful. Let’s just have an example:
1 #include <stdio.h>
2

3 int main(void)
4 {
5 char a = 10;
6 char b = 'B'; // ASCII value 66
7

8 printf("%d\n", a + b); // 76!


9 }

This depends on your execution environment and the character set used6 . One of the most popular character
sets today is Unicode7 (which is a superset of ASCII), so for your basic 0-9, A-Z, a-z and punctuation, you’ll
almost certainly get the ASCII values out of them.

14.3 More Integer Types: short, long, long long


So far we’ve just generally been using two integer types:
• char
• int
and we recently learned about the unsigned variants of the integer types. And we learned that char was
secretly a small int in disguise. So we know the ints can come in multiple bit sizes.
5
[Link]
6
[Link]
7
[Link]
Chapter 14. Types II: Way More Types! 92

But there are a couple more integer types we should look at, and the minimum minimum and maximum values
they can hold.
Yes, I said “minimum” twice. The spec says that these types will hold numbers of at least these sizes, so your
implementation might be different. The header file <limits.h> defines macros that hold the minimum and
maximum integer values; rely on that to be sure, and never hardcode or assume these values.
These additional types are short int, long int, and long long int. Commonly, when using these
types, C developers leave the int part off (e.g. long long), and the compiler is perfectly happy.
// These two lines are equivalent:
long long int x;
long long x;

// And so are these:


short int x;
short x;

Let’s take a look at the integer data types and sizes in ascending order, grouped by signedness.

Type Minimum Bytes Minimum Value Maximum Value


char 1 -127 or 0 127 or 2558
signed char 1 -127 127
short 2 -32767 32767
int 2 -32767 32767
long 4 -2147483647 2147483647
long long 8 -9223372036854775807 9223372036854775807
unsigned char 1 0 255
unsigned short 2 0 65535
unsigned int 2 0 65535
unsigned long 4 0 44294967295
unsigned long long 8 0 9223372036854775807

There is no long long long type. You can’t just keep adding longs like that. Don’t be silly.
Two’s complement fans might have noticed something funny about those numbers. Why does,
for example, the signed char stop at -127 instead of -128? Remember: these are only the
minimums required by the spec. Some number representations (like sign and magnitude9 ) top
off at ±127.
Let’s run the same table on my 64-bit, two’s complement system and see what comes out:

Type My Bytes Minimum Value Maximum Value


char 1 -128 12710
signed char 1 -128 127
short 2 -32768 32767