C Stack allocation

B

Billy Mays

Does C define which way (higher addresses or lower addresses) when
allocating variables?

Ex.

/**************************/
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);


printf("%d\n", d);

/**************************/

What will the value of 'd' be? Can I be sure that it will always be 1?

Bill
 
K

Kenny McCormack

Does C define which way (higher addresses or lower addresses) when
allocating variables?

Ex.

/**************************/
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);


printf("%d\n", d);

/**************************/

What will the value of 'd' be? Can I be sure that it will always be 1?

Bill

Boy are you going to get it now (from the regs...)

--
(This discussion group is about C, ...)

Wrong. It is only OCCASIONALLY a discussion group
about C; mostly, like most "discussion" groups, it is
off-topic Rorsharch [sic] revelations of the childhood
traumas of the participants...
 
K

Keith Thompson

Billy Mays said:
Does C define which way (higher addresses or lower addresses) when
allocating variables?
No.

Ex.

/**************************/
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);


printf("%d\n", d);

/**************************/

What will the value of 'd' be? Can I be sure that it will always be 1?

Absolutely not. The C standard says nothing about the relative
addresses of distinct objects, and implementations vary. (If you
want the value of a, the expression ``a'' is a much easier way to
get it.)
 
B

bart.c

Billy Mays said:
Does C define which way (higher addresses or lower addresses) when
allocating variables?

Ex.

/**************************/
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);


printf("%d\n", d);

/**************************/

What will the value of 'd' be? Can I be sure that it will always be 1?

On my machine, two compilers gave 1, and two gave 1245088.

C doesn't have control over variable addresses like this. You end doing
things like:

int cba[3]={3,2,1};
#define a (cba[0])
#define b (cba[1])
#define c (cba[2])

int d;

d = *(&b + 1);
 
D

Default User

Billy Mays said:
Does C define which way (higher addresses or lower addresses) when
allocating variables?

Ex.

/**************************/
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);


printf("%d\n", d);

/**************************/

What will the value of 'd' be? Can I be sure that it will always be 1?

No. That's undefined behavior, so theoretically anything could happen. In
practical terms, the compiler might have put a into a register, so nothing
you did with funky pointer arithmetic would end up accessing it.

If this is mere curiosity, then you're done. If you think you need this,
then post what you're trying to accomplish.



Brian
 
S

Seebs

Does C define which way (higher addresses or lower addresses) when
allocating variables?

No. It doesn't even promise you that there is a meaningful comparison
between the addresses of different automatic variables. Comparisons are
meaningful only within an object (and also addresses within an object can
be compared to the address "one past the end" of that object).

-s
 
E

Eric Sosman

Does C define which way (higher addresses or lower addresses) when
allocating variables?

No. It doesn't specify any pattern at all (that is, the choice
can be something other than high-to-low or low-to-high: it could be
bounce-around-apparently-at-random).
Ex.

/**************************/
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);


printf("%d\n", d);

/**************************/

What will the value of 'd' be?

There's no way to tell. It's not even certain that `d' will
be assigned a value at all, since the improper pointer dereference
yields undefined behavior. "Undefined behavior" means that anything
at all can happen, including things that would prevent the program
from getting as far as storing something in `d'.
Can I be sure that it will always be 1?

No.

(True story: I once had the dubious pleasure of working on some
code whose authors assumed that the file-scope variables in each
module were allocated in the order they were declared. I was trying
to port this beast to a platform which had the peculiar habit of
allocating storage alphabetically by variable name, thus completely
scrambling the order the authors had relied upon ... It's memories
like this that make me so unforgiving of needless non-portability.)
 
B

Billy Mays

No. That's undefined behavior, so theoretically anything could happen. In
practical terms, the compiler might have put a into a register, so nothing
you did with funky pointer arithmetic would end up accessing it.

If this is mere curiosity, then you're done. If you think you need this,
then post what you're trying to accomplish.



Brian

I have some very large structs that are in a hashtable. The key for
each struct is actually in the struct itself and is actually two ints.

Thus:
struct dummy{
int a;
int b;
char data[ENORMOUS];
};


Since I did not want to allocate a very large struct on the stack to
just do the key lookup, I was hoping to make two ints and just pass the
address of the first one to the hashtable getter.

Ex.

int akey = 4;
int bkey = 7;

void * big = ht_get(&bkey);

That way just the keys would be allocated instead of an entire struct.
Note that the hashing function depends on both a and b;

Bill
 
B

Ben Pfaff

Billy Mays said:
I have some very large structs that are in a hashtable. The key for
each struct is actually in the struct itself and is actually two ints.

Thus:
struct dummy{
int a;
int b;
char data[ENORMOUS];
};


Since I did not want to allocate a very large struct on the stack to
just do the key lookup, I was hoping to make two ints and just pass
the address of the first one to the hashtable getter.

Ex.

int akey = 4;
int bkey = 7;

void * big = ht_get(&bkey);

That way just the keys would be allocated instead of an entire
struct. Note that the hashing function depends on both a and b;

Separate the key from the auxiliary data and pass only the key to
the hash table lookup routine.
 
