【发布时间】:2021-04-04 15:58:46
【问题描述】:
C 标准中关于指针算术结果通过同一结构中的前一个成员地址指向另一个结构成员的指针的观点是什么?
代码 1(无结构),神秘_1
int mystery_1(void)
{
int one = 1, two = 2;
int *p1 = &one + 1;
int *p2 = &two;
unsigned long i1 = (unsigned long) p1;
unsigned long i2 = (unsigned long) p2;
if (i1 == i2)
return p1 == p2;
return 2;
}
从代码1,我知道结果是不确定的,因为无法保证栈上的局部变量是如何放置的。
如果我使用这样的结构(代码 2)会怎样?
代码 2(带结构),神秘_2
int mystery_2(void)
{
struct { int one, two; } my_var = {
.one = 1, .two = 2
};
int *p1 = &my_var.one + 1;
int *p2 = &my_var.two;
unsigned long i1 = (unsigned long) p1;
unsigned long i2 = (unsigned long) p2;
if (i1 == i2)
return p1 == p2;
return 2;
}
编译器输出
天箭链接:https://godbolt.org/z/jGoKfETn7
GCC 10.2
mystery_1:
xorl %eax, %eax # return 0, while clang returns 2 (fine as no guarantee)
ret
mystery_2:
movl $1, %eax # return 1, as compiler must consider the memory order of struct members
ret
Clang 11.0.1
mystery_1: # @mystery_1
movl $2, %eax # return 2, while gcc returns 0 (fine as no guarantee)
retq
mystery_2: # @mystery_2
movl $1, %eax # return 1, as compiler must consider the memory order of struct members
retq
我的理解
- 在代码1中,返回值是不确定的,因为堆栈上的局部变量的内存布局没有保证。
- 在代码 2 中,返回值被确定并明确定义为
1,因为p1 == p2产生 true,因为 struct 保证了内存布局。所以my_var.one的下一个地址是my_var.two,不允许编译器假设p1和p2因为出处不同而不同。
问题
- 我的理解正确吗?
- 根据 C 标准,
mystery_2是否总是返回 1,因为p1 == p2产生 true? - 在
mystery_2中,是否允许编译器假定p1 != p2,所以函数返回0?
问题
我与某人讨论了 struct 案例 (mystery_2),他们说:
p1 指向(过去的)一,p2 指向二。在 C 规范中,这些被视为不同的“对象”。然后规范继续定义指向不同对象的指针可能比较不同,即使两个指针具有完全相同的位模式
【问题讨论】:
-
不,我不认为这是有保证的。编译器可以在成员之间放置填充等。
-
@cigien 不,这不能回答我的问题。
-
编译器可能做什么或可能不做什么完全无关紧要。您将其标记为 [language-lawyer],这意味着您想知道该语言所保证的内容。该语言允许编译器添加填充,因此无法保证行为。
-
您似乎对语言标准的含义有误解。该标准只是对编译器的行为提出了要求。除了这些要求之外,编译器可以做任何它想做的事情并且仍然符合标准。例如如果标准没有对填充强加要求,编译器可以在整数之间插入 1GB 的填充并符合要求。编译器是否会这样做是无关紧要的。
-
@AmmarFaizi 您认为“应该”的内容不会对编译器施加限制。
标签: c pointers language-lawyer