6

I am making a memory block copy routine and need to deal with blocks of raw memory in efficient chunks. My question is not about the specialized copy routine I'm making, but in how to correctly examine raw pointer alignment in C.

I have a raw pointer of memory, let's say it's already cast as a non-null char *. In my architecture, I can very efficiently copy memory in 64 byte chunks WHEN IT IS ALIGNED TO A 64 BYTE chunk. So the (standard) trick is that I will do a simple copy of 0-63 bytes "manually" at the head and/or tail to transform the copy from an arbitrary char* of arbitrary length to a 64 byte aligned pointer with some multiple of 64 bytes in length.

Now the question is, how do you legally "examine" a pointer to determine (and manipulate) its alignment? The obvious way is to cast it into an integer and just examine the bits:

char *pointer=something.
int p=(int)pointer;
char *alignedPointer=(char *)((p+63)&~63);

Note here I realize that alignedPointer doesn't point to the same memory as pointer... this is the "rounded up" pointer that I can call my efficient copy routine on, and I'll handle any other bytes at the beginning manually.

But compilers (justifiably) freak out at casting a pointer into an integer. But how else can I examine and manipulate the pointer's lower bits in LEGAL C? Ideally so that with different compilers I'd get no errors or warnings.

SPWorley
  • 11,550
  • 9
  • 43
  • 63
  • That should be ok as long as `int` is the same size as your pointer types. – Carl Norum Feb 17 '10 at 23:39
  • You may also want to have a look at http://stackoverflow.com/questions/1898153/how-to-determine-if-memory-is-aligned-testing-for-alignment-not-aligning/1898194 – Robert Paulson Feb 17 '10 at 23:43
  • Ah, but you presuppose that pointers are stored in binary from MSB to LSB. What do we have? *undefined behavior!* (said in the same way that the lethal weapon guy says *diplomatic immunity!*) Just because it works in the real world doesn't make it any less undefined. ;-) – Richard Pennington Feb 17 '10 at 23:44
  • 1
    @pennington - why do you say endian-ness is presupposed? If you say "& 0x0f", the meaning of the 0x0F is unambiguous. It will be stored by the compiler in the same endian-ness as the pointers. Won't it? Now, if you cast your pointer to an array of bytes, then you would indeed have to worry. – JustJeff Feb 17 '10 at 23:52
  • 2
    Has the system `memcpy( )` on your platform really not been tuned to take advantage of this? – Stephen Canon Feb 17 '10 at 23:52
  • @JustJeff: Trying to be funny. There is no guarantee that pointers are stored in binary. ;-) – Richard Pennington Feb 17 '10 at 23:55
  • @pennington - ouch! yeah that's right. let us never forget the dark days of the 8088! =) – JustJeff Feb 17 '10 at 23:59
  • @justjeff the meaning of int &0x0f is unambigous. The problem is that you started with a pointer and lied to the compiler when you went (int)pointer. ON a machine thats stores pointers with different format than simple integers then you are toast. I have worked on machines where pointers cast to integers are meaningless when manipulated under int mask operations (unless you knew the format of pointers to start with). Just to add spice on that system NULL pointers were 0xFFFFFFFF – pm100 Feb 18 '10 at 00:10
  • @pm100 I'm not familiar with such machines (apart from the segmented pointers of 8088, for which you could still do manipulation of the lower few bits I think). I'd be interested to hear what machines you are referring to. – Craig McQueen Feb 18 '10 at 00:21
  • What kind of scum-sucking architecture do you use where the CRT's implementation of memcpy() doesn't already do this? I'm collecting buying advice here. – Hans Passant Feb 18 '10 at 01:36
  • I've seen this on compilers targeting systems with a modified version of a common architecture (such as MIPS or POWER). It's annoying, but not quite angering :) The compiler just comes with the generic runtime library, that uses only the standard instruction set. HW-oriented companies don't always have the software-oriented resources to get a custom library written, it seems. –  Feb 19 '10 at 21:44
  • Yeah, but `memcpy` is like ... the most basic of the basic. If you're going to optimize *anything at all*, you optimize `memcpy`. – Stephen Canon Feb 19 '10 at 23:46

4 Answers4

7

For integer types that are large enough to hold pointers, C99 stdint.h has:

For data lengths there are:

which have been around since well before C99.

If your platform doesn't have these, you can maximise your code's portability by still using these type names, and making suitable typedefs for them.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
1

I don't think that in the past people were as reluctant to do their own bit-banging, but maybe the current "don't touch that" mood would be conducive to someone creating some kind of standard library for aligning pointers. Lacking some kind of official api, you have no choice but to AND and OR your way through.

JustJeff
  • 12,640
  • 5
  • 49
  • 63
0

Instead of int, try a datatype that's guaranteed to be the same size as a pointer (INT_PTR on Win32/64). Maybe the compiler won't freak out too much. :) Or use a union, if 64-bit compatibility is not important.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
0

Casting pointers to and from integers is valid, but the results are implementation-defined. See section 6.3.2.3 of the standard. The intention seems to be that the results are what anybody familiar with the system would expect, and indeed this appears to be routinely the case in practice.

If the architecture in question can efficiently manipulate pointers and integers interchangeably, and the issue is just whether it will work on all compilers for that system, then the answer is that it probably will anyway.

(Certainly, if I were writing this code, I would think it fine as-is until proven otherwise. My experience has been that compilers for a given system all behave in very similar ways at this sort of level; the assembly language just suggests a particular approach, that all then take.)

"Probably works" isn't very good general advice though, so my suggestion would be just write the code that works, surround it enough suitable #ifdefs that only the known compiler(s) will compile it, and defer to memcpy in other cases.

#ifdef is rarely ideal, but it's fairly lightweight compared to other possibilities. And if implementation-defined behaviour or compiler-specific tricks are needed then the options are pretty limited anyway.

  • "the assembly language just suggests a particular approach"-- I'm just going to start using this phrase in software debates, regardless of applicability. – davidtbernal Aug 20 '10 at 05:13