KansaiRobot
KansaiRobot

Reputation: 10022

__attribute(aligned) test. What does it do?

I am using a couple of online compilers to test a sample program (for reference, the one in ideone and the one in tutorialspoint) The program is:

#include <stdio.h>
#include <stdint.h> 

uint8_t Array[5]__attribute((aligned(32)));
uint8_t Array2[5]__attribute((aligned(8)));
static uint8_t Array3[5]__attribute((aligned(8)));
static uint8_t Array4[5]__attribute((section("bbs"),aligned(32)));


int main()
{
    printf("%p %p %p %p %p \n", &Array[0], &Array[1], &Array[2], &Array[3],&Array[4]);
    printf("%p %p %p %p %p \n", &Array2[0], &Array2[1], &Array2[2], &Array2[3],&Array2[4]);
    printf("%p %p %p %p %p \n", &Array3[0], &Array3[1], &Array3[2], &Array3[3],&Array3[4]);
    printf("%p %p %p %p %p \n", &Array4[0], &Array4[1], &Array4[2], &Array4[3],&Array4[4]);

    return 0;
}

The results are for example

0x2aff9175b0a0 0x2aff9175b0a1 0x2aff9175b0a2 0x2aff9175b0a3 0x2aff9175b0a4 
0x2aff9175b080 0x2aff9175b081 0x2aff9175b082 0x2aff9175b083 0x2aff9175b084 
0x2aff9175b068 0x2aff9175b069 0x2aff9175b06a 0x2aff9175b06b 0x2aff9175b06c 
0x2aff9175b040 0x2aff9175b041 0x2aff9175b042 0x2aff9175b043 0x2aff9175b044 

I can see that aligned does not seem to have an effect on where the array elements are stored. What does aligned actually do? (I am asking after reading the explanation in here which I didn't quite catch.)

Upvotes: 0

Views: 321

Answers (2)

Andre Kampling
Andre Kampling

Reputation: 5630

The alignment attribute is described here in the GNU Compiler documentation.

It said about it:

This attribute specifies a minimum alignment for the variable or structure field, measured in bytes.

That means you will set the minimum alignment but for the beginning of the array. The rest of the array is guaranteed to be contiguous, that means the addresses that follows are dependent on the type in your example uint8_t. If the array start address is at 0x2aff9175b0a0 like in your case the next address must be 1 byte after it at 0x2aff9175b0a1 (0xa0 --> 0xa1).

To explain your example:

Alignment 32 beginning at: 0x2aff9175b0a0 --> 0xa0 = 160 = 32 * 5
Alignment 8  beginning at: 0x2aff9175b080 --> 0x80 = 128 = 8  * 16
Alignment 8  beginning at: 0x2aff9175b068 --> 0x68 = 104 = 8  * 13
Alignment 32 beginning at: 0x2aff9175b040 --> 0x40 = 64  = 32 * 2

As you see the alignment of the beginning of the array is as you want it. You could increase the alignment and watch the addresses again. But also consider:

Note that the effectiveness of aligned attributes may be limited by inherent limitations in your linker. On many systems, the linker is only able to arrange for variables to be aligned up to a certain maximum alignment.

Further if you use printf() to print the addresses with %p, then also cast the addresses to a void*, because the pointer types doesn't need to have the same representations except for some of them. That fact is also explained here.

Upvotes: 4

Jonathan Leffler
Jonathan Leffler

Reputation: 754970

You might get a better picture of what's going on with slightly different sample code. Here's an adaptation of your program. There's no virtue in printing the addresses of elements indexed by [1] etc; those positions are 100% controlled by the type (uint8_t) and the start address of the array.

#include <stdio.h>
#include <stdint.h> 

uint8_t Array1[5]__attribute((aligned(32)));
uint8_t Array2[5]__attribute((aligned(8)));
static uint8_t Array3[5]__attribute((aligned(8)));
static uint8_t Array4[5]__attribute((aligned(32)));

uint16_t u16_a1[3];
uint16_t u16_a2[3];
uint16_t u16_a3[3];

uint16_t u16_a4[3] __attribute((aligned(32)));
uint16_t u16_a5[3] __attribute((aligned(32)));
uint16_t u16_a6[3] __attribute((aligned(32)));

int main(void)
{
    printf("%s: %p\n", "Array1", (void *)Array1);
    printf("%s: %p\n", "Array2", (void *)Array2);
    printf("%s: %p\n", "Array3", (void *)Array3);
    printf("%s: %p\n", "Array4", (void *)Array4);

    printf("%s: %p\n", "u16_a1", (void *)u16_a1);
    printf("%s: %p\n", "u16_a2", (void *)u16_a2);
    printf("%s: %p\n", "u16_a3", (void *)u16_a3);
    printf("%s: %p\n", "u16_a4", (void *)u16_a4);
    printf("%s: %p\n", "u16_a5", (void *)u16_a5);
    printf("%s: %p\n", "u16_a6", (void *)u16_a6);

    return 0;
}

I had to remove the section(bbs), directive; the Mac linker didn't accept it. I also renamed Array to Array1 for self-consistency with the rest of the array names.

The output I got was:

Array1: 0x10a1fc040
Array2: 0x10a1fc048
Array3: 0x10a1fc028
Array4: 0x10a1fc020
u16_a1: 0x10a1fc04e
u16_a2: 0x10a1fc054
u16_a3: 0x10a1fc05a
u16_a4: 0x10a1fc060
u16_a5: 0x10a1fc080
u16_a6: 0x10a1fc0a0

Note that the address of Array1 is aligned on a 32-byte boundary, and Array2 is on an 8 byte boundary just after Array1. Similarly, Array3 and Array4 are located on appropriately aligned boundaries. (Of course, a 32-byte boundary is an address which ends z0 in hex, where z is an even number. An 8-byte boundary is an address that ends in 8 or 0 in hex.)

The u16_a1 to u16_a3 arrays are not alignment constrained. They're 6 bytes each, and the three addresses differ by 6 bytes.

By contrast, the u16_a4 to u16_a6 arrays are constrained to a 32-byte boundary. As you can see, their addresses differ from each other by 32 bytes, and each is located on a 32-bit boundary, whereas if they were unconstrained, they'd be much closer together. You might note that u16_a3 and u16_a4 happen to be contiguous in memory — pure chance, not planning on my part.

You can adapt this code to play with other types and alignments.

Upvotes: 3

Related Questions