How does compiler place variable and structure variable in memory

Thread Starter

Embededd

Joined Jun 4, 2025
204
So, when there is no data, I can visualize the memory like this:
Code:
Address     Byte 1   Byte 2   Byte 3   Byte 4
----------------------------------------------
0x00       empty   empty   empty   empty
0x04       empty   empty    empty   empty
0x08       empty   empty    empty   empty
0x0C       empty   empty    empty   empty
Each row represents a 4-byte region. For example, the row starting at 0x00 represents addresses 0x00–0x03, and the row starting at 0x04 represents addresses 0x04–0x07.

Suppose we define a structure like this:
C:
struct test {
    char a;
    int x;
};
Let's assume a contains 'A' and x contains the 4-byte value 0x12345678.

With alignment, I understand the memory layout would look like this:

Code:
Address     Byte 1     Byte 2     Byte 3     Byte 4
----------------------------------------------------
0x00        'A'        padding    padding    padding
0x04        0x12       0x34       0x56       0x78
0x08        empty      empty      empty      empty
Here, the 4-byte int starts at 0x04, which is a 4-byte boundary, so all four bytes of the int fit inside one 4-byte region.

Without alignment, the 4-byte value could theoretically start immediately after the char:

Code:
Address     Byte 1     Byte 2     Byte 3     Byte 4
----------------------------------------------------
0x00        'A'        0x12       0x34       0x56
0x04        0x78       empty      empty      empty
0x08        empty      empty      empty      empty
Here, the int starts at 0x01 and crosses the 0x04 boundary. In the simplified 32-bit memory model we're discussing, the CPU may need two memory accesses to obtain the complete 4-byte value, followed by additional work to combine the bytes.

So I think the main advantage of alignment is that it can make memory access simpler and more efficient.

The trade-off is that we use some extra memory for padding. In this example, we waste 3 bytes of memory to ensure that the 4-byte int starts at a 4-byte boundary.
 
Last edited:

BobTPH

Joined Jun 5, 2013
11,609
My understanding is that RAM is not physically divided into fixed 4-byte blocks. Each address represents a byte, so the CPU can request data starting from any address.
Your understanding is wrong, as explained by multiple people multiple tines. If you start with a false assumption, you cannot get to the truth until you discard it.

The RAM used in PCs reads larger data blocks, which has changed over time, but more like 64 bits or more. The original 8088 had an 8-bit data bus as you assume, but everything since uses wider memory paths.

Here is Google’s summary:


The external data bus size in the x86 processor family ranges from eight bits in early budget variants up to 64 bits in modern multi-core processors. Bus widths scaled up across generations to match performance needs, transitioning from 8-bit and 16-bit designs to 32-bit and 64-bit standards. [1]

Early 8-bit and 16-bit Generations
    • 8088 / 80188: 8-bit data bus (used to lower system cost with 8-bit peripheral chips).
    • 8086 / 80186: 16-bit data bus.
    • 80286: 16-bit data bus.
    • 386SX: 16-bit data bus (cost-reduced version of the 386 line). [1, 2, 3]

32-bit Generations (IA-32)
    • 386DX: 32-bit data bus.
    • 80486 (and variants): 32-bit data bus.
    • Pentium / Pentium Pro / Pentium II / Pentium III: 64-bit data bus (despite having 32-bit internal architecture registers, the external data bus widened to 64 bits starting with the original Pentium to fetch data faster). [1, 2, 3, 4, 5]

64-bit Generations (x86-64 / AMD64)
    • Modern x86-64 processors: 64-bit external data bus, paired with extended internal 64-bit general-purpose registers and wider internal vector/SIMD data paths (such as 128-bit SSE, 256-bit AVX, and 512-bit AVX-512). [1, 2]

If you'd like, I can also break down the corresponding address bus sizes and maximum memory limits for each of these x86 generations.
 

Futurist

Joined Apr 8, 2025
925
So, when there is no data, I can visualize the memory like this:
Code:
Address     Byte 1   Byte 2   Byte 3   Byte 4
----------------------------------------------
0x00       empty   empty   empty   empty
0x04       empty   empty    empty   empty
0x08       empty   empty    empty   empty
0x0C       empty   empty    empty   empty
Each row represents a 4-byte region. For example, the row starting at 0x00 represents addresses 0x00–0x03, and the row starting at 0x04 represents addresses 0x04–0x07.

Suppose we define a structure like this:
C:
struct test {
    char a;
    int x;
};
Let's assume a contains 'A' and x contains the 4-byte value 0x12345678.

With alignment, I understand the memory layout would look like this:

Code:
Address     Byte 1     Byte 2     Byte 3     Byte 4
----------------------------------------------------
0x00        'A'        padding    padding    padding
0x04        0x12       0x34       0x56       0x78
0x08        empty      empty      empty      empty
Here, the 4-byte int starts at 0x04, which is a 4-byte boundary, so all four bytes of the int fit inside one 4-byte region.

Without alignment, the 4-byte value could theoretically start immediately after the char:

