- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
在 C 语言中,编译器将按照声明的顺序排列结构成员,并在成员之间或最后一个成员之后插入可能的填充字节,以确保每个成员都正确对齐。
gcc 提供了一个语言扩展,__attribute__((packed))
,它告诉编译器不要插入填充,允许结构成员错位。例如,如果系统通常要求所有 int
对象都具有 4 字节对齐,则 __attribute__((packed))
可以导致 int
结构成员以奇数偏移量分配。
引用 gcc 文档:
The `packed' attribute specifies that a variable or structure field should have the smallest possible alignment--one byte for a variable, and one bit for a field, unless you specify a larger value with the `aligned' attribute.
显然,使用此扩展会导致数据需求更小但代码速度更慢,因为编译器必须(在某些平台上)生成代码以一次访问未对齐的成员一个字节。
但是在某些情况下这是不安全的吗?编译器是否总是生成正确的(虽然速度较慢)代码来访问未对齐的压缩结构成员?甚至有可能在所有情况下都这样做吗?
最佳答案
是的,__attribute__((packed))
在某些系统上可能不安全。该症状可能不会出现在 x86 上,这只会使问题更加隐蔽;在 x86 系统上测试不会揭示问题。 (在 x86 上,未对齐的访问在硬件中处理;如果您取消引用指向奇数地址的 int*
指针,它会比正确对齐时慢一点,但您会得到正确的结果。)
在某些其他系统上,例如 SPARC,尝试访问未对齐的 int
对象会导致总线错误,从而导致程序崩溃。
还有一些系统,其中未对齐的访问悄悄地忽略了地址的低位,导致它访问错误的内存块。
考虑以下程序:
#include <stdio.h>
#include <stddef.h>
int main(void)
{
struct foo {
char c;
int x;
} __attribute__((packed));
struct foo arr[2] = { { 'a', 10 }, {'b', 20 } };
int *p0 = &arr[0].x;
int *p1 = &arr[1].x;
printf("sizeof(struct foo) = %d\n", (int)sizeof(struct foo));
printf("offsetof(struct foo, c) = %d\n", (int)offsetof(struct foo, c));
printf("offsetof(struct foo, x) = %d\n", (int)offsetof(struct foo, x));
printf("arr[0].x = %d\n", arr[0].x);
printf("arr[1].x = %d\n", arr[1].x);
printf("p0 = %p\n", (void*)p0);
printf("p1 = %p\n", (void*)p1);
printf("*p0 = %d\n", *p0);
printf("*p1 = %d\n", *p1);
return 0;
}
在带有 gcc 4.5.2 的 x86 Ubuntu 上,它产生以下输出:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = 0xbffc104f
p1 = 0xbffc1054
*p0 = 10
*p1 = 20
在带有 gcc 4.5.1 的 SPARC Solaris 9 上,它产生以下内容:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = ffbff317
p1 = ffbff31c
Bus error
在这两种情况下,程序编译时都没有额外的选项,只是 gcc packed.c -o packed
。
(使用单个结构而不是数组的程序不能可靠地显示问题,因为编译器可以在奇数地址上分配结构,因此 x
成员正确对齐。使用两个 struct foo
对象的数组,至少一个或另一个将有一个未对齐的 x
成员。)
(在这种情况下,p0
指向一个未对齐的地址,因为它指向紧跟在 char
成员之后的打包 int
成员。p1
正好对齐,因为它指向数组第二个元素中的同一个成员,所以它前面有两个 char
对象——在 SPARC Solaris 上数组 arr
似乎分配在一个偶数地址,但不是 4 的倍数。)
当通过名称引用 struct foo
的成员 x
时,编译器知道 x
可能未对齐,并将生成额外的正确访问它的代码。
一旦arr[0].x
或arr[1].x
的地址被存储在一个指针对象中,编译器和运行程序都不知道它指向一个未对齐的 int
对象。它只是假定它已正确对齐,导致(在某些系统上)出现总线错误或类似的其他故障。
我认为,在 gcc 中修复此问题是不切实际的。一个通用的解决方案需要,对于每次尝试取消引用指向具有非平凡对齐要求的任何类型的指针,或者(a)在编译时证明指针不指向打包结构的未对齐成员,或(b)生成可以处理对齐或未对齐对象的更大、更慢的代码。
我已经提交了 gcc bug report .正如我所说,我认为修复它不切实际,但文档应该提到它(目前没有)。
更新:截至 2018 年 12 月 20 日,此错误已标记为已修复。该补丁将出现在 gcc 9 中,并添加了一个默认启用的新 -Waddress-of-packed-member
选项。
When address of packed member of struct or union is taken, it may result in an unaligned pointer value. This patch adds -Waddress-of-packed-member to check alignment at pointer assignment and warn unaligned address as well as unaligned pointer
我刚刚从源代码构建了那个版本的 gcc。对于上述程序,它会生成以下诊断信息:
c.c: In function ‘main’:
c.c:10:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
10 | int *p0 = &arr[0].x;
| ^~~~~~~~~
c.c:11:15: warning: taking address of packed member of ‘struct foo’ may result in an unaligned pointer value [-Waddress-of-packed-member]
11 | int *p1 = &arr[1].x;
| ^~~~~~~~~
关于c - gcc 的 __attribute__((packed))/#pragma pack 不安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43087085/
我有一个目前正在用 C 编写的应用程序,其中我在作为项目一部分的库中声明了几个 TLS 静态全局变量。 TLS 变量是使用 gcc 的 __thread 指令声明的。 我想知道我是否可以使用 .cto
如果 C++ 类构造函数只能通过其参数访问数据,那么它们是否可以并且应该声明为 __attribute__((pure))?在什么情况下它们应该被限定为 __attribute__((const))?
遵循 GCC __attribute__(packed) 将打包到字节边界,对齐用于此目的:-- u8 rx_buf[14] __attribute__((aligned(8))); struct
__attribute__((const)) 和 GNU C 中的 __attribute__((pure)) 有什么区别? __attribute__((const)) int f() {
这个问题不太可能对任何 future 的访客有帮助;它只与一个较小的地理区域、一个特定的时间点或一个非常狭窄的情况相关,通常不适用于全世界的互联网受众。如需帮助使此问题更广泛适用,visit the
我想利用 GCC 的可见性属性。 为此,在我的可执行文件中,我有一个函数,我希望它对任何插件都可见,这样它们就可以使用它来注册自己。因此,我在其上使用 __attribute__((visibilit
有时我会出于各种原因围绕 printf 风格的函数编写包装器,我真的很喜欢让 clang 告诉我格式说明符和实际参数之间的类型不匹配。考虑: #include #include static __
我对以下代码行有疑问: char buffer[256] __attribute__((aligned(4096))); 全局数组“缓冲区”的内容是字符串,我从标准输入中获取。我读过https://g
在gcc documentation它们提供有关整数如何对齐的信息。 打包整数可以做同样的事情吗? 例如,这在不会自动处理不正确对齐数据的系统上有效吗? typedef uint16_t __attr
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: GCC __attribute__((aligned(x)) explanation What is mea
__attribute__ 指令是否适用于一行中声明的所有成员? int a, b, c; 声明三个 int 变量。 int *a, b, c; 将变量“a”声明为指向 int 的指针,将 b 和 c
在使用 gcc __attribute__ 函数时,我注意到代码生成存在差异,具体取决于我放置属性的位置。在下面的示例中,我希望编译器不要优化我对 use() 的调用。 编译器:x86-64 gcc(
为什么gcc需要__attribute__((__malloc__))?难道不应该通过将 malloc (和类似的函数)声明为返回 restricted 指针来传达相同的信息 (void *restr
我得到了一段将 __attribute__((const)) 应用于某些函数的代码。现在,我宁愿在它可用时不删除它,但另一方面,我确实想要更便携,所以 - 我想说 #if some condition
我可能对 weak 理解有误: 我的情况: 具有一些弱回调的“物理层” 实现这些回调的“框架层”,并为应用层提供新的 WEAK 回调 主要 - 应用层。 phy.h #pragma once void
我知道从C编程语言的角度main() 是程序的入口点。 但从操作系统的角度来看,__start 位于 ctrt0 启动例程,它链接到一个程序中,该程序在调用程序的 main() 函数(如有错误请指正)
这几天一直让我抓狂。如果我将数组声明为 static,则无法让数组对齐 16。 非常感谢任何帮助。 修订版: #include #include #define MAX_INPUTS 250 in
我正在尝试在 android NDK 上进行开发。我实际上有一个带有模块的项目+子文件夹(编译为静态库)并与主项目文件(libs .a + main object .o --> 可执行文件)链接在一起
这个问题在这里已经有了答案: Is there a standard "never returns" attribute for C# functions? (10 个答案) 关闭 8 年前。 C#
__attribute__ ((__packed__)) 对嵌套结构有什么影响?例如: // C version struct __attribute__ ((__packed__)) { s
我是一名优秀的程序员,十分优秀!