|
|
structs, padding is added automatically, so that each field is located at an offset (from the start of the struct) that is an integer multiple of the field's size. For example, an int field would located at an offset that is an integer multiple of 4 (assuming sizeof(int) == 4), a double field would be located at an offset that is an integer multiple of 8 (assuming sizeof(double) == 8), and so on...structs. For example, GCC can use: __attribute__((__packed__))__attribute__((aligned(N))malloc() and friends do not give you any guarantees that the start address of the allocated memory block is aligned to anything bigger than 1 – even though, in reality, malloc() on most platforms has a certain minimum alignment. There are functions like _aligned_malloc() (MSVC) or posix_memalign() (Unix) to request a specific alignment.load or store instructions require that the source/target memory address is a multiple of N, when loading/storing an element of size N (in bytes). Often "unaligned" loads or stores are supported by the CPU, but they are less efficient, because each "unaligned" load or store effectively has to be translated into multiple "aligned" loads or stores!&sh = 00000069600FFB54 &a[0] = 00000069600FFB78 &a[1] = 00000069600FFB7C &a[2] = 00000069600FFB80 |
__attribute__((aligned(N))), then the compiler will use the "default" alignment for the target platform. This probably means that "global" or "local" variables will be aligned to an integer multiple of their type size.short variable likely would be aligned to a multiple of 2, and that the int array (and therefore each element of the array) likely would be aligned to a multiple of 4 – assuming that sizeof(short) == 2 and that sizeof(int) == 4. |
|
2, 0x564b6dbdf010 2, 0x564b6dbdf012 8, 0x564b6dbdf018 |
short variables are aligned to a multiple of 2, whereas the long variable is aligned to a multiple of 8. Because of this, the short variables happen to be stored "back to back", but there needs to be some extra space/gap (4 bytes) before the long variable. |
|
2, 0x562f1ba32010 2, 0x562f1ba32020 8, 0x562f1ba32030 |
|
|
&sh = 00000093A7CFF994 &sh2 = 00000093A7CFF9B4 &d = 00000093A7CFF9D8 |
| As far as dynamically allocated memory is concerned, I think malloc() and friends do not give you any guarantees that the start address of the allocated memory block is aligned to anything bigger than 1 – even though, in reality, malloc() on most platforms has a certain minimum alignment. There are functions like _aligned_malloc() (MSVC) or posix_memalign() (Unix) to request a specific alignment. |
"Immediate" operands are fixed values (constants) that are hard-coded into the program code. |
const int i {2}; ?)
| Could you write a simple example to figure out those fixed values. (Do you by any chance mean constant objects, e.g., const int i {2}; ?) |
i++ 😏i is the constant value 1, it can be hard-coded as "immediate" operand; otherwise we would have to first load the value "1" into a register and then add it to i from there, which clearly is an unnecessary overhead)
| Whenever the compiler can figure out the value of one of the operands as "fixed" (constant), at compile time, it can (and probably will) translate that value into an "immediate" operand. |
constexpr int i {3};?
const helps the compiler in optimizing, though.
|
|
short here is better at least in terms or RAM usage. Although registers might be 16, 32, 64 or more bytes in size, the memory used for short is 2 bytes while for int it's 4 bytes, usually. Am I correct?
|
|
mov WORD PTR [rbp-0x2],0x4d2 mov DWORD PTR [rbp-0x8],0x4d2 mov WORD PTR [rbp-0x2],0x1b0 mov DWORD PTR [rbp-0x8],0x1b0 |
mov WORD PTR [rbp-0x2],0x4d2 stores the WORD (2 bytes) value 0x4D2 (decimal: 1234) at the memory location rbp-0x2, i.e. at the address of the local variable sh. Similarly, the instruction mov DWORD PTR [rbp-0x8],0x4d2 stores the DWORD (4 bytes) value 0x4D2 (decimal: 1234) at the memory location rbp-0x8, i.e. at the address of the local variable i.[rbp-0x2] is a memory operand, whereas 0x4d2 is an immediate value. No registers involved.