K

Keith Thompson

Seebs said:
No. It doesn't even promise you that there is a meaningful comparison
between the addresses of different automatic variables. Comparisons are
meaningful only within an object (and also addresses within an object can
be compared to the address "one past the end" of that object).

There is one additional condition, but it's not terribly useful, and it
applies only to equality operators.

N1256 6.5.9p6-7:

6 Two pointers compare equal if and only if both are null pointers,
both are pointers to the same object (including a pointer to an
object and a subobject at its beginning) or function, both are
pointers to one past the last element of the same array object,
or one is a pointer to one past the end of one array object and
the other is a pointer to the start of a different array object
that happens to immediately follow the first array object in
the address space.

7 For the purposes of these operators, a pointer to an object that
is not an element of an array behaves the same as a pointer to
the first element of an array of length one with the type of
the object as its element type.

(Paragraph 7 doesn't appear in the C99 standard; it's more of a
clarification than new semantics. Both C99 and N1256 have similar
wording for relational operators (<, <=, >, >=)).

There's a footnote attached to the end of paragraph 6:

Two objects may be adjacent in memory because they are adjacent
elements of a larger array or adjacent members of a structure
with no padding between them, or because the implementation
chose to place them so, even though they are unrelated. If prior
invalid pointer operations (such as accesses outside array
bounds) produced undefined behavior, subsequent comparisons
also produce undefined behavior

The purpose of the "happens to immediately follow" isn't to permit
code to depend on distinct objects being adjacent; it applies
only if they *happen* to be adjacent, but there's no way to cause
them to be adjacent unless they're elements of the same array
(even struct members can have padding between them). The point
is that implementations aren't required to go out of their way to
cause two pointers to appear to be unequal if they happen to point
to the same location in memory.

For example, the following program could produce any of three possible
outputs:

#include <stdio.h>
int main(void)
{
int x;
int y;
if (&y == &x + 1) {
puts("&y == &x + 1");
}
else if (&x == &y + 1) {
puts("&x == &y + 1");
}
else {
puts("x and y are not adjacent");
}
return 0;
}
 
E

Eric Sosman

[...]
I have some very large structs that are in a hashtable. The key for each
struct is actually in the struct itself and is actually two ints.

Thus:
struct dummy{
int a;
int b;
char data[ENORMOUS];
};


Since I did not want to allocate a very large struct on the stack to
just do the key lookup, I was hoping to make two ints and just pass the
address of the first one to the hashtable getter.

Hash table implementations vary, but most will look up items
given just the key: It's a key/value pair sort of thing, even if
the key is embedded in the value.
Ex.

int akey = 4;
int bkey = 7;

void * big = ht_get(&bkey);

What you want is more like

struct keypair {
int a;
int b;
};

struct dummy {
struct keypair key;
char data[ENORMOUS];
};

struct keypair both = { 4, 7 };
void *big = ht_get(&both);
 
E

Eric Sosman

Billy Mays said:
[...]
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);

What will the value of 'd' be? Can I be sure that it will always be 1?

yes if&b+1==&a

No, not even then.

Perhaps you recall discussions of why

int array[3][10] = ...;
int x = array[0][12];

is undefined, even though there's a perfectly good int value, better
known as array[1][2]? The same reasoning applies here, treating `b'
as equivalent to a one-element int array.
 
K

Keith Thompson

Eric Sosman said:
Billy Mays said:
[...]
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);

What will the value of 'd' be? Can I be sure that it will always be 1?

yes if&b+1==&a

No, not even then.

Perhaps you recall discussions of why

int array[3][10] = ...;
int x = array[0][12];

is undefined, even though there's a perfectly good int value, better
known as array[1][2]? The same reasoning applies here, treating `b'
as equivalent to a one-element int array.

&b+1 is a valid address (it's just past the end of that one-element
array), and if b and a *happen to be* adjacent in memory, &b+1==&a
could be well-defined and true; see C99 6.5.9p6.

This isn't particularly useful for programmers, since there's no
way to guarantee that a and b are adjacent, but it makes it much
easier for compilers to implement the "==" and "!=" operators
without generating extra code to pretend that adjacent objects
aren't adjacent.

I mentioned this in Message-ID: <[email protected]>
<http://groups.google.com/group/comp.lang.c/msg/c2ab0974bc5e0512>.
 
E

Eric Sosman

Eric Sosman said:
"Billy Mays"<[email protected]> ha scritto nel messaggio
[...]
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);

What will the value of 'd' be? Can I be sure that it will always be 1?

yes if&b+1==&a

No, not even then.

Perhaps you recall discussions of why

int array[3][10] = ...;
int x = array[0][12];

