当我试图检查两个变量之间的差异时,我发现了一些有趣的东西(你可以在下面的代码中看到)
.html()
每次结果都是12。我不知道为什么结果是12,我认为结果必须是4(或-4)。我的电脑是64位,请解释一下。
答案 0 :(得分:6)
不能说你的结果必须是4
,因为你知道sizeof int
是4
。
没有符合标准的,
做你正在寻找的便携式方法(获得两个int
变量的地址之间的差异,而不是任何数组的一部分)。
在两个连续行中声明两个int
变量并不意味着它们将连续放入内存中。订购可能很有可能与您的预期不同。 (在这种情况下,int a,b
我在这里谈到)。如果您希望int
在内存中相邻,则数组(如int ab[2]
)是ISO C保证在所有实现中为您提供的唯一选项。 (在大多数C实现中,您也可以使用struct
,但理论上这不是完全可移植的。 2 )
正如所指出的,这段代码是对指向int
的指针进行类型转换,该指针调用实现定义的行为。另请注意,有符号整数溢出是UB,并且无法保证int
可以保存特定系统中的地址。因此intptr_t
应该是一种安全的方法来避免UB并通过减去单独对象的地址的整数值来获得仅仅实现定义的结果。
如上所述,如果我们认为架构实现了平面寻址(就像实际使用的几乎所有C实现一样)那么我们可以简单地将指针转换为intptr_t
并减去它以得到结果 1 。但正如它所说 - 标准从未限制这种特定的内存布局(它不要求架构如此) - 更加健壮并适用于大量系统。无论说什么都是正确的,直到我们认为架构没有平面地址空间的实现可能会有一些问题需要它以复杂的方式访问元素。
注意:如果您使用gcc
使用或不使用不同的优化标记(-O3
,-O2
等)运行此段代码,您可能会得到{ {1}}或+4
。这必须是编译器特定的情况,它给你这个结果。 (很可能不是-4
)。
<强>脚注强>
gcc
,然后转换为void *
之类的整数。要打印两个这样的整数的差异,请使用intptr_t/uintptr_t
。 PRIdPTR/PRIuPTR
和intptr_t
是可选类型,但自C99以来非常常见。如果uintptr_t
不可用,则转换为最宽的可用类型并使用其匹配的说明符。
intptr_t/uintptr_t
#include <inttypes.h>
// printf("%d", (int)&a - (int)&b);
printf("%" PRIdPTR, (intptr_t)(void*)&a - (intptr_t)(void*)&b);
// or pre-C99
printf("%ld", (long)(void*)&a - (long)(void*)&b);
布局和类型尺寸:
在实践中,struct
在主流实现上也会有连续的struct intpair { int a,b; } ab;
和a
,但ISO C允许在结构布局中任意数量的填充。但它确实要求struct成员具有增加的地址,因此编译可以填充但不重新排序结构。 (或者是C ++中的类;那里的规则是相同的。)
因此,为了最小化填充(对于速度/缓存空间效率),将成员从大到小排序通常是个好主意,因为许多类型的对齐要求等于它们的宽度。或者,如果要将较小的成员放在较宽的成员之前,则将它们成对/四边形分组。请记住,许多实际实现在32/64位指针和/或32/64位b
之间有所不同。例如x86-64 Windows上的64位指针和32位long
,但x86-64上的64/64指针。当然,纯ISO C仅设置类型必须能够表示的最小值范围,并且它们的最小long
是sizeof
,但是大多数现代CPU(以及它们的主流C实现)已经确定32位1
。
避免编写依赖于正确性等假设的代码,但在考虑性能时请记住这些代码。
答案 1 :(得分:3)
由于标准没有为指向不相关对象的指针指定算术,因此减去这些指针很容易出现UB,并且它依赖于实现。
由于它依赖于实现,因此不能指望结果。
通常,根据经验,编译器会在程序堆栈上分配两个整数(彼此并排)。在这种情况下,对于具有平坦存储器架构的系统,减去地址将为我们提供int
的大小。
这是测试你的程序可以给你什么的测试:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main() {
int a, b;
// Print address of the variables of a and b
printf("Address of b %p\n", (void *)&b);
printf("Address of a %p\n", (void *)&a);
// THIS IS PRONE TO UB since pointers `&a` and '&b' not related to each other:
printf("Substructing pointers: %lld\n", &b - &a );
// Now we substract addresses:
// Get the distance in memory on any architecture with flat addressing.
printf("\nSubtracting addr: %lld\n", (long long int)&b - (long long int)&a);
printf("Subtracting addr: %lld\n", (__intptr_t)(void *)&b - (__intptr_t)(void *)&a);
printf("%" PRIdPTR, (intptr_t)(void*)&a - (intptr_t)(void*)&b);
return 0;
}
输出:
Address of b 0x7ffc2d3cd2d4
Address of a 0x7ffc2d3cd2d0
Substructing pointers: 1
Subtracting addr: 4
Subtracting addr: 4
-4