C pointers, arrays and initialization pitfalls
I’ve recently came head first into yet another C quirk, regarding arrays, pointers and how they can be initialized, mainly due to certain embedded tool chains lacking one particular warning about pointer initialization. Let’s begin.
As is common C knowledge, character strings are arrays of chars, and can be declared and initialized by a character string indistinctly.
/*Declare and initialize...*/
/*...a char array with a string */
const char char_array_from_string[] = "HELLO";
/*...a char pointer from a string */
const char *char_pointer_from_string = "WORLD";
In a similar way, a char array can be initialized with an array of integers like this:
/*Declare and initialize...*/
/*...a char array with an array of numbers */
const char char_array_from_array[] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x00};
It follows the same initialization should be correct if we want to initialize a char pointer with an array of integers like this:
/*Declare and initialize...*/
/*...a char pointer with an array of numbers */
const char *char_pointer_from_array = {0x45, 0x45, 0x45, 0x45, 0x45, 0x00};
When compiling this code using GCC, the following warning is shown. Other tool chains are not so nice about it.
$ gcc -Wall --pedantic --std=c99 arrays-and-pointers.c
arrays-and-pointers.c: In function ‘main’:
arrays-and-pointers.c:22:42: warning: initialization of ‘const char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
22 | const char *char_pointer_from_array = {0x45, 0x45, 0x45, 0x45, 0x45, 0x00};
| ^~~~
arrays-and-pointers.c:22:42: note: (near initialization for ‘char_pointer_from_array’)
arrays-and-pointers.c:22:48: warning: excess elements in scalar initializer
22 | const char *char_pointer_from_array = {0x45, 0x45, 0x45, 0x45, 0x45, 0x00};
| ^~~~
In this case, the pointer is initialized with the first value of the array instead of the base address of the array. This will segfault your program provided your platform has memory protection, on many embedded platforms it will “just work” by reading garbage values from the wrong pointer address.
If we actually wanted to (for some reason) declare and initialize a pointer from an array literal, in C99 (not ANSI/C89) we would need to explicitly cast the array literal as… and array literal.
/*Declare and initialize...*/
/*...char pointer from an array of numbers explicitly casted as such*/
const char *char_pointer_from_casted_array
= (const char[]) {0x41, 0x41, 0x41, 0x41, 0x41, 0x00};
This last option works, and will not throw any warning when compiled with
-Wall --pedantic
on GCC in C99 mode. Still a quite ugly hack.