A malloc question, part 2

A

av

Now, you can avoid the casts by "cheating" with a "void *"
temporary variable:

T *p;
void *tmp;
T2 *q;

p = malloc(sizeof *p + N); /* room for 1 p, plus N extra bytes */
if (p == NULL) ... handle error ...
*p = whatever; /* OK */

tmp = p + 1; /* OK */

q = tmp; /* danger */

Even though the cast has now been removed, the danger remains.
The problem is -- still -- that "p + 1" is well-aligned for use as
a "T" (because p came from malloc, so it was well-aligned to start
with), but not necessarily for use as a "T2".

Again, the underlying fundamental problem -- which is hardware
and sometimes compiler dependent -- is "alignment".

It would perhaps be nice if C had a tool for checking whether
something was "well-aligned":

tmp = p + 1;
if (IS_WELL_ALIGNED_FOR_USE_AS_A_T2(tmp))
... proceed ...
else
... something ...

except this leaves two problems, one obvious:

- what do we put in the "else"?

and one slightly less obvious:

- what would we need to do to *guarantee* that tmp is well-aligned
for use as a T2?

i try the answer

#define IS_WELL_ALIGNED_FOR_USE_AS_A_T2(tmp) !((tmp)& (sizeof(T2)-1))

should be ok for all the above cases (x86, powerpc, arm)
 
C

Chris Dollin

Chris said:
On yet another kind of hardware, the ARM family of CPUs, something
even weirder happens. If you create a "poorly aligned" pointer,
and then ask the CPU to use it, the CPU *does* use it -- but it
first "shaves off" the bits that caues it to be poorly aligned,
resulting in a well-aligned pointer that it *can* use. The resulting
well-aligned pointer no longer points where the original, poorly-aligned
pointer used to point. As with the PowerPC, alignment requirements
are a function of data access size -- so the result is that:

*(int *)p = 42;

is equivalent to:

*(int *)((intptr_t)p & ~3) = 42;

On the Archimedes and RISC PC family of ARM machines, it is (or perhaps
was) even weirder, if I recall correctly.

The pointer is shaved, the word is fetched, and /then/ the shaved
address bits are used to rotate the fetched data bits so that the
low byte of the data is the byte a load-byte instruction would have
fetched.

This made loading halfwords easy: do the half-aligned fetch, then
clear out the high halfword. (Storing, not so simple.)

I think it was some collaboration between the CPU and the memory
management unit, rather than a purely CPU operation. But time
passes and bits degrade, so I may be worng in the details.
 
J

Jean-Marc Bourguet

av said:
i try the answer

#define IS_WELL_ALIGNED_FOR_USE_AS_A_T2(tmp) !((tmp)& (sizeof(T2)-1))

should be ok for all the above cases (x86, powerpc, arm)

Its safe but there are false negative: the alignment is a divisor of the
size as reported by sizeof.

Yours,
 
S

Simon Biber

David said:
This is an interesting topic on its own. He apparently wanted to calculate
the numerical difference between two pointers. I'm not sure if there is a
"legal" way to do this.

Any object in C may be accessed as if it were an array of unsigned char
by converting a pointer within that object to type (unsigned char *).

If you do that to both y and x, and subtract them, you will get their
difference in bytes, since the size of unsigned char is by definition 1.

Casting them to int, even if the operation is valid, is not guaranteed
to give the same result. Generally this is a problem if the size of int
is smaller than the size of a pointer.
 
E

Eric Sosman

av said:
i try the answer

#define IS_WELL_ALIGNED_FOR_USE_AS_A_T2(tmp) !((tmp)& (sizeof(T2)-1))

should be ok for all the above cases (x86, powerpc, arm)

Try it with any of

typedef struct { double x, y, z; } T2;
typedef int T2[42];
typedef char T2[8192];

.... and you'll see that the test is too stringent. It can be
shown that the alignment requirement for a type is a divisor
of the type's sizeof (otherwise arrays wouldn't work), but the
sizeof itself may overstate the requirement. (Also, the `&(N-1)'
trick only works for N a power of two; the % operator would be
an improvement.)
 
K

Keith Thompson

Simon Biber said:
Any object in C may be accessed as if it were an array of unsigned
char by converting a pointer within that object to type (unsigned char
*).

If you do that to both y and x, and subtract them, you will get their
difference in bytes, since the size of unsigned char is by definition
1.

Casting them to int, even if the operation is valid, is not guaranteed
to give the same result. Generally this is a problem if the size of
int is smaller than the size of a pointer.

It can also be a problem if pointers have a non-obvious
representation, and pointer-to-integer conversion just preserves the
bits.

I once ran across a statement in production software that assumed that
converting pointers to int and subtracting them was a valid way to
determine their difference. This failed to work on one of the
platforms that the software was expected to support. I changed it to
a simple pointer subtraction, which worked on all platforms.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top