2024-07-03

array literals in c

part of c programming

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: on-stack arrays with literal initializer

example:

int a[] = {1, 2, 3};
  • can only be used once where a variable is defined

  • can only be used for stack arrays, not for heap arrays (allocated by malloc, etc)

  • 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] come with restrictions on the types that variables and function parameters using the type can have. at least the last dimension has to be provided in types, for example int arr[][2]. pointers can be used for more flexibility

  • #include could be used to keep large definitions in a separate file and include the definitions where needed

this method can also be used to define nested arrays:

int a[3][2] = {{1, 2}, {3, 4}, {5, 6}};

more information:

option: structs with arrays

struct int_array3 {
  int data[3];
}

struct int_array3 x = {.data = {1, 2, 3}}
  • if the struct is allocated on the stack, it can be passed to functions and returned like scalar values
  • basic types are aligned and, given that it is an single-element structs, no padding is added

option: functions as literal array expressions

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);

memory allocation can fail, therefore the question of how to handle this failure arises.

option: return a null pointer

this interface is similar to malloc, which also just returns a null pointer on failure.

option: exit the program

no further error checking code is needed, nor relevant, with this option. this option does not compose well because it exits the using process, which tends to be particularly undesirable when the code is used as part of a library.

example, inside the array creating function:

if (!x) exit(1);

option: macro that checks the result and uses goto

needs a temporary variable and a local goto label

int* x = int_array4(1, 2, 3, 4);
if (!x) goto exit;

option: macro that sets values

  • array must have been declared and allocated separately
  • is not an expression that returns an array pointer in place
  • same macro can be used for multiple types (int, unsigned int, float, etc)
#define array_set4(x, a, b, c, d) x[0] = a; x[1] = b; x[2] = c; x[3] = d;
int* x = malloc(4 * sizeof(int));
if (!x) exit(1);
array_set4(x, 2, 3, 4, 5);