# array literals in c part of [c programming](/computer/guides/c.html) sometimes it would be useful to assign array content literally by listing the contents. something like this is possible in most scripting languages: ~~~ a = [1, 2, 3, 4] ~~~ but c is limited and the following shows some possible options and alternatives. # option: stack-allocated aggregate arrays ~~~ int a[] = {1, 2, 3}; ~~~ ~~~ int a[3][2] = {{1, 2}, {3, 4}, {5, 6}}; ~~~ * can only be used once where a variable is defined * can only be used for stack arrays, not for heap arrays (for example, allocated by malloc or similar functions) * can be passed to and accessed in sub-functions * can not be accessed after the declaring function has returned unless declared as a static variable * type names like int[4][2] restrict variable and function parameter types, requiring at least the last dimension, for example int[][2]. pointer types allow flexibility but need type casting * #include can be used to keep large definitions in separate files more information: * [array initialization](https://en.cppreference.com/w/c/language/array_initialization) * [array declaration](https://en.cppreference.com/w/c/language/array) # option: compound literals ~~~ int *a = (int[]){1, 2, 3, 4}; ~~~ ~~~ void print_array(int *arr, size_t size); print_array((int[]){1, 2, 3, 4}, 4); ~~~ * available since c99 * only valid within a function, just like stack-allocated aggregate arrays * can be used to initialize pointers or pass arrays to functions ## with heap allocation ~~~ int *a = malloc(4 * sizeof(int)); if (!a) exit(1); memcpy(a, (int[]){1, 2, 3, 4}, 4 * sizeof(int)); ~~~ ## with inline functions ~~~ static inline int *new_array(size_t size, int values[]) { int *a = malloc(size * sizeof(int)); if (a) memcpy(a, values, size * sizeof(int)); return a; } int *a = new_array(4, (int[]){1, 2, 3, 4}); ~~~ # option: structs with arrays ~~~ typedef struct int_array3 { int data[3]; } int_array3_t; int_array3_t x = {.data = {1, 2, 3}}; ~~~ ~~~ x.data[1] int_array3_t y = x; ~~~ this is aggregate initialization with the added benefit that the whole array can be used like a scalar value. * a field name must be declared and used to access elements * stack-allocated structs can be passed to functions, copied, and returned including elements like other simple values - even as a return value * struct field types align to multiples of their size, with padding potentially added between members and at the end to maintain alignment ## flexible array members since c99, the last member of a struct can be an array of unspecified size. ~~~ typedef struct { size_t size; int data[]; } int_array_t; ~~~ however, to make use of this, the struct has to be allocated on the heap. since it is only accessible via a pointer, the resulting struct can not be copied like a scalar value. # option: array creation expressions functions that return heap arrays. ~~~ #include int* int_array4(int a, int b, int c, int d) { int* x = malloc(4 * sizeof(int)); if (!x) return 0; x[0] = a; x[1] = b; x[2] = c; x[3] = d; return x; } ~~~ ~~~ int* e = int_array4(2, 3, 4, 5); ~~~ ## variadic functions this can also be used with a variable number of arguments. ~~~ #include int *new_array(size_t n, ...) { int *a = malloc(n * sizeof(int)); if (!a) return 0; va_list args; va_start(args, n); for (size_t i = 0; i < n; i += 1) { a[i] = va_arg(args, int); } va_end(args); return a; } int *a = new_array(4, 1, 2, 3, 4); ~~~ ## error handling memory allocation can fail, therefore the question of how to handle this failure arises. ### option: return a null pointer this is similar to malloc, which also just returns a null pointer on failure. this has to be checked to avoid null pointer access ### option: exit the program no further error-checking code is needed, nor relevant, with this option. however, this option doesnt compose well, as it exits the calling process - a behavior that is particularly undesirable when the code is used within a library. example, inside the array creating function: ~~~ if (!x) exit(1); ~~~ # option: macro that sets values * the data area must have been declared and allocated separately * is usually not an expression that can return an array pointer like a function * the same macro can be used for any type ~~~ #define array_set4(x, a, b, c, d) x[0] = a; x[1] = b; x[2] = c; x[3] = d; ~~~ ~~~ #include int* x = malloc(4 * sizeof(int)); if (!x) exit(1); array_set4(x, 2, 3, 4, 5); ~~~ ## with a variable number of arguments this allows setting any number of elements. ~~~ #define new_array(...) ((int[]){__VA_ARGS__}) int *a = new_array(1, 2, 3, 4); ~~~ ## with heap allocation ~~~ #define new_array(size, ...) ({ \ int* _arr = malloc(size * sizeof(int)); \ if (_arr) { \ int _tmp[] = {__VA_ARGS__}; \ memcpy(_arr, _tmp, size * sizeof(int));\ } \ _arr; \ }) int *a = new_array(4, 1, 2, 3, 4); ~~~