C programming language¶
See also:
i++ and –i¶
x = ++i;is equivalent of++i; x = i;ori += 1; x = i;x = i++;is equivalent ofx = i; i++;orx = i; i += 1;x = --i;is equivalent of--i; x = i;ori -= 1; x = i;x = i--;is equivalent ofx = i; i--;orx = i; i -= 1;
Lack of namespaces¶
Common prefix for identifiers to prevent conflicts:
glib:
g_Gtk:
gtk_Python:
Py_,_Py_(“private”) orPY_(define)
statickeywordfunction 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
Undefined Behaviour¶
memcpy(dst, src, 0)is safe (if dst is a valid address): https://stackoverflow.com/questions/29844298/is-it-legal-to-call-memcpy-with-zero-length-on-a-pointer-just-past-the-end-of-anWhat Every C Programmer Should Know About Undefined Behavior #1/3
What Every C Programmer Should Know About Undefined Behavior #2/3
What Every C Programmer Should Know About Undefined Behavior #3/3
GCC Undefined Behavior Sanitizer,
ubsan:-fsanitize=undefined
Strict Aliasing¶
Understanding Strict Aliasing (Mike Acton, June 1, 2006)
Demystifying The Restrict Keyword (Mike Acton, May 29, 2006)
Type punning isn’t funny: Using pointers to recast in C is bad. (April, 2008) by Matt Gallagher
Magic UNION_CAST() macro:
#define UNION_CAST(x, destType) \
(((union {__typeof__(x) a; destType b;})x).b)
stdint.h types¶
<stdint.h>
int32_t
uint64_t
printf/scanf:
PRIu64: uint64_t
PRIu64 is a string, ex:
"llu".
C aliasing¶
-fstrict-aliasingvs-fno-strict-aliasing__restrict__(or justrestrict)clang “bug”: aliasing bug with union in clang 4.0
Understanding Strict Aliasing (Mike Acton, June 1, 2006)
Demystifying The Restrict Keyword (Mike Acton, May 29, 2006)
Type-punning and strict-aliasing (Thiago Macieira, June 2011)
Python 2 slower: the C code base doesn’t respect strict aliasing and so must be compiled with
-fno-strict-aliasing(to avoid bugs when the compiler optimizes the code) which is inefficient. The structure of Python C type has been deeply rewritten to fix the root cause.gcc-help: Missed optimization when using a structure (2013-04)
Detecting Strict Aliasing Violations by P. Cuoq et. al.
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
volatile¶
volatile is discouraged in the Linux kernel in favor of smaller locks: https://github.com/torvalds/linux/blob/master/Documentation/process/volatile-considered-harmful.rst
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 _AIXFreeBSD:
#ifdef __FreeBSD__HP-UX:
#ifdef __hpuxLinux:
#ifdef __linux__NetBSD:
#ifdef __NetBSD__Solaris:
#ifdef sunWindows:
_WIN32or_WIN64macOS:
#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
GCC flags¶
Compile in 32-bit mode on Fedora¶
dnf install glibc-devel.i686
gcc -m32
Example:
$ echo 'int main() { return sizeof(void *); }' > x.c
$ gcc x.c -o x -m32 && ./x; echo $?
4
Configure in 32-bit:
./configure CFLAGS="-m32" LDFLAGS="-m32" && make
Build Python in 32-bit mode¶
Building Python requires more dependencies:
dnf install -y bzip2-devel.i686 libffi-devel.i686 libuuid-devel.i686 ncurses-devel.i686 openssl-devel.i686 readline-devel.i686 xz-devel.i686 zlib-ng-compat-devel.i686
Fix pyconfig.h:
sed -i -e 's!#define PY_HAVE_PERF_TRAMPOLINE 1!#undef PY_HAVE_PERF_TRAMPOLINE!g' pyconfig.h
Compiler and linker options¶
C macros (preprocessor)¶
typeof(expr): C99offsetof(type, member):<stddef.h>, C89_builtin_types_compatible_p(type1, type2): true if type1 is type2; GCC and clang.
Magic BUILD_ASSERT_EXPR() macro by Rusty Russell:
#define BUILD_ASSERT_EXPR(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
Magic ARRAY_LENGTH() macro by Rusty Russell,
compilation error with GCC if the argument is not an array but a pointer:
#if (defined(__GNUC__) && !defined(__STRICT_ANSI__) && \
(((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) || (__GNUC__ >= 4)))
/* Two gcc extensions.
&a[0] degrades to a pointer: a different type from an array */
#define ARRAY_LENGTH(array) \
(sizeof(array) / sizeof((array)[0]) \
+ BUILD_ASSERT_EXPR(!__builtin_types_compatible_p(typeof(array), \
typeof(&(array)[0]))))
#else
#define ARRAY_LENGTH(array) \
(sizeof(array) / sizeof((array)[0]))
#endif
Is a type signed or unsigned?
#define IS_TYPE_UNSIGNED(type) (((type)0 - 1) > 0)
Libc¶
__GLIBC__: GNU libc
Convert to a string¶
STRINGIFY(expr) macro:
#define _XSTRINGIFY(x) #x
/* Convert the argument to a string. For example, STRINGIFY(123) is replaced
with "123" by the preprocessor. Defines are also replaced by their value.
For example STRINGIFY(__LINE__) is replaced by the line number, not
by "__LINE__". */
#define STRINGIFY(x) _XSTRINGIFY(x)
<sys/cdefs.h> defines two macros:
#define __CONCAT(x,y) x ## y
#define __STRING(x) #x
But __CONCAT and __STRING are not portable. For example, NetBSD says
“only works with ANSI C”. Comment on Linux: “For these things, GCC
behaves the ANSI way normally, and the non-ANSI way under -traditional.”
const¶
First read: Why const Doesn’t Make C Code Faster (August 2019) by Simon Arneaud.
General:
const int *xis the same thanint const *x: it only matters ifconstis before or after*
Single *, constant x, but *x is mutable:
int * const x = (int * const)1;
x = (int * const)2; /* compilation error */
*x = 3; /* ok */
Single *, constant *x, but x is mutable:
const int *x = (const int *)1;
x = (const int *)2; /* ok */
*x = 3; /* compilation error */
Single *, constant x and constant *x:
const int * const x = (const int * const)1;
x = (const int * const)2; /* compilation error */
*x = 3; /* compilation error */
Problem of casting char ** to const char **:
http://c-faq.com/ansi/constmismatch.html
GCC: use -Wcast-qual option.
Atomic variables¶
C11 <stdatomic.h>. Supported by:
GCC
clang
MSC: Interlocked functions like InterlockedAdd. Only support 32-bit and 64-bit variables.
Linux kernel: Detecting and handling split locks. Only x86 and x86-64 architectures are impacted. Other architectures (such as ARM or RISC-V) do not allow misaligned memory access. Follow-up: VMX virtualization runs afoul of split-lock detection.
Generic functions:
__atomic_load_n(&var, __ATOMIC_RELAXED)__atomic_add_fetch(&var, 1, __ATOMIC_SEQ_CST)__atomic_fetch_add(&var, 1, __ATOMIC_SEQ_CST)etc.
GCC: Built-in Functions for Memory Model Aware Atomic Operations
Memory order:
__ATOMIC_SEQ_CST: Enforces total ordering with all other __ATOMIC_SEQ_CST operations.
__ATOMIC_RELAXED: Implies no inter-thread ordering constraints.
See also GCC: Legacy __sync Built-in Functions for Atomic Memory Access like
__sync_fetch_and_add(&var, 1).
C FAQ¶
Thread Local Storage (TLS)¶
GCC and clang extension:
__thread. Example:__thread int i;. See GCC Thread Local documentation. GCC and clang use the FS register on x86-64.pthread
pthread_getspecific(), pthread_setspecific()
pthread_key_create(), pthread_key_delete()
Modern C¶
LLVM clang 15 (release notes):
-Wimplicit-function-declarationis now treated as error in C99 and later.