C programming language

See also:

Lack of namespaces

  • Common prefix for identifiers to prevent conflicts:
    • glib: g_
    • Gtk: gtk_
    • Python: Py_, _Py_ (“private”) or PY_ (define)
  • static keyword
    • function only accessible from the current file, not exported
    • variable only accessible from the current file, not exported
    • variable local to a function
  • #include: C preprocessor

Python has the make smelly tool to detect exported symbols which doesn’t start with Py_ nor _Py_.

Issues with types

  • comparison between signed and unsigned: who wins? cast signed to unsigned, or cast unsigned to signed? => obvious integer overflow
  • integer overflow: undefined behaviour, the compiler is free to optimize. Check if an operation will overflow is hard, especially for signed numbers. GCC added builtin functions to check that in a portable and efficient way.
  • portability: int==long==void*, no warning. if int and long have the same size, downcasting a long into an int is allowed, and don’t emit any warning.
  • int stored in void*
  • void* vs intptr_t vs uintptr_t
  • Use “int” to access an item of an array in a loop: the compiler may have to handle integer overflow. size_t or ssize_t preferred here?

Portability

  • Perl still uses C89
  • Python only started to move to C99 with Python 3.6, only CPython is restricted to a subset of C99, the most portable parts of C99…
  • GNU extensions of GCC
  • glibc vs all other C libraries (ex: musl libc and uclibc). GNU extensions, again. Implementation bugs. Errno is sometimes set to 0 on success, sometimes it is not set on failure, it depends on the called function and the libc.
  • <stdint.h> not fully supported in 2017, need some hacks to support old C compilers
  • <stdatomic.h> is still “new” and not well supported by C compilers
  • <stdbool.h>?

Issues with C++

  • C code used in C++ has to be careful with exceptions: need to compile C with -fexceptions?
  • extern { trickery for header files

Strict Aliasing

C aliasing

Change which fixed a crash after the merged of the new dict implementation on a specific platform (don’t recall which one!): https://github.com/python/cpython/commit/186122ead26f3ae4c2bc9f6715d2a29d339fdc5a

Example:

#include <stdint.h>
#include <stdio.h>

uint32_t
swap_words( uint32_t arg )
{
  uint16_t* const volatile sp = (uint16_t*)&arg;
  uint16_t        hi = sp[0];
  uint16_t        lo = sp[1];

  sp[1] = hi;
  sp[0] = lo;

  return (arg);
}

int main(void)
{
    uint32_t x = 0xabcd1234;
    uint32_t y = swap_words(x);
    printf("x=%lx\n", (long unsigned int)x);
    printf("y=%lx\n", (long unsigned int)y);
    return 0;
}

Bug:

$ LANG= gcc -O3 x.c -o x -fstrict-aliasing -Wstrict-aliasing=2 && ./x
x.c: In function 'swap_words':
x.c:7:3: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
   uint16_t* const volatile sp = (uint16_t*)&arg;
   ^~~~~~~~
x=abcd1234
y=abcd1234

GCC warnings

  • -Wall: some warnings
  • -Wall -Wextra: more warnings
  • -Wall -Wextra -O3: even more warnings. Some warnings are only emitted when the compiler optimizes the code, like dead code or unused variables.
  • There are even more. GCC is able to emit even more warnings, but they must be enabled explictly!
    • -fstrict-aliasing -Wstrict-aliasing=2

Platforms #define

  • AIX: #ifdef _AIX
  • FreeBSD: #ifdef __FreeBSD__
  • Linux: #ifdef __linux__
  • NetBSD: #ifdef __NetBSD__
  • Solaris: #ifdef sun
  • Windows: _WIN32 or _WIN64
  • macOS: #ifdef __APPLE__

Compiler defines

  • GCC: #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))
  • Clang: #ifdef __clang__
  • Visual Studio: #if defined(_MSC_VER) && _MSC_VER >= 1800