Your first program
#include <stdio.h>
int main(void)
{
puts("Hello, world!");
return 0;
}main is the entry point in a hosted program. Return 0 for success.
Syntax, patterns, and the details that are easy to forget.
Start with the essentials. Jump to a topic when you need a quick reminder.
Every C program starts with a little structure.
#include <stdio.h>
int main(void)
{
puts("Hello, world!");
return 0;
}main is the entry point in a hosted program. Return 0 for success.
cc -std=c17 -Wall -Wextra -Wpedantic \
hello.c -o hello
./helloUse GCC or Clang. These commands assume a Unix-like shell.
;{ }///* */Choose a type, give it a name, and initialize it.
int count = 42;
double price = 19.95;
char initial = 'C';
const int limit = 100;
#include <stdbool.h>
bool ready = true;Single quotes hold a character. Double quotes hold a string.
Integer widths depend on the implementation. Use sizeof to inspect a type or object.
#include <stddef.h>
size_t bytes = sizeof(double);sizeof returns size_t. A C byte is not guaranteed to have exactly eight bits; see CHAR_BIT in <limits.h>.
Calculate, compare, and combine conditions.
| Operators | Purpose |
|---|---|
+ - * / % | Arithmetic; % is integer remainder |
= += -= *= /= | Assignment and compound assignment |
== != < > <= >= | Comparison; produces 0 or 1 |
&& || ! | Logical AND, OR, NOT |
& | ^ ~ << >> | Bitwise operations and shifts |
++ -- | Increment and decrement |
condition ? a : b | Choose between two expressions |
&& and || short-circuit: the right side runs only when needed.
int whole = 7 / 2; // 3
double ratio = 7.0 / 2; // 3.5
int remainder = 7 % 2; // 1
int max = (a > b) ? a : b;
// Short-circuit the null check.
// p must otherwise point to a valid int.
if (p != NULL && *p > 0) {
/* p points to a positive value */
}Integer division truncates toward zero. Division by zero and signed integer overflow are undefined behavior.
Branch when you need to. Repeat while it makes sense.
if (score >= 80) {
puts("Great work");
} else if (score >= 50) {
puts("Passed");
} else {
puts("Try again");
}
switch (choice) {
case 1:
puts("Start");
break;
default:
puts("Unknown choice");
break;
}Zero is false; nonzero is true. Without break, a switch case falls through into the next case.
for (int i = 0; i < 3; ++i) {
printf("%d\n", i);
}
int n = 3;
while (n > 0) {
--n;
}
do {
++n;
} while (n < 3);breakcontinueA do … while loop runs at least once. Use <stdio.h> for puts and printf.
Declare an interface. Give each function one clear job.
int add(int a, int b); // prototype
int main(void)
{
int result = add(2, 3);
return result == 5 ? 0 : 1;
}
int add(int a, int b)
{
return a + b;
}Declare a function before calling it. In C17, f(void) means no parameters; a declaration f() leaves the parameter types unspecified.
void increment(int *value)
{
++*value;
}
int main(void)
{
int count = 0;
increment(&count);
return 0;
}Arguments are passed by value, including pointers. Pass an address to let a function change the caller’s object.
voidstaticContiguous elements. Zero-based indexes. Explicit bounds.
#include <stddef.h>
int values[] = {10, 20, 30};
size_t length = sizeof values
/ sizeof values[0];
for (size_t i = 0; i < length; ++i) {
values[i] += 1;
}
int grid[2][3] = {
{1, 2, 3}, {4, 5, 6}
};The length formula works on an actual array. A function parameter declared as an array is adjusted to a pointer; pass its length separately.
#include <stdio.h>
#include <string.h>
char name[] = "Ada"; // 4 bytes with '\0'
const char *label = "C";
size_t length = strlen(name); // 3
int same = strcmp(name, "Ada") == 0;
char message[32];
int written = snprintf(message,
sizeof message, "Hi, %s", name);A C string ends with '\0'. Don’t modify a string literal. strcmp compares contents; == on pointers compares addresses.
snprintf returns the length it would have written, excluding the terminator. A negative result is an error; a result at least as large as the buffer means truncation.An address is useful only while the object it refers to is valid.
#include <stddef.h>
int value = 42;
int *p = &value; // address of value
int copy = *p; // read through p
*p = 7; // changes value
int *empty = NULL;
int items[] = {10, 20, 30};
int second = *(items + 1); // items[1]&x gets an address; *p accesses the pointed-to object. Never dereference a null, dangling, or uninitialized pointer.
int a = 1;
int b = 2;
const int *read_only = &a;
read_only = &b; // pointer can change
// *read_only = 3; // not allowed
int *const fixed = &a;
*fixed = 3; // object can change
// fixed = &b; // not allowed
const int *const both = &a;Pointer arithmetic stays within one array, or one past its end. A one-past pointer is valid to form, but not to dereference.
Every allocation needs a failure check and a clear owner.
#include <stdlib.h>
int main(void)
{
size_t count = 4;
int *values = malloc(count * sizeof *values);
if (values == NULL) {
return EXIT_FAILURE;
}
values[0] = 42;
free(values);
values = NULL;
return EXIT_SUCCESS;
}malloc leaves contents uninitialized. calloc(count, size) initializes all bits to zero. Use <stdlib.h>; no allocation cast is needed in C.
count * sizeof *values cannot overflow before allocating.// values owns an existing allocation.
// new_count is positive; size does not overflow.
int *temporary = realloc(values,
new_count * sizeof *values);
if (temporary != NULL) {
values = temporary;
} else {
// values is still valid; handle failure.
}malloccallocreallocfreefree(NULL) is safeAfter a successful realloc, old pointers into the allocation are invalid. Avoid zero-size requests. Free each allocation exactly once.
Give related data a shared shape and named values.
typedef struct {
int x;
int y;
} Point;
Point origin = {.x = 0, .y = 0};
Point *p = &origin;
origin.x = 10; // access an object
p->y = 20; // access through a pointerp->y is shorthand for (*p).y. Struct assignment copies member values; pointer members still point to the same objects.
enum Status {
STATUS_OK = 0,
STATUS_ERROR = 1
};
enum Status result = STATUS_OK;
#include <stdint.h>
typedef uint_least32_t Counter;
Counter visits = 0;typedef adds a name for an existing type. uint_least32_t is at least 32 bits; exact-width types such as uint32_t are optional.
memcmp for logical equality.Match formats to types, bound your input, and check results.
| Type | printf output | scanf input pointer | Note |
|---|---|---|---|
int | %d | %d | Decimal integer |
unsigned int | %u / %x | %u / %x | Decimal / hexadecimal |
long / long long | %ld / %lld | %ld / %lld | Match the length modifier |
size_t | %zu | %zu | Sizes and array lengths |
float | %f | %f | Promoted to double in printf |
double | %f / %g | %lf | scanf needs the l |
char | %c | %c | Use " %c" to skip input whitespace |
| Character string | %s | %31s for char[32] | Leave space for the terminator |
| Pointer | %p | — | Pass a (void *) cast to printf |
For <stdint.h> types, use the PRI… and SCN… macros in <inttypes.h>. A format/type mismatch can cause undefined behavior.
#include <stdio.h>
char line[128];
if (fgets(line, sizeof line, stdin) != NULL) {
printf("You entered: %s", line);
} else {
// EOF or an input error.
}fgets keeps the newline if it fits and adds a terminator on success. A long input line may arrive in several chunks.
strtol or strtod, checking range and trailing characters. Always check scanf’s conversion count when using it.#include <stdio.h>
FILE *file = fopen("notes.txt", "r");
if (file != NULL) {
int ch;
while ((ch = fgetc(file)) != EOF) {
if (putchar(ch) == EOF) {
// Handle an output error.
break;
}
}
if (ferror(file)) {
// Handle a read error.
}
if (fclose(file) == EOF) {
// Handle a close error.
}
} else {
perror("notes.txt");
}Store fgetc’s result in an int to preserve EOF. Modes: r read, w create/truncate, a append; add b for binary.
Include declarations and choose code before compilation.
#ifndef PROJECT_POINT_H
#define PROJECT_POINT_H
typedef struct {
int x;
int y;
} Point;
int point_distance_squared(Point p);
#endifUse #include <stdio.h> for a standard header and #include "point.h" for a project header. Guards prevent repeated declarations.
#define BUFFER_SIZE 256
#ifdef DEBUG
/* Debug-only code */
#endif
// Prefer a function for calculated values.
static inline int square(int x)
{
return x * x;
}Macros substitute tokens. A macro such as SQUARE(i++) may evaluate its argument multiple times; a function avoids that trap.
const for typed objects and enum for integer constant expressions. const int is not an integer constant expression in C17.The quick check before you trust a program.
n elements has indexes 0 through n - 1.'\0'. A three-character string needs at least four bytes.free.i = i++; unsequenced modifications cause undefined behavior.== to compare. = assigns, even inside a condition.cc -std=c17 -Wall -Wextra -Wpedantic \
-g -fsanitize=address,undefined \
-fno-omit-frame-pointer \
program.c -o program
./programOn supported platforms, sanitizers help find memory errors and undefined behavior while you run the program. They don’t catch every bug.
C17 baseline for hosted programs; examples may be fragments. The WG14 C11 draft covers the core language and library used here. C17 incorporates defect corrections. Compiler details: GCC language options and Clang user manual.