【发布时间】:2020-07-24 16:23:08
【问题描述】:
我对从指针到整数和各种相关操作的强制转换的定义性(未定义性、实现定义性)感兴趣。主要是我对 C11 感兴趣,但欢迎其他标准版本(甚至 C++)的答案。
就本问题而言,假设 C 实现提供 intptr_t。
考虑以下函数:
#include <assert.h>
#include <stdint.h>
int x;
int y;
int z[2];
void f1(void) {
int *p = &x;
intptr_t i = p;
}
void f2(void) {
int *p = &x;
intptr_t i1 = p;
intptr_t i2 = p;
assert(i1 == i2);
}
void f3(void) {
int *p1 = &x;
int *p2 = &y;
intptr_t i1 = p1;
intptr_t i2 = p2;
assert(i1 != i2);
}
void f4(void) {
int *p1 = &x;
intptr_t i1 = p1;
int *p2 = i1;
intptr_t i2 = p2;
assert(i1 == i2);
}
void f5(void) {
int *p1 = &z[0];
int *p2 = &z[1];
intptr_t i1 = p1;
intptr_t i2 = p2;
assert(i1 < i2);
}
- 哪些函数调用未定义(实现定义)的行为?
- 如果使用
void*而不是int*,会有什么变化吗?作为指针目标的任何其他数据类型怎么样? - 如果我使用从
int*到intptr_t并返回的显式转换,会有什么变化吗? (问,因为 GCC 警告演员。) - 保证不会触发哪个
asserts?
【问题讨论】:
-
需要注意的重要一点是,如果
p是指向T的第一个字节的指针,则通常可能存在一个指针q,其比较等于p,但会指向内存中恰好位于*p之前的任何对象的末尾,并且无法访问*p。因此,没有什么可以禁止反复无常的实现将(T*)(intptr_t)p视为等于p但不能取消引用以访问*p的指针(例如q),因此大多数代码会尝试取消引用由通过intptr_t的往返会调用未定义的行为。
标签: c++ c language-lawyer