is undefined, even though there's a perfectly good int value, better
known as array[1][2]? The same reasoning applies here, treating `b'
as equivalent to a one-element int array.

&b+1 is a valid address (it's just past the end of that one-element
array), and if b and a *happen to be* adjacent in memory,&b+1==&a
could be well-defined and true; see C99 6.5.9p6.

Yes, but that's not relevant. The (attempted) dereference
outside the extent of the "array" `b' produces undefined behavior,
regardless of whether something else "lives there" or not. See
6.5.6p8, final sentence: "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." (See also 6.5.6p7 for
the treatment of `b' as an array.)

Here's my array example again, cleaned of a gratuitous error
and with the argument spelled out:

int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } };
int *p = &array[0][3]; // no problem
int *q = &array[1][0]; // no problem
assert (p == q); // no problem
assert (*q == 3); // no problem
assert (*p == 3); // U.B.; p not dereferenceable

Pointers that compare equal need not be equally valid. As the
folks on Antiques Road Show often say, provenance matters.
 
S

sandeep

Eric said:
int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; int *p = &array[0] [3];
// no problem int *q = &array[1][0]; // no problem assert (p == q);
// no problem assert (*q == 3); // no problem assert (*p
== 3); // U.B.; p not dereferenceable

I find it very difficult to believe that there's any compiler out there
for which this will give undefined behavior! Do you have an example where
it does?
 
K

Keith Thompson

Eric Sosman said:
Eric Sosman said:
On 6/9/2010 1:12 PM, io_x wrote:
"Billy Mays"<[email protected]> ha scritto nel messaggio
[...]
int a = 1;
int b = 2;
int c = 3;
int d;

d = *(&b + 1);

What will the value of 'd' be? Can I be sure that it will always be 1?

yes if&b+1==&a

No, not even then.

Perhaps you recall discussions of why

int array[3][10] = ...;
int x = array[0][12];

is undefined, even though there's a perfectly good int value, better
known as array[1][2]? The same reasoning applies here, treating `b'
as equivalent to a one-element int array.

&b+1 is a valid address (it's just past the end of that one-element
array), and if b and a *happen to be* adjacent in memory,&b+1==&a
could be well-defined and true; see C99 6.5.9p6.

Yes, but that's not relevant. The (attempted) dereference
outside the extent of the "array" `b' produces undefined behavior,
regardless of whether something else "lives there" or not.
[snip]

I believe you're right, and I was w..., wr..., wro..., um, not
entirely right.
 
K

Keith Thompson

sandeep said:
Eric said:
int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; int *p = &array[0] [3];
// no problem int *q = &array[1][0]; // no problem assert (p == q);
// no problem assert (*q == 3); // no problem assert (*p
== 3); // U.B.; p not dereferenceable

No, he actually wrote the following:

int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } };
int *p = &array[0][3]; // no problem
int *q = &array[1][0]; // no problem
assert (p == q); // no problem
assert (*q == 3); // no problem
assert (*p == 3); // U.B.; p not dereferenceable

Please figure out how to keep your newsreader from mangling quoted text.
I find it very difficult to believe that there's any compiler out there
for which this will give undefined behavior! Do you have an example where
it does?

It gives undefined behavior for *all* compilers. In many cases, that
behavior will happen to be what you might expect.

You need to understand what "undefined behavior" means. It doesn't
mean "the program will crash". It means, to quote C99 3.4.3:

behavior, upon use of a nonportable or erroneous program
construct or of erroneous data, for which this International
Standard imposes no requirements

"imposes no requirements". Nothing more, nothing less.

I think what you meant to ask is:

Do you have an example where *p does something other than accessing
the value of array[1][0]?

Personally, no, I don't have such an example, but I do have a
couple of plausible hypothetical examples. A compiler that performs
strict bounds checking, using "fat pointers" to keep track of the
permitted range of an object, could recognize that p points just
past the end of a 3-element array and take some special action on an
attempt to dereference it. Or an optimizing compiler could *assume*
that you don't dereference anything past the end of an array, and
generate code that behaves in some arbitrary way if that assumption
is violated. Or some combination of these.

The behavior is undefined *because the standard doesn't define the
behavior*. If you disagree, show us where the standard defines the
behavior.
 
E

Eric Sosman

sandeep said:
Eric said:
int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } }; int *p =&array[0] [3];
// no problem int *q =&array[1][0]; // no problem assert (p == q);
// no problem assert (*q == 3); // no problem assert (*p
== 3); // U.B.; p not dereferenceable

No, he actually wrote the following:

int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } };
int *p =&array[0][3]; // no problem
int *q =&array[1][0]; // no problem
assert (p == q); // no problem
assert (*q == 3); // no problem
assert (*p == 3); // U.B.; p not dereferenceable

Please figure out how to keep your newsreader from mangling quoted text.
I find it very difficult to believe that there's any compiler out there
for which this will give undefined behavior! Do you have an example where
it does?

It gives undefined behavior for *all* compilers. In many cases, that
behavior will happen to be what you might expect.
[... further explanation of what UB is and isn't ...]

To be fair, any implementation is allowed to define behaviors
that the Standard leaves undefined, and somewhere there might be a
compiler that actually *defines* the effect of this out-of-bounds
reference. I've never encountered one that did, even though they
all wound up doing the same thing (accessing array[1][0]). This
was a "by accident" thing, though, not something the implementations
defined and documented.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,220
Latest member
AugustinaJ

Latest Threads

Top