0

I wonder what defines if the start of an memory object is at lower or higher addresses than the end of an object. For example:

char buffer[10];

char* p = &buffer[0];
printf("%p\n",p); //0x7fff064a6276

p = &buffer[9];
printf("%p\n",p); //0x7fff064a627f

In this example the start of object is at a lower address than the end. Even though the stack grows towards lower addresses.

Why does the layout goes the reverse direction of the stack growth?

What defines this direction? Language? OS? Compiler? CPU architecture? ...

Is it always the case that the end of the object is at a higher address than the beginning?

Fee
  • 719
  • 9
  • 24
  • *"Always"* is a strong term in computing. For every rule, there's at least one oddball system out there that's the exception. So all you can say is that on a typical modern desktop, the end of the object is at a higher address than the beginning. – user3386109 Oct 08 '16 at 02:20
  • 2
    It is always the case, as for a citation to where it is defined, that will take some digging. If it were not the case *pointer arithmetic* would fail. – David C. Rankin Oct 08 '16 at 02:20
  • 1
    @DavidC.Rankin Pointer arithmetic won't fail because the arithmetic operators for pointers does not care about pointers numeric value, e.g. pointer-plus-integer, pointer-minus-integer, pointer-minus-pointer, pointer-compare-pointer are still (or, can be, if implemented with care, ) all well defined if arrays grows to lower addresses. I don't think there is any technical limitation, but rather a design choice to meet people's intuition (incrementing the pointer makes its value greater) – user3528438 Oct 08 '16 at 02:46
  • This might be helpful http://stackoverflow.com/questions/4560720/why-does-the-stack-address-grow-towards-decreasing-memory-addresses – Mobius Oct 08 '16 at 03:02
  • @user3528438 In the C11 standard (n1570), Section "6.3.2.3 Pointers", paragraph 7: _When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object._ So, you couldn't use C. And, if you could, every `ptr += 1` would have to generate code to _subtract_ from the pointer [or vice versa]. There would be lots of extraneous negations. It would be, well, insane to generate code that is the opposite of the source – Craig Estey Oct 08 '16 at 03:49
  • @user3528438 If you have: `int x = 3; int y = 5`, then `x + y` is 8, which is higher. If you have `char *p = (char *) 0x1005; int y = 5;` and do `p + y`, you don't get `0x100A` as you would normally, but you'd get `0x1000`. So, an pointer addition/increment is no longer the same as adding two integers and would be a subtract. So, this isn't just intuition, but changes the fundamentals of the language arithmetic – Craig Estey Oct 08 '16 at 04:17
  • Objects and arrays always start low and end high. Stacks may grow upwards or downwards or even not really exist (compiler maps to global memory). Whilst ultimately the compiler decides, usually the architecture has a stack convention which it would be insane not to use. – Malcolm McLean Oct 08 '16 at 04:36
  • @DavidC.Rankin In case you're interested in citations, I found one [as did Jonathan--and he found some more]. Also, I came up with an example that shows how the pointer arithmetic breaks using "grow down" arrays. – Craig Estey Oct 08 '16 at 04:38
  • I guess the answer is, C language does not care. If the compiler writer is crazy enough, he can implement `printf %p` to interpret the pointer as sign-magnitude representation and print out signed decimal numbers. Then depending on which half you are in, you can either see an array has a positive address and growing up, or a negative address growing to negative infinity. Then the computer works just fine but your intuition is destructed. – user3528438 Oct 08 '16 at 10:33

2 Answers2

2

One part of the standard that is relevant is in §6.3.2.3 Pointers (under §6.3 Conversions):

¶7 … When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

Another relevant portion is §6.7.2.1 Structure and union specifiers:

¶15 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.

The definition of addition (and subtraction) is partly relevant (§6.5.6 Additive operators):

¶8 When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

¶9 is a similar paragraph defining the behaviour of subtraction.

And then there's §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).

From these, you know that the address of an object converted to a char * must point to the lowest byte address holding the object. In practice, this means that the 'object pointer' address of the object also points to the lowest address. The rule in no way enforces that the data in an int type must be little-endian or big-endian; both are valid.

You also know that the first element in a structure is at a lower address within the structure than later elements.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Then how do we define "low/lower/lowest address"? C defines byte as "char". Then byte address is `char *`. Clearly you are assuming "lower address has a lower numeric value". That's not what the standard says. Address A is lower than B if it compares smaller than B, and that compare uses the `operator <` and `>` for pointer types, which does not have to follow rules of integer types. – user3528438 Oct 08 '16 at 09:49
  • Under what theory of the normal interpretation of the English language would you construe a "lower address" as being one with a numerically larger value? Any such attempt flies in the face of normal "use of English". There comes a point at which such suggestions become … puzzling. A "lower address" is one where the numeric value corresponding to the address is smaller than the other, "higher address" with which it is being compared. The words "smaller" and "bigger" can also be used with the same sense. – Jonathan Leffler Oct 08 '16 at 13:24
  • If that's the case ("computer scientists and engineers all speak English and all have common sense"), N1570: 6.5.8.5 wouldn't be so puzzling as it is now, would it? Instead, 6.5.8.5 has carefully and cleverly avoided the whole "numerical value" thing. – user3528438 Oct 08 '16 at 16:31
  • The wording in §6.5.8¶5 (Relation operators) supports my basic contention. I think it is phrased like that in part to deal with segmented addressing architectures (as found on 80x86 chips, though it generally isn't used any more) where different combinations of segment:offset can refer to the same memory location. – Jonathan Leffler Oct 08 '16 at 16:50
1

Most compilers will allocate space on the stack for all of the local variables in one block, with the start of the arrays at the lowest address going upwards.

You need to go to a deeper subroutine to see the addresses "going down".

For example call other subroutine which also has a local buffer. You will find its memory 'lower' in the address space (as the stack has gotten bigger) that the local array in the parent routine.

Secto Kia
  • 990
  • 4
  • 12