C without the ++

This page is intended to be a quick reference to C assuming you are already familiar with C++.

1 Summary of differences from C++

  1. C is (mostly) a subset of C++. There’s (almost) no new language to learn, but there are some things to unlearn and some simpler replacements for C++’s more complicated components.

  2. No templates or namespaces.. If the C++ includes a template (e.g. x<y>, it’s not permitted in C. If it contains a namespace (e.g. std::y or using std) it’s also not permitted in C.

  3. No classes. The class and this keywords1 Other class-specific keywords are also not in C++, like friend, public, virtual, and so on. are C++ additions not present in C. A struct can store data like a class, but can’t have member methods. Object orientation can still be implemented using pointers to structs and member function pointers, but this is rarely done in practice.

  4. No function overloading. Each function name can have one and only on definition and signature. If you have a foo(int) you can’t also have a foo(double) or foo(int, int). To help with this, C functions often have suffixes on their names that indicate their argument types, like strtol, and strtoll which differ in having an argument of type long or long long.

    As a seeming exception to this rule, C make more use of variadic functions than C++. These have some number of fixed arguments followed by any number of additional arguments of any type. The fixed argument values are used to dynamically determine the number and type of the remaining arguments. printf is by far the best-known example of this type of function.

  5. No new or delete operators. Instead, memory is allocated with the malloc, calloc, and realloc functions and deallocated with the free and realloc functions. These all handle untyped memory as void *s and need to be told the size of the data in bytes, meaning C code typically has many more sizeof(type) operators than would the corresponding C++ code.

  6. No pass-by-reference. Equivalent semantics can be created by passing pointers and dereferencing them during use at the cost of slightly more verbose code.

  7. No operator overloading. << only means left-shift, never output. + only means addition, never string concatenation. And so on.

  8. Different common library code. Common C++ library functions and types like string, cout, cin, vector, and map do not exist in C. Strings are stored as char *s; I/O is handled with FILE * and functions like fopen, printf, and fread; and if you want a data structure you’ll generally have to program it yourself.

2 Code snippets

The following code snippets are intended to demonstrate some patterns common to C coding.

2.2 Singly-linked list of ints

Several peculiarities with struct definitions like this deserve additional note:

The list is comprised of several nodes, each pointing to the next. We reference the list using a pointer to the first node in the list. Because the functions can modify the list, we pass in a pointer to that pointer; that way the functions can modify *list to change the pointer, and thus which node is first in the list.

#include <stdio.h> // for printf
#include <stdlib.h> // for strtol, malloc, free
#include <string.h> // for strlen

typedef struct list_node_t {
    int val;
    struct list_node_t *next;
} list_node;

/**
 * Given a list and a number, pushes the number onto the head of the list.
 *
 * @param  list  A pointer to the list; the list is a pointer to a node.
 *               Will be modified to point to the new head of the list.
 * @param  n     A number to push onto the list.
 */
void list_push(list_node **list, int n) {
    // allocate memory for a new node
    list_node *node = malloc(sizeof(list_node));
    // initialize its contents
    node->val = n;
    node->next = *list;
    // and make it the new head of the list
    *list = node;
}

/**
 * Given a list, pops off the head of the list.
 *
 * @param  list  A pointer to the list; the list is a pointer to a node.
 *               Will be modified to point to the new head of the list.
 * @return       The value previously in the head of the list -- i.e. 
 *               what (*list)->val was before this function was called.
 */
int list_pop(list_node **list) {
    // copy the head of the list into a variable
    list_node *head = *list;
    // change the list to start after the head
    *list = (*list)->next;
    // copy the value
    int result = head->val;
    // and deallocate the detached head node's memory
    free(head);
    // then return the value
    return result;
}

// Demo: print the length of each command-line argument in reverse order
int main(int argc, char *argv[]) {
    list_node *list = NULL; // make an empty list
    for (int i=0; i<argc; i+=1) {
        // push the length of each argument
        list_push(&list, strlen(argv[i]));
    }
    while (list) { // repeat while list != NULL
        // pop a value and print it
        // list_pop also updates what `list` points to
        printf("arg length: %d\n", list_pop(&list));
    }
    return 0;
}

3 Control constructs

C has the following control constructs:

4 Data types

C has the following datatypes:

The typedef keyword gives a new name to an existing data type and is used extensively in C code to create everything from renamed integer types like size_t and uint8_t to renamed structs with other renamed components inside.

Notably absent in the above list:

5 Literals

In the following code, each x, y, and z are pointers to arrays containing the same byte sequence, but x and y point to arrays in modifiable global memory while z points to an array in read-only global memory.

char x_data[8] = {'S', 'y', 's', 't', 'e', 'm', 's', 0};
char y_data[8] = {83, 121, 115, 101, 109, 115, 0};
char *x = &x_data;
char *y = &y_data;
char *z = "Systems";

6 Operators