Author(s): Brian “Beej Jorgensen” Hall
Edition: 0.9.13 Letter One-Sided Black & White
Year: 2023
Foreword
Audience
How to Read This Book
Platform and Compiler
Official Homepage
Email Policy
Mirroring
Note for Translators
Copyright and Distribution
Dedication
Hello, World!
What to Expect from C
Hello, World!
Compilation Details
Building with gcc
Building with clang
Building from IDEs
C Versions
Variables and Statements
Variables
Variable Names
Variable Types
Boolean Types
Operators and Expressions
Arithmetic
Ternary Operator
Pre-and-Post Increment-and-Decrement
The Comma Operator
Conditional Operators
Boolean Operators
The sizeof Operator
Flow Control
The if-else statement
The while statement
The do-while statement
The for statement
The switch Statement
Functions
Passing by Value
Function Prototypes
Empty Parameter Lists
Pointers—Cower In Fear!
Memory and Variables
Pointer Types
Dereferencing
Passing Pointers as Arguments
The NULL Pointer
A Note on Declaring Pointers
sizeof and Pointers
Arrays
Easy Example
Getting the Length of an Array
Array Initializers
Out of Bounds!
Multidimensional Arrays
Arrays and Pointers
Getting a Pointer to an Array
Passing Single Dimensional Arrays to Functions
Changing Arrays in Functions
Passing Multidimensional Arrays to Functions
Strings
String Literals
String Variables
String Variables as Arrays
String Initializers
Getting String Length
String Termination
Copying a String
Structs
Declaring a Struct
Struct Initializers
Passing Structs to Functions
The Arrow Operator
Copying and Returning structs
Comparing structs
File Input/Output
The FILE* Data Type
Reading Text Files
End of File: EOF
Reading a Line at a Time
Formatted Input
Writing Text Files
Binary File I/O
struct and Number Caveats
typedef: Making New Types
typedef in Theory
Scoping
typedef in Practice
typedef and structs
typedef and Other Types
typedef and Pointers
typedef and Capitalization
Arrays and typedef
Pointers II: Arithmetic
Pointer Arithmetic
Adding to Pointers
Changing Pointers
Subtracting Pointers
Array/Pointer Equivalence
Array/Pointer Equivalence in Function Calls
void Pointers
Manual Memory Allocation
Allocating and Deallocating, malloc() and free()
Error Checking
Allocating Space for an Array
An Alternative: calloc()
Changing Allocated Size with realloc()
Reading in Lines of Arbitrary Length
realloc() with NULL
Aligned Allocations
Scope
Block Scope
Where To Define Variables
Variable Hiding
File Scope
for-loop Scope
A Note on Function Scope
Types II: Way More Types!
Signed and Unsigned Integers
Character Types
More Integer Types: short, long, long long
More Float: double and long double
How Many Decimal Digits?
Converting to Decimal and Back
Constant Numeric Types
Hexadecimal and Octal
Integer Constants
Floating Point Constants
Types III: Conversions
String Conversions
Numeric Value to String
String to Numeric Value
char Conversions
Numeric Conversions
Boolean
Integer to Integer Conversions
Integer and Floating Point Conversions
Implicit Conversions
The Integer Promotions
The Usual Arithmetic Conversions
void*
Explicit Conversions
Casting
Types IV: Qualifiers and Specifiers
Type Qualifiers
const
restrict
volatile
_Atomic
Storage-Class Specifiers
auto
static
extern
register
_Thread_local
Multifile Projects
Includes and Function Prototypes
Dealing with Repeated Includes
static and extern
Compiling with Object Files
The Outside Environment
Command Line Arguments
The Last argv is NULL
The Alternate: char **argv
Fun Facts
Exit Status
Other Exit Status Values
Environment Variables
Setting Environment Variables
Unix-like Alternative Environment Variables
The C Preprocessor
#include
Simple Macros
Conditional Compilation
If Defined, #ifdef and #endif
If Not Defined, #ifndef
#else
General Conditional: #if, #elif
Losing a Macro: #undef
Built-in Macros
Mandatory Macros
Optional Macros
Macros with Arguments
Macros with One Argument
Macros with More than One Argument
Macros with Variable Arguments
Stringification
Concatenation
Multiline Macros
Example: An Assert Macro
The #error Directive
The #pragma Directive
Non-Standard Pragmas
Standard Pragmas
_Pragma Operator
The #line Directive
The Null Directive
structs II: More Fun with structs
Initializers of Nested structs and Arrays
Anonymous structs
Self-Referential structs
Flexible Array Members
Padding Bytes
offsetof
Fake OOP
Bit-Fields
Non-Adjacent Bit-Fields
Signed or Unsigned ints
Unnamed Bit-Fields
Zero-Width Unnamed Bit-Fields
Unions
Unions and Type Punning
Pointers to unions
Common Initial Sequences in Unions
Unions and Unnamed Structs
Passing and Returning structs and unions
Characters and Strings II
Escape Sequences
Frequently-used Escapes
Rarely-used Escapes
Numeric Escapes
Enumerated Types: enum
Behavior of enum
Numbering
Trailing Commas
Scope
Style
Your enum is a Type
Pointers III: Pointers to Pointers and More
Pointers to Pointers
Pointer Pointers and const
Multibyte Values
The NULL Pointer and Zero
Pointers as Integers
Casting Pointers to other Pointers
Pointer Differences
Pointers to Functions
Bitwise Operations
Bitwise AND, OR, XOR, and NOT
Bitwise Shift
Variadic Functions
Ellipses in Function Signatures
Getting the Extra Arguments
va_list Functionality
Library Functions That Use va_lists
Locale and Internationalization
Setting the Localization, Quick and Dirty
Getting the Monetary Locale Settings
Monetary Digit Grouping
Separators and Sign Position
Example Values
Localization Specifics
Unicode, Wide Characters, and All That
What is Unicode?
Code Points
Encoding
Source and Execution Character Sets
Unicode in C
A Quick Note on UTF-8 Before We Swerve into the Weeds
Different Character Types
Multibyte Characters
Wide Characters
Using Wide Characters and wchar_t
Multibyte to wchar_t Conversions
Wide Character Functionality
wint_t
I/O Stream Orientation
I/O Functions
Type Conversion Functions
String and Memory Copying Functions
String and Memory Comparing Functions
String Searching Functions
Length/Miscellaneous Functions
Character Classification Functions
Parse State, Restartable Functions
Unicode Encodings and C
UTF-8
UTF-16, UTF-32, char16_t, and char32_t
Multibyte Conversions
Third-Party Libraries
Exiting a Program
Normal Exits
Returning From main()
exit()
Setting Up Exit Handlers with atexit()
Quicker Exits with quick_exit()
Nuke it from Orbit: _Exit()
Exiting Sometimes: assert()
Abnormal Exit: abort()
Signal Handling
What Are Signals?
Handling Signals with signal()
Writing Signal Handlers
What Can We Actually Do?
Friends Don't Let Friends signal()
Variable-Length Arrays (VLAs)
The Basics
sizeof and VLAs
Multidimensional VLAs
Passing One-Dimensional VLAs to Functions
Passing Multi-Dimensional VLAs to Functions
Partial Multidimensional VLAs
Compatibility with Regular Arrays
typedef and VLAs
Jumping Pitfalls
General Issues
goto
A Simple Example
Labeled continue
Bailing Out
Labeled break
Multi-level Cleanup
Tail Call Optimization
Restarting Interrupted System Calls
goto and Thread Preemption
goto and Variable Scope
goto and Variable-Length Arrays
Types Part V: Compound Literals and Generic Selections
Compound Literals
Passing Unnamed Objects to Functions
Unnamed structs
Pointers to Unnamed Objects
Unnamed Objects and Scope
Silly Unnamed Object Example
Generic Selections
Arrays Part II
Type Qualifiers for Arrays in Parameter Lists
static for Arrays in Parameter Lists
Equivalent Initializers
Long Jumps with setjmp, longjmp
Using setjmp and longjmp
Pitfalls
The Values of Local Variables
How Much State is Saved?
You Can't Name Anything setjmp
You Can't setjmp() in a Larger Expression
When Can't You longjmp()?
You Can't Pass 0 to longjmp()
longjmp() and Variable Length Arrays
Incomplete Types
Use Case: Self-Referential Structures
Incomplete Type Error Messages
Other Incomplete Types
Use Case: Arrays in Header Files
Completing Incomplete Types
Complex Numbers
Complex Types
Assigning Complex Numbers
Constructing, Deconstructing, and Printing
Complex Arithmetic and Comparisons
Complex Math
Trigonometry Functions
Exponential and Logarithmic Functions
Power and Absolute Value Functions
Manipulation Functions
Fixed Width Integer Types
The Bit-Sized Types
Maximum Integer Size Type
Using Fixed Size Constants
Limits of Fixed Size Integers
Format Specifiers
Date and Time Functionality
Quick Terminology and Information
Date Types
Initialization and Conversion Between Types
Converting time_t to struct tm
Converting struct tm to time_t
Formatted Date Output
More Resolution with timespec_get()
Differences Between Times
Multithreading
Background
Things You Can Do
Data Races and the Standard Library
Creating and Waiting for Threads
Detaching Threads
Thread Local Data
_Thread_local Storage-Class
Another Option: Thread-Specific Storage
Mutexes
Different Mutex Types
Condition Variables
Timed Condition Wait
Broadcast: Wake Up All Waiting Threads
Running a Function One Time
Atomics
Testing for Atomic Support
Atomic Variables
Synchronization
Acquire and Release
Sequential Consistency
Atomic Assignments and Operators
Library Functions that Automatically Synchronize
Atomic Type Specifier, Qualifier
Lock-Free Atomic Variables
Signal Handlers and Lock-Free Atomics
Atomic Flags
Atomic structs and unions
Atomic Pointers
Memory Order
Sequential Consistency
Acquire
Release
Consume
Acquire/Release
Relaxed
Fences
References
Function Specifiers, Alignment Specifiers/Operators
Function Specifiers
inline for Speed—Maybe
noreturn and _Noreturn
Alignment Specifiers and Operators
alignas and _Alignas
alignof and _Alignof