4
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.

SpawN
  • 140
  • 11
  • These are pointing to the same address (start of an array): &a,a,&a[0]. And, **'p'** is a pointer to array of four integers. So, if you print adress of **p+1** (`printf("p = %p\np++ = %p\n", p, p+1);`) you will see that the address is **4 * sizeof(int)** bigger than address of **'p'**. – mar Jun 05 '20 at 07:16

7 Answers7

5

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.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • Thanks !.. It means doing p = a; is not wrong conceptually, but the compiler is like a strict person who likes to stay by rules so he thinks if p is green, it should point to only green things,as in it points to array here, so &a looks syntactically right to compiler.(even if &a[0] is right, it sees it as a single element and thus doesn't allow this assignment) Am I right ? – SpawN Jun 05 '20 at 10:11
  • yep ... `p` is a pointer to array of 4 ints, `a` is (converted to) a pointer to `int`. "Don't mix the colors" – pmg Jun 05 '20 at 10:15
  • Compilers are *not* color-blind, you wanted to say ;-) – Peter - Reinstate Monica Jun 05 '20 at 11:57
  • It depends @Peter.. often they aren't; within `printf()` (or when dealing with `void*`, for example) they are color-blind. – pmg Jun 05 '20 at 12:03
  • 2
    Well, in this particular case. The sentence does not makes sense: If they were color-blind ("address is address, they are all alike"), they wouldn't even notice that they were being tricked, would they? – Peter - Reinstate Monica Jun 05 '20 at 12:04
3

"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 not p = 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.

Community
  • 1
  • 1
0

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 because a decays to a pointer-to-int when assigned, while p points to a different type. Read more about decaying: What is array to pointer decay?
  • ptr = &a fails because ptr is actually a pointer-to-int; it doesn't have the same type as p. 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.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Okay, so its different in size. Still, why do some of these assignments gives error when all the assignments are just the memory address of starting variable ? – SpawN Jun 05 '20 at 07:14
0

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).

0___________
  • 60,014
  • 4
  • 34
  • 74
0

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].

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

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.

:)

SpawN
  • 140
  • 11
  • Because of operator precedence rules you need to bracket `(*ptr)[4]`. – Peter - Reinstate Monica Jun 05 '20 at 12:10
  • Agreed. But here the issue was regarding what *ptr holds. In one case it holds the address (int) and in other case it holds range of addresses(array) wherein ptr actually holds a kind of identifier of these range of addresses, and compiler is programmed to accept the code according to these concepts only. – SpawN Jun 05 '20 at 16:37
  • It was just a C grammar remark. (But the grammar is important because it defines what we say. If you say `int *ptr[4]`you define an entity ptr which you index, resulting in a pointer to int; in other words, you talk about *an array of pointers*, not about *a pointer to an array.* A pointer to array needs to be dereferenced first, and indexed after that. This early dereferencing is enforced by the brackets.) By the way, all pointers "hold" just a number, the address; no type information is stored in them. All type information is only in the compiler's "mind" during translation. – Peter - Reinstate Monica Jun 06 '20 at 07:29
  • Thinking of it, that's true for all types (in C). It's different in languages like Java and C# where each object carries information about itself which can be inspected at *runtime* through reflection. In C there is no runtime mechanism like an [Invalidcastexception](https://learn.microsoft.com/en-us/dotnet/api/system.invalidcastexception?view=netcore-3.1). An array also does not store its length; only at *compile time* does the compiler enforce certain type checks. – Peter - Reinstate Monica Jun 06 '20 at 07:35
0

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.


  1. 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 for T (*f)() T cannot be a function or array type.

John Bode
  • 119,563
  • 19
  • 122
  • 198