计算机如何分配两个变量,我们如何计算两个变量之间的距离?

How does computer allocate two variables and how can we calculate the distance between two variables?

本文关键字:变量 两个 之间 距离 计算 我们 何分配 计算机 分配 何计算      更新时间:2023-10-16

当我尝试检查两个变量之间的差异时,我发现了一些有趣的东西(你可以在下面的代码中看到)

#include <stdio.h>
#include <conio.h>
int main() {
int a, b;  
printf("%d", (int)&a - (int)&b);
getch();
return 0;
}

每次的结果都是 12 .我不知道为什么结果是 12,我认为结果一定是 4(或 -4)。我的电脑是64位的,请解释一下。

不,你不可能仅仅因为你知道在你的情况下sizeof int4就说结果必须4

没有符合标准, 执行您正在寻找的操作的便携式方法(获取不属于任何数组的两个int变量的地址之间的差异)。

在两行中声明两个intvar 并不意味着它们将连续放置在内存中。排序很可能与您预期的不同。(在这种情况下,我在这里谈论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等),你可能会得到+4-4的预期结果。这必须是为您提供此结果的编译器特定情况。(很可能不是gcc)。


脚注

  1. 将对象地址隐藏为整数是一个 2 阶段的过程:首先转换为void *,然后转换为像intptr_t/uintptr_t这样的整数。 要打印两个此类整数的差值,请使用PRIdPTR/PRIuPTRintptr_tuintptr_t是可选类型,但自 C99 以来非常普遍。 如果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);

  1. struct布局和类型大小:

    在实践中,struct intpair { int a,b; } ab;也会对主流实现进行连续的ab,但ISO C允许在结构布局中使用任意数量的填充。 但是,它确实要求结构成员具有递增的地址,因此编译可以填充但不能对结构进行重新排序。 (或C++中的类;那里的规则是一样的)。

    因此,为了最大程度地减少填充(为了速度/缓存空间效率),通常最好从大到小对成员进行排序,因为许多类型的对齐要求等于其宽度。 或者,如果您想将较小的成员放在较宽的成员之前,则可以将它们成对/四边形分组。 请记住,许多实际实现在 32/64 位指针和/或 32/64 位long之间有所不同。 例如,x84-64 Windows上的64位指针和32位long,但在x86-64上为64/64。 当然,纯ISO C只设置类型必须能够表示的最小值范围,并且它们的最小sizeof1,但大多数现代CPU(以及它们的主流C实现)已经确定了32位int

    避免编写依赖于此类假设以确保正确性的代码,但在考虑性能时记住这一点很有用。

由于该标准没有指定指向不相关对象的指针的算术,因此减去此类指针很容易产生 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 %pn", (void *)&b);
printf("Address of a %pn", (void *)&a);
// THIS IS PRONE TO UB since pointers `&a` and '&b' not related to each other: 
printf("Substructing pointers:  %lldn",  &b - &a );
// Now we substract addresses:
// Get the distance in memory on any architecture with flat addressing.
printf("nSubtracting addr:  %lldn", (long long int)&b - (long long int)&a);
printf("Subtracting addr:  %lldn", (__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