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

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

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

#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位的,请解释一下。

不,你不可能说结果一定是 4,仅仅因为你知道 sizeof int 在你的情况下是 4

没有standard-compliant, 做你正在寻找的事情的便携方式(获取两个 int 变量的地址之间的差异,而不是任何数组的一部分)。

在连续两行中声明两个 int 变量并不意味着它们将被连续放置在内存中。排序很可能与您预期的不同。 (在这种情况下它是 int a,b 我在这里谈论)。如果您希望 int 在内存中相邻,则数组(如 int ab[2])是 ISO C 保证在所有实现中都会为您提供的唯一选项。 (在大多数 C 实现中,您也可以使用 struct,但理论上不能完全移植。2


正如所指出的那样,此代码将指针转换为指向 int 的指针,它调用实现定义的行为。另请注意,有符号整数溢出是 UB,并且不能保证 int 可以保存特定系统中的地址。因此 intptr_t 应该是避免 UB 的安全方法,并且通过减去单独对象的地址的整数值仅得到 implementation-defined 结果。

前面提到的好的一点是,如果我们认为架构实现了平面寻址(就像实际使用中的几乎每个 C 实现一样),那么我们可以简单地将指针转换为 intptr_t 并减去它以获得结果1。但正如它所说的那样——标准从不限制这种特定的内存布局(它不要求架构是这样的)——更加健壮并适用于大量系统。在我们考虑到架构 没有 平面地址 space 的实现可能存在一些问题需要它以复杂的方式访问元素之前,无论说什么都是正确的。

注意:如果您 运行 这段带有 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++ 中的 类;那里的规则相同)。

    因此为了最小化填充(为了速度/缓存 space 效率),将成员从最大到最小排序通常是个好主意,因为许多类型的对齐要求等于它们的宽度。或者,如果您想将较小的成员放在较宽的成员之前,则将较小的成员成对/四边形分组。请记住,许多实际实现在 32 / 64 位指针和/或 32 / 64 位 long 之间有所不同。例如x86-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 %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