Skip to content

C

Background

Textbooks

  • Modern C — Great resource, although somewhat oppinionated. Free ebook is available.

Compiling

GCC

To compiline a single file with no dependencies into an executable, run:

gcc -Wall -o example example.c

To compile multiple files with no external dependencies into an executable, run:

gcc -Wall -o example example1.c example2.c example3.c

To link against external libraries, we can provice the -l{libname} argument after the list of file names:

gcc -Wall -o example example1.c example2.c example3.c -lm

If we do not wish to recomplie the entire project every time we modify a single file, we can compile our code into libraries and then link against those libraries:

gcc -Wall -c square.c
gcc -Wall -o main main.c square.o -lm

We could even generate shared libraries and link against those:

gcc -c -fPIC square.c
gcc -shared -o libsquare.so square.o -lm
gcc -Wall -L. -o main main.c -lsquare
LD_LIBRARY_PATH=. ./main

Note that if we try running ./main above without setting the LD_LIBRARY_PATH environment variable, we are likely to receive the following error:

./main: error while loading shared libraries:
  libsquare.so: cannot open shared object file: No such file or directory

One way to circumvent this is to provide the -Wl,-rpath,'$ORIGIN/.' argument when compiling our program. This will create an executable that will automatically look in the same directory as the executable for the required library files.

gcc -c -fPIC square.c
gcc -shared -o libsquare.so square.o -lm
gcc -Wall -L. -Wl,-rpath,'$ORIGIN/.'  -o main main.c -lsquare
./main

Static vs. dynamic libraries

Standard library

Manpages:

  • man 2 - posix Posix.
  • man 3 - libc C standard library.

IO

One character at a time:

  • putc - Writed a character to stdout. To write a null-terminted string to stdout, use puts. To write to a file, use the f... variants.
  • getc - Read a character from stdin.

Using formatting strings:

  • printf - Write formatted string to stdout. Use fprintf to write formatted string to a file.
  • fscanf - Read a formatted string from stdin.

Multiple lines at a time:

  • fread
  • fwrite

Working with file handles:

  • fopen - Opens a file.
  • fclose - Flushes the stream and closes the file descriptor.
  • opendir - Open directory stream.
  • readdir - Read a directory.
  • closedir - Close a directory.
  • perror - Print a system error message.

File status:

  • stat - Get file status.
#include <stdio.h>

int main() {
    FILE *fp = fopen("input.txt");
    if (fp == NULL) {
        perror("input.txt");
        return 1;
    }
    fclose(fp);

    DIR *dp = *opendir(const char *name);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int fexists(char *file) {
    struct stat statbuf;
    return(stat(file, &statbuf) == 0 && S_ISREG(statbuf.st_mode));
}

Strings

  • strcat - Concatenate two strings.
  • strlen - Length of a string.

File descriptors

https://en.wikipedia.org/wiki/File_descriptor

  • select - Synchronously monitor multiple file descriptors for activity.
  • poll - Synchronously monitor multiple file descriptors for activity.
  • epoll - Asynchronously monitor multiple file descriptors for activity. Used under the hood by libuv, which backs Node.js and Python acync event loops.

See also:

Unix file system

Sockets

Data types

struct

A collection of several attributers. The struct fruit_order part defines fruit_order in the struct "namespace" (so you can write void foo(struct fruit_order order) {...}), while the typedef ... fruit_order defines fruit_order in the global namespace (so you can write void foo(fruit_order order) {...}).

#include <stdio.h>

typedef struct fruit_order {
    char* name;
    char* country;
    struct fruit_order* next;
} fruit_order;


int main(void) {
    // your code goes here
    fruit_order x;
    x.name = "hello world";
    printf("%s\n", x.name);

    fruit_order* y;
    y->name = "goodbye world";
    printf("%s\n", y->name);

    fruit_order* z;
    z->next = y;
    printf("%s\n", z->next->name);

    return 0;
}

# hello world
# goodbye world
# goodbye world

union

Define a single region in memory that can be used to store data with different types. Not neccessarily used much in basic code.

#include <stdio.h>

typedef union {
    short count;
    float weight;
    float volume;
} quantity;

int main() {
    quantity x;
    x.count = 10;
    printf("%i\n", x);
}

enum

typedef enum {
    COUNT, POUNDS, PINTS
} unit_of_measure;

int main() {
    unit_of_pressure unit = COUNT;
}

Dynamic memory allocation

  • malloc
  • free

Parallel computing

#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid == 0)
    {
        // child process
    }
    else if (pid > 0)
    {
        // parent process
    }
    else
    {
        // fork failed
        perror("fork");
        return 1;
    }
}

Signals

  • sigaction - Examine and change a signal action.
int main() {
    struct sigaction sa;
    extern void myhandler();

    sa.sa_handler = myhandler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(15, &sa, NULL);
}

Function pointers

int (*my_int_f)(int) = &abs;
// the & operator can be omitted, but makes clear that the "address of" abs is used here

Casting

  • Declaration mimics use.

Macros

#DEFINE WITH_TAX(x) ((x) * 1.08)
// Parenthesis around x above are needed in order for the macro
// to be evaluated for each argument independently.

int main() {
    double purchase1 = 123.0;
    double purchase2 = 321.0;
    double total = WITH_TAX(purchase1 + purchase2);
    printf("%f\n", total);
}