-2

I am trying to find out alignment on my 64bit machine(Win10 on Intel iCore7). I thought of this experiment:

void check_alignment(char c1, char c2 )
{
   printf("delta=%d\n", (int)&c2 - (int)&c1); // prints 4 instead of 8
}

void main(){
    check_alignment('a','b');
}

I was expecting delta=8. Since it's 64bit machine char c1 and char c2 should be stored on multiples of 8. Isn't that right?

Even if we assume compiler has done optimization to have them stored in less space, why not just store them back to back delta=1? Why 4 byte alignment?

I repeated the above experiment with float types, and still gives delta=4

void check_alignment(float f1, float f2 )
{
   printf("delta=%d\n", (int)&c2 - (int)&c1); // prints 4
}

void main(){
    check_alignment(1.0,1.1);
}
doubleE
  • 1,027
  • 1
  • 12
  • 32
  • 2
    It is very possible that the arguments were passed via registers and the compiler was required to create temporary variables with automatic storage duration in order to satisfy the requirements of the address-of operator. In short, this has nothing to do with alighment. – Daniel Kamil Kozar Jul 31 '18 at 22:12
  • 1
    Post the code that calls `check_alignment()`. A [mcve] – chux - Reinstate Monica Jul 31 '18 at 22:14
  • "Since it's 64bit machine char c1 and char c2 should be stored on multiples of 8. Isn't that right? " No, it is not right. The compiler is at liberty to do whatever it wants, as long as the resulting program produces results that are compliant what what the C standard dictates. – dgnuff Jul 31 '18 at 22:18
  • The code invokes undefined behaviour. You are not allowed to subtract two pointers which don't point into (or exactly past) the same scalar or array. – too honest for this site Aug 01 '18 at 04:30

2 Answers2

6

Firstly, if your platform is 64-bit, then why are you casting your pointer values to int? Is int 64-bit wide on your platform? If not, your subtraction is likely to produce a meaningless result. Use intptr_t or ptrdiff_t for that purpose, not int.

Secondly, in a typical implementation a 1-byte type will typically be aligned at 1-byte boundary, regardless of whether your platform is 64-bit or not. To see a 8-byte alignment you'd need a 8-byte type. And in order to see how it is aligned you have to inspect the physical value of the address (i.e. whether it is divisible by 1, 2, 4, 8 etc.), not analyze how far apart two variables are spaced.

Thirdly, how far apart c1 and c2 are in memory has little to do with alignment requirements of char type. It is determined by how char values are passed (or stored locally) on your platform. In your case they are apparently allocated 4-byte storage cells each. That's perfectly fine. Nobody every promised you that two unrelated objects with 1-byte alignment will be packed next to each other as tightly as possible.

If you want to determine alignment by measuring how far from each other two objects are stored, declare an array. Do not try to measure the distance between two independent objects - this is meaningless.

klutt
  • 30,332
  • 17
  • 55
  • 95
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • I measured how far they are apart because they are passed as arguments of a function, so they will be placed immediately after each other in the function stack. That makes sure they are not located in random location in memory. – doubleE Jul 31 '18 at 22:16
  • @Learner: No, they won't be. Almost nobody uses "stack" to pass scalar arguments anymore. Most likely they would be passed in CPU registers, without involving any "stack" at all. The only reason your parameter got stored locally is that you applied `&` operator to them. This forced the compiler to store them (on the "stack"). And it stored them 4 bytes apart. But even if "stack" was used to pass the arguments, there's still nothing to prevent the compiler from passing them 4 bytes apart. There's no requirement to pack them tightly. – AnT stands with Russia Jul 31 '18 at 22:18
  • What would be a correct way? I used `long`, and still `delta=4`. It must be at least 8. – doubleE Jul 31 '18 at 22:24
  • @Learner: Where did you get this? `long` is at least 4, not 8. `long long` is at least 8. – AnT stands with Russia Jul 31 '18 at 22:25
  • 1
    @Learner: `(int)(&c2 - &c1)` would have been safer but still risky (e.g. after inlining, `c1` and `c2` might be far apart). But `(unsigned)&c2 - (unsigned)&c1` would definitely avoid UB, because unsigned overflow is defined as wrapping around. The low bytes of a subtraction result doesn't depend on the high bytes of the input, so it's ok to throw them away early if you want to assume that your objects are nearby. – Peter Cordes Jul 31 '18 at 22:44
  • 1
    @ant: on Windows x64, those two function args will be passed in RCX and RDX (so actually CL and DL for 1 byte integers). The compiler has to spill them to give them an address. (Or imagine spilling them, if the addresses are only subtracted.) x86-64 System V would be similar, but in RDI and RSI (actually dil / sil). – Peter Cordes Jul 31 '18 at 22:46
4

To determine the greatest fundamental alignment in your C implementation, use:

#include <stdio.h>
#include <stddef.h>

int main(void)
{
    printf("%zd bytes\n", _Alignof(max_align_t));
}

To determine the alignment requirement of any particular type, replace max_align_t above with that type.

Alignment is not purely a function of the processor or other hardware. Hardware might support aligned or unaligned accesses with different performance effects, and some instructions might support unaligned access while others do not. A particular C implementation might choose to require or not require certain alignment in combination with choosing to use or not use various instructions. Additionally, on some hardware, whether unaligned access is supported is configurable by the operating system.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    The OP might be interested to know that if you pass so many args that the calling convention doesn't have registers for them, the Windows x64 calling convention (like x86-64 System V) will pass them in 8-byte stack slots. (4 byte minimum alignment for stack args would have been a valid choice, too. [Difference in data alignment in struct vs parameter?](https://stackoverflow.com/q/51564190)). `max_align_t` doesn't tell you anything about the calling convention, but it's normal for the minimum stack alignment to be large enough to get `max_align_t` alignment without an extra `and` instruction. – Peter Cordes Aug 01 '18 at 05:15