【发布时间】:2017-05-13 21:22:38
【问题描述】:
以下哪些处理和尝试恢复 C 指针的方法可以保证有效?
1) 转换为 void 指针并返回
int f(int *a) {
void *b = a;
a = b;
return *a;
}
2) 转换为适当大小的整数并返回
int f(int *a) {
uintptr_t b = a;
a = (int *)b;
return *a;
}
3) 几个简单的整数运算
int f(int *a) {
uintptr_t b = a;
b += 99;
b -= 99;
a = (int *)b;
return *a;
}
4) 整数运算非常重要,足以掩盖出处,但仍会使值保持不变
int f(int *a) {
uintptr_t b = a;
char s[32];
// assume %lu is suitable
sprintf(s, "%lu", b);
b = strtoul(s);
a = (int *)b;
return *a;
}
5) 更多的间接整数运算将保持值不变
int f(int *a) {
uintptr_t b = a;
for (uintptr_t i = 0;; i++)
if (i == b) {
a = (int *)i;
return *a;
}
}
显然情况 1 是有效的,情况 2 肯定也是。另一方面,我看到了 Chris Lattner 的一篇文章——很遗憾我现在找不到了——说类似于案例 5 的内容 not 有效,标准许可编译器只编译它到一个无限循环。然而,每个案例看起来都像是前一个案例的无可争议的延伸。
有效案例和无效案例之间的界限在哪里?
根据 cmets 中的讨论添加:虽然我仍然找不到启发案例 5 的帖子,但我不记得涉及哪种类型的指针;特别是,它可能是一个函数指针,这可能就是为什么该案例显示无效代码而我的案例 5 是有效代码的原因。
第二个补充:好的,这是另一个说有问题的来源,我确实有这个链接。 https://www.cl.cam.ac.uk/~pes20/cerberus/notes30.pdf - 关于指针出处的讨论 - 说,并用证据支持,不,如果编译器忘记了指针的来源,这是未定义的行为。
【问题讨论】:
-
Case 5 不会产生无限循环——当然,除非编译器坏了。循环完成可能需要足够的时间才能被观察到,具体取决于
a的值,尤其是在没有优化的情况下编译。但是编译器也可以简单地完全优化循环。 -
我怀疑你记错了 Chris Lattner 说的话。案例 5 不能是无限循环,因为
uintrptr_t应该能够表示数据指针值可以是的所有可能值(除非整个[u]intrptr_t机制完全损坏)。你最好找到他说的实际例子。 -
@rwallace
[u]intptr_t不能存储函数指针(它们仅为数据指针定义)。所以,是的,如果将函数指针转换为 [u]intptr_t,它不能保证工作。 -
@EugeneSh。后者不遵循前者。
void*不能保证保存函数指针。 -
完全模糊出处的整数运算可能不像那些涉及编译器可以看到的“不相关”但实际上指向同一个对象的整数的问题那么大。我不确定是否有任何有用的规则来说明大多数编译器能够可靠地处理哪些情况。
标签: c pointers undefined-behavior