TheMeaningfulEngineer
TheMeaningfulEngineer

Reputation: 16359

What happens in the background of array initialization in c?

Given that this is legal

uint8_t bytes[4] = { 1, 2, 3, 4 };

And this is not:

uint8_t bytes2[4];
bytes2 = { 1, 2, 3, 4 };

what does { 1, 2, 3, 4 } represent?

Am assuming it's neither an rvalue or an lvalue. A preprocessor code candy that expands to something?

Upvotes: 5

Views: 253

Answers (4)

Lundin
Lundin

Reputation: 213969

{1,2,3,4} is an initializer-list, a particular syntax token that can only be used on the line where the array is declared.

This is regulated purely by the C standard syntax. There's no particular rationale behind it, this is just how the language is defined. In the C syntax, arrays cannot be assigned to, nor copied by assignment.

You can however dodge the syntax restrictions in several ways, to overwrite all values at once. The simplest way is just to create a temporary array and memcpy:

uint8_t tmp[] = {5,6,7,8};
memcpy(bytes, tmp, sizeof bytes);

Alternatively, use a compound literal:

memcpy(bytes, (uint8_t[]){5,6,7,8}, sizeof bytes);

If it makes sense for the specific application, you can also wrap the array in a struct to bypass the syntax restrictions:

typedef struct
{
  uint8_t data [4];
} array_t;

...

array_t bytes = { .data = {1,2,3,4} };
array_t tmp   = { .data = {5,6,7,8} };
bytes = tmp; // works just fine, structs can be copied this way

Upvotes: 7

Sourav Ghosh
Sourav Ghosh

Reputation: 134346

A syntax like {1,2,3,4}; is called brace-enclosed initializer list, it's an initializer. It can be only used for initialization (for array type).

Quoting C11, chapter §6.7.9

  • P11

The initializer for a scalar shall be a single expression,

[an array is not a scalar type, so not applicable for us]

  • P14

An array of character type may be initialized by a character string literal or UTF−8 string literal, optionally enclosed in braces.

[We are not using a string literal here, so also not applicable for us]

  • P16

Otherwise, the initializer for an object that has aggregate or union type shall be a brace-enclosed list of initializers for the elements or named members.

[This is the case of our interest]

and, P17,

Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union.[....]

So, here, the values from the brace enclosed list are not directly "assigned" to the array, they are used to initialize individual members of the array.

OTOH, an array type, is not a modifiable lvalue, so it cannot be assigned. In other words, an array variable cannot be used as the LHS of the assignment operator.

To elaborate, from C11, chapter §6.5.16

assignment operator shall have a modifiable lvalue as its left operand.

Upvotes: 8

chqrlie
chqrlie

Reputation: 144780

{1,2,3,4} is an initializer list. It an be used to specify the initial value of an object with at least 4 elements, be they array elements or structure members including those of nested objects.

You cannot use this syntax to assign values to an array as this:

bytes2 = {1,2,3,4};

Because the syntax is not supported, and arrays are not lvalues.

You can use intializer lists as part of a C99 syntax known as compound literals to create objects and use them as rvalues for assignment, return values or function arguments:

struct quad { int x, y, z, t; };

struct quad p;
p = (struct quad){1,2,3,4};

You still cannot use this for arrays because they are not lvalues, but you can achieve the same effect with a call to memcpy():

uint8_t bytes2[4];
memcpy(bytes2, (uint8_t[4]){1,2,3,4}, sizeof(bytes2));

This statement is compiled by clang as a single intel instruction as can be seen on Godbolt's Compiler Explorer

Upvotes: 3

user2371524
user2371524

Reputation:

Initialization and assignment are fundamentally different things. As for the language C, you just have to accept the fact that they are distinguished, but of course, there's a technical reason it's defined this way:

In many systems, you can have a data segment in your executable file. This segment could be read/write and given an initialized array like

uint8_t foo[] = {1, 2, 3, 4}; // assume this has static storage duration

the compiler could just decide to output this exact byte sequence directly into your executable. So there's no code at all doing an assignment, the data is already there in memory when your program starts.


OTOH, arrays cannot be assigned to (only their individual members). That's how C is defined, and it's sometimes unfortunate.

Upvotes: 5

Related Questions