0

I'm new to C and trying to understand the following code

#include <stdio.h>

int main() {
    int i[3] = {1, 2, 3};
    int* p = &i;
    printf("%d\n", *p);
    printf("%d\n", *(&i));
    return 0;
}

What I thought was i is a pointer to first element of the array, then '&i' must be a pointer to pointer. But here we have an assignment

int* p = &i;

C still allows it with a warning of int* differs in levels of indirections from int(*)[3]. I thought this assignment shouldn't be allowed since it kinda weird.

Then the result of *p is 1, and it differs from *(&i) which gives the address of the first element. I don't understand why it became like that. I tried to google for the warning above, assignment from pointer to address of array to int pointer, etc. but haven't found what I need.

Thank you for your time.

4
  • Does this answer your question? Is an array name a pointer? Commented Oct 17, 2023 at 2:14
  • int* p = &i; is invalid C. Your compiler is either incorrectly configured or you aren't paying attention to warnings. What compiler options are recommended for beginners learning C? Commented Oct 17, 2023 at 6:12
  • @Lundin I'll check it out. It happened to be inside a small test of my class, and I don't know what my professor meant to put it in a test. It does give a warning but still accept the code. I used cl.exe and also an online C compiler link Commented Oct 17, 2023 at 10:24
  • "It does give a warning but still accept the code." That's all it needs to do. Why you should configure the compiler to give an error instead. See also the question in the linked post which covers what a compiler must do as well. Commented Oct 17, 2023 at 10:51

1 Answer 1

2

What I thought was 'i' is a pointer to first element of the array ...

Not quite correct. The variable i may decay to a pointer to the first element under many circumstances, but i itself is the integer array {1, 2, 3}.

And, since there is no padding before the 1, the address of i is the same as the address of that 1. The warning appears because you're using the pointer in a way that violates a constraint in the standard(1).

The ISO C17 standard (see 6.3.2.1 Lvalues, arrays, and function designators) has this to say:

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.

This is very much a "unary & operator" scenario hence it does not decay.

The reason why *(&i) does not cause a similar issue is because:

  • The i variable is an lvalue (all array names are, they're just not modifiable lvalues that you can assign to) and it's type is therefore the full array;
  • There's a footnote in the standard that states (irrelevant detail removed): "It is always true that if E is an lvalue that is a valid operand of the unary & operator, *&E is an lvalue equal to E" - hence its type is also the full array;
  • However, since you evaluate that full array from the previous bullet point in a context that does not prevent decay (giving it to sizeof or &), it decays to the pointer.

In other words, the second and third lines below are equivalent, both of them decay before assigning and will not produce the warning you see with the non-decaying first and fourth lines. That fourth line is simply the exact same operation that was used to assign to p1 but with *&i rather than just i, both having the same (array) type:

int *p1 =     &i  ;  // no decay.
int *p2 =      i  ;  // decay.
int *p3 =   *(&i) ;  // decay.
int *p4 = &(*(&i));  // no decay.

(1) As per C17 6.5.16 Assignment operators (and 6.7.9 Initialization which references the former), the constraints do not allow for assignment or initialisation where the source object is an array. The only types are arithmetic, structure or union, or pointer. Arrays are an aggregate type, like structures and unions, but are not explicitly included.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you. As what I understand from your answer, &i == i, and the dereference *(&i) is equivalent to i, which still gives the address of the first element.
Re “The warning appears because you're using the pointer in a way that may be problematic”: The warning appears because initializing an int * with an int (*)[] is a constraint violation.
@GiaoLeXuan: not quite. &i is still the address of i but that i is the full array object (hence the warning). The *(&i) is subtly different. I've updated the answer with the detail needed to understand this.
@EricPostpischil, that's a good point. I've updated that bit based on your comment, and referenced the relevant ISO sections for completeness.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.