为什么可以在 c 中存储指向 int 变量的 int 指针的信息内容



让我们考虑以下一段代码:

#include <stdio.h>
int main()
{
int v1, v2, *p;
p = &v1;
v2 = &v1;
printf("%dt%dn",p,v2);
printf("%dt%dn",sizeof(v2),sizeof(p));
return 0;
}

正如预期的那样,我们可以看到 v2 变量 (int) 占用 4 个字节,p变量 (int指针) 占用 8 个字节。

那么,如果一个指针占用了超过 4 个字节的内存,为什么我们可以将其内容存储在int变量中呢?

在底层实现中,指针变量是只存储另一个变量的内存地址,还是存储其他内容?

我们可以看到,正如预期的那样,v2 变量 (int) 占用 4 个字节 并且p变量(int指针)占用 8 个字节。

我不确定你的期望到底是什么来源。 C 语言不指定int或指针的大小。它对类型int的可表示值范围的要求int提供小至两个 8 位字节的大小,从历史上看,这曾经是int相对常见的大小。 现在的一些实现有更大的ints(也许还有更大的char,这是sizeof的度量单位!

我想你在这里的观点是,在测试的实现中,int的大小小于int *的大小. 很公平。

那么,如果指针占用超过 4 个字节的内存,为什么我们可以 将其内容存储在int变量中?

谁说代码将指针的(全部)内容存储在int中? 它将指针转换为int*,但这并不意味着结果包含足够的信息来恢复原始指针值。

这同样适用于将double转换为int或将int转换为unsigned char(例如)。 这些赋值在没有显式类型转换的情况下是允许的,但它们不一定是值保留的。

也许您的困惑反映在"内容"一词中。 赋值不会将右侧的表示形式存储到左侧对象。 如有必要,它将转换为目标对象的类型,并存储结果。

在底层实现中,指针变量是否存储 只有另一个变量的内存地址,或者它存储了一些东西 还?

实现可以而且已经有所不同,不同机器的"地址"含义也是如此。 但如今最常见的是,指针表示为二进制数,用于指定平面地址空间中的位置。

但这并不重要。 C 指定指针可以转换为整数,反之亦然。 它还提供整数类型intptr_tuintptr_t(以stdint.h为单位),支持从全保真往返void *到整数到void *的转换。 指针表示与所有这些无关。 实现的责任是实现所涉及的类型和转换,以便它们按要求运行,并且有多种方法可以做到这一点。

>*C 实际上需要在指针和整数之间进行显式转换(即类型转换)。 语言规范未定义示例代码中无强制转换赋值的含义,但某些编译器确实接受这一点并隐式执行所需的转换。 我的发言假定有这样的实施。

总是有一个警告,见下文。

main.c: In function ‘main’:
main.c:6:8: warning: assignment to ‘int’ from ‘int *’ makes integer from pointer without a cast [-Wint-conversion]
v2 = &v1;
main.c: In function ‘main’:
main.c:6:10: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
v2 = (int) &v1;

在第一种情况下,仅将整数值设置为指针值是不合适的,因为它不是兼容的类型。

在第二种情况下,通过将指针强制转换为整数,编译器可以识别不同大小的问题,这意味着 v2 不能完全保持 (int) &v1;

结论:这两种情况在造成不良行为方面都是"坏的"。

关于你的问题"那么,如果指针占用超过4个字节的内存,为什么我们可以将其内容存储在int变量中?"- 它不能完全存储在int变量中。

关于你的问题"在底层实现中,指针变量只存储另一个变量的内存地址,还是存储其他东西?"- 指针只指向一个地址。(它可以是另一个变量的地址,也可能不是。没关系。它只指向一个地址。

理解这里发生的事情的关键是 C 是底层 ISA 之上的抽象层。大多数架构只有寄存器和存储器地址1可供使用,所有这些地址的大小都是固定的。在操作"变量"时,你实际上只是在表达你的意图,编译器将其转化为更具体的指令。

在x86_64(一种常见的体系结构)上,int实际上要么是 64 位寄存器的一部分,要么是内存中在 4 字节边界上对齐的 4 字节位置。int*是内存中的 64 位值或 8 字节位置,具有相应的对齐约束。

允许将int*值放入大小合适的变量(如uint64_t)中。可能不允许将该值放回指针并执行该指针,这取决于您的体系结构。

从程序员的角度来看,指针只有64位数据。从 CPU 的角度来看,它可能包含更多内容,现代架构具有内部"指针身份验证代码"(PAC) 之类的东西,可确保指针不能从外部源注入。引擎盖下变得更加复杂。

一般来说,最好将指针视为不透明的,也就是说它们的实际值与随机性一样好,与程序的内部操作无关。只有当您使用足够强大的分析工具在体系结构级别进行更深入的分析时,指针的实际内部才能提供信息或相关。

您可以对指针执行几个定义明确的操作,例如p[n]访问结构或分配范围内的特定偏移量,但除此之外,您可以执行的操作甚至推断都非常有限。请记住,现代 CPU 和操作系统使用虚拟内存,因此指针地址是"假的",并不表示它们在物理内存中的位置。事实上,它们被故意打乱,使它们更难猜测。


1这忽略了 VLIW、SIMD 和其他不那么简单的扩展。

那么,如果一个指针占用了超过 4 个字节的内存,为什么我们可以将其内容存储在 int 变量中呢?

你不能,事实上你发布的代码是不合法的。


#include <stdio.h>
int main()
{
int v1, v2, *p;

这声明了int变量和指向int的指针,称为p

p = &v1;

这是合法的,因为您分配p整数变量v1的地址。

v2 = &v1;  /* INCORRECT!!! */

这不是。 它将另一个变量的地址分配给一个int变量(这是一个指针,正如你所说,这是不可能的) 代码编写者最可能的意图是:

v2 = *p;

v2存储在p指向的地址处的整数值(它指向v1,因此它v2分配存储在v1中的值。

最新更新