我有以下C代码:
#include<stdio.h>
#include<stdlib.h>
int main() {
int a[4] = {1,2,3,4};
int b[4] = {1,2,3,4};
int n = &b[3] - &a[2];
printf("%d\n", n);
return 0;
}
为什么n
的值为-3?我对此没有一个纯粹的解释。变量地址之间的区别如何是-3
?为什么它正是-3
?
答案 0 :(得分:14)
虽然当你减去指向不同数组中元素的指针时C标准没有定义行为,但似乎发生的事情是你的编译器将b
放在内存之前(地址低于){{ 1}}。因此,内存布局看起来像:
a
鉴于此内存布局,+------+------+------+------+------+------+------+------+
| b[0] | b[1] | b[2] | b[3] | a[0] | a[1] | a[2] | a[3] |
+------+------+------+------+------+------+------+------+
为+3,因为&a[2] - &b[3]
在内存中的后面是a[2]
的三个元素而不是b[3]
。然后&b[3] - &a[2]
为-3,因为b[3]
在内存中早于三个元素而不是a[2]
。 (注意,像这样不符合C标准的简单指针算法可能会中断,因为编译器可能会执行各种优化,或者因为某些体系结构上的地址算法很复杂.C标准不仅不定义减法的结果;它一旦执行了未定义的减法,就不会定义程序的行为。)
许多C实现将对象放在堆栈上(如果它们具有自动存储持续时间,而不是静态,分配或线程),并且堆栈通常从高内存地址开始并向低地址增长。所以,如果编译器只是按照你声明它们的顺序为对象分配空间(这通常不是预期的;有许多理由做更好的事情),那么它实际上会将a
放在首先堆栈,然后减少堆栈指针,然后将b
放在堆栈上,因此b
的地址低于a
。
顺便提一下,减去两个指针的结果的大小为ptrdiff_t
,不一定是int
,所以它应该打印printf("%td\n", n);
而不是%d
,并且应使用n
而不是ptrdiff_t n;
声明int n;
。 ptrdiff_t
类型在<stddef.h>
标题中定义。
答案 1 :(得分:4)
代码的行为是 undefined 。
指针算法,包括两个指针的减法,仅对指向同一数组中元素的指针有效。 (为此,您可以设置指向数组末尾的指针,对象计为包含一个元素的数组。)
答案 2 :(得分:-3)
使用C调用约定(在x86上),局部变量被推送到本地堆栈,因此在内存中相邻。
在这种情况下,两个数组在内存中相对相邻。 (确切的值可以通过字节/字/ qword对齐等方式实现)
最终结果是,您可以获得一致的值,这可以根据您的构建环境/选项进行预测。