Initialization of C array at time other than declaration?
Asked Answered
K

7

24

I know in C that I can do the following.

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

Now this is only legal when declaring the array. However I was wondering why this is not legal to do later? But then later in the program it is not legal to do the following.

test[5] = {10, 20, 30, 40, 50}; 

Or something similar. Why is this? I know it's not legal, and I'm not complaining, but could someone give me a more technical explanation of why I can't do this? (i.e. don't just say the C spec does not allow it or something like that)

I'm assuming it has to do something with the time when memory gets allocated on the stack for the array, so at that point C can auto fill in my values, but then why can't it do it later?

Thanks guys

Kathiekathleen answered 5/3, 2012 at 15:2 Comment(6)
So if, hypothetically, it was the case that the C standard just would not allow this (although it would be technically possible), we'd have to make something different up instead? Or would you prefer not to get an answer at all? Just want to be sure.Potaufeu
@NiklasB.: I guess in that case a well-argued educated guess as to why the designers of the C language decided to omit this feature would be fine as an answer.Glyndaglynias
It seems like it would be possible for that to work, but I'm not sure why you would want to do it.Sergei
@mzoz: I guess our issue was only one of vocabulary. An array is an object with an address that is fixed at the point of definition, but unless it is defined as const, the value of its elements can be changed after the definition. Arrays are not lvalues, so talking about the value of an array is somewhat confusing and the C Standard carefully avoid doing so, but it is IMHO even more confusing to use value to refer to the array's address, despite the fact that arrays decay as pointers to their first element when used in most expression contexts...Edea
@mzoz: ... Other aggregates such as struct indeed have a value and the C99 trick can also be used to change this value in a single assignment.Edea
This behaviour is not unique to C, but also present in e.g. Java. See this answer to String array initialization in JavaCierracig
F
21

It's not just arrays, you cannot provide an initializer for anything at any point other than in a definition. People sometimes refer to the second statement of something like int i; i = 0; as "initializing i". In fact it's assigning to i, which previously holds an indeterminate value because it wasn't initialized. It's very rarely confusing to call this "initializing", but as far as the language is concerned there's no initializer there.

Assignment and initialization are separate things to the language, even though they both use the = character. Arrays are not assignable.

The reason arrays are not assignable is covered elsewhere, for example Why does C++ support memberwise assignment of arrays within structs, but not generally?. The short answer is, "historical reasons". I don't think there's any killer technical reason why the language could not be changed to allow array assignment.

There's a secondary issue, that grammatically {1, 2, 3, 4, 5} is an initializer, not an array literal, and hence could not be used in assignment even if arrays were assignable. I'm not sure exactly why C89 doesn't have array literals, probably just nobody got around to requiring them. C99 introduces a syntax for "compound literals" in general and array literals in particular: (int[]) {1, 2, 3, 4, 5}. You still can't assign to an array from it.

Fluvial answered 5/3, 2012 at 15:20 Comment(0)
N
8

The point is that using int array[]={ } is declaring and initializing the object that you have created.

You can actually assign values to an array after it has been declared:

int array[5];
array[0] = 1, array[1] = 2, ...

What you were doing was assigning several values to one single array entry:

array[5] = {1,2,3,4,5}; // array[5] can only contain one value

This would be legal instead:

array[5] = 6;

Hope this makes sense. Just a question of syntax.

Notions answered 5/3, 2012 at 15:17 Comment(1)
array[5] = 6; would not be legal as array is defined with 5 elements :)Edea
D
5

Note that the C99 compound literal allows you to 'pass arrays to functions':

int your_func(int *test)
{
    ...use test as an array...
}

void other_func(void)
{
    int x = rand();
    if (your_func((int[]){ 0, 1, 2, x, 3, 4 }) > 0 ||
        your_func((int[]){ 9, x, 8, 7, 6, 5 }) > 0)
        ...whatever...
}

This isn't the same as re-initializing an array with different values, but it may be sufficiently close that it works for you.

Duello answered 5/3, 2012 at 15:30 Comment(0)
K
5

Here's The Solution

//Declaration
int a[5];
int a_temp[5] = {1, 2, 3, 4, 5 };
memcpy(a, a_temp, sizeof(a));
//Now array a have values 1, 2, 3, 4, 5
Karlsbad answered 28/10, 2016 at 14:7 Comment(0)
N
0

The reason for that at the surface is that arrays are almost everywhere converted to a pointer to the first element. If you have two arrays

double A[5];
double B[5];

in an expression such as

A = B;

both are already converted to pointers. The A on the left is in particular not an "lvalue" so you can't assign to it.

This sounds like a lame excuse, but my guess is that historically it just happened like that. C (and C++ with it) was trapped in an early syntax choice and if you want to stay compatible with legacy code there is probably not much way out of it.

