0

I was going through the answers to this question Why is it allowed to omit the first dimension, but not the other dimensions when declaring a multi-dimensonal array? and some other questions as well and I understood that we can omit the first dimension but other dimensions are inevitable to be specified.
To my surprise, the following code executed perfectly fine:

#include<stdio.h>
void f1(int (*p)[]){
    //SOMETHING
}
int main()
{
    int a[3][3]={1,2,3,4,5,6,7,8,9};
    f1(a);
}

However, if I use the pointer p to print some value like

printf("%d",p[0][1]);

It gives an error message saying:

error: invalid use of array with unspecified bounds

Why does C allow such kind of declaration?
If it is very certain that it is going to throw an error on pointer's use then why is it not throwing error at the time of declaration itself? Why wait for pointer's use to throw an error?
Is there any specific reason for allowing such a declaration?

Vinay Yadav
  • 1,206
  • 1
  • 10
  • 19

2 Answers2

4

The expression p[0][1] contains p[0]. p[0] is defined to be equivalent to *(p+0). This uses the addition of a pointer and an integer.

There is a rule that addition with a pointer requires a pointer to a complete object type. This is because calculating how many bytes the address must be changed by usually requires knowing the size of the objects being pointed to. It does not when adding zero, but there is no exception in the rule for this.

The type of p is “pointer to array of unknown number of int. Because the number of elements in the array is unknown, the type is incomplete. So the addition is not allowed.

Interestingly, (*p)[1] is allowed, even though it refers to the same thing p[0][1] would. It is allowed because calculating *p does not require pointer addition. p points to an array, so we know where that array starts even though we do not know its size.

The need to know object size for pointer arithmetic is why the elements of an array must have a complete type (so, if those elements are themselves arrays, their length must be given, to make them complete, and so all inner dimensions of arrays must be known). But pointers are allowed to point to incomplete types, so, when a pointer points to an array, it is not required that the array dimension be known.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

I understood that we can omit the first dimension but other dimensions are inevitable to be specified.

Yes. When you declare an array, the element type must be a complete type. This is a formal constraint specified in paragraph 6.7.6.2/1 of the standard, so conforming compilers must diagnose violations.

But so what? You're asking about the declaration of parameter p in ...

void f1(int (*p)[]){

... which has a pointer type. Specifically, it is a pointer to an array of an unknown number of ints. That array type omits only the first dimension, which, as you know, is allowed, though it makes the type an incomplete one. Pointers to incomplete types are allowed (and are themselves complete types), with type void * being the poster child. Furthermore, type int(*)[] is compatible with the type of the argument you are passing, which is int(*)[3] (not int[3][3]).

Why does C allow such kind of declaration?

Why shouldn't it? I mean, incomplete types are a bit weird, but they serve a useful purpose. The parameter declaration you present is consistent with C's requirements.

If it is very certain that it is going to throw an error on pointer's use then why is it not throwing error at the time of declaration itself? Why wait for pointer's use to throw an error?

Because only some uses of the pointer are erroneous. You can convert it to an integer or to another pointer type, for instance, or assign it to a variable of compatible type. In fact, although C does not define the behavior of dereferencing pointers to other varieties of incomplete types, it does allow you to dereference pointers to incomplete array types.

Is there any specific reason for allowing such a declaration?

Consistency? Usefulness? Your question seems predicated on the belief that allowing it is inconsistent, useless, or both, but it is neither. C has a concept of incomplete types. It allows pointers to incomplete types, which is very important to the language, and it does not discriminate among incomplete types in this regard. C also makes no special-case rule against pointers to incomplete types being the types of function parameters.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • That's a good approach to it as well. I had also thought about explaining the 6.3.2.1 array/pointer conversion aspect of the reason the first dimension is omitted. If the remaining dimensions were omitted, then you have the pointer to incomplete type@Eric discussed. – David C. Rankin Aug 28 '20 at 03:58