问题
int (*p)[4] , *ptr;
int a[4] = {10,20,30,40};
printf("%p\n%p\n%p",&a,a,&a[0]);
p = &a ;
//p=a; gives error
//ptr = &a; gives error
ptr = a;
Output:
0x7ffd69f14710
0x7ffd69f14710
0x7ffd69f14710
I tried to understand what a
, &a
, and &a[0]
returns and its the memory address of starting variable. So, why am I getting errors in some of these assignments ?
I mean, if p = &a = 0x7ff...
works, why not p = a = 0x7ff..
?
If possible, can anyone please make me understand through a block diagram of where this p and ptr is actually pointing to too. Or are they just pointing same. But they're different things that for sure I know.
回答1:
Imagine pointers are laser pointers, with different colors (red for pointers to int, green for pointers to arrays, ...) and variables are things you can point to with the correct laser pointer, ie, you cannot use a green laser pointer to point to a char variable.
Ok, so you have int a[4]
an array (of 4 ints). Use a green pointer to point to it: int (*green)[4] = &a;
... you also have an int (a[0]
) which you can point to with a red pointer: int *red = &a[0]; /* in most contexts 'a' by itself is converted to "address of first element": &a[0] is the same as a */
.
Now ask your color-blind friend where the pointers point to :)
As far as your friend is concerned, they are equal and point to the same "place"... but you tricked your friend! Compilers are color-blind and don't like being tricked.
回答2:
"What
int (*ptr)[4]
really means and how is it different than*ptr
?"
First of all, we take a look at the declarations itself:
int * ptr
- ptr
is of type int *
- pointer to int
.
int (*ptr)[4]
- ptr
is of type int (*)[4]
- pointer to array of four int
.
The types are different.
I mean, if
p = &a = 0x7ff...
works, why notp = a = 0x7ff..
?
(Nitpicky side note: These expressions won´t get compiled, but I understand that this is just an example to illustrate the context.)
In C, expressions of array type are able to decay to pointers to the first element of the array.
Quote from the C18 standard, ISO/IEC 9899:2018:
"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."
Source: C18, §6.3.2.1/3
But since the &
operator is used at a
, a
does not decay to a pointer to the first element of a
. Instead, &
is applied to the array itself and yields a pointer to the whole array (type int (*)[4]
).
It is a syntactical type difference/mismatch, int *
vs. int (*)[4]
, although of course both point to the same address in memory.
The compiler is obliged to throw a diagnostic for any type mismatch as it is a syntax violation.
Of course, both have the same address, but the type incompatibility at the assignment makes the difference.
回答3:
p
is a pointer to values of type int[4]
, i.e. a pointer to arrays of 4 integers each. Note that sizeof(*p)
is 4 times sizeof(int)
.
Now,
p = a
fails becausea
decays to a pointer-to-int when assigned, whilep
points to a different type. Read more about decaying: What is array to pointer decay?ptr = &a
fails becauseptr
is actually a pointer-to-int; it doesn't have the same type asp
. Declaring multiple variables on the same line is often confusing, because not all syntax applies to everything you declare; better split up those definitions to separate lines.
回答4:
It's a matter of different types, and types is a concept that exists in the compiler but not in the compiled binary. That's why you get compiler errors even though the two pointer types actually point at the same address.
You can think of int (*p)[4]=&arr;
as a pointer to the whole array, whereas int* ptr=arr;
is a pointer to the first element in the array.
Normally when used in an expression, the array name "decays" into a pointer to the first element. This is what happens when we write int* ptr=arr;
- it is 100% equivalent to writing int* ptr = &arr[0];
.
Formally the "array decay" rule is defined in C17 6.3.2.1/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.
As we can see, the &
operator is a special exception from the rule. Meaning that in case of &arr
, the arr
part does not decay. So we are expected to get a pointer to the array type and not just to the first element. That's where int (*p)[4]
fits in.
But of course, "a pointer to the whole array" will at the same time point at the address of the first item, because that's the address where the array starts. If we printf("%p\n", p)
, we will get the very same address no matter if we pass the array pointer or the pointer to the first element.
Array pointers are there to keep the language type system consistent. We sometimes run into them practically too, when we start to work with multi-dimensional arrays. If we for example define an int arr[2][3]
array, it is actually an array of 2 items, where each item is a int[3]
array. So what happens when we type arr
for this 2D array? As usual, the array decays into a pointer to the first item. And the first item is an array, so for the rule of array decay to stay consistent, it has to give a pointer to such an array of 3 integers. The type for such a pointer is int(*)[3]
.
回答5:
I tried to understand what
a
,&a
, and&a[0]
are.
In C arrays decay to pointers. All of those pointers reference the same memory location (first element of the array). The only difference is the type.
a
and &a[0]
have the type of the array elements (in this case int
)
&a
is of type pointer to array of elements type (in this case array of 4 integers).
回答6:
Here are the basic rules:
For any1 type T
, you can have any of the following:
T *p; // p is a pointer to T
T *a[N]; // a is an array of pointer to T
T (*a)[N]; // a is a pointer to an array of T
T *f(); // f is a function returning pointer to T
T (*f)(); // f is a pointer to a function returning T
The postfix []
and ()
operators have higher precedence than unary *
, so an expression like *p[i]
is parsed as *(p[i])
. If you want to index into what p
points to, then you need to explicitly group the *
operator with p
, or (*p)[i]
. This precedence rule applies to both expressions and declarations.
Except when it is the operand of the sizeof
, _Alignof
, or unary &
operator, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T
" will be converted ("decay") to an expression of type "pointer to T
" and the value of the expression will be the address of the first element of the array. So given the declaration
int a[4] = {10, 20, 30, 40};
all of the following are true:
Expression Type Decays to Equivalent value
---------- ---- --------- ----------------
a int [4] int * &a[0]
&a int (*)[4] n/a &a[0]
*a int n/a a[0]
The expression a
has type "4-element array of int
" (int [4]
). a
is not the operand of the sizeof
, _Alignof
, or unary &
operators, so the expression "decays" to type "pointer to int
" and the value of the expression is the address of the first element. The result of this expression is exactly equivalent to &a[0]
.
The expression &a
has type "pointer to 4-element array of int
" (int (*)[4]
). In this case, a
is the operand of the unary &
operator, so the decay rule doesn't apply.
All of the expressions a
, &a
, and &a[0]
yield the same value (the address of the first element of a
), but the types of the expressions are different (which may affect how the value is represented). The type of a
and &a[0]
is int *
, but the type of &a
is int (*)[4]
.
Type matters for things like pointer arithmetic. Assume the following declarations:
int a[4] = {0, 1, 2, 3};
int *p = a;
int (*ap)[4] = &a;
Both p
and ap
initially point to the same address. However, the expression p + 1
will yield the address of the next int
object following whatever p
is pointing to (IOW, &a[1]
), while ap + 1
will yield the address of the next 4-element array of int
following a
.
This is exactly how array subscripting works - the expression a[i]
is evaluated as *(a + i)
. Given a starting address a
, offset i
objects (not bytes) and dereference the result.
And this is why you get the errors in some of your assignments - the types int *
and int (*)[4]
are not compatible. For one thing, they don't have to be represented the same way (although on any system you're likely to use they will be), and they behave differently when using pointer arithmetic.
Pointers to different types are themselves different types, and are not normally interchangeable.
- Well, almost any - functions cannot return array or function types, and you cannot have an array of function type, so for
T (*a)[N]
T
cannot be a function type, and forT (*f)()
T
cannot be a function or array type.
回答7:
Both answers have been cleared through these discussions. I would like to conclude them :
1. What is the difference between int *ptr and int *ptr[4];
Answer : Both of the pointer variables are same in size because both of them hold addresses only. It is just a conceptual difference that ptr holds the address of an integer. Well, ofcourse you can use it to point to the starting location of the array. But what this says the compiler is : It can hold any integer. When you try to do "ptr++" in your code, it will just shift the memory address 1 unit ahead(according to whatever bytes are reserved for an integer for that system). But, int *ptr[4] says that, ptr is a pointer that points to an entire whole array with only the starting location stored. Well, ofcourse ptr's in both cases store the same address. But when you try to do "ptr++" in this case, it will shift to 4 units ahead, because compiler interprets this as a pointer of an array, rather than a pointer of integer.
2. Why ptr=&a works and ptr =&a[0] or ptr=a doesn't work even if these all values are same ?
Answer : ptr=a and ptr=&a both are conceptually right. But, compiler works on strict rules. If you wanted to say that ptr contains address of an integer, well it should be assigned that way which is ptr = a OR ptr=&a[0] (denoting the assigned space is an integer). While, if ptr is declared to be the address of an array, ptr = &a[0] pr ptr = a is interpreted by compiler as this ptr getting the address of an integer, which is not true because ptr here denotes the address of an array. It should not hold address of an integer in this case. So, p=&a looks syntactically very right to compiler. Thus, this is the only option it accepts.
:)
来源:https://stackoverflow.com/questions/62209942/what-int-ptr4-really-means-and-how-is-it-different-than-ptr