which one is the correct type of an array variable? (for using with pointers in C)

In most cases whenever an array name is used in an expression, it decays into a pointer to it's first element. Therefore int *ptr = &data[0]; is 100% equivalent to int *ptr = data;.

As for accessing individual items of an array, or of an array pointed at through a pointer to the first element, arr[i] is 100% equivalent to *(arr + i), the latter is just needlessly hard to read. See Do pointers support "array style indexing"?

However, one exception to the mentioned array decay rule is when an array name is used as operand to the & operator. In that case it does not decay - instead we get a pointer to "the array as whole". In your case int *ptr = &data; is an invalid pointer conversion because &data is of type int(*)[3], a pointer to array.

Please note however that in the expression &data[i], operator precedence says that [] "binds tighter" to the data operand than &. So it is equivalent to &(data[i]). The previously mentioned exception to array decay does not apply, since the & is never applied to the array name, but to an item in the array. Array decay into a pointer to the first element has already happened when we reach &, since it happened at the point where [] was encountered.


From the C Standard (6.5.2.1 Array subscripting)

2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

And (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof 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.

Thus in this expression

*(data+i)

the array data is implicitly converted to a pointer to its first element. And the expression is equivalent to data[i] or i[data].

So the expression

 *(&data[0] + i);

can be rewritten like

 *(&*( data + 0 ) + i);

that is the same as

 *( data + 0 + i);

and in turn as

 *( data + i);

Also in the declaration of a pointer

int *ptr = &data[0];

you could just write

int *ptr = data;