Alignment on stack arrays

  • Thread starter H.K. Kingston-Smith
  • Start date
H

H.K. Kingston-Smith

I understand that a line like

char *a = malloc(64) ;

when succeeding will initialize a to a pointer that is aligned at worst
on the natural boundary of the underlying architecture, possibly on a
multiple thereof.

Now what about

char a[64] ;

Do we have the guarantee that &a[0] is always going to be aligned
according to the same criteria as above?
 
R

Richard Tobin

H.K. Kingston-Smith said:
Now what about

char a[64] ;

Do we have the guarantee that &a[0] is always going to be aligned
according to the same criteria as above?

No. You could use a union to ensure it is aligned suitably for
some particular other type(s).

-- Richard
 
B

Barry Schwarz

I understand that a line like

char *a = malloc(64) ;

when succeeding will initialize a to a pointer that is aligned at worst
on the natural boundary of the underlying architecture, possibly on a
multiple thereof.

malloc will return an address that is properly aligned for any type of
C object (at least in part because malloc has no idea what the type of
a is). There may well be other objects available in the architecture
with other alignment requirements that malloc knows nothing about.
Now what about

char a[64] ;

Do we have the guarantee that &a[0] is always going to be aligned
according to the same criteria as above?

No. In this case, the compiler knows at a[0] is a char and it only
has to align a on a "char boundary".

On most systems char has no alignment requirements, or equivalently is
aligned on a multiple of one, so this is not the best example.
Consider a non-C99 system where long and double are the most stringent
and need to be aligned on an 8-byte boundary while int needs only be
on a 4-byte boundary. malloc must return an address which is a
multiple of 8. But an int (or an array of int) can be aligned on any
multiple of 4 (of which only half are also multiples of 8).


Remove del for email
 
K

Keith Thompson

H.K. Kingston-Smith said:
I understand that a line like

char *a = malloc(64) ;

when succeeding will initialize a to a pointer that is aligned at worst
on the natural boundary of the underlying architecture, possibly on a
multiple thereof.

Now what about

char a[64] ;

Do we have the guarantee that &a[0] is always going to be aligned
according to the same criteria as above?

As others have said, the answer is no.

However, it's fairly likely that a 64-byte object will be more
strictly aligned anyway, simply because it can make some operations
more efficient. For example, if you call

memset(a, 0, sizeof a);

then memset() *might* be able to zero a word at a time if a is
word-aligned.

Don't let the fact that a is *likely* to be word-aligned (whatever a
"word" happens to be) fool you into thinking that it's guaranteed.

Others have also suggested using a union to force it to have a
stricter alignment. That's likely to be good enough, but if you want
the same alignment guarantees provided by malloc(), there's really no
portable way to do it.
 
B

Bart

        I understand that a line like

        char *a = malloc(64) ;

when succeeding will initialize a to a pointer that is aligned at worst
on the natural boundary of the underlying architecture, possibly on a
multiple thereof.

        Now what about

        char a[64] ;

Do we have the guarantee that &a[0] is always going to be aligned
according to the same criteria as above?

I don't know if you actually need a solution or not, but anyway..

If you're happy using pointers to the 'stack' arrays, then perhaps
something like the following. I need a 16-character array to be
aligned to a multiple of 8. An extra 7 bytes are allocated and a
pointer to the array is stepped until the alignment is correct.

This makes some assumptions however about being able to access the
bottom 3 address lines via the pointer value.

#include <stdio.h>

int main(void)
{
char a0[23]; /* Need char a[16] */
char *a=a0;

while ((int)a & 7) ++a;

printf("%x\n",a);

}
 
V

vippstar

I understand that a line like
char *a = malloc(64) ;
when succeeding will initialize a to a pointer that is aligned at worst
on the natural boundary of the underlying architecture, possibly on a
multiple thereof.
Now what about
char a[64] ;
Do we have the guarantee that &a[0] is always going to be aligned
according to the same criteria as above?

I don't know if you actually need a solution or not, but anyway..

If you're happy using pointers to the 'stack' arrays, then perhaps
something like the following. I need a 16-character array to be
aligned to a multiple of 8. An extra 7 bytes are allocated and a
pointer to the array is stepped until the alignment is correct.

This makes some assumptions however about being able to access the
bottom 3 address lines via the pointer value.
That code snipped makes much more assumptions than just that.
#include <stdio.h>

int main(void)
{
char a0[23]; /* Need char a[16] */
char *a=a0;

while ((int)a & 7) ++a;

printf("%x\n",a);

}
That code is rubbish. Please don't recommend code that doesn't work.
 
B

Bart

#include <stdio.h>
int main(void)
{
char a0[23];     /* Need char a[16] */
char *a=a0;
while ((int)a & 7) ++a;

}

That code is rubbish. Please don't recommend code that doesn't work

It's not a recommendation. It's just ideas. But as for not working:

