Variables#
A variable in C names a typed slot. Type, storage class, and
scope are explicit; the compiler infers nothing. Storage classes
(auto, register, static, extern) control
lifetime and linkage; scope is determined by where the
declaration appears (block, function, file, program).
/types`.
Declaration#
A declaration is T name; (with optional initialiser). A
declaration that allocates storage is a definition.
int n; /* uninitialised; value is indeterminate */
int m = 0; /* defined and initialised */
const int PI3 = 3; /* read-only */
char buf[256]; /* array */
char *p; /* pointer */
struct user u; /* struct instance */
Globals (file-scope) and static locals are zero-initialised;
block-scope locals are not, and reading one before assignment
is undefined behaviour.
Storage classes#
Specifier |
Effect |
|---|---|
|
default for block-scope locals. Lifetime ends when the enclosing block exits. |
|
hint to keep in a register. Modern compilers ignore;
|
|
one slot per program lifetime; preserved across calls. |
|
internal linkage; not visible to other translation units. |
|
declaration without definition; lives in another translation unit and the linker resolves it. |
|
per-thread storage; one slot per thread. |
/* file scope */
static int cache_size = 0; /* private to this .c file */
extern int global_counter; /* defined elsewhere */
void hit(void) {
static int call_count = 0; /* persists across calls */
call_count++;
printf("%d\n", call_count);
}
Scope#
Scopes in C: block (inside {...}), function (only
labels), file (the rest of the translation unit), and
function prototype (only inside a prototype’s parameter
list).
int main(void) {
int x = 1;
{
int x = 2; /* shadows the outer x */
printf("%d\n", x); /* 2 */
}
printf("%d\n", x); /* 1 */
return 0;
}
Avoid shadowing intentionally; -Wshadow
catches it.
Type qualifiers#
Qualifier |
Meaning |
|---|---|
|
object cannot be modified through this name. |
|
the compiler may not optimise reads / writes away.
Required for MMIO and |
|
this pointer is the only access path to the object through this function. Lets the compiler optimise. |
const char *msg = "ok"; /* msg can move; *msg cannot */
char *const HEAD = buf; /* HEAD fixed to buf; *HEAD mutable */
volatile uint32_t *reg = (uint32_t*)0xDEADBEEF;
void memcpy_fast(void *restrict dst, const void *restrict src, size_t n);
typedef#
typedef creates an alias for an existing type. The operator
uses it to name struct-as-tag types (http_client_t) and to
abstract platform widths.
typedef unsigned long long u64;
typedef struct http_client http_client_t;
u64 counter = 0;
http_client_t *c = http_client_new();
Initialisation#
Compound literals and designated initialisers (C99+) let the operator build structs and arrays inline.
struct config {
const char *host;
int port;
};
struct config c = {.host = "0.0.0.0", .port = 80};
struct config defaults[] = {
{.host = "a", .port = 1},
{.host = "b", .port = 2},
};
int xs[10] = {[5] = 42}; /* zero elsewhere, 42 at index 5 */
A zero initialiser for an aggregate fills every field with the zero of its type.
struct user u = {0}; /* every field zeroed */
char buf[256] = {0}; /* every byte zeroed */
Lifetime#
Automatic (block-scope
auto/ default): lives from block entry to exit. Stored on the stack.Static (
static, file-scope,extern): lives the whole program. Stored in BSS / data segment.Allocated (
malloc/calloc/realloc): lives untilfree. Stored on the heap.Thread-local (
_Thread_local): one slot per thread, lives the thread’s lifetime.