Array Initialization Syntax When Not in a Declaration

Array initialization syntax when not in a declaration

Why is this blocked by Java?

You'd have to ask the Java designers. There might be some subtle grammatical reason for the restriction. Note that some of the array creation / initialization constructs were not in Java 1.0, and (IIRC) were added in Java 1.1.

But "why" is immaterial ... the restriction is there, and you have to live with it.

I know how to work around it, but from time to time it would be simpler.

You can write this:

AClass[] array;
...
array = new AClass[]{object1, object2};

Why can't I use array initialisation syntax separate from array declaration?

The reason there is a difference is that the folks at Microsoft decided to lighten the syntax when declaring and initializing the array in the same statement, but did not add the required syntax to allow you to assign a new array to it later.

This is why this works:

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

but this does not:

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

Could they have added the syntax to allow this? Sure, but they didn't. Most likely they felt that this usecase is so seldom used that it doesn't warrant prioritizing over other features. All new features start with minus 100 points and this probably just didn't rank high enough on the priority list.

Note that { 1, 2, 3, 4, 5 } by itself has no meaning, it can only appear in two places:

  • As part of an array variable declaration:

    int[] a = { 1, 2, 3, 4, 5 };
  • As part of an array creation expression:

    new int[] { 1, 2, 3, 4, 5 }

The number 5, on the other hand, has a meaning everywhere it appears in C#, which is why this works:

int a;
a = 5;

So this is just special syntax the designers of C# decided to support, nothing more.

This syntax is documented in the C# specification, section 12.6 Array Initializers.

How does an unsized array declaration act as a pointer?

In the context of a function parameter declaration (and only in that context), T a[N] and T a[] are the same as T *a; they declare a as a pointer to T, not an array of T.

Chapter and verse:

6.7.6.3 Function declarators (including prototypes)


...

7     A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.

Anywhere else, T a[]; declares a as an array with an as-yet-unspecified size. At this point the declaration is incomplete, and a cannot be used anywhere until a size has been specified, either by specifying it explicitly:

T a[N];

or using an initializer:

T a[] = { /* comma-separated list of initial values */ }

Chapter and verse, again:

6.7.6.2 Array declarators

...

3     If, in the declaration ‘‘T D1’’, D1 has one of the forms:
        D[ type-qualifier-listopt assignment-expressionopt ]
D[ static type-qualifier-listopt assignment-expression ]
D[ type-qualifier-list static assignment-expression ]
D[ type-qualifier-listopt * ]
and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list
T
’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T’’.142)
(See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.)


4     If the size is not present, the array type is an incomplete type. If the size is * instead of
being an expression, the array type is a variable length array type of unspecified size,
which can only be used in declarations or type names with function prototype scope;143)
such arrays are nonetheless complete types. If the size is an integer constant expression
and the element type has a known constant size, the array type is not a variable length
array type; otherwise, the array type is a variable length array type. (Variable length
arrays are a conditional feature that implementations need not support; see 6.10.8.3.)


142) When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared.

143) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3).



...

6.7.9 Initialization

...

22     If an array of unknown size is initialized, its size is determined by the largest indexed
element with an explicit initializer. The array type is completed at the end of its
initializer list.

So, why are arrays as function parameters treated differently than arrays as regular objects? This is why:

6.3.2.1 Lvalues, arrays, and function designators

...

3     Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.

Under most circumstances, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T". If you pass an array expression as an argument to a function, like so:

int foo[10];
...
bar( foo );

what the function bar actually receives is a pointer to int, not a 10-element array of int, so the prototype for bar can be written

void bar( int *f );

"But why..." I hear you starting to ask. I'm getting to it, really.

C was derived from an earlier language called B (go figure), and in B the following things were true:

  • Pointer objects were declared using empty bracket notation - auto p[];. This syntax was kept for pointer function parameter declarations in C.
  • Array declarations set aside memory not only for the array elements, but also for an explicit pointer to the first element, which was bound to the array variable (i.e., auto v[10] would set aside 11 memory cells, 10 for the array contents, and the remaining one to store an offset to the first element).
  • The array subscript operation a[i] was defined as *(a + i) -- you'd offset i elements from the base address stored in the variable a and dereference the result (B was a "typeless" language, so all scalar objects were the same size).

For various reasons, Ritchie got rid of the explicit pointer to the first element of the array, but kept the definition of the subscript operation. So in C, when the compiler sees an array expression that isn't the operand of the sizeof or unary & operators, it replaces that expression with a pointer expression that evaluates to the address of the first element of the array; that way the *(a + i) operation still works the way it did in B, without actually setting aside any storage for that pointer value. However, it means arrays lose their "array-ness" in most circumstances, which will bite you in the ass if you aren't careful.

how to initialize all elements of an array at one go when definition and declaration are separate?

It is simply a part of the language definition that arrays can be initialised, but not directly assigned. You can do what you want in C99 using memcpy() with a compound literal:

int arr[5];

/* ... */

memcpy(&arr, &(int [5]){ 0 }, sizeof arr);

With GCC's typeof extension, you can add a little more safety:

memcpy(&arr, &(typeof(arr)){ 0 }, sizeof arr);

In C89 you must give the source array a name:

{ static const int zero[5] = { 0 }; memcpy(&arr, &zero, sizeof arr); }

Double brace initializer and array

The {} as part of a int foo[] = {1, 2, 3}; is what is termed as brace initialization, and is a shortcut for int foo[] = new int[]{1, 2, 3}; because the new int[] can be inferred from the left hand side.

In the second case foo({1, 2, 3}); no inference of what you intend can be determined as there is no hint as to what the {1, 2, 3} means, so the compiler complains.

When you rewrite it as foo(new int[]{1, 2, 3}); you're telling the compiler what the item in {} is meant to represent.

The compiler won't (and does not try) to auto-interpret the {} expression until it matches - that would lead to potential ambiguities.

There is also this question, which seems to cover exactly the same ground.

As mentioned by @benzonico, it is part of the language specification as to this being a supported syntax, which doesn't include it being used in the invocation of a method.

How to initialize all members of an array to the same value?

Unless that value is 0 (in which case you can omit some part of the initializer
and the corresponding elements will be initialized to 0), there's no easy way.

Don't overlook the obvious solution, though:

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };

Elements with missing values will be initialized to 0:

int myArray[10] = { 1, 2 }; // initialize to 1,2,0,0,0...

So this will initialize all elements to 0:

int myArray[10] = { 0 }; // all elements 0

In C++, an empty initialization list will also initialize every element to 0.
This is not allowed with C until C23:

int myArray[10] = {}; // all elements 0 in C++ and C23

Remember that objects with static storage duration will initialize to 0 if no
initializer is specified:

static int myArray[10]; // all elements 0

And that "0" doesn't necessarily mean "all-bits-zero", so using the above is
better and more portable than memset(). (Floating point values will be
initialized to +0, pointers to null value, etc.)

Array declaration and initialization in Java. Arrays behave differently, when the position of their subscript indices is changed in their declaration

Look at JLS on Arrays:

The [] may appear as part of the type at the beginning of the declaration, or as part of the declarator for a particular variable, or both.

and

Brackets are allowed in declarators as a nod to the tradition of C and C++. The general rules for variable declaration, however, permit brackets to appear on both the type and in declarators, so that the local variable declaration:

float[][] f[][], g[][][], h[];  // Yechh!

is equivalent to the series of declarations:

float[][][][] f;
float[][][][][] g;
float[][][] h;

So for example:

int []p, q[];

is just

int[] p, q[]

which is in fact

int p[]; int q[][]

The rest are all similar.



Related Topics



Leave a reply



Submit