By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,463 Members | 2,858 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,463 IT Pros & Developers. It's quick & easy.

A question regarding the portability in "alligned memory allocation" post...

P: 3
Hi,

In the alligned memory allocation post (a quite good article indeed).

Christian Bau suggests the following:
Expand|Select|Wrap|Line Numbers
  1. char* p = calloc (bytes + alignment + sizeof (void *));
  2. char* q = p + sizeof (void *) + alignment;
  3. q -= ((unsigned long) q) % alignment;
  4.  
  5. // If you want your code unreadable like the original author,
  6. // then change the last line to
  7. // q -= ((unsigned long) q) & (alignment - 1);
  8.  

My question is: Is the following statement portable (I have turned unsigned long into uintptr_t) - My concern is more about the % or & operators ?
Expand|Select|Wrap|Line Numbers
  1. q -= ((uintptr_t) q) % alignment;
  2.  
Guilleuss
Dec 3 '09 #1
Share this Question
Share on Google+
4 Replies


Banfa
Expert Mod 5K+
P: 8,916
Yes it is portable, in fact it is more portable than the original since you are using the integer type that is defined as being the right size to hold a pointer, the original would fail on 64 bit systems on Windows for example because unsigned long has 32 bits and the pointer has 64 bits.
Dec 3 '09 #2

P: 3
Thanks for your quick answer.

In fact, my question was more about the usage of the operators & or % in this case:

Since we convert the pointer into an integer and then apply the operator %, I wonder if we can use this way to align addresses on all systems.
Dec 3 '09 #3

Banfa
Expert Mod 5K+
P: 8,916
Actually I just realised its non-portable but I will get onto that.

In the last line, in both cases, the arithmetic on the right hand side of the equation is all integer arithmetic so there is no problem with that, everything is operating on an integer and the result is a (relatively small) integer that is then used in pointer arithmetic to adjust the size of the pointer all seems fine.

But I said all seems fine and here is the kicker, the standard makes no requirements on the value of an integer that has got its value from casting from a pointer other than the value can be cast back to a similar pointer. Specifically the standard does not require that the value of the integer is the actual address of the memory location (although it would have to be related in some way) but if you imagine a system where the value of the integer representation of a pointer was not the actual memory address the arithmetic doesn't work.

Heres a worked example, imagine for a moment a system that has 31 bit addresses and uses the least significant bit to store a status or trap value of some kind for the memory location with the most significant 31 bits used for the actual memory address. A memory location of 0x00001003 would have a pointer value of 0x0001003x (where x is the status bit) assuming a status of 0 and an alignment of 8 and the right side of the equation becomes

0x00010030 % 8 = 0

However to get the address 0x00001003 to an 8 byte aligned address you need an offset of

0x00001003 % 8 = 3

The maths doesn't cause a problem, the pointer arithmetic isn't an issue but because the standard makes no requirement of the value of pointers the logic in calculating the required offset can break down when the pointers aren't the actual memory address.

However it does have to be said that I have never worked on a system where pointers were the actual memory address.


However another important point is that malloc guarantees to return a block of memory at a location that is aligned suitable for any object that the system may have. That means that if you are allocating memory only for use by the processor then you would never have to do you own alignment adjustment like this.

The only time I have seen something like this done (and I have seen it once) was when the memory was shared with an external hardware device and that external device was more efficient if the memory blocks where allocated at a specific alignment that was greater than the processors own alignment. In that case you are writing code for a very specific hardware platform (not just processor but external hardware too) so using a non-portable solution may be acceptable (well was) but it should be very clearly documented/commented.
Dec 3 '09 #4

P: 3
Ok, just some more precisions and reformulations...

Actually I just realised its non-portable but I will get onto that.
This is what I was suspecting.

In the last line, in both cases, the arithmetic on the right hand side of the equation is all integer arithmetic so there is no problem with that, everything is operating on an integer and the result is a (relatively small) integer that is then used in pointer arithmetic to adjust the size of the pointer all seems fine.
I do agree.

But I said all seems fine and here is the kicker, the standard makes no requirements on the value of an integer that has got its value from casting from a pointer other than the value can be cast back to a similar pointer. Specifically the standard does not require that the value of the integer is the actual address of the memory location (although it would have to be related in some way) but if you imagine a system where the value of the integer representation of a pointer was not the actual memory address the arithmetic doesn't work.
I also agree and I would just add that with the standard there is no insurance that:
- We will get the same integer values when the same pointer is casted twice into uintptr_t; we only have the insurance that once casted back the pointers will compare equal (so hashing the resulting integer is platform specific).
- We will get a valid pointer if we perform any arithmetics on the resulting integer.
- Casting a pointer into an unsigned long (or unsigned long long) and into an uintptr_t will produce the same values (there maybe some platform specific computations done)

Heres a worked example, imagine for a moment a system that has 31 bit addresses and uses the least significant bit to store a status or trap value of some kind for the memory location with the most significant 31 bits used for the actual memory address. A memory location of 0x00001003 would have a pointer value of 0x0001003x (where x is the status bit) assuming a status of 0 and an alignment of 8 and the right side of the equation becomes
I think that we should have 0x0002006 + x = 0x1003 * 2 + x.

And consequently:
0x0002006 % 8 = 6

Nevertheless your demonstration is still relevant.

I have had a look to the way the memalign function is implemented in glibc2.11 and something similar to "q -= ((uintptr_t) q) % alignment;" is done.

My guess is that using this method to align the memory should work on most of the platforms but is somewhat specific (and not so portable).
Dec 4 '09 #5

Post your reply

Sign in to post your reply or Sign up for a free account.