Skip to content

C programming

A quick review for C programming

Tutorial

Hello world

// Hello world
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

compile it with gcc:

gcc -o hello.out hello.c
./hello.out

Operator Precedence

Precedence Operator Description Associativity
1 ++ -- Suffix/postfix increment and decrement Left-to-right
() Function call
[] Array subscripting
. Structure and union member access
-> Structure and union member access through pointer
(*type*){*list*} Compound literal(C99)
2 ++ -- Prefix increment and decrement[note 1] Right-to-left
+ - Unary plus and minus
! ~ Logical NOT and bitwise NOT
(*type*) Cast
* Indirection (dereference)
& Address-of
sizeof Size-of[note 2]
_Alignof Alignment requirement(C11)
3 * / % Multiplication, division, and remainder Left-to-right
4 + - Addition and subtraction
5 << >> Bitwise left shift and right shift
6 < <= For relational operators < and ≤ respectively
> >= For relational operators > and ≥ respectively
7 == != For relational = and ≠ respectively
8 & Bitwise AND
9 ^ Bitwise XOR (exclusive or)
10 | Bitwise OR (inclusive or)
11 && Logical AND
12 || Logical OR
13 ?: Ternary conditional[note 3] Right-to-left
14[note 4] = Simple assignment
+= -= Assignment by sum and difference
*= /= %= Assignment by product, quotient, and remainder
<<= >>= Assignment by bitwise left shift and right shift
&= ^= |= Assignment by bitwise AND, XOR, and OR
15 , Comma Left-to-right
  • % in c keeps the sign:
-1 % 3 = -1
-2 % 3 = -2

(different from python, which always return a positive integer)

  • bit operators are even slower than compare operator !
a|b == c; // a | (b == c)
(a|b) == c; // (a|b) == c    

Function prototype

/*
If a function was not previously declared, compiler would not be able to perform compile-time validity.
We need function prototyp in this case.
*/
double max(double a, double b);// function prototype

int main() {
    double c = max(10, 12);
}

double max(double a, double b){
  if (a > b)
        return a;
    else
        return b;
}

Implicit type conversion

bool -> char -> short int -> int -> 
unsigned int -> long -> unsigned -> 
long long -> float -> double -> long double

Pointer

int i=0;
int* p = &i; // & is for getting address of i, p will be address, *p can get value of i
int j = *p; // j=0
printf("%p\n", p); // 0x7ffeefbff56c
printf("%p\n", &i); // 0x7ffeefbff56c
printf("%d\n", *p); // 0

int* p, q //This is the same as int *p, q. p is a pointer, and q is an int variable
p = NULL; // NULL pointer
int **pp = &p;

Some concepts:

  • null pointer: pointing to NULL. int *p = NULL; static int* p;
  • wild pointer: uninitialized pointer, may point to a random address or even not valid. int *p;
  • dangling pointer: pointint to a deleted address.

Array

int a[5]; // a is a ptr to the first element of this array
int* pa = &a[0]; // *pa == *a == a[0];
printf("%p\n", a); // 0x7ffeefbff560
printf("%p\n", &a); // 0x7ffeefbff560
printf("%p\n", &a[0]); // 0x7ffeefbff560


int b[3][3]; // b is a ptr to ptr
int** pb = &b[0][0]; // **pb == **b == b[0]

// initalize
int x[] = {1,2,3};  // x has type int[3] and holds 1,2,3
int y[5] = {1,2,3}; // y has type int[5] and holds 1,2,3,0,0
int z[3] = {0};     // z has type int[3] and holds all zeroes

int z[][5] = {
  {0, 1, 2, 3, 4},
  {2, 3, 4, 5, 6},
}; // Must give colum num...

int z[2][2] = {0, 1, 2, 3}; // {{0, 1}, {2, 3}}

int y[4][3] = { // array of 4 arrays of 3 ints each (4x3 matrix)
    { 1 },      // row 0 initialized to {1, 0, 0}
    { 0, 1 },   // row 1 initialized to {0, 1, 0}
    { [2]=1 },  // row 2 initialized to {0, 0, 1}
};              // row 3 initialized to {0, 0, 0}
Size of c arrays:
// pitfall: sizeof
// sizeof only works if the array is on the stack (declared in the same namespace)
// if the array is passed to a function, it will be recognized as a pointer, thus losing size information.

#include <stdio.h>
#include <stdlib.h>

void printSizeOf(int intArray[]);
void printLength(int intArray[]);

int main(int argc, char* argv[])
{
    int array[] = { 0, 1, 2, 3, 4, 5, 6 };

    printf("sizeof of array: %d\n", (int) sizeof(array)); 
    // sizeof of array: 28
    printSizeOf(array); 
    // sizeof of parameter: 8

    printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) )); 
    // Length of array: 7
    printLength(array); 
    // Length of parameter: 2
}

