- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在 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/8568432/
我在运行 git gc --prune=now 并得到结果 Counting objects: 100% (619263/619263), done. Delta compression using
我想获取某个类中的所有链接。 HTML 的一个例子是 ES M3 E-mini S&P500 June 2013
我正在 32 位 ARM mcu(Atmel SAM4SD32C,Cortex-M4/ARMv7E-M 部件)上实现二进制日志记录系统,并且正在设计我的数据结构。我的目标是将日志格式描述为一个压缩结构
以下测试代码: [Test] public void PossibleHtmlAgilityPackBug() { const string html = @""; var doc =
如何利用 D3 圆包布局来得到类似这样的图表: (即使有更细长的椭圆)? 这种图表样式的关键应用是更容易放置标签。 这是 jsfiddle这演示了我为其他目的制作的圆包,但我想对于任何想要试验和测试涉
在 C 语言中,编译器将按照声明的顺序排列结构成员,并在成员之间或最后一个成员之后插入可能的填充字节,以确保每个成员都正确对齐。 gcc 提供了一个语言扩展,__attribute__((packed
在 C 语言中,编译器将按照声明的顺序排列结构成员,并在成员之间或最后一个成员之后插入可能的填充字节,以确保每个成员都正确对齐。 gcc 提供了一个语言扩展,__attribute__((packed
在我正在处理的 WPF 应用程序中尝试使用合并的 ResourceDictionaries 解决问题时,我遇到了这个奇怪的问题。 我在外部 DLL(“通用”)中定义了自定义控件(TextButton、
在使用 webpack、webpacker gem 和 Rails 5.1 时,我注意到第一次运行 rspec 会生成 public/packs 和 public/packs-test 。 这些目录是
给定一个 HTML 文档,我想识别文档中的所有数字并在数字周围添加自定义标签。现在,我使用以下内容: HtmlNodeCollection bodyNode = htmlDoc.DocumentNod
我正在尝试使用 #pragma pack (n) 对齐数据成员.以下面为例: #include using namespace std; #pragma pack(8) // or (16) str
我正在尝试让 Html Agility Pack 在我的情况下工作。我需要检测现有 HTML 页面中的所有脚本元素并删除它们,将更改保存到另一个文件。在这里,bodyNode 返回正确数量的脚本标签,
我正在使用 GitLab,需要创建一个 .gitlab-ci.yml 脚本来为生成 nuGet 包的项目运行持续集成管道。 我在寻找可靠的文档来回答这个问题时遇到严重问题: 我应该使用 dotnet
我一直在尝试查找这个问题,但没有找到有效的解决方案。我的编译器忽略了#pragma pack(push) #pragma pack(2) 和 __ attribute __ ((aligned (2)
我正在尝试在 Ubuntu 10.10 上使用 Solaris Studio for Linux。 当我尝试构建以下代码时, const char * 名称 [] = { "苹果", "橙子", "芒
我正在尝试从 HTML 中删除不必要的内容。具体来说,我想删除评论。我找到了一个很好的解决方案( Grabbing meta-tags and comments using HTML Agility
我正在VHDL中的一个项目上工作,我需要从开关中获取4位输入,并根据其他开关的值向右或向左移动一定数量的位,该按钮需要在向右/向左移位之间切换。但是,当我尝试在Xilinx ISE中实现代码时,出现以
我有一台具有 SSH 访问权限的服务器,我想在那里放置一个 Git 源代码库。我刚刚在本地创建了一个 --bare --shared 存储库并将其复制到每个 SCP 的服务器。现在我想根据 SSH u
-moz-box-pack(或 box-pack)的 justify 属性似乎在 Firefox 中被忽略了。 Live example从我们的网站。每行中的数字应该右对齐,它们在 Chrome 中也
这是我的代码。我想知道如何“正确地”读取 BMP 文件,然后在不强制打包结构的情况下读取 header 值。 typedef struct __attribute__((packed)){ uint8
我是一名优秀的程序员,十分优秀!