2

I'm currently learning C and I'm confused about memory layout and pointers.

In the following code, it is my understanding that the array is allocated on the stack.

#include <stdio.h>

int main () {
   int x[4];
   x[0] = 3; x[1] = 2; x[2] = 1;
   printf("%p\n",x);
   printf("%p\n", &x);
}

My question is, why do the two print calls output the same value?

I tried a similar snippet using malloc (allocate on the heap), and the values differ.

#include <stdio.h>
#include <stdlib.h>

int main () {
   int *x = malloc(sizeof(int) * 4);
   x[0] = 3; x[1] = 2; x[2] = 1;
   printf("%p\n",x);
   printf("%p\n", &x);
}
trincot
  • 317,000
  • 35
  • 244
  • 286
macsj200
  • 149
  • 3
  • 12
  • 3
    Possible duplicate of [How come an array's address is equal to its value in C?](http://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to-its-value-in-c) – Sneftel Jan 25 '17 at 21:02
  • 1
    C does not require using a stack for automatic variables. That's implementation specific. And an array is not a pointer. – too honest for this site Jan 25 '17 at 21:02
  • @Olaf it's implementation specific insofar as all implementations specifically allocate on the stack ;-). – Peter - Reinstate Monica Jan 25 '17 at 21:05
  • @PeterA.Schneider It's not uncommon to use registers for locals. – Eugene Sh. Jan 25 '17 at 21:10
  • @PeterA.Schneider: You mean "all implementations **you** are aware of". There are implementations which don't. And even those which use a stack do not place all automatic variables (including small arrays!) on the stack. Ever heard about register allocation? – too honest for this site Jan 25 '17 at 21:11
  • @Olaf (1) Which implementation doesn't use stack allocation? (2) Which address would that register have in this program, in your opinion? – Peter - Reinstate Monica Jan 25 '17 at 21:11
  • @PeterA.Schneider why does it have to have an address? Only if the program is actually using the address to perform some operations with side effects the address is required. – Eugene Sh. Jan 25 '17 at 21:13
  • @EugeneSh. Did you read the OP? – Peter - Reinstate Monica Jan 25 '17 at 21:14
  • @Olaf I realize that arrays and pointers are different, but my understanding is that in the first snippet `x` is a pointer to the 0th element of the array, i.e. a pointer. – macsj200 Jan 25 '17 at 21:14
  • @macsj200 - your understanding is wrong, I'm afraid. Not your fault, most tutorials I'm aware of tell this lie when introducing arrays. – StoryTeller - Unslander Monica Jan 25 '17 at 21:15
  • @macsj200 Re "my understanding is that in the first snippet x is a pointer:" You are mistaken. Proof: ask for `sizeof(x)` vs. `sizeof(y)`. – Peter - Reinstate Monica Jan 25 '17 at 21:15
  • @Sneftel - The topic you link to is not quite a duplicate, since it only answers why an array address and value (on conversion) are the same. It doesn't address why the case with `malloc()` has different values. – Peter Jan 25 '17 at 21:19
  • @Olaf I'm still waiting for an implementation which doesn't use stack allocation. – Peter - Reinstate Monica Jan 25 '17 at 21:25
  • 2
    @PeterA.Schneider: There are some embedded CPUs like PIC which don't have a hardware stack. And there are implementations for e.g. HC08 which allow to allocate automatic variables statically for performance reasons for example. An optimised ABI might pass arrays in registers and copy back. An implementation can do quite a lot of things and taking too much for granted is a good way to write code which breaks with good compilers. The world is larger than we think... About taking the address of an array: Try `register int a[10];`. – too honest for this site Jan 25 '17 at 21:38
  • @Peter `malloc()` has no bearing on what's going on; the only thing that matters is the types. The linked question shows the same effect: `&array == array` and `&ptr != ptr`. – Sneftel Jan 25 '17 at 22:04
  • @Sneftel True; I didn't want to imply that. After an edit `y` is not present in the OP any longer, but the point of the two `sizeof`s was that a pointer has a different size than a differently sized array ;-). – Peter - Reinstate Monica Jan 26 '17 at 06:26
  • @Olaf (0) Re "The world is larger than we think": I know, Hamlet. (1) Re: "don't have a hardware stack": Sure. It's a software stack. (2) Re " allocate automatic variables statically": Obviously not reentrant, therefore not conforming, in other words: Not C, actually. If the implementation is used in conformant mode it of course *does store variables on the stack* (e.g. `–stack-auto` or equivalent). – Peter - Reinstate Monica Jan 26 '17 at 09:19
  • @PeterA.Schneider You are wrong, (Olaf is reight, of course) The Standard does not *dictate* a stack to be used. For example: the caller could construct the activation record, *send* it to some (remote) magic function-execution-node, and wait for the return value to be received. The function-node is allowed to use whatever method to store its (temp) objects. – joop Jan 26 '17 at 11:22
  • 1
    @joop Of course: I know, and never said that. The standard actually does not contain the word "stack". But also of course, all implementations known to mankind *do provide* one. Note that you are entering an ontological discussion about stacks: What is a stack? I'm not saying that a stack has a register ESP, or hardware support of *any* kind, or a certain memory layout. Perhaps we need a stack Turing test: If it behaves like a stack, it's a stack, however it's implemented? What you describe is just a complicated stack implementation, and thus unlikely to be used instead of a simpler one. – Peter - Reinstate Monica Jan 26 '17 at 11:48

3 Answers3

3

The reason is that unlike you were probably taught, arrays are not pointers. Arrays in C decay into pointers1 under some circumstances. When you pass an array to a function, it decays into a pointer to the first element. The address of that element is the same as the address of the entire array (an address is always to the first byte of an object).

What you get from malloc is not an array, but the address of a chunk of memory. You assign the address to a pointer. But the pointer and the chunk are separate entities. So printing the value of the pointer, as opposed to its address, yields different results.


(1) Decay is a fancy term for a type of implicit type conversion. When an array expression is used in most places (such as being passed as an argument to a function that expects a pointer), it automatically turns into a pointer to its first element. The "decay" is because you lose type information, i.e. the array size.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
3

Your two print calls print the same value because one tries to print the array, which decays to a pointer to the array, and the other prints the address of the array. A pointer to the array contains the address of the array, so they're the same value.

In the second case, one prints the value of x, the other prints the address of x. Since x is a pointer to the block of memory you allocated, these must be different values.

So in the first case, all you have is an array (x). In the second case, you have an allocated block of memory (unnamed) and a pointer to that allocated block (x).

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Correction: the name of an arra decays to a pointer **to the first element** (not to the array) for most usages. – too honest for this site Jan 25 '17 at 21:04
  • 1
    I think saying the name decays is misleading. And there's no *value* distinction between a pointer to an array and a pointer to the first element of the array. Arrays keep all their objects in contiguous memory. But there is a *meaning* distinction, and here I want the meaning of pointing to the entire array. – David Schwartz Jan 25 '17 at 21:05
  • Well, the array itself does not decay. Actually, according ot the standard, it is the expression of "array of type" which is converted. And the standard does not disallow using different representations for different types (they need not compare equal). The type is always part of an expression, along with the value. Again you confuse the standard and implementation details. While they are relevant for the question, it is a good idea to point out these differences. Oversimplification and mashing up all together does not really help a beginner. – too honest for this site Jan 25 '17 at 21:14
2

It is perhaps surprising that one can indeed take the address of a whole array, partly because one doesn't need to very often. The array in a sense is a single object, which has one address, which is the address of its first byte. Like with all objects, the address is obtained with the address operator, &.

The first element of an array (like all of its elements) has an address, too, which is the address of its first byte. A pointer to its first element is what the array type is "adjusted" to when it is passed as an argument to a function.

These two bytes are identical, and have the same address. But they have different types, which becomes obvious if you add 1 to them and print them again.

The pointer y, by contrast, is its own distinct object (probably 4 or 8 bytes in size; enough to store an address in it). Like any object it has an address which can be obtained with the & operator. Perhaps confusingly, it also contains an address, in this case the address of the first byte of the array. The two are of course not equal: The pointer object resides at a different location than the array (namely next to it on the stack, even if Olaf doesn't like that).

Minor remark: You use %p for printing pointers, which is good. If you do that, you should strictly spoken cast the pointer which you print to a void pointer: printf("%p\n", (void *)x);.

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62