Code:
Address     Byte 1     Byte 2     Byte 3     Byte 4
----------------------------------------------------
0x00        'A'        0x12       0x34       0x56
0x04        0x78       empty      empty      empty
0x08        empty      empty      empty      empty
Here, the int starts at 0x01 and crosses the 0x04 boundary. In the simplified 32-bit memory model we're discussing, the CPU may need two memory accesses to obtain the complete 4-byte value, followed by additional work to combine the bytes.

So I think the main advantage of alignment is that it can make memory access simpler and more efficient.

The trade-off is that we use some extra memory for padding. In this example, we waste 3 bytes of memory to ensure that the 4-byte int starts at a 4-byte boundary.
Bear in mind, you are assuming a big-endian layout here, ARM processors can support big or little endianess, but the default is little endian, so the usual layout would be

Code:
Address     Byte 1     Byte 2     Byte 3     Byte 4
----------------------------------------------------
0x00        'A'        padding    padding    padding
0x04        0x78       0x56       0x34       0x12
0x08        empty      empty      empty      empty
finally, I'd recommend you say "unused" rather than "empty", those unused bytes will contain values, probably zero but not necessarily, every byte though always contains a value, so describing what it's role is rather than what it contains, will make your understanding to be clearer to people.
 
Last edited:

Thread Starter

Embededd

Joined Jun 4, 2025
204
Your understanding is wrong, as explained by multiple people multiple tines. If you start with a false assumption, you cannot get to the truth until you discard it.
I think you may have missed my post #21, where I clarified that point.
Bear in mind, you are assuming a big-endian layout here, ARM processors can support big or little endianess, but the default is little endian, so the usual layout would be
You were right, I didn't pay attention to the byte order.
finally, I'd recommend you say "unused" rather than "empty"
Yes I mean unused
Also, if the first variable is 1-byte long, there is no need to pad the unused three bytes. They can contain any 8-bit pattern.
Thank you for the point
 

WBahn

Joined Mar 31, 2012
33,065
Your understanding is wrong, as explained by multiple people multiple tines. If you start with a false assumption, you cannot get to the truth until you discard it.
It would be nice if you didn't attribute statements, especially wrong ones, to me when I'm not the one that made them.
 

WBahn

Joined Mar 31, 2012
33,065
So, when there is no data, I can visualize the memory like this:
Code:
Address     Byte 1   Byte 2   Byte 3   Byte 4
----------------------------------------------
0x00       empty   empty   empty   empty
0x04       empty   empty    empty   empty
0x08       empty   empty    empty   empty
0x0C       empty   empty    empty   empty
I would recommend avoiding things like "empty" or anything else that implies that there is nothing there. There is ALWAYS something there. I prefer to use "unknown" or "?".

This is a trap that lots of people fall into, because they think that since there's "nothing there" that it somehow then behaves as if it has a value of 0 and then fail to catch uninitialized variables, even when they bench check a program by hand. I've seen it over and over, so it's valuable to adopt a convention that emphasizes that we don't know what is there.

Suppose we define a structure like this:
C:
struct test {
    char a;
    int x;
};
Let's assume a contains 'A' and x contains the 4-byte value 0x12345678.

With alignment, I understand the memory layout would look like this:

Code:
Address     Byte 1     Byte 2     Byte 3     Byte 4
----------------------------------------------------
0x00        'A'        padding    padding    padding
0x04        0x12       0x34       0x56       0x78
0x08        empty      empty      empty      empty
This would be correct if your machine is big endian. For a hypothetical exercise like this, that's fine. But I'd recommend working in little endian since that is what most machines you are likely to work with will be and you want that to be the natural way that you think about things, if for no other reason that it is NOT the natural way that we tend to think about this.

If you want to label the pad bytes "pad" instead of "unknown", that's fine and probably what I would do, just always keep in mind that they still contain unknown values.

Now consider what the layout would be if you had:

C:
struct test {
    char a; // contains 'A'
    char b; // contains 'B'
    int x; // contains 0x12345678
};
There are two possible layouts that satisfy the alignment rules:

Code:
Addr    0     1     2     3
-----------------------------
0x00   'A'   'B'   pad   pad
0x04  0x78  0x56  0x34  0x12
0x08    ?     ?     ?     ?

Addr    0     1     2     3
-----------------------------
0x00   'A'   pad   'B'   pad
0x04  0x78  0x56  0x34  0x12
0x08    ?     ?     ?     ?
The compiler is free to choose either one and different compilers will make different choices. The same compiler may even make a different decision after an upgrade or if the compiler options, such as optimization level, change. The main point is don't get sucked into relying an assumptions about how the compiler is going to behave once you get beyond defined behavior.
 

BobTPH

Joined Jun 5, 2013
11,609
It would be nice if you didn't attribute statements, especially wrong ones, to me when I'm not the one that made them.
Sorry about that. I accidentally quoted the TS’s words from your post that quoted him. Did not mean to attribute it to you.
 

Thread Starter

Embededd

Joined Jun 4, 2025
204
Thank you everyone for your help and for taking the time to explain this. I understand the structure padding much better now. I really appreciate all the explanations and corrections.
 
Top