使用C语言操作硬件寄存器的精髓在于结构体指针的灵活应用,而结构体的定义一般又需要对数据成员在内存中如何存放有精准的把握。这时候就需要一些属性关键字来修饰所定义的结构体,使结构体中的成员变量按我们想要的规则存放。对于gcc而言,__attribute__
就是这样一个关键字。
在读一些C语言开源代码的时候会经常看到一些复杂的struct
定义之后跟着__attribute__
的选项用来指定struct
的属性。最近正好看到__attribute__((packed, aligned(4)))
这样的定义,网上查了下,发现都说得不太清楚,联想到之前看到的__attibute__((packed))
以及__attribute__((aligned(4)))
。觉得有必要把它们之间的区别搞清楚,特此记录下来,以便后面查阅。
这里我们主要区分以下三种定义:
__attribute__((packed, aligned(4)))
__attribute__((aligned(4)))
__attribute__((packed))
下面以实例来看这三种定义的区别:
struct __attribute__((packed, aligned(4))) test_a {
char a;
int b;
char c;
};
struct __attribute__((aligned(4))) test_b {
char a;
int b;
char c;
};
struct __attribute__((packed)) test_c {
char a;
int b;
char c;
};
上面分别定义了三个struct: test_a, test_b, test_c。以不同的attribute定义。
分别打印它们的size:
fprintf(stderr, "sizeof(test_a) is %d, sizeof(test_b) is %d, sizeof(test_c) is %d.\n",
(int)sizeof(struct test_a),
(int)sizeof(struct test_b),
(int)sizeof(struct test_c));
结果:
可以看到test_a
sizeof(test_a) is 8, sizeof(test_b) is 12, sizeof(test_c) is 6.
再分别查看这个3个struct, 每个成员的offset:
struct test_a *sa = NULL;
struct test_b *sb = NULL;
struct test_c *sc = NULL;
fprintf(stderr, "%ld, %ld, %ld.\n",
(long)&sa->a,
(long)&sa->b,
(long)&sa->c
);
fprintf(stderr, "%ld, %ld, %ld.\n",
(long)&sb->a,
(long)&sb->b,
(long)&sb->c
);
fprintf(stderr, "%ld, %ld, %ld.\n",
(long)&sc->a,
(long)&sc->b,
(long)&sc->c
);
结果:
0, 1, 5.
0, 4, 8.
0, 1, 5.
能过比较上面的结果,可以看出,test_a
和test_c
的内部成员在内存中的排列方式都是一样, 因为它们都包含了packed
定义,但size不一样,因为test_a
定义了aligned(4), 所以最终的size是4字节对齐的。
test_b
每个成员都是4字节对齐的,这和aligned(4)
定义吻合。