假设我们有以下代码
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
int main(void) {
char *w = malloc(sizeof(long));
long *n;
*w = 'x';
printf("some character: %c\n", *w);
n = (long *)w;
*n = LONG_MAX;
printf("LONG_MAX: %ld", *n);
return 0;
}
在一个系统上long
比更严格地对齐char
(这将是大多数系统)。
如果我们不打印long
object *n
, 的结果malloc
只会用于char
(其中有对齐1
)。也就是说,智能编译器似乎不必受到以下约束:malloc
与任何对象类型兼容(参见这个问题:malloc如何理解对齐? https://stackoverflow.com/q/8752546/2057969);因此它可以有malloc
返回任意对齐的内存片段。
额外的代码行处理n
,这样的编译器将无法再这样做,因为结果malloc
现在还必须适合long
。据我了解,上面的代码没有违反对齐要求或严格的别名规则。
我的理解正确吗?
- 编译器可以优化对以下内容的调用吗
malloc
如果它知道内存的用途,则返回更多弱对齐的内存?
- 同样,只要不存在对齐违规,是否允许编译的代码在幕后重新排列内存以实现较弱的对齐?也就是说:在上面的情况下,如果我们省略所有处理的行
n
,机器改变是否合法w
紧接该行之后的任意对齐地址char *w = malloc(sizeof(long));
?
相反的行为是,一旦内存被分配malloc
,它必须保持其最大兼容对齐。
据我了解,上面的代码没有违反对齐要求或严格的别名规则。
我的理解正确吗?
是的,动态分配的内存保证有足够的对齐long
,并且标准允许灵活使用动态分配的内存;使用新的非字符对象类型存储到动态分配的内存会将其有效类型更改为用于存储的类型。
我们可以分三步来分析标准的要求。
A. 标准规定了什么行为
C 标准指定了抽象机中程序的行为。在这个抽象机器中:
-
char *w = malloc(sizeof(long));
分配适当对齐的内存long
从 C 1999 到未来的 C 2023 的所有 C 标准(在标准的某些版本中可能更加严格地对齐)。出于讨论的目的,我们假设malloc
成功了。
-
*w = 'x';
将字符“x”写入内存。
-
printf("some character: %c\n", *w);
打印带有字符“x”的消息。
-
n = (long *)w;
将地址转换为long *
。这是由 C 2018 6.3.2.3 7 定义的,它支持在指针到对象类型之间进行转换,前提是地址适当对齐(我们知道它是这样的)。
-
*n = LONG_MAX;
writes LONG_MAX
进入记忆。这不会将内存别名为不同类型,因为 C 6.5 6 表示动态分配内存的有效类型将成为用于存储它的类型,因此内存被视为具有类型long
对于此访问。
-
printf("LONG_MAX: %ld", *n);
使用类型访问内存long
,这是内存的当前有效类型,因此这会打印带有LONG_MAX
value.
B. 可观察的行为
C 2018 5.1.2.3 表示,仅需要符合标准的 C 实现来产生可观察的行为,而可观察的行为是:
- 对易失性对象的访问严格按照抽象机的规则进行评估。
- 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。
- 交互设备的输入和输出动态应按照 7.21.3 的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保提示消息实际上在程序等待输入之前出现。
所以代码的可观察行为是:
- 它打印“某个字符:x”,后跟一个换行符。
- 它打印“LONG_MAX:”,后跟值
LONG_MAX
十进制和换行符。
C.应用以上内容回答问题
编译器可以优化对以下内容的调用吗malloc
如果它知道内存的用途,则返回更多弱对齐的内存?
如果编译器生成一个程序:
- 打印“某些字符:x”,后跟换行符。
- 打印“LONG_MAX:”,后跟值
LONG_MAX
十进制和换行符。
则满足C标准的要求。因此,如果编译器生成的程序使用比 C 2018 中抽象要求更多的弱对齐内存,并且该程序打印这些消息,那么它就符合 C 标准。例如,如果基本对齐要求是 16 字节,那么malloc
在抽象机中需要产生一个与 16 字节的倍数对齐的地址。但是,这不是可观察的行为,因此编译器生成的实际程序不需要这样做。只要它打印出所需的消息,就符合要求。
同样,只要不存在对齐违规,是否允许编译的代码在幕后重新排列内存以实现较弱的对齐?也就是说:在上面的情况下,如果我们省略所有处理的行n
,机器改变是否合法w
紧接该行之后的任意对齐地址char *w = malloc(sizeof(long));
?
If the n
行被删除,程序的可观察行为将是:
只要编译器生成了打印该消息的程序,用于执行该操作的任何内存的对齐方式就与它是否符合 C 标准无关。 C 标准所要求的只是打印该消息。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)