Syntax#

C’s grammar is small, free-form, and unforgiving. Source is a sequence of translation units (one .c file plus everything it includes). The preprocessor runs first, then the compiler sees the expanded text. Statements end with ;; blocks are braced. UTF-8 in identifiers requires explicit support in C23.

/types`. For operators, see Operators.

#include <stdio.h>

static void greet(const char *name) {
    printf("hello, %s\n", name);
}

int main(void) {
    greet("operator");
    return 0;
}

Comments#

Line comments start with //; block comments wrap with /* ... */ and do not nest. C89 had only block comments; // arrived in C99 and is universal.

// single-line comment
/* block comment */

/*
 * doc-style block comment
 */

Identifiers#

Letters, digits, and underscores; cannot start with a digit. Names starting with two underscores or one underscore followed by uppercase are reserved for the implementation; the operator does not collide with them.

int user_count;             // local / file-scope
const int MAX_RETRY = 5;    // by convention, constants
typedef struct http_client http_client_t;

C is case-sensitive; Count and count are distinct.

Keywords#

auto break case char const continue default do double else
enum extern float for goto if inline int long register restrict
return short signed sizeof static struct switch typedef union
unsigned void volatile while _Alignas _Alignof _Atomic _Bool
_Complex _Generic _Imaginary _Noreturn _Static_assert
_Thread_local

C23 adds alignas alignof bool constexpr false nullptr static_assert thread_local true typeof typeof_unqual.

Literals#

Every primitive has a literal form.

int   n  = 42;
int   x  = 0xff;            // hex
int   o  = 0755;            // octal (leading 0)
int   b  = 0b1010;          // binary (GCC / Clang extension; C23 standard)
long  L  = 1234L;
long long ll = 1234LL;
unsigned u = 1234u;

float  f = 3.14f;
double d = 3.14;
double e = 6.022e23;

char   c = 'A';             // an int in C, not a char
const char *s = "hello";    // null-terminated string literal
const char *raw = "no \\n escapes";

void  *p = NULL;            // (or nullptr in C23)
bool   t = true;            // requires <stdbool.h> before C23

Preprocessor#

The preprocessor runs before the compiler. Lines starting with # are directives; the most common are #include, #define, #if, #ifdef, #ifndef, #endif, #pragma.

#include <stdio.h>
#include "myheader.h"            /* local search path */

#define MAX_BUF 4096
#define MIN(a, b) ((a) < (b) ? (a) : (b))

#if defined(__linux__)
    /* Linux-only code */
#elif defined(_WIN32)
    /* Windows-only */
#endif

#ifdef DEBUG
#  define LOG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#  define LOG(fmt, ...) ((void)0)
#endif

Macros are text substitution. Wrap every parameter and the whole expression in parentheses so precedence does not bite.

Header guards prevent double inclusion.

#ifndef MYHEADER_H
#define MYHEADER_H
/* declarations */
#endif

Statements#

A statement does something. Assignment, return, break, continue, goto, if / for / while / switch blocks, function calls.

x = 1 + 2;                  /* expression statement */
return 0;                   /* return statement */
if (x > 0) process(x);      /* compound */
for (int i = 0; i < 3; i++) step(i);
goto cleanup;               /* rare */

Semicolons are required. Every statement ends with one; the compiler is strict.

Expressions#

An expression evaluates to a value. Arithmetic, function calls, ? : (ternary), the comma operator, sizeof, cast (T)x.

1 + 2
strlen(s) > 0
(x > 0) ? x : -x
(int)f
sizeof(int)
sizeof *p                   /* sizeof an expression */

The ternary and the comma operator are expressions; the operator uses them sparingly because they read worse than the equivalent if.

Blocks#

{ ... } introduces a block (its own scope). C99 onward allows mixed declarations and statements; older C89 required all declarations at the top of the block.

{
    int local = 0;
    printf("%d\n", local);
}                           /* local out of scope */

References#