计算机内存中变量及其引用的存储和访问方式是怎样的?

4
计算机内存中如何存储变量?
例如:当我们指定
int x = 15;
通常情况下,计算机在其RAM中分配了一个4字节的内存块,在分配的4字节中以0和1的形式存储值15,并且x引用分配的4字节的地址。
现在,我的疑问是x如何引用内存位置。引用存储在哪里?如何存储?
当我们使用x时,它如何知道引用该内存位置。
这对于不同的语言(如Java和Python)是否不同?
这对于不同的数据类型是否不同?
如果您能制作视频或博客来澄清这一点,我将不胜感激。
1个回答

6

通常,计算机在其内存中分配4个字节......

整个句子完全正确。只是,有不同类型的分配方式。最简单的一种是,在某个时刻计算机具有一定量的自由且连续的内存。当一个程序启动时,所有这些(自由且连续的)内存都会被赋予新的程序。完全由程序负责管理内存,不要在内存区域之外进行写操作。这就是DOS(实模式)的工作方式——非常简单。程���(或其运行时)仅被告知内存区域的起始和结束地址。它将一些“基址”寄存器映射到该内存中,因此例如地址0指向开始地址,程序可以知道变量X所在的位置。更先进的操作系统采用更高级的机制,例如阻止程序尝试在其允许的内存范围之外读取或写入。更复杂的操作系统甚至可以在程序请求时为程序提供更多的内存。

现在,我的疑问是X如何引用内存位置。引用存储在哪里?如何存储?

通常,引用在(编译后的)程序中根本不存储。你(程序员)称为“X”的东西,对于程序来说只是一个地址。第一个变量地址为0,第二个变量地址为4,以此类推。调试符号(如果未被剥离)跟踪赋予地址的“高级”名称;但它们由调试器使用,并不是程序的技术部分。

当我们使用X时,它如何知道引用该内存位置。

让我们写一个简单的C程序:

int x = 15;
int y = 20;
int s;

s = x+y;

编译器看到“int x”并将地址0分配给x。然后看到“int y”并将地址4分配给y。然后将地址8分配给“s”。当然,这些地址在整个编译过程中都被记住。 同时,它看到了“x=15”和“y=20”,因此输出以下指令:
"store an integer of 4 bytes, value 15, at address 0"  
"store an integer ..., 20, at address 4"

最终它看到了"s=x+y"并输出了以下内容:
"take in the left hand the int (4 bytes) value at address 0"
"take in the right hand the int value at address 4"
"pour the left hand in the right hand"
"pour the right hand in 4 bytes at address 8"

如你所见,没有了 x y 或 s,只有地址(这是一个非常简化的解释)。

不同语言(比如 Java 和 Python)是否有所不同?

有所不同,但差别并不大。变量名称始终指向一个地址。纯解释型语言必须跟踪变量名称,因为编译和执行之间没有分离,而中间语言(如 Java,因为 Java 既是编译器又是解释器)可以做更复杂的事情。

不同数据类型是否有所不同?

对于简单类型来说,没有什么区别。在 C 语言中,如果不考虑指针和堆,所有变量都是同等对待的,只有它们的长度不同。int 可能是 4 个字节长,char 一个字节,数组的长度由程序员指定,但是变量始终指向包含值的缓冲区的第一个位置。编译器会保留每个变量的地址、类型和长度,以便在源代码中遇到变量名称时知道该怎么做(编译哪些指令)。


请问能否具体说明“在计算机编译期间,x 如何变成 0”这一过程?

这个概念非常简单。编译器读取源代码的文本。每次遇到变量声明时,它会将其分配给第一个空闲地址,然后将该地址增加变量的大小。在开始时,此地址为 0。编译器读取“int x;”之后,x 的地址变为 0,并且“当前”地址被递增(对于 int,可能是 4 或 8)。变量名称、类型和地址被保留下来,并形成一个“查找表”。这个表用于检查是否重复声明标识符,并用于在源代码引用已声明的变量时知道从哪里读取和写入。如果源代码引用了未声明的变量,编译器会报错,因为在查找表中找不到该变量。顺便说一句:你发布了一个涉及指针的视频链接;指针也是变量,但它们引入了其他概念。我所说的有关地址的内容也适用于指针,因为它们只是变量,但它们的值使用方式不同——我不明白为什么你要坚持谈论它们。


在左手中拿住地址为0的int(4字节)值。编译器如何知道从哪个地址开始?它是否在运行时维护一个查找表?请看这个视频:链接 - acekuber
@acekuber 绝对不是在运行时。查找表仅在编译期间存在。您可以将编译器重命名变量:x 变成 0,y 变成 4,s 变成 8。然后在运行时,程序将使用它们的新名称“0”、“4”、“8”等操作这些变量。 - linuxfan says Reinstate Monica
明白了。但是你能告诉我在编译过程中计算机是如何实现“x变为0”的部分吗? - acekuber
非常有帮助的解释,但是需要一些关于变量查找表的澄清。这个查找表是在编译时还是运行时形成的?这个表存储在内存的哪个部分,栈、堆...? - acekuber
1
我认为这很清楚...查找表在运行时不存在,因此只在编译期间构建和使用。至于位置,可能是堆,因为它具有收缩/增长的能力。 - linuxfan says Reinstate Monica
@acekuber 在SO上发布了另一个问题:一个问题,一个主题。此外,消息不应该变成聊天。 - linuxfan says Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接