#include <stdio.h>

int main(void)
{
char a0[23];
char *a=a0;

printf("Address before: %x\n",a);

while ((int)a & 7) ++a;

printf("Address after: %x\n",a);
}

Output:

Address before: 12ff59
Address after: 12ff60
 
K

Keith Thompson

Bart said:
If you're happy using pointers to the 'stack' arrays, then perhaps
something like the following. I need a 16-character array to be
aligned to a multiple of 8. An extra 7 bytes are allocated and a
pointer to the array is stepped until the alignment is correct.

This makes some assumptions however about being able to access the
bottom 3 address lines via the pointer value.

#include <stdio.h>

int main(void)
{
char a0[23]; /* Need char a[16] */
char *a=a0;

while ((int)a & 7) ++a;

printf("%x\n",a);

}

That might be acceptable in system-specific code (though incrementing
the address one at a time is wasteful).

If you want alignment matching a particular type, declare an object of
that type or use a union. If you want alignment appropriate for any
possible type, use malloc.
 
B

Bart

Keith Thompson said:
char a0[23]; /* Need char a[16] */
char *a=a0;

while ((int)a & 7) ++a;
That might be acceptable in system-specific code (though incrementing
the address one at a time is wasteful).

That's true. Maybe it could even be done in the char *a=a0 line. But I
was more likely to get it right this way..
If you want alignment matching a particular type, declare an object of
that type or use a union.

Yes, but doesn't the union get in the way? You'd have to use u.a
instead of a. If there already existed lots of references to a, it
would mean many changes and is a little obfuscatory.

Also the obvious solution (to use, for example, int a[16] then access
it as as though it were char a[64]), would have casts sprinkled
everywhere. The use of macros could alleviate these shortcomings
although the global nature of macros might be a problem.

My pointer idea leaves the original code using a[] unchanged but at a
cost of a pointer dereference (plus a few wasted bytes in the array).
This might be acceptable if a word-aligned char buffer in automatic
storage is what's wanted.
 
F

Flash Gordon

Bart wrote, On 15/06/08 22:00:
#include <stdio.h>
int main(void)
{
char a0[23]; /* Need char a[16] */
char *a=a0;
while ((int)a & 7) ++a;
printf("%x\n",a);
}
That code is rubbish. Please don't recommend code that doesn't work

It's not a recommendation. It's just ideas. But as for not working:

<snip>

Implementations where pointers are larger than int are becoming popular
again (a lot of 64 bit implementations have this property) and on such
implementations casting the pointer to int invokes undefined behaviour.
It may well still work (and is likely to on a lot of implementations)
but it is not guaranteed and the compiler is under no obligation to make
it work on such systems.
 
F

Flash Gordon

Bart wrote, On 15/06/08 23:39:
Keith Thompson said:
char a0[23]; /* Need char a[16] */
char *a=a0;

while ((int)a & 7) ++a;
That might be acceptable in system-specific code (though incrementing
the address one at a time is wasteful).

That's true. Maybe it could even be done in the char *a=a0 line. But I
was more likely to get it right this way..

Well, it is often said here that one should get it right before worrying
about efficiency, but...
If you want alignment matching a particular type, declare an object of
that type or use a union.

Yes, but doesn't the union get in the way? You'd have to use u.a
instead of a. If there already existed lots of references to a, it
would mean many changes and is a little obfuscatory.


Why?

char *a = a0.c

Where a0.c is the part of the union that is a char array.
Also the obvious solution (to use, for example, int a[16] then access
it as as though it were char a[64]), would have casts sprinkled
everywhere.

Why?

int a0[WHATEVER];
char *a = (char*)a0;
The use of macros could alleviate these shortcomings
although the global nature of macros might be a problem.

My pointer idea leaves the original code using a[] unchanged but at a
cost of a pointer dereference (plus a few wasted bytes in the array).
This might be acceptable if a word-aligned char buffer in automatic
storage is what's wanted.

There is nothing about the other methods that requires changing the rest
of the code. You used an additional pointer, and that additional pointer
could be used with any other conceivable method!
 
V

vippstar

#include <stdio.h>
int main(void)
{
char a0[23]; /* Need char a[16] */
char *a=a0;
while ((int)a & 7) ++a;
printf("%x\n",a);
}
That code is rubbish. Please don't recommend code that doesn't work

It's not a recommendation. It's just ideas. But as for not working:

#include <stdio.h>

int main(void)
{
char a0[23];
char *a=a0;

printf("Address before: %x\n",a);

while ((int)a & 7) ++a;

printf("Address after: %x\n",a);

}

Output:

Address before: 12ff59
Address after: 12ff60
What is that supposed to prove?
You invoke undefined behavior in the while loop and in the printf call
and there is no 'return' from main.
Here's a possible fix:
/* a = ... */
while((uintptr_t)a & 7) ++a;
printf("%p\n", (void *)a);

