结构体指针比较、成员裁剪操作及未定义行为技术咨询
先把代码格式化,方便查看:
int main() { typedef struct { int first; float second; } type; type whole = { 1, 2.0 }; void * vp = &whole; struct { int first; } * shorn = vp; printf("values: %d, %d\n", ((type *)vp)->first, shorn->first); if (vp == shorn) printf("ptrs compare the same\n"); return 0; }
问题1:指针相等比较vp == shorn是否属于未定义行为?
完全不属于未定义行为,这个比较是符合C标准规定的:
- 从语法约束来看,C标准(比如C17 §6.5.9)明确允许等式运算符的两个操作数一个是
void*指针,另一个是指向任意对象类型的指针。这里vp是void*,shorn是指向匿名结构体的对象指针,满足合法比较的条件。 - 从地址值的逻辑来看,结构体对象的起始地址必然和它的第一个成员的地址完全相同(C17 §6.7.2.1规定结构体的起始位置不会有匿名填充)。
vp指向whole这个type类型的结构体,shorn是从vp转换而来的指针,它的地址值和vp完全一致,所以比较结果会是true,代码会输出ptrs compare the same。
问题2:初始化shorn指针的转换是否合法?解引用访问剩余成员是否有效?
这个操作是合法的,解引用访问first成员也是完全有效的:
指针转换的合法性:
C标准(C17 §6.3.2.3)允许void*与任意对象类型的指针相互转换。这里把void*类型的vp直接赋值给struct { int first; }*类型的shorn,属于标准明确许可的操作,没有问题。解引用访问的有效性:
这里的核心是严格别名规则和结构体布局规则:shorn指向的地址就是whole的起始地址,也就是whole.first的地址(结构体起始无填充)。当我们通过shorn->first访问时,本质上是在访问whole.first这个int类型的成员。- 根据严格别名规则(C17 §6.5),允许通过与对象实际类型兼容的类型访问对象。
shorn->first的类型是int,和whole.first的类型完全一致,所以这个访问完全符合规则,不属于未定义行为。 - 补充一点:如果我们尝试通过
shorn访问原结构体中不存在的成员(比如这个匿名结构体如果有第二个成员,去访问它),那就是未定义行为,但在这个例子里只访问第一个成员,是安全合法的。
内容的提问来源于stack exchange,提问作者textral
相关产品推荐
相关产品推荐

