Alignment on stack arrays

Discussion in 'C Programming' started by H.K. Kingston-Smith, Jun 15, 2008.

  1. 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?
     
    H.K. Kingston-Smith, Jun 15, 2008
    #1
    1. Advertising

  2. In article <g33gn4$fj6$>,
    H.K. Kingston-Smith <> wrote:

    > 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


    --
    In the selection of the two characters immediately succeeding the numeral 9,
    consideration shall be given to their replacement by the graphics 10 and 11 to
    facilitate the adoption of the code in the sterling monetary area. (X3.4-1963)
     
    Richard Tobin, Jun 15, 2008
    #2
    1. Advertising

  3. On Sun, 15 Jun 2008 16:41:09 +0000 (UTC), "H.K. Kingston-Smith"
    <> wrote:

    > 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
     
    Barry Schwarz, Jun 15, 2008
    #3
  4. "H.K. Kingston-Smith" <> writes:
    > 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Jun 15, 2008
    #4
  5. H.K. Kingston-Smith

    Bart Guest

    On Jun 15, 5:41 pm, "H.K. Kingston-Smith" <> wrote:
    >         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);

    }

    --
    Bartc
     
    Bart, Jun 15, 2008
    #5
  6. H.K. Kingston-Smith

    Guest

    On Jun 15, 11:28 pm, Bart <> wrote:
    > On Jun 15, 5:41 pm, "H.K. Kingston-Smith" <> wrote:
    >
    > > 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.
     
    , Jun 15, 2008
    #6
  7. H.K. Kingston-Smith

    Bart Guest

    On Jun 15, 9:43 pm, wrote:
    > On Jun 15, 11:28 pm, Bart <> wrote:


    > > #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


    --
    Bartc
     
    Bart, Jun 15, 2008
    #7
  8. Bart <> writes:
    [...]
    > 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.

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
     
    Keith Thompson, Jun 15, 2008
    #8
  9. H.K. Kingston-Smith

    Bart Guest

    "Keith Thompson" <> wrote in message
    news:...
    > Bart <> writes:


    >> 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.

    --
    Bartc
     
    Bart, Jun 15, 2008
    #9
  10. H.K. Kingston-Smith

    Flash Gordon Guest

    Bart wrote, On 15/06/08 22:00:
    > On Jun 15, 9:43 pm, wrote:
    >> On Jun 15, 11:28 pm, Bart <> wrote:

    >
    >>> #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.
    --
    Flash Gordon
     
    Flash Gordon, Jun 16, 2008
    #10
  11. H.K. Kingston-Smith

    Flash Gordon Guest

    Bart wrote, On 15/06/08 23:39:
    > "Keith Thompson" <> wrote in message
    > news:...
    >> Bart <> writes:

    >
    >>> 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!
    --
    Flash Gordon
     
    Flash Gordon, Jun 16, 2008
    #11
  12. H.K. Kingston-Smith

    Guest

    On Jun 16, 12:00 am, Bart <> wrote:
    > On Jun 15, 9:43 pm, wrote:
    >
    > > On Jun 15, 11:28 pm, Bart <> wrote:
    > > > #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.
     
    , Jun 16, 2008
    #12
  13. H.K. Kingston-Smith

    Bart Guest

    On Jun 16, 12:15 am, wrote:
    > On Jun 16, 12:00 am, Bart <> wrote:


    > > #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.

    > 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.

    --
    Bartc
     
    Bart, Jun 16, 2008
    #13
  14. H.K. Kingston-Smith

    Guest

    On Jun 16, 3:05 am, Bart <> wrote:
    > On Jun 16, 12:15 am, wrote:
    >
    >
    >
    > > On Jun 16, 12:00 am, Bart <> wrote:
    > > > #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.
    > > 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

    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.
    > > 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.

    Most likely because you compile with C99 mode, in which is fine not to
    return a value from main.
    > > Here's a possible fix:
    > > /* a = ... */
    > > while((uintptr_t)a & 7) ++a;
    > > printf("%p\n", (void *)a);

    >
    > Yes nice textbook stuff.

    But, some lines ago you said it's *best* to use an integer type big
    enough.
     
    , Jun 16, 2008
    #14
  15. H.K. Kingston-Smith

    Bart Guest

    On Jun 16, 12:12 am, Flash Gordon <> wrote:
    > 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..

    --
    Bartc
     
    Bart, Jun 16, 2008
    #15
  16. On Jun 15, 9:28 pm, Bart <> wrote:

    > #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.
     
    christian.bau, Jun 17, 2008
    #16
  17. H.K. Kingston-Smith

    Bart Guest

    On Jun 17, 12:33 am, "christian.bau"
    <> wrote:
    > On Jun 15, 9:28 pm, Bart <> wrote:
    >
    > > #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.


    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?

    --
    Bartc
     
    Bart, Jun 17, 2008
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Surinder Singh
    Replies:
    1
    Views:
    1,228
    Richard Bos
    Dec 20, 2007
  2. Philipp
    Replies:
    21
    Views:
    1,157
    Philipp
    Jan 20, 2009
  3. omkarenator

    stack pointer alignment on x86 and x86_64

    omkarenator, May 1, 2009, in forum: C Programming
    Replies:
    2
    Views:
    1,473
    omkarenator
    May 1, 2009
  4. Casey Hawthorne
    Replies:
    3
    Views:
    1,116
    Flash Gordon
    Nov 1, 2009
  5. Debajit Adhikary
    Replies:
    36
    Views:
    2,353
    Andre Kaufmann
    Feb 10, 2011
Loading...

Share This Page