-4

How can a[][3] declaration is right where as a[3][] is wrong in C?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Ankan Das
  • 3
  • 1
  • The short answer is: *That's because the standard says so*. As far as why the standard say so, the answer is too long, I think. – R Sahu Apr 19 '21 at 14:49
  • `T a[][3]` would be `T (*a)[3]`. for the other, you may use `T* a[3];` – Jarod42 Apr 19 '21 at 14:50
  • Can you elaborate or send any referance – Ankan Das Apr 19 '21 at 14:51
  • `a[3]` is an array of three objects. How big are those objects when they're identified with just `[]`? There's no way to tell, and you can't make an array of objects unless you know how big they are. – Andrew Henle Apr 19 '21 at 14:53
  • Having `a[][3]` you know the type of `a[0]` and `a[1]`... but you don't know how many `a[??]` there are; having `a[3][]` you know you have `a[0]`, `a[1]`, and `a[2]` but you don't know what each of those 3 elements really is – pmg Apr 19 '21 at 14:54
  • Note, that it might be fine as a declaration, but as definition you must provide both dimensions (either explicitly or implicitly providing an initializer) – Eugene Sh. Apr 19 '21 at 14:56
  • From the [C++11 standard/dcl.array](https://timsong-cpp.github.io/cppwp/n3337/dcl.array#3), *When several “array of” specifications are adjacent, a multidimensional array is created; only the first of the constant expressions that specify the bounds of the arrays may be omitted..* – R Sahu Apr 19 '21 at 14:57
  • @klutt While true, there is a good and logical reason for that. As said, the compiler needs to know the type of the object the array element is. – Eugene Sh. Apr 19 '21 at 14:57
  • `type a[][3]` is not a valid array variable definition unless part of a function parameter list. Or in case an initializer list follows. So what's the context here? Please provide an example. – Lundin Apr 19 '21 at 15:06
  • @Lundin `extern type a[][3];` is also valid (as a declaration). Also `type a[][3];` is valid as a _tentative_ definition. – Ian Abbott Apr 19 '21 at 16:16
  • @EugeneSh. In hindsight, I realize that comment maybe was a bit unfriendly. I'll remove it. – klutt Apr 19 '21 at 16:22
  • Why do all these homework dumps have [3] as the defined dimension? Are all the profs plagging the same text? – Martin James Apr 19 '21 at 17:35
  • @Ankan Das I do not agree that your question was down voted. It is a good question for a beginner. I upvoted your question.:) – Vlad from Moscow Apr 19 '21 at 17:40

3 Answers3

2

You may declare identifiers for objects of incomplete types. For example, consider this declaration:

extern int a[][3];

This tells the compiler that when a is used, it refers to an array of arrays of three int. If it is used, that array must be defined somewhere else. However, to use this array, the compiler does not need to know its size. If a has starting address x and int is four bytes, we can calculate the address of the element a[i][j] by starting with x, adding the size of i arrays of three int (3•4 bytes for each array) and the size of j int (4 bytes each). So the address of a[i][j] is x + 3•4•i + 4•j.

At the place where this array is defined, it cannot be incomplete; the compiler needs to know the complete size in order to know how much memory to reserve.

Although you can declare an identifier for an object of incomplete type, the elements of arrays must have complete type. This rule is stated in C 2018 6.7.6.2 1: “The element type shall not be an incomplete or function type.” In the calculation above, we could not calculate the address of a[i][j] if we did not know the size of the subarrays. So the declaration a[3][] is not allowed.

Note that when defining an array with initializers, we can omit the first dimension. However, it is still specified through the initializers; the compiler examines the initializers and figures out what the first dimension must be. So, when the array is defined, its type is complete. For example, in:

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

the compiler will see there are two subarrays and know that the type of a is int [2][3]. This is not allowed for the other dimensions because it creates complications. Although I have used well-structured braces above, the language permits omitting them, as in:

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

If any dimensions other than the first were omitted, there would be ambiguity in which initializers initialized which elements and what they implied the dimensions were.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

It is important to remember that, while initializing a 2-D array, it is necessary to mention the second (column) dimension, whereas the first dimension (row) is optional -

Thus the declarations,

int arr[ 2 ][ 3 ] = { 12, 34, 23, 45, 56, 45 } ;
int arr[ ][ 3 ] = { 12, 34, 23, 45, 56, 45 } ;

are perfectly acceptable,

whereas,

int arr[ 2 ][ ] = { 12, 34, 23, 45, 56, 45 } ;
int arr[ ][ ] = { 12, 34, 23, 45, 56, 45 } ;

would never work.

Imagine you are a compiler and you are told that there are 3 rows in your 2D array. How would you calculate the position of an element based on this information ? Internally all those elements would be stored in a sequential format, and to calculate that position you'll definitely require col.

arr[i][j] = *( arrr + i * col + j )

Detailed answer to the question Why ? Here

anirudh
  • 1,436
  • 5
  • 18
0

Such a declaration without an initializer

T a[][3];

(where T is some type specifier) declares an array of an incomplete type (the size of the array is unknown). Declared that way an array may not have automatic storage duration. But may be declared in the file scope. In any case the element type of such an array (in the example of the array above the element type is T[3]) shall be complete. That is you may not declare an array like for example

T a[][];

or

T a[3][];

Here is a demonstrative program that shows an allowed array declaration

#include <stdio.h>

int a[][3];

int main(void) 
{
    extern int a[][3];
        
    for ( size_t i = 0; i < 3; i++ )
    {
        a[0][i] = i;
    }

    for ( size_t i = 0; i < 3; i++ )
    {
        printf( "%d ", a[0][i] );
    }
    
    putchar( '\n' );
    
    return 0;
}

The declaration in the file scope

int a[][3];

is a tentative definition and is equivalent tp

int a[1][3];

The compiler can issue a warning that the declaration int a[][3] is assumed to be declared like int a[1][3].

If you will use initializers like for example

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

then the compiler will determine the number of elements in the array by the number of initializers. The above array has the type int [2][3]. Or if you will write for example

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

then the array will have 11 elements. That is the type of the array will be int[11][3].

You may also use an incomplete type of an array as a function parameter like for example

void f( int a[][3] );

The compiler will adjust the parameter to a pointer type to the array element type like

void f( int ( *a )[3] );

That is these two function declarations declare the same one function and the both can be present in a program simultaneously though the compiler can issue a message that there are redundant declarations.

It is interesting to note that a function declaration with a parameter of a variable length array nay be declared for example like

void f( size_t, size_t, int[*][*] );

You also may declare a pointer to an incomplete array type provided that you will not dereference the pointer. Here is a demonstrative program.

#include <stdio.h>

int main(void) 
{
    int ( *p )[];
    
    printf( "sizeof( p ) = %zu\n", sizeof p );
    
    return 0;
}

Its output might look like

sizeof( p ) = 8
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335