c语言 - 函数参数地址之间的差异总是 4 个字节吗?



我一直在用 C 语言进行一些指针测试,我只是好奇函数参数的地址是否总是相差 4 个字节。

我尝试运行以下代码:

#include <stdio.h>
void func(long a, long b);
int main(void)
{
func(1, 2);
getchar();
return 0;
}
void func(long a, long b)
{
printf("%dn", (int)&b - (int)&a);
}

此代码似乎总是打印 4,无论 func 的参数类型是什么。 我只是想知道它是否总是 4,因为如果是这样,它对我正在尝试做的事情很有用(但如果不一定是 4,我想我可以将va_list用于我的函数或其他东西)。 所以:一定是4个字节吗?

绝对不是,在很多方面很难将它们全部计算出来。

首先,参数的内存布局根本不是由 C 语言指定的。 句点。 未指定。 因此,答案立即是否定的。

va_list之所以存在,是因为需要能够浏览一系列变量参数,因为除此之外没有指定它。va_list故意非常有限,因此它可以在堆栈形状与您的直觉不匹配的平台上工作。

它不能总是 4 的其他原因:

  • 如果传递长度为 8 的对象怎么办?
  • 如果编译器优化引用以实际指向另一帧中的对象,该怎么办?
  • 如果编译器添加填充,也许是为了在 64 位边界上对齐 64 位数字怎么办?
  • 如果堆栈以相反的方向构建(使得差异将是 -4 而不是 +4)怎么办

这样的例子不胜枚举。 C 不指定参数之间的相对地址。

正如其他答案正确说的那样:

不。

此外,即使试图确定地址是否相差 4 个字节,这取决于你如何做到这一点,也可能有未定义的行为,这意味着 C 标准没有说明你的程序做什么。

void func(long a, long b)
{
printf("%dn", (int)&b - (int)&a);
}

&a&blong*类型的表达式。将指针转换为int是合法的,但结果是实现定义的,并且"如果结果无法以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。

指针很可能是 64 位,int是 32 位,因此转换可能会丢失信息。

转换很可能会为您提供类型int的值,但它们不一定有任何意义,它们的差异也不一定。

现在你可以直接减去指针值,结果是有符号整数类型ptrdiff_t(与int不同,它可能足够大,可以保存结果)。

printf("%tdn", &b - &a);

但是"当减去两个指针时,两个指针都应指向同一数组对象的元素,或者一个指针指向数组对象的最后一个元素;结果是两个数组元素的下标的差异。 指向不同对象的指针无法有意义地进行比较或减去。

综上所述,您使用的实现很可能具有相当简单的内存模型,并且指针值实际上表示为整体内存空间的索引。 比较&b&a语言不允许,但检查这些值可以提供有关幕后发生的事情的一些见解 - 如果您正在跟踪错误,这可能特别有用。

您可以携带以下操作来检查地址:

printf("&a = %pn", (void*)&a);
printf("&b = %pn", (void*)&b);

您看到的减法 (4) 结果表明,系统上的类型long可能是 4 字节(32 位)。我猜你在Windows上。它还建议了一些关于函数参数分配方式的信息——作为程序员,你几乎不应该关心这些东西,但无论如何都值得理解。

[...]我只是好奇函数参数的地址是否总是相差 4 个字节。

推理中最大的错误是认为参数根本不存在于内存中。

我在x86-64上运行此程序:

#include <stdio.h>
#include <stdint.h>
void func(long a, long b)
{
printf("%dn", (int)((intptr_t)&b - (intptr_t)&a));
}
int main(void)
{
func(1, 2);
}

并编译它gcc -O3打印8,证明你的猜测是绝对错误的。除了。。。当我在没有优化的情况下编译它时,它会打印-8.

X86-64 SYSV 调用约定表示,参数在寄存器中传递,而不是在内存中传递。ab没有地址,直到你用&获取他们的地址 - 然后编译器被抓到裤子,因为它欺骗了好像规则,它迅速拉起裤子并将它们塞到某个内存位置,以便他们可以获取他们的地址,但它在存储位置上绝不一致。

最新更新