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

问题描述 投票:1回答:2

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

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

c++ c cpu-architecture
2个回答
6
投票

没有办法你可以说结果必须是4只是因为你知道sizeof int在你的情况下是4

没有符合标准,可移植的方式来做你正在寻找的东西(获得两个int变量的地址之间的区别不是任何数组的一部分)。

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


正如所指出的,这段代码是对指向int的指针进行类型转换,int调用实现定义的行为。另请注意,有符号整数溢出是UB,并且无法保证intptr_t可以保存特定系统中的地址。因此,intptr_t应该是一种安全的方法来避免UB并通过减去单独对象的地址的整数值来获得仅仅实现定义的结果。

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

注意:如果你运行这段代码与-O3有或没有不同的优化标志(-O2+4等),你可能会得到-4gcc的预期结果。这必须是编译器特定的情况,它给你这个结果。 (很可能不是void *)。


脚注

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

  1. struct intpair { int a,b; } ab;布局和类型大小: 在实践中,a在主流实现上也会有连续的blong,但ISO C允许在结构布局中任意数量的填充。但它确实要求struct成员具有增加的地址,因此编译可以填充但不重新排序结构。 (或者C ++中的类;规则在那里是相同的)。 因此,为了最小化填充(对于速度/缓存空间效率),将成员从最大到最小排序通常是个好主意,因为许多类型的对齐要求等于它们的宽度。或者,如果要将较小的成员放在较宽的成员之前,则将它们成对/四边形分组。请记住,许多实际实现在32/64位指针和/或32/64位long之间有所不同。例如x86-64 Windows上的64位指针和32位sizeof,x86-64上的64/64,其他所有内容。当然,纯ISO C只设置类型必须能够表示的最小值范围,并且它们的最小1int,但是大多数现代CPU(以及它们的主流C实现)已经确定为32位int。 避免编写依赖于类似假设的代码来保证正确性,但在考虑性能时要记住这一点很有用。

3
投票

由于标准没有为指向不相关对象的指针指定算术,因此减去这些指针很容易出现UB,并且它依赖于实现。

由于它是依赖于实现的,因此不能指望结果。

通常,根据经验,编译器会在程序堆栈上为彼此分配两个整数(大小并排)。在这种情况下,对于具有平坦存储器架构的系统,减去地址将给出我们#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

}

输出:

qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.