Nobel answered 5/3, 2012 at 16:0 Comment(4)
Do you have any citation that this was the reason behind the design decision?Merkle
@DavidHeffernan, no unfortunately not, otherwise I would have given it. In C11, 6.7.1 p6 (plus footnote) still shows that this obligation to convert to a pointer is taken as very basic feature of arrays. I think this is unfortunate, in particular assignment and designators would be good features to have. But it would be unrealistic to expect such a modification to happen.Nobel
But without citation this is speculation. And I can't actually see a technical reason why assignment of arrays could not be implemented.Merkle
@DavidHeffernan, I said it like that, I thought in English the usage of "I guess" has exactly that meaning, marking a statement as speculative. And I can't see a real technical reason either, but that that they probably wanted the interpretation be as simple as possible. If you'd like we could create an initiative "make C arrays first class citizens", it should be not too difficult to come up with some proposals, as I said, for designators and assignment, and perhaps for initialization, too.Nobel
E
0

You want to separate the definition and initialization for the array test. This is possible if test is not defined as const: you can assign values to each array element explicitly:

int test[5];  // definition

...

test[0] = 10;
test[1] = 20;
test[2] = 30;
test[3] = 40;
test[4] = 50;

Your syntax test[5] = {10, 20, 30, 40, 50}; does not work for multiple reasons:

  • test[5] refers to an element beyond the end of the array
  • {10, 20, 30, 40, 50} can only be used as an initializer in an object definition. In other contexts, it is parsed as a block, which cannot appear as the right operand of the assignment operator =, and the contents is invalid for a block anyway.

Redefining test as int test[5] = { 10, 20, 30, 40, 50 }; would not work either, because this would define a separate object test, which is not allowed in the same scope (but allowed in an inner scope, albeit confusing and error prone in practice).

Since C99, you can define compound objects and use one for your purpose:

    memcpy(test, ((int[]){ 10, 20, 30, 40, 50 }), sizeof(test));

(int[]){ 10, 20, 30, 40, 50 } defines an initialized array of 5 ints. Arrays are not lvalues, so you cannot use the assignment operator =, but you can use memcpy. The extra set of () is needed because memcpy happens to be a macro on some systems and unlike parentheses (), curly brackets {} do not hide commas for macro argument scanning.

Here is an example with a definition of test and a later assignment of a different set of values in a single call using a C99 compound literal:

#include <stdio.h>
#include <string.h>

void print_array(const char *msg, int *a, size_t len) {
    if (msg)
        printf("%s:", msg);
    for (size_t i = 0; i < len; i++)
        printf(" %d", a[i]);
    printf("\n");
}

int main() {
    int test[5] = { 1, 2, 3, 4, 5 };

    print_array("initial values", test, sizeof(test) / sizeof(*test));

    memcpy(test, ((int[]){ 10, 20, 30, 40, 50 }), sizeof(test));

    print_array("new values", test, sizeof(test) / sizeof(*test));
    return 0;
}

Output:

initial values: 1 2 3 4 5
new values: 10 20 30 40 50
Edea answered 2/7, 2022 at 10:22 Comment(0)
O
0

There are several different things going on here.

One is that, strictly speaking, initialization is quite a bit different than assignment. They look pretty similar, but they're almost completely different "under the hood" (so to speak).

And then one of the big differences between initialization and assignment is that there are some special things you can do in initializations. The biggest one is the one you're asking about: the list-of-values syntax {1, 2, 3, 4, 5} is (with a special exception which we'll get to) only valid in initializers.

There's also the issue that you can't assign to arrays at all. After you do

int test[5];

or

int test[5] = whatever;

you can't later do the assignment

test = anything_else;                                   /* WRONG */

at all. There is nothing you can put on the right-hand sign of that assignment operator (that is, in the place of "anything_else") that will assign new values to all the elements of the array test in one fell swoop. Arrays are "second class citizens" in C, and part of that second-class status is that arrays can't be assigned.

In particular, even if you tried to do

int test[] = {1, 2, 3, 4, 5};
int newvals[] = {6, 7, 8, 9, 10};
test = newvals;                                         /* WRONG */

this would not work. One compiler I tried it with just now says "array type 'int [5]' is not assignable".

If you want to copy values from one array to another, you can do it with the standard library function memcpy, if you want. So this would work:

memcpy(test, newvals, 5 * sizeof(int));

This basically just does a bitwise copy of 5 int's worth of data from newvals to test.

So, you might be wondering, is there a way to skip the explicit newvals array and do something like

memcpy(test, {6, 7, 8, 9, 10}, 5 * sizeof(int));        /* WRONG */

Once upon a time, the answer was "no". Once upon a time, there was just no way to have a "temporary array value" like {6, 7, 8, 9, 10}. The only place you could put the list-of-values syntax {6, 7, 8, 9, 10} was, as I said earlier, in an initialization. That's because, in an initialization, you're not creating a "temporary array value", what you're doing is supplying the initial values for an actual array value.

However, recent versions of C do have a way to do this. (And it's not really all that "recent" any more; I think it's been at least ten years by now.) It looks like this:

memcpy(test, (int [5]){6, 7, 8, 9, 10}, 5 * sizeof(int));

Notice that you can't just say {6, 7, 8, 9, 10}; you have to put that (int [5]) thing in front of it, that looks like a cast, to tell the compiler what kind of temporary structure you intend to represent with the stuff between the braces {}.

You can read more about the "second class" status of arrays, and the inability to assign them, in this answer.

Orvie answered 3/7, 2022 at 16:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.