This post is more than two years old and may contain outdated information.
August 12, 2019
A short tutorial for the C programming language syntax and features in a series of small executable programs. All programs can be copy-pasted here.
In this post:
Execute with gcc hello-c.c -o _hello ; ./_hello
, or gcc hello-c.c -o _hello-c.s -S -O0 -masm=intel -fno-stack-protector; cat _hello-c.s
to get assembly.
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("filename: %s\n", argv[0]);
// arguments
if (argc > 1) {
printf("%s\n", "argv: ");
for (int i = 1; i < argc; i++) {
printf("\t%s\n", argv[i]);
}
}
}
Memory operations:
allocate an array of n
elements of size s
with void *calloc(int n, int s);
release a block of memory at address with void free(void *address);
allocate an array of b
bytes (uninitialised) with void *malloc(int b);
re-allocate memory at address (extending to new size s
) with void *realloc(void *address, int s);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char name[36];
char *description; // pointer to char without size limit
// copy string of 35 bytes into buffer of 36 bytes
strcpy(name, "Wolfeschlegelsteinhausenbergerdorff");
// allocate memory dynamically (can result in fragmented memory)
// description = malloc(10 * sizeof(char));
// strcpy(description, "A too long text to store in only 10 bytes...");
// printf("%s\n", description);
// malloc(): corrupted top size
// Aborted
description = malloc(100 * sizeof(char));
strcpy(description, "A too long text to store in only 10 bytes...");
printf("%s\n", description);
// A too long text to store in only 10 bytes...
// free memory
free(description);
}
#include <stdio.h>
// declare recursive function
int factorial(int i);
// define recursive function
int factorial(int i) {
if (i <= 1) {
return 1;
}
return i * factorial(i - 1);
}
int main() {
printf("factorial of 12 is %d\n", factorial(12));
// factorial of 12 is 479001600
}
Linked list implementation in C.
#include <stdio.h>
#include <stdlib.h>
// element is struct with a value and a pointer to the next element
struct Element {
int value;
struct Element *next;
};
// allocate memory for element
struct Element *create_element(int value) {
struct Element *element = (struct Element*) malloc(sizeof(struct Element));
element->value = value;
element->next = NULL;
return element;
}
// insert
void ll_insert(struct Element **element, int value) {
struct Element *new_element = create_element(value);
// list is empty
if (*element == NULL) {
*element = new_element;
}
// list is not empty
else {
struct Element *current = *element;
// iterate until last element
while (current->next != NULL) {
current = current->next;
}
// insert new element
current->next = new_element;
}
}
// remove
void ll_remove(struct Element **element, int value) {
struct Element *current = *element;
struct Element *previous = NULL;
// iterate until last element
while (current != NULL) {
// value is found
if (current->value == value) {
// value is first element
if (previous == NULL) {
*element = current->next;
}
// value is not first element
else {
previous->next = current->next;
}
// free memory
free(current);
return;
}
// update pointers
previous = current;
current = current->next;
}
}
// print
void print(struct Element *element) {
struct Element *current = element;
// iterate until last element
while (current != NULL) {
printf("%d\n", current->value);
current = current->next;
}
}
int main() {
struct Element *ll = NULL;
// insert
ll_insert(&ll, 5);
ll_insert(&ll, 10);
ll_insert(&ll, 15);
ll_insert(&ll, 20);
ll_insert(&ll, 25);
ll_insert(&ll, 30);
print(ll);
// 5
// 10
// 15
// 20
// 25
// 30
// remove
ll_remove(&ll, 15);
ll_remove(&ll, 30);
print(ll);
// 5
// 10
// 20
// 25
}
Variable argument lists in C using va_list
.
#include <stdio.h>
#include <stdarg.h>
// declare function (normally in header file)
double average(int n, ...);
// define function
double average(int n, ...) {
va_list valist; /* stdarg.h */
// return value
double sum = 0.0;
// init valist for n arguments
va_start(valist, n);
// access arguments
for (int i=0; i<n; i++) {
sum += va_arg(valist, int);
}
// free memory
va_end(valist);
// success
return sum / n;
}
int main() {
printf("average of {1,2,3,4,5}: %f\n", average(5, 1, 2, 3, 4, 5));
// average of {1,2,3,4,5}: 3.000000
}
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main() {
// expressions
printf("%d\n",
12 + 2 * (40 / 4) + 10
);
// 42
// built-ins
printf("%f\n",
pow(10, 2) /* math.h */
);
// 100.000000
// floating-point conversion
printf("%f\n",
12.0 + (10 + 20)
);
// 42.000000
// promote char to int
printf("%d\n",
'*'
);
// 42
// type casting
printf("%f\n",
(double) 42
);
// 42.000000
// integer division
printf("%d\n",
42 / 40
);
// 1
// integer modulo (remainder)
printf("%d\n",
42 % 40
);
// 2
// floating-point modulo
printf("%f\n",
fmod(42.5, 40.0)
);
// 2.500000
// absolute value
printf("%d\n",
abs(-42)
);
// 42
// rounding value
printf("%f\n",
round(42.42)
);
// 42.000000
// increment and decrement
int answer = 41;
answer++;
printf("%d\n", answer);
// 42
answer--;
printf("%d\n", answer);
// 41
}
// filename.h
extern const int global_num; // extern makes variable global
#include <stdio.h>
#include "filename.h" // if declared in header file
const int global_num = 42;
char ch[255]; // array of chars is string
float x;
double y; // double size of float
int main() {
printf("%d\n", global_num);
// 42
printf("%lu\n", sizeof(x));
// 4
printf("%lu\n", sizeof(y));
// 8
char ch[] = "this is some text";
x = y = 1.0;
printf("%f, %f\n", x, y);
// 1.000000, 1.000000
}
#include <stdio.h>
// #define is a preprocessor directive (substitute text, inlining)
#define A 100
#define B 200
int main() {
// const is an immutable variable
const int C = 300;
const int D = 400;
printf("%d\n", A);
// 100
printf("%d\n", B);
// 200
printf("%d\n", C);
// 300
printf("%d\n", D);
// 400
}
#include <stdio.h>
#include <string.h>
// structures hold data of different types
typedef struct {
char chars[255];
int numbers[10];
double value;
} Element;
// declare function with struct as argument
void function(Element element);
int main() {
// declare struct type
Element element;
strcpy(element.chars, "this is my first element");
element.numbers[0] = 1;
element.numbers[2] = 2;
element.value = 3.14;
printf("%s\n", element.chars);
// this is my first element
printf("%i\n", element.numbers[2]);
// 2
printf("%f\n", element.value);
// 3.140000
// specify number of bits (bit fields)
struct {
unsigned int x;
unsigned int y;
} bigger;
struct {
unsigned int x : 1;
unsigned int y : 1;
} smaller;
printf("%lu\n", sizeof(bigger));
// 8
printf("%lu\n", sizeof(smaller));
// 4
}
#include <stdio.h>
int main() {
// array of doubles
double doubles[] = { 200.0, -2.2, 1.0, 0.0 };
printf("%f\n", doubles[1]);
// -2.200000
// arrays are mutable
doubles[1] = 42;
printf("%f\n", doubles[1]);
// 42.000000
// array of arrays of char with max size 10 (strings)
char strings[][10] = { "first", "second" };
printf("%s\n", strings[1]);
// second
// nd-array of ints
int matrix[2][10] = {
{ 0, 1, 2, 3, 4 },
{ 5, 6, 7, 8, 9 }
};
printf("%d\n", matrix[1][2]);
// 7
// calculate length based on type (in bytes)
printf("%lu\n", sizeof(doubles) / sizeof(double));
// 4
}
Union store different types of data in the same memory location (note that only one member can store data at a time).
#include <stdio.h>
#include <string.h>
union Data {
int number;
float value;
char str[20];
} data;
int main() {
union Data data;
// size of union is size of largest member
printf("%lu\n", sizeof(data));
// 20
// assign values
data.number = 42;
printf("%d\n", data.number);
// 42
// subsequent assignments overwrite previous values
data.value = 12.5;
printf("%f\n", data.value);
// 12.500000
printf("%d\n", data.number);
// 1095237632 (number is overwritten)
// strings are copied with strcpy
strcpy(data.str, "this is some text");
printf("%s\n", data.str);
// this is some text
printf("%d\n", data.number);
// 1936287860 (number is overwritten)
printf("%f\n", data.value);
// 18492488542240085843427383574528.000000 (value is overwritten)
}
#include <stdio.h>
#define ANSWER 42
int main() {
int value = 42;
if (value < 1) {
// no
} else if (value == 1) {
// no
} else if (value == ANSWER) {
// yes!
printf("%d is the answer: %d\n", value, ANSWER);
} else {
// no
}
// 42 is the answer: 42
// single line
printf("%s\n", (value != ANSWER) ? "no" : "this is the answer");
// this is the answer
}
#include <stdio.h>
int main() {
for (int i=0; i<5; i++) {
printf("%d ", i);
}
// 0 1 2 3 4
// nested
for (int i=0; i<2; i++) {
for (int j=0; j<2; j++) {
printf("(%d,%d)\n", i, j);
}
}
// (0,0)
// (0,1)
// (1,0)
// (1,1)
// iterate an array of ints
// note first number is length of array
int numbers[6] = { 6, 1, 2, 3, 4, 5 };
for (int i=1; i<numbers[0]; i++) {
printf("%d ", numbers[i]);
}
// 1 2 3 4 5
// iterate an array of ints without knowing length
int items[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < (sizeof(items) / sizeof(items[0])); i++) {
printf("%d ", items[i]);
}
// 1 2 3 4 5
// while
int n = 0;
while (n < 5) {
printf("%d ", n);
// increment
n++;
}
// 0 1 2 3 4
// do-while
n = 0;
do {
printf("%d ", n);
// increment
n++;
} while (n < 5);
// 0 1 2 3 4
}
#include <stdio.h>
enum Item {
LOW = 10,
MEDIUM = 20,
ANSWER = 42,
HIGH = 30
};
const enum Item items[4] = { LOW, MEDIUM, ANSWER, HIGH };
int main() {
// iterate array of enums
for (int i=0; i<(sizeof(items) / sizeof(items[0])); i++) {
switch (items[i]) {
case 10:
printf("%s\n", "LOW");
break;
case 20:
continue;
case 30:
printf("%s\n", "HIGH");
break;
default:
printf("what is this? %u\n", items[i]);
break;
}
}
// LOW
// what is this? 42
// HIGH
}
#include <stdio.h>
// declare function (normally in header file)
int power(int base, int x);
// define function
int power(int base, int x) {
int result = 1;
for (int i=0; i<x; i++) {
result *= base;
}
// success
return result;
}
int main() {
printf("%d\n", power(2, 8));
// 256
}
#include <stdio.h>
int main() {
FILE *file;
// write to file
file = fopen("_file.txt", "w+");
fprintf(file, "%s\n", "Text to write to file.\n Next line.");
// close file
fclose(file);
// read file
file = fopen("_file.txt", "r");
char buffer[255];
// stop read after first space
// note that reading a word removes it from stream
fscanf(file, "%s\n", buffer);
printf("%s\n", buffer);
// Text
// stop read after first newline
fgets(buffer, 255, (FILE*) file);
printf("%s\n", buffer);
// to write to file.
}
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main() {
// a string is an array of chars
char string[] = "This is some text";
printf("%c, %c\n", string[0], string[3]);
// T, s
// built-ins
printf("%c\n", toupper('a'));
// A
printf("%c\n", tolower('A'));
// a
printf("%d\n", isdigit('5'));
// 1
printf("%d\n", islower('a'));
// 1
printf("%d\n", isupper('A'));
// 1
// comparison (lexicographic order)
char answer[] = "the answer is 42";
printf("%d\n", strcmp(answer, "the answer is 42"));
// 0 (equal)
printf("%d\n", strcmp(answer, "the answer is 96"));
// -5 (first argument is smaller than second)
// length of string
printf("%lu\n", strlen(answer));
// 16
}
#include <stdio.h>
#include <stdbool.h>
int main() {
const bool YES = true;
const bool NO = false;
printf("%d\n", YES || NO);
// 1
printf("%d\n", YES && (YES && NO));
// 0
printf("%d\n", !YES);
// 0
printf("%d\n", !(!YES));
// 1
// equality
printf("%d\n", 1 == 1);
// 1
printf("%d\n", 1 != 1);
// 0
printf("%d\n", 1 > 0);
// 1
printf("%d\n", 1 < 0);
// 0
printf("%d\n", 1 >= 1);
// 1
printf("%d\n", 1 <= 0);
// 0
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
extern int errno;
int main() {
// pointer to file
FILE *file;
// error
int errnum;
// open file that does not exist
file = fopen("file_does_not_exist", "rb"); // error returns -1 or NULL
if (file == NULL) {
// store error code
errnum = errno;
// print error code
fprintf(stderr, "error code: %d\n", errno);
// error code: 2
// perror to print string representation of error code
perror("");
// No such file or directory
// strerror to print pointer to string representation of error code
fprintf(stderr, "error opening file: %s\n", strerror(errnum));
// error opening file: No such file or directory
} else {
fclose(file);
}
// exit codes
if (1 == 0) {
// exit as failure (since something must be wrong?)
exit(EXIT_FAILURE);
} else {
exit(EXIT_SUCCESS);
}
}
// filename.h
#ifndef FILENAME_H
#define FILENAME_H
const int number = 10;
#endif
#include <stdio.h>
#include "filename.h"
int main() {
printf("%d\n", number);
// 10
}
Macros replace text before compilation.
#include <stdio.h>
#include <stddef.h>
// stringify error messages
#define error_message(error) \
printf("error: " #error "\n")
// if not defined (used in header files to avoid multiple inclusion)
#ifndef MESSAGE
#define MESSAGE "This is the message if not already defined."
#endif
// parameterized macros
#define square(x) ((x) * (x))
#define max(x, y) ((x) > (y) ? (x) : (y))
int main() {
// built-in macros
printf("%s\n", __FILE__);
// macros.c
printf("%s\n", __DATE__);
// Aug 21 2019
printf("%s\n", __TIME__);
// 01:22:18
printf("%d\n", __LINE__);
// 12
printf("%d\n", __STDC__);
// 1
error_message("This is an error.");
// error: "This is an error."
printf("%s\n", MESSAGE);
// This is the message if not already defined.
printf("%d\n", square(2));
// 4
printf("%d\n", max(4,5));
// 5
}
Pointers store memory addresses.
#include <stdio.h>
#include <stddef.h>
int main() {
int num = 5;
int *ip = NULL; // null pointer
printf("%p\n", ip);
// 0x0 (nil)
// point to address of num
ip = #
printf("%p\n", &num);
// 0x7ffdb78c2664
printf("%p\n", ip);
// 0x7ffdb78c2664
// dereference (get value at address)
printf("%d\n", *ip);
// 5
}