views:

136

answers:

6

So I was teaching my friend about pointers. While doing so, I noticed that the address of two identical structs are exactly back-to-back.

struct Foo
{
    int a;
};

struct Bar
{
    int b;
};

Which allowed me to do this:

Foo foo; foo.a = 100;
Bar bar; bar.b = 100;

Foo *pFoo = &foo;
Bar *pBar = &bar;

(pFoo+1)->a = 200;

This overrides the value in bar.b and sets it to 200.

Now, I'm not questioning the merits of doing such a thing- it will probably never see the light of day in a real program. I was just wondering, does the OS always allocate identical structs back-to-back? Provided there is enough memory space free in the given area.

+6  A: 

The operating system doesn't allocate stack space for local variables, the compiler does. C++ compilers don't guarantee the order of allocation.

Paul Keister
The compiler is under the constraint of the OS. So ultimately, the OS decides whether or not it gives the space that the compiler needs? I thought that's how OSs worked?
SoulBeaver
@SoulBeaver - Programs ask operating systems for memory, but they don't say anything about what's going in the memory. So the OS has no idea if two identical structs are being allocated on the stack or not. In fact, the program can only ask for the space in page-size chunks, and a page is typically 4096 bytes long, so both structs are likely allocated at the same time and the program determines how the memory corresponds to the structs.
Omnifarious
OS provides memory, but it does not decide how it is used. Of course OS does not know which variable should go first.
zvonimir
I think it bears mentioning that, were they allocated on the heap via new/malloc(), the OS would decide on where they ended up in memory. Since these are stack-allocated structs, Paul is correct -- the compiler arranges the stack frame, and yes, the specifics are implementation-dependent.
Faisal
-1: The OP is in C++, not C.
DeadMG
@DeadMG: Does changing "C" to "C++" materially change the content of the answer?
James McNellis
@DeadMG: Just because I'm in C++ doesn't mean I can't learn about C. I don't see why you had to downvote him for that. If I ever decided to screw around in pure-C then I would certainly want to know if it behaved differently.
SoulBeaver
@Omnifarious: Actually, on Windows programs can ask for as much or as little memory as they wish, because the heap manager is provided by the Operating System rather than by the compiler. This is one of the most significant differences between the Doug Lea's `malloc` implementations used on Unix boxes and `RtlHeap` used on Windows boxes.
Billy ONeal
@Billy: A heap manager (actually several, one called the heap manager and others simply described as allocators, which mostly likely also use heaps) is provided by the Windows OS. The code in this question doesn't use it, and probably 99% of your code doesn't either. The C++ compiler comes with its own heap manager (in the runtime library) which is quite different from the Windows allocators and potentially adds all kinds of debug data on top (which incidentally guarantees that the data is not "where the OS decided it should be").
Ben Voigt
@Ben: Usually the C/C++ functions are implemented in terms of the Win32 functions. See: http://www.labri.fr/perso/betrema/winnt/images/virtmm_1.gif
Billy ONeal
@Billy: But the CRT functions often add lead and trail bytes, there's no justification for Faisal's claim that "the OS would decide on where they end up in memory", and the OS can't place two structures appear next to each other. I guess HeapAlloc could guarantee that there are intervening padding bytes (or bytes reserved for the memory manager's internal use) but it can't guarantee that there are not.
Ben Voigt
@Ben: That is true. However, my response was to the comment by Omnifarious stating that you can **only** get page-sized blocks from the OS. You are correct in saying the point he was trying to get across is correct though.
Billy ONeal
+8  A: 

No, not necessarily. It is possible that some architectures/compilers will align structures on boundaries such that there are different spacings between structs in an array and structs on the call stack. You're allocating on the call stack and treating them as a contiguous array, that's an unreliable assumption.

David Gladfelter
+1. If he used a type where the size was not equal to the alignment, this code would fail, as it should.
DeadMG
+4  A: 

No - quite often the compiler/linker will place variables in wildly different locations that bear no relation to their location in the source files.

I've had the distinct pleasure to have to work with C code that was translated from assembly that made this assumption all over the place (in assembly, when you have 2 variables next to each other in the same segment/section, that's what you'll get in the image).

That wasn't fun...

Note that you may have tools at your disposal (linker config files or compiler pragmas, for example) that might give you some control over the placement of variables in memory. But at that point, you're decidedly in 'implementation specific' territory, and you still might have to deal with alignment issues and whatnot (though if the structs are the same size, that's probably not going to be a concern).

A portable way to have 2 structs adjacent to one another is to wrap them inside another struct (again, there might still be issues with pointer arithmetic and aliasing, though).

Michael Burr
With back-to-back I didn't mean their distance in the source code, but in memory. Would be pretty funny if it was dependent on source-code distance though.
SoulBeaver
@SoulBeaver: I understand that you're looking to know whether the structs would end up adjacent in memory, but I thought you were asking if this was something that might be controlled by the placement in the source file. What other factor were you thinking might control the placement?
Michael Burr
@Michael: Size, memory fragmentation, division of the two sizes( perhaps the chance that they are a contiguous block in memory is dependent on whether or not they are a multiple of each other ), OS and Compiler optimizations, that kind of stuff. My question was more general, however, to see if it always did that or if that's just a dangerous asssumption. Which it seems to be.Thank you for the edit however! That struct in a struct idea is pretty good.
SoulBeaver
+3  A: 

No, this is not guaranteed to work.

First and foremost, there is no guarantee on the placement of unrelated objects in memory. The only times that objects are guaranteed to be placed contiguously in memory are

  • if you put them next to each other yourself (e.g., you manually allocate some number of bytes and place them one after the other) or
  • if they are in an array (array elements are always contiguous

So, the following is valid:

int ints[2];
*(&ints[0] + 1) = 42;

The code in the question is not valid because the foo and bar objects are entirely unrelated and could be placed anywhere in memory.

There is a somewhat related issue that within a struct there may be unnamed padding bytes either

  • between members of the struct (so that members are correctly aligned) and
  • at the end of the struct (so that objects of that struct type can be placed contiguously in an array)

This means that the following is not valid because there could be unnamed padding at the end of a Foo:

Foo foos[2];
*(&foos[0].a + 1) = 42;

As a rule, you should never rely on the placement of two objects relative to each other unless they are in an array. It doesn't matter whether the objects are declared next to each other in the source code, they have the same type, or they have the same size: there simply is no guarantee.

James McNellis
+1  A: 

I remember encountering a situation using the whole program optimization on gcc where it completely reordered all the global variables around as part of the optimization process. That said, there is no such guarantee for such kind of layouts for separate variables in C++.

5ound
+1  A: 
KikoV