c - 关于数组外指针算术的 C 标准
问题描述
我阅读了很多关于指针算法和未定义行为(链接、链接、链接、链接、链接)的内容。它总是得出相同的结论:指针算法仅在数组类型上以及在数组[0] 和数组[array_size+1] 之间定义良好(对于 C 标准来说,末尾的一个元素是有效的)。
我的问题是:这是否意味着当编译器看到一个与任何数组无关的指针算术(未定义的行为)时,它可以发出它想要的东西(甚至什么都没有)?还是更高级的“未定义行为”意味着您可以访问未映射的内存、垃圾数据等,并且不能保证地址的有效性?
在这个例子中:
char test[10];
char * ptr = &test[0];
printf("test[-1] : %d", *(ptr-1))
通过“未定义的行为”,只是该值根本不能保证(可能是垃圾、未映射的内存等),但我们仍然可以肯定地说,我们正在访问与数组连续 8 个字节的内存地址开始前? 还是编译器根本无法发出此代码的“未定义行为”?
另一个简单的用例:您想计算一个函数的内存大小。一个幼稚的实现可能是下面的代码,假设函数以相同的顺序在二进制文件中输出,是连续的并且中间没有任何填充。
#include <stdint.h>
#include <stdio.h>
void func1()
{}
void func2()
{}
int main()
{
uint8_t * ptr1 = (uint8_t*) &func1;
uint8_t * ptr2 = (uint8_t*) &func2;
printf("Func 1 size : %ld", ptr2-ptr1);
return 0;
}
由于ptr1
andptr2
不是数组的一部分,它被认为是未定义的行为。同样,这是否意味着编译器无法发出这些代码?或者“未定义的行为”是否意味着减法根据系统(内存中不连续的函数、填充等)没有意义,但仍按预期发生?有没有明确定义的方法来计算两个不相关的指针之间的减法?
解决方案
C 标准没有定义未定义行为的未定义程度。如果它是未定义的,它总是所有的赌注都是关闭的。
此外,现代编译器会混淆这个指针出处,编译器甚至会观察一个可能有效的指针是否正确派生,如果不是,它可以调整程序行为。
如果您想要没有 UB 可能性的数学指针算术,您可以尝试uintptr_t
在进行数学运算之前将指针转换为。
例如:
#include <stdio.h>
int main()
{
char a,b;
printf("&a=%p\n", &a);
printf("&b=%p\n", &b);
printf("&a+1=%p\n", &a+1);
printf("&b+1=%p\n", &b+1);
printf("%d\n", &a+1==&b || &b+1==&a);
}
在我的机器上,用 编译gcc -O2
,结果:
&a=0x7ffee4e36cae
&b=0x7ffee4e36caf
&a+1=0x7ffee4e36caf
&b+1=0x7ffee4e36cb0
0
即,&a+1
具有相同的数字地址,&b
但被视为不等于,&b
因为地址来自不同的对象。
(这个 gcc 优化有点争议。它不跨越函数调用/翻译单元边界,clang 不这样做,而且没有必要,因为6.5.9p6确实允许意外的指针相等。请参阅dbush对这个Keith汤普森的 回答了解更多细节。)
推荐阅读
- f# - Akka.Remote F# 序列化仅适用于有区别的联合的第一个字段
- php - 如何强制 Heroku 中的网站在访问页面时始终重新加载缓存?
- flutter - 如何防止 iOS 14 Widget 打开应用并响应回调
- python - 二维布尔数组到图像
- unicode - 为什么要对撇号进行这种精心设计的 RTF 编码?
- javascript - 在 Wordpress 块开发的上下文中从道具理解 const 分配的问题
- spring - 在执行@Modifying spring jpa之前注入方法或代码
- html - CSS背景URL源未将图像设置为背景
- python - 从表中提取元素的问题
- python - Python中正确的包结构