void printSizeOf(int intArray[])
{
    printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
}

void printLength(int intArray[])
{
    printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
}
Pass & Return array in c function:
  • pass: pointer + size is the most recommended way.

  • return: can only return dynamically allocated array's pointer.

instead of return a pointer, it's better to pass the return value as parameter and return void.

  • 2d array can be folded as 1d, and fix index liike i * N + j
const int M = 3;
const int N = 3;

// pass 1d
void f(int* arr, int size) {} // dynamic, recommended
void f(int arr[], int size) {} // same
void f(int arr[2]) {} // must be a const size

// return 1d
int* f(int size) {
    // malloc 1d array
    int* res = (int*)malloc(size * sizeof(int)); // don't forget free(res)
    return res; 
}

// wrong! never declare stack variable in function
int* f(int size) {
    int res[size];
    return res; // buggy, res is only valid inside the function!
}

// pass 2d
void f(int** arr, int m, int n) { // dynamic, recommended.
    // ...
}

const int M = 10;
const int N = 10;
void f(int arr[M][N]) { // must use two consts
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < N; j++) {
            // ...
        }
    }
}

const int N = 10;
void f(int arr[][N], int m) { // the second dim is necessary!
    // ...    
} 

// return 2d
int** f(int h, int w) {
    // ...
}


int main() {
    // declare 2d array in stack
    int a[M][N];

    // malloc 2d array in heap
    int** b;
    b = (int**)malloc(M * sizeof(int*));
    for (int i = 0; i < M; i++) 
        b[i] = (int*)malloc(N * sizeof(int));

}

Chars

char* s;
s = "string"; 

char s[] = "string";

// char to int
char c = '1';
int ic = c; // 49, ascii code of '1'
int i = c - '0'; // 1

// int to char
int i = 1;
char c = i + '0'; // '1'

// chars to int, do not recommend, use c++ string.
char s[] = "12";
int i = atoi(s); // 12

pass-by-reference in function args

// this doesn't work, because calls in C are pass-by-value.
void inc(int x) { x += 1; }
// this works, by pass-by-reference
void inc(int &x) { x += 1; }
// this also works, by pointer, but need to pass in ptr.
void int(int *px) { *px += 1; }

CMD args

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    while(argc--) printf("%s\n", *argv++);
    return 0;
}

Struct

// basic
struct mystr {
    int len;
    char * s;
};
struct mystr s1;

// typedef
typedef struct mystr {
    int len;
} mystr;
mystr s1;

int len = s1.len;

mystr* ps1 = &s1;
int len = ps1->len;


// annonymous
struct {
    int len;
} s1;

typedef

typedef unsigned char BYTE;
BYTE b;

Macro

#define ONE 1
#undef ONE

#ifdef 
#ifndef
#endif

#pragma

IO

#include <stdio.h>

int main() {
    int x = 0;
    printf("%d", x);
    char* s = "string";
    printf("%s", s);

    char str[100];
    int i;
    scanf("%s %d", str, &i);
    printf( "\nYou entered: %s %d \n", str, i);
}

C Library

#include

typedef long long unsigned int size_t // x64

FILE *stdin = (FILE *) &_IO_2_1_stdin_;
FILE *stdout = (FILE *) &_IO_2_1_stdout_;
FILE *stderr = (FILE *) &_IO_2_1_stderr_;

FILE *fopen(const char* filename, const char* mode);
int getchar(void);
int putchar(int char);
int printf(const char *format, ...);
int scanf(const char *format, ...);

#include

double exp(double x);
double log(double x); // log_e 
double log2(double x);
double log10(double x);
double pow(double x, double y);
double sqrt(double x);
double fabs(double x);
double floor(double x);
double ceil(double x);

#include

double atof(const char *str);
int atoi(const char *str);

double strtod(const char *str, char **endptr);
long int strtol(const char *str, char **endptr, int base);

void *calloc(size_t nitems, size_t size);
void *malloc(size_t size);
void free(void *ptr);

void exit(int status);

int abs(int x);
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*));

int rand(void);
void srand(unsigned int seed);
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
   char *str;

   str = (char *) malloc(15 * sizeof(char));
   strcpy(str, "runoob");
   printf("String = %s,  Address = %u\n", str, str);
   free(str);

   return 0;
}

#include

void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *str, int c, size_t n);
int memcmp(const void *str1, const void *str2, size_t n);

char *strcat(char *dest, const char *src);
int strcmp(const char *str1, const char *str2);
char *strcpy(char *dest, const char *src);
size_t strlen(const char *str);
char *strstr(const char *haystack, const char *needle);

Reference

Kai Weng's C programming course

Kiui's notebook: https://note.kiui.moe/