But its purpose I am not aware of.
 
B

Bart

#include <stdio.h>
int main(void)
{
char a0[23];
char *a=a0;
printf("Address before: %x\n",a);
while ((int)a & 7) ++a;
printf("Address after:  %x\n",a);


Address before: 12ff59
Address after:  12ff60

What is that supposed to prove?

That the int representation of a pointer has had it's 3 low bits
changed from 001 to 000. Which is very likely to correspond to the low
bits of the physical address.
You invoke undefined behavior in the while loop and in the printf call

Why? Converting a pointer to an int isn't exactly unheard of. It's
best to use an int big enough to represent the pointer, but I have a
feeling it will just be truncated if not, and I'm only interested in
the bottom 3 bits anyway. The pointer is only changed using ++a.

For printing, again I'm interested in the /int/ representation of the
pointer (also I'd forgotten about %p).
and there is no 'return' from main.

Well my compilers (3 out of 4 anyway) keep insisting on not
complaining about it.. so I'm not going to argue when a 5-line
expression of an /idea/ is at stake.
Here's a possible fix:
/* a = ... */
while((uintptr_t)a & 7) ++a;
printf("%p\n", (void *)a);

Yes nice textbook stuff.
But its purpose I am not aware of

Clearly not.
 
V

vippstar

#include <stdio.h>
int main(void)
{
char a0[23];
char *a=a0;
printf("Address before: %x\n",a);
while ((int)a & 7) ++a;
printf("Address after: %x\n",a);
}
Output:
Address before: 12ff59
Address after: 12ff60
What is that supposed to prove?

That the int representation of a pointer has had it's 3 low bits
changed from 001 to 000. Which is very likely to correspond to the low
bits of the physical address.
It is not guaranteed that int can correctly represent a pointer.
Why? Converting a pointer to an int isn't exactly unheard of. It's It is not unheard, so?
best to use an int big enough to represent the pointer, but I have a
int is not guaranteed to be big enough to represent the pointer.
Perhaps you mean integer type big enough; in which case yes, there is
intptr_t and uintptr_t, and indeed it's best to use these.
feeling it will just be truncated if not, and I'm only interested in
the bottom 3 bits anyway. The pointer is only changed using ++a.
It has nothing to do with "truncated".
See 6.3.2.3 p 6
For printing, again I'm interested in the /int/ representation of the
pointer (also I'd forgotten about %p).
What int representation? That doesn't make sense. You are passing
different types than the ones expected in a variadic function/macro
which causes undefined behavior.
Well my compilers (3 out of 4 anyway) keep insisting on not
complaining about it.. so I'm not going to argue when a 5-line
expression of an /idea/ is at stake.
Most likely because you compile with C99 mode, in which is fine not to
return a value from main.
Yes nice textbook stuff.
But, some lines ago you said it's *best* to use an integer type big
enough.
 
B

Bart

Bart wrote, On 15/06/08 23:39:
Yes, but doesn't the union get in the way? You'd have to use u.a

Why?

char *a = a0.c

Where a0.c is the part of the union that is a char array.
Also the obvious solution (to use, for example, int a[16] then access
it as as though it were char a[64]), would have casts sprinkled
everywhere.

Why?

int a0[WHATEVER];
char *a = (char*)a0;
There is nothing about the other methods that requires changing the rest
of the code. You used an additional pointer, and that additional pointer
could be used with any other conceivable method!

You're completely right. I'd only considered the union and
equivalenced schemes for fast direct access. With an extra pointer
level to play with, there are other, simpler, possibilities than my
idea (although manipulating a pointer like that to play around with
alignments still has uses).

Never mind, the OP now has some concrete examples to look at. And I'm
off to bed..
 
C

christian.bau

#include <stdio.h>

int main(void)
{
char a0[23];     /* Need char a[16] */
char *a=a0;

while ((int)a & 7) ++a;

printf("%x\n",a);

}

This is quite non-portable. You don't know how (int) a changes when a
is incremented. You seem to think that inrementing a changes the last
three bits in (int) a. That is in no way guaranteed.
 
B

Bart

#include <stdio.h>
int main(void)
{
char a0[23];     /* Need char a[16] */
char *a=a0;
while ((int)a & 7) ++a;

}

This is quite non-portable. You don't know how (int) a changes when a
is incremented. You seem to think that inrementing a changes the last
three bits in (int) a. That is in no way guaranteed.

No, but I did add this comment:

Bart wrote:
...
This makes some assumptions however about being able to access the
bottom 3 address lines via the pointer value.

For this example, there were more direct ways of achieving the
result..

But in general, I think there are many implementations where the lower
bits of a pointer, converted to an int, do directly correspond to
address lines. And in this case why not exploit that? If you can't
mess with this sort of thing in C, what non-ASM language can?
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top