4

One of the solutions which often turns up in several posts on how to determine if a void * points to aligned memory involves casting the void pointer. That is say I get a void *ptr = CreateMemory() and now I want to check if memory pointed to by ptr is a multiple of some value, say 16.

See How to determine if memory is aligned? which makes a specific claim.

A pointer p is aligned on a 16-byte boundary iff ((unsigned long)p & 15) == 0.

A solution in a similar vein appears in this post. How to check if a pointer points to a properly aligned memory location?

Can someone clarify, how does this casting work? I mean, if I had a void *ptr = CreateMem();, it seems to me that (unsigned long)(ptr) would give me the pointer itself reinterpreted as an unsigned long. Why would the value of this reinterpreted void pointer have any bearing on the alignment of the memory pointed to?

EDIT: Thanks for all the useful comments. Please bear with me a bit more. Perhaps a simplified example will help me understand this better.

#include <iostream>
using namespace std;
struct __attribute__((aligned(16))) Data0 {
  uint8_t a;  
};

struct Data1 {
  uint8_t a;  
};

int main() {
    std::cout<<sizeof(Data0) << "\n"; //< --(1)
    std::cout<<sizeof(Data1) << "\n";
    Data0 ptr0[10];
    Data1 ptr1[10];
    std::cout<<(unsigned long) (ptr0 + 1) - (unsigned long) ptr0   << "\n"; //< --- (2)
    std::cout<<(unsigned long) (ptr1 + 1) - (unsigned long) ptr1   << "\n";
    return 0;
}

To date, I always interpreted aligned memory to have the following two requirements. sizeof() should return a multiple of the the specified size (See condition (1)). And the while incrementing a pointer to aligned struct array, the stride would end up also being a multiple of the specified size (See condition (2)).

So I am somewhat surprised to see there is a third requirement on the actual value of ptr0 as well. Or I might have misunderstood this entirely. Would the memory pointed to by ptr0 in the above example be considered aligned and not so for ptr1?

As I am typing this, I realize I do not actually understand what aligned memory itself means. Since I have mostly dealt with this while allocating cuda buffers, I tend to relate it to some sort of padding required for my data structs.

Consider a second example. That of aligned_alloc. https://en.cppreference.com/w/c/memory/aligned_alloc

Allocate size bytes of uninitialized storage whose alignment is specified by alignment.

I am not sure how to interpret this. Say, I do a void *p0 = aligned_alloc(16, 16 * 2), what is different about the memory pointed to by p0 as compared to say p1 where std::vector<char> buffer(32); char *p1 = buffer.data().

cplusplusrat
  • 1,435
  • 12
  • 27
  • 1
    a pointer is just a number containing a memory address. You should use `uintptr_t ` rather than `unsigned long` as the former is guaranteed to be large enough to hold any address – Alan Birtles Sep 17 '19 at 06:37
  • @AlanBirtles I understand pointer is just a number. But why does this number have any bearing on the alignment of the memory pointed to? – cplusplusrat Sep 17 '19 at 06:38
  • 3
    if the number is a multiple of 16 it is aligned to a 16 byte boundary – Alan Birtles Sep 17 '19 at 06:40
  • @AlanBirtles I am sure I am missing some c++ concept here. But as far as I can tell, we have a pointer and memory pointed to. What you are claiming is that the pointer value has some relationship to the alignment of the memory it points to? Is this correct? Is this part of the c++ standard? – cplusplusrat Sep 17 '19 at 06:46
  • I believe the answer is in https://stackoverflow.com/a/1898487/1416029 – mkrasowski Sep 17 '19 at 06:53
  • Yes a pointer is just a memory address. Aligned memory is stored at an address which is a multiple of a certain number. I don't understand what you don't understand. Maybe you could make your question clearer and more explicit? – Alan Birtles Sep 17 '19 at 07:01
  • @AlanBirtles I'm sure OP knows that if the memory is aligned it is stored on a multiple of a certain number. I believe OP wants to know why the cited answer suggests that if the value is multiple of a certain number it needs to be aligned... Am I reading the question right? – W.F. Sep 17 '19 at 07:19
  • 1
    To me, it sounds like what you're asking is in the definition of alignment. As in 16-byte alignment needs an address that is a multiple of 16 because that's what 16-byte alignment means in the first place. It feels like I'm missing something about the question, though. – chris Sep 17 '19 at 08:11
  • @chris Yes. I don't think I have truly grasped what aligned memory is and I think this discussion has helped me verbalize the questions a little better. I have updated the question. – cplusplusrat Sep 17 '19 at 17:50

2 Answers2

1

(unsigned long)(ptr) casting a pointer to an unsigned integer returns the pointer's memory address.

p & 15 is equivalent of p % 16, which is the rest of the division by 16. If it's 0, then it means the memory is aligned to multiple of 16.

((unsigned long)(ptr) & 15) == 0 returns true if the memory is aligned to multiple of 16.

uintptr_t is a better type for such casting, as it should be more platform-agnostic.

mkrasowski
  • 207
  • 1
  • 11
  • 1
    I am afraid this does not address my question. – cplusplusrat Sep 17 '19 at 06:53
  • Agreed. Though I believe that the answer to your question is already in the second comment of [How to determine if memory is aligned?](https://stackoverflow.com/a/1898487/1416029) – mkrasowski Sep 17 '19 at 12:51
1

Why would the value of this reinterpreted void pointer have any bearing on the alignment of the memory pointed to?

Because in common architectures the conversion of a void pointer to an unsigned long is the address of the memory pointed to by the pointer. Or because of the way conversion to an unsigned type works, the low bits of the address if it is too big to fit in an unsigned long. So is is enough to test for alignment because in that case you only need the lowest bits of the address.

I am not sure whether it is really mandated per standard, because the actual representation of a memory address if left to the implementation, and I can remember the segment+address representation used by 16 bits intel processors. In those architectures the addressable memory used 20 bits, and a far pointer (able to represent any address) was represented as a 32 bits unsigned value where the high 16 bits were the segment and the low 16 bits were the offset. Those were then combined as:

Segment S S S S _    (4 * 4 = 16 bits)
Offset  _ O O O O    (4 * 4 = 16 bits)
Address A A A A A    (5 * 4 = 20 bits)

You can see that this architecture did not allow to easily test for alignment higher than 16. For example this pointer value 0x00010040 is divisible by 64 but the actual address is 0x00050 (80) which is only divisible by 16.

But only dinosaurs can remember the Intel 8086 and its segmented address representation, and even with it, testing alignment up to 16 bytes was straightforward...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252