变量和存储位置有什么区别?[关闭]


38

最近,我一直在尝试以视觉方式将指针解释为抽认卡。

问题001:这是计算机内存中某个位置的图形。它的地址是真的0x23452吗?为什么?

在此处输入图片说明

答:是的,因为0x23452描述了计算机可以在哪里找到此位置。


问题002:字符b存储在存储单元中是否正确0x23452?为什么?

在此处输入图片说明

答:不,因为字符a实际上存储在其中。


问题003:指针存储在存储单元中是否正确0x23452?为什么?

在此处输入图片说明

答:是的,因为存储位置的地址0x34501存储在其中。


问题004:指针是否存储在存储单元内是否正确0x23452?为什么?

在此处输入图片说明

答:是的,因为另一个存储位置的地址存储在其中。


现在,让我担心的部分。一位软件工程师这样向我解释了一些建议:

指针是一个变量,其值是另一个变量的内存地址。

根据我向大家展示的四个抽认卡,我将以略有不同的方式定义指针:

指针是一个存储位置,其值是另一个存储位置的存储地址。

可以肯定地说变量与存储位置相同吗?

如果没有,那么谁是对的?变量和存储位置有什么区别?


37
这里有一个隐含的假设,即每个阅读这些图片的人都会知道您的意图,即盒子下面的十六进制数字是内存地址,而a0x23453nil等。里面的东西就是价值。对于您来说,这似乎很明显,但是如果不了解这些字段的定义方式,我就不愿意为这些问题提供决定性的答案。确实没有办法知道a第二张图片中是字符,字符串(如果它们有所不同)还是变量名。如果是字符串,那么nil字符串也是吗?还是“空”值?
ilkkachu,

39
问题1是一个坏问题。那是您需要告诉读者的内容,然后他们才能回答其他问题。应该向读者提供信息,而不是一个问题:“在以下问题中,方框是存储位置,下面的十六进制数字是它们的地址”。
of

15
在上下文中无法回答问题3。无法在字节级别上告诉如何在应用程序级别解释/使用存储在内存中的值。
of

6
值得注意的是:您在此处编写的所有内容对于C或C ++都是正确的,但是对于基本上没有显式指针引用/解引用的任何语言,基本上都是false。变量的整个比喻是值被放入的槽,对于一种语言(例如Python,Java,C#,Ruby,JavaScript或许多其他语言),细分就是这样,其中赋值只是使变量指向对象而无需复制它,并且通过指向该对象的所有变量都可以看到该对象的突变。出于这个原因,Python的文档使用变量的替代隐喻作为挂在对象上的名称标签。
Mark Amery

19
顺便说一句,请原谅我,如果您已经理解这一点,但是这似乎有点令人困惑-这种“ 0x23452”表示法只是一种以十六进制格式表示数字的方式,并且为了方便起见。但这只是一个数字-0x前缀绝不表示它是一个指针,存储在内存中的内容实际上只是一个毫无意义的数字(您可以使用纯十进制整数来标记内存位置)。含义(即,数字的解释方式)来自语言-变量的类型及其使用方式。
FilipMilovanović

Answers:


69

变量是符合算法目的的逻辑结构,而内存位置是描述计算机操作的物理结构。一般而言,为了执行程序,在变量的逻辑概念与计算机的存储之间存在(编译器生成的)映射。

(即使在汇编语言中,我们也有(逻辑)变量用于算法和意图以及(物理)内存位置的概念,尽管它们在汇编中更为混杂。)

变量是一个高级概念。变量代表未知数(如在数学或编程分配中)或可以用值替代的占位符(如在编程中:参数)。

内存位置是一个低级概念。内存位置可用于存储值,有时可用于存储变量的值。但是,CPU寄存器是另一种存储某些变量值的方法。CPU寄存器也是较低级别的存储位置,但是它们不是内存位置,因为它们没有地址,只有名称。

从某种意义上说,变量是表示程序意图的抽象机制,而内存位置是提供存储和检索的处理环境的物理实体。

问题003:指针存储在存储单元0x23452中是否正确?为什么?

我们不能肯定地说。仅仅因为那里有一个可以用作地址的值,并不意味着它就是那个地址,它可以是整数(十进制)‭144466‬。我们不能仅仅根据数值的显示方式对数值的解释做出假设。

问004:是否在存储单元0x23452中存储了一个指针?为什么?

这确实是一个奇怪的问题。他们期望基于盒子的一些假设,但是,请注意,每个盒子的地址增加1。在任何现代计算机中,这意味着每个盒子都可以容纳一个字节-字节寻址能力已成为数十年来的标准。但是,一个字节只有8位,范围为0到255(对于无符号值);但是它们显示出存储在这些地址之一中的更大的值,因此非常可疑。(如果这是一个用词寻址的机器,这可能会起作用,但是并没有这么说,而且今天的机器很少,尽管有些教育机器也是如此。)

根据我向大家展示的四个抽认卡,我将以略有不同的方式定义指针:

指针是一个存储位置,其值是另一个存储位置的存储地址。

尽管在某些情况下这种想法是正确的,但您在这里混用了一些隐喻。变量的概念取决于算法及其意图—无需假设所有变量都具有存储位置。某些变量(尤其是数组)具有内存位置,因为内存位置支持寻址(而CPU寄存器只能命名为未索引)。

为了执行,变量和语句与处理器内存位置和处理器指令序列之间存在逻辑映射。其值从不改变的变量(例如常量)甚至不需要存储位置,因为可以随意复制该值(例如,根据编译器生成的代码序列的需要)。


4
甚至8位字节仍然不是通用的。
重复数据删除器

14
@JimmyJames for当编译器决定完全展开循环时,请考虑循环索引的情况。在产生的输出代码(无论是汇编代码,机器代码还是字节代码)中都没有存储循环计数器的存储位置。但这仍然是一个变量。
dmckee

4
@JimmyJames,对于展开式循环指针,则是的,如果您的代码实际上使用了计数器的值,则必须将其装入某个位置,但是(a)该位置可以是一个寄存器,而(b)从原则上讲,没有理由为什么在展开循环的每次迭代中它都必须位于相同的位置。
所罗门慢

3
如果该循环正在执行将固定长度数组复制source到等长数组之类dest的操作,则编码为的循环很for (int i=0; i<8; ++i) dest[i] = source[i];可能会编译为等同于重复dest++ = source++;适当次数的循环。使用循环计数器本身并没有证据(甚至在寄存器中也没有),只有重复的次数告诉您有关循环条件的信息。
dmckee

2
这种区别在某种程度上被诸如C之类的语言所混淆,这些语言的语义紧密地基于内存由编号位置组成的机器的抽象。
Michael Kay

20

可以肯定地说变量与存储位置相同吗?

否。变量和内存位置是处于两个不同抽象级别的两个抽象。在代码/语言级别,变量和指针是较高级别的概念,在机器级别,存储器位置是较低级别的概念。将代码编译成可执行文件后,就不再有任何变量。尝试以这种方式谈论内存位置和变量是一个分类错误。

可以使用内存来实现变量,但并非总是如此,因为编译器可以优化计算并完全在寄存器中进行与变量有关的所有计算,或者可以将单个变量放入多个内存位置,或者可以使用单个内存多个变量的位置。

指针是一个存储位置,其值是另一个存储位置的存储地址。

这一系列的抽认卡是如此混乱,它们不仅不正确,而且甚至没有错。


1
Once a code had been compiled into an executable, there's no longer any variables.我可以说是不同意的。正确的是,您所知道的变量(即名称)不再存在,但是您的措辞似乎表明编译的可执行文件仅使用内存地址。那是不对的。您的已编译但未执行的可执行文件不知道执行时将使用哪个内存地址。变量的概念(即对在运行时将分配的任何内存地址的可重用引用)仍然存在于已编译的可执行文件内部。
平坦的

2
或者,编译器可以通过各种方式完全优化变量。预计算某些内容,修剪不必要的变量。如果变量是一个常量,则编译器最终可能会使用使用常量的CPU指令,因此我认为不再将变量视为任何位置。
kutschkem,

16

变量是语言构造。它们具有名称,位于范围内,可以被代码的其他部分引用,等等。它们是逻辑实体。只要可观察到的行为是语言标准规定的行为,编译器就可以自由地以其喜欢的任何方式来实现此语言构造。这样,如果编译器可以证明不需要,则该变量甚至不需要存储在任何地方。

内存位置是一个硬件概念。它们表示虚拟/物理内存中的位置。每个内存位置都只有一个物理地址以及可用于操作它的任意数量的虚拟地址。但是,每个存储位置始终只存储一个字节。

指针是一种特殊的说某物是一个指针,就像说某物是类型double。它表示该值使用了多少位,以及如何解释这些位,但这并不意味着该值存储在变量中,也不意味着该值存储在内存中。


举一个C语言的例子:当我有一个2D数组int foo[6][7];并使用来访问它的一个元素时foo[1][2],则foo是一个保存数组的变量。当foo在该上下文中使用时,它被变成指针数组的第一个元素。该指针既不存储在任何变量中,也不存储在内存中,它的值仅在CPU的寄存器中生成,使用并被忘记。同样,foo[1]在这种情况下,表达式又变成了另一个指针,该指针又不在变量中,也不存储在内存中,而是在CPU中计算,使用和遗忘。变量内存位置指针这三个概念实际上是三个不同的概念。


顺便说一句,我真正的意思是“每个存储位置总是存储一个字节”。在大约50年前的计算时代,情况并非如此,但实际上对于当今使用的所有硬件都是如此。每当您在内存中存储的值大于一个字节时,实际上您就在使用多个连续的内存位置。即(假设大字节序),数字0x01234567存储为

+------+------+------+------+
| 0x01 | 0x23 | 0x45 | 0x67 |
+------+------+------+------+
    ^      ^      ^      ^
    |      |      |      |
 0x4242 0x4243 0x4244 0x4245

(像X86架构这样的小字节序机器按相反的顺序存储字节。)对于指针也是如此:在64位机器上的指针存储在八个连续的字节中,每个字节都有自己的内存地址。您不能看着一个存储单元说:“哦,这是一个指针!” 当您查看内存时,您始终只会看到字节


计算机如何知道一组连续的内存位置何时开始和结束?
progner

6
@progner不会。它根据获取的指令解释内存中的字节。这些指令本身也只存储在字节序列中。到CPU持有的指令,保持一个字符一个字节,并拥有一些浮点位一个字节一个字节的唯一区别,是怎么也奉命使用此字节。如果由于程序计数器指向该字节而获取了该字节,则将其用作指令。如果由于指令要求将其加载到浮点寄存器而被提取,则将其用作浮点数据。
cmaster

7
@progner这实际上是von-Neuman体系结构的关键创新:将指令和数据都存储在同一存储器中,允许指令更改数据,然后将其作为更多指令执行。这允许进行自我修改的代码,但也允许系统的内核将某些程序加载到内存中,然后告诉CPU执行该程序。在von-Neuman之前,像Zuse机器之类的计算机将通过完全独立于其操作数据的通道来获取指令。
cmaster

5

让我关注您的实际问题- “谁是对的?” 比较这两个语句时:

  • 指针是一个变量,其值是另一个变量的内存地址
  • 指针是一个存储位置,其值是另一个存储位置的存储地址。

没有答案。第一个讲的是“另一个变量的内存地址”,但是变量不一定具有内存地址,正如其他答案已经说明的那样。第二个说“指针是一个存储位置”,但是指针实际上只是一个数字,可以存储在变量中,但是像以前一样,变量不一定具有存储地址。

一些更精确的陈述的例子:

  • “指针是代表存储位置的存储地址的数字”,或

  • “指针变量是一个变量,其值是存储位置的存储地址。”

  • “内存地址可以保存一个代表内存位置内存地址的指针。”

注意有时术语“指针”被用作“指针变量”的快捷方式,只要不引起混淆,就可以。


您可以将“另一个”更改为“ a”,因为指针可以指向自身。
Pieter B

@PieterB:好了,好了;-)不知道这是否真的更清楚了,因为我只想将原始措词更改为使它们变得明智所需的程度。但是,我进行了编辑。
布朗

公平地讲,如果您得到的挑剔是“但指针实际上只是一个数字”也不正确,那么实际上指针是一个代表数字的标识符;)至少我们必须知道特定于语言的语言才能细节。
Zaibis

2
指针是可能引用某个对象的值(数字对于某些实现来说已经太具体了)。可能还有空指针,野生指针和悬空指针,尽管其中的某些(甚至全部!)可能会被所使用的语言所排除。
重复数据删除器

2
@Deduplicator:您是对的,但是我认为指针作为数字的心理模型足以解决此问题。因此,让我们保持简单。
布朗

5

我当然不会说指针是包含地址的内存位置。首先,我不知道0x23453可以容纳一个字节的体系结构。:)即使您手动取消了字节/字的区分,仍然存在每个存储位置都包含一个地址的问题。地址只是数字,而存储器的内容只是数字。

我认为这里的窍门是“指针”描述的是人的意图,而不是体系结构的任何特定功能。这类似于“字符”或“字符串”不是您可以在内存中看到的具体事物的方式-它们全都只是数字,但它们用作字符串,因为这就是对它们的处理方式。“指针”仅表示打算用作地址的值。

老实说,如果您的目标是教一种特定的语言(目标C?),我不确定画出经典的存储磁带是否有用。通过显示键入的值和对于字节而言太大的值,您已经在告诉白色谎言。讲授语义而不是技巧-关于指针的关键见解是它们提供了间接访问,这是一种非常有用的理解工具。

我认为可能与URL比较好,URL告诉您在哪里可以找到一些数据,但不是数据本身。听我说:

  • 您很少关心URL实际什么;他们中的绝大多数人都与名字联系在一起。很多人在不知道URL 如何导致页面的情况下使用Internet 。有些人完全不使用URL。

  • 并非每个字符串都是URL,也并非打算用作URL。

  • 如果您尝试访问伪造的URL或曾经存在但已被删除的页面,则会收到错误消息。

  • URL可能指向图像,某些文本,某些音乐或许多其他单个项目–或者可能指向包含各种内容的页面。有一整页具有相似布局但数据不同的页面是很常见的。

  • 如果创建网页,并且要引用其他网页上的数据,则无需全部复制和粘贴;您可以直接链接到它。

  • 任何数量的其他页面都可以链接到相同的URL。

  • 如果您有一组相似的页面,则可以创建一个索引页面,列出所有页面的链接,或者您可能只是在页面1的底部有一个“下一个”链接,将您带到页面2,依此类推。两种方法的优缺点都显而易见,尤其是当您考虑网站管理员在不同位置添加或删除页面时需要做的事情时。

这个比喻非常清楚什么是指针,这是理解他们的关键-否则他们只是显得随意的,复杂的,和毫无意义的。如果您已经了解某件事情的功能以及为什么有用,那么了解它的工作原理就容易得多。如果您已经内部化了一个指针,它是一个告诉您其他位置的黑盒子,然后您了解了内存模型的复杂性,那么将指针表示为地址就很明显了。另外,语义教学将使您的学生处于更好的理解和发明其他形式的间接寻址的地方-当大多数主要语言根本没有指针时,这很好!


every memory location contains an address-每个存储位置都有一个地址。它不包含在任何地方,除了可能包含指针变量中。
罗伯特·哈维

@RobertHarvey每个存储位置(至少一个单词)都包含一个数字,可以简单地将其解释为地址。关键是硬件中没有任何东西可以真正区分地址和非地址
Eevee

2

我知道您已经接受了一个答案,并且这个问题已经有五个答案,但是有一点他们没有提到,我认为这使您感到困惑。CS教科书经常尝试不了解编程语言的选择,这导致了一个隐含的假设,即用来描述事物的术语是通用的。不是。

在C中,一元&运算符称为“地址”运算符。C程序员会毫不犹豫地说该表达式的&x值等于变量x的地址。当然,它们的意思是“存储变量x的值的内存地址”,但是在随意的交谈中,没有人会那么讨厌。在C语言中,“指针”一词通常是指旨在以内存地址作为其值的变量的数据类型。或等效地,值的数据类型。但是有些人会使用“指针”作为值本身。

在Java中,对象或数组类型的所有变量的行为都非常类似于C指针(指针算术除外),但是Java程序员将其称为引用,而不是指针。

C ++认为引用和指针是不同的概念。它们是相关的,但不完全相同,因此C ++程序员必须在对话中加以区分。在某些情况下,与符号被理解为“地址”,而在另一些情况下则被理解为“引用”。

指针是一个变量,其值是另一个变量的内存地址。

这就是C程序员使用“指针”和“整数”的含义来描述它的方式。(例如,“指针保存内存地址,而整数保存某个范围内的整数。”)

指针是一个存储位置,其值是另一个存储位置的存储地址。

这是一种奇怪的说法,因为它要求对“ is”进行非常宽松和非正式的定义。

可以肯定地说变量与存储位置相同吗?

可以说存储器地址是存储器中存储变量值的位置,这将更加清楚。(由于编译器的优化,并非所有变量都存储在内存中,但是使用其地址的任何变量&x都会被保存。)


当我们正在学书时:存储某物的地址。除了不能存储任何内容的地址之外,通常将内容存储在多个相邻的位置中(仅使用可能的多个地址之一)(通常由某种程度一致的规则选择)寻址其中的一个。
重复数据删除器

@Deduplicator我不是一个想学究的人。
gatkin

C标准甚至在形式上区分必须在每个“序列点”严格遵循抽象机步骤的变量(出于线程安全和在内存映射硬件上进行某些低级操作的目的)与那些没有t,可以自由移动到寄存器中或完全优化。
戴维斯洛

@Davislor:C标准在其他语言规范使用“变量”的地方使用了“对象”一词,并描述了不是变量的其他东西。一些讨论可能会使用与语言无关的术语“变量”,但是无论出于何种原因,标准都缺少一个术语来区分命名的不相交分配(变量)与其他类型的对象,例如嵌套分配(结构/联合成员)还是产生的未命名对象通过取消引用指针。非正式地,“变量”是一个很好的术语,但是标准没有使用它。
超级猫

@supercat这是不正确的。C11标准使用“变量”一词超过一百次,其中几十个是名词,例如,“即使通过原子操作并发访问要初始化的变量,也构成了数据竞争。”
Davislor,

1

语句A指针是一个变量,其值是另一个变量的内存地址,被简化了。但是,当读者了解确切的内存位置以及它与变量的区别时,他们将已经了解指针的确切位置,因此不再需要依靠这种不正确的解释。

语句A指针是一个存储位置,其值是另一个存储位置的存储地址错误。指针的值不需要存储在内存位置,如果指针需要指向内存位置,则有争议,这取决于“内存”的预期定义。

变量和存储位置有什么区别

存储位置是可以存储数据的多个可能位置之一。该数据可以是变量,也可以是变量的一部分。变量是标记数据的一种方式。


0

这个答案集中在C和C ++上。这似乎是适当的,因为您的问题所关注的是指针,与其他语言相比,指针是C / C ++不可或缺的一部分。这篇文章的大部分内容将适用于大多数编译语言,而无需复杂的运行时间(例如Pascal或Ada,但不适用于Java或C#)。

已经给出的好的答案强调,变量是比物理内存更抽象的语言构造。我想强调的是,这种抽象具有一定的原理和系统:

抽象主要在于使用名称而不是文字地址。

原理是变量是类型对象的命名句柄。C / C ++中的对象通常位于内存中。这些语言随后为类型转换添加了有关生命周期管理和数据封送处理的一些技巧。变量的概念比物理地址更抽象,因为我们实际上并不关心地址的数值或函数在内存中的确切位置。我们只是简单地命名它们,然后再按名称寻址,然后编译器,链接器和运行时系统将处理详细的细节。

并且不要假装C / C ++与内存无关:毕竟,存在通用的地址运算符。是的,没错,您不能在寄存器存储类中使用C变量的地址;但是您上一次使用过什么?这是一般概念的一个特殊例外,而不是大肆驳斥该论点。相反,一般规则是,获取变量的地址实际上会迫使编译器确实在内存中创建对象,即使它否则不会这样做(例如,使用常量)。“命名句柄”概念对于C ++引用也是一个很好的范例:引用只是同一对象的另一个名称

当我为68k编写内联汇编程序时,很高兴看到如何使用变量名称作为地址寄存器的偏移量(并且可以使用声明的变量名称register代替裸机寄存器名称!)。对编译器而言,变量是恒定的地址偏移量。重申一下:变量被称为句柄,通常用于内存中的对象。


指针也是C#,Java,JS和其他语言的非常基本的部分。以不同的方式称呼他们并不会改变,尽管这是很好的公关。
重复数据删除器

@Deduplicator :-)好醇”托尼...
彼得-恢复莫妮卡

0

听起来问题似乎是针对通过用附加保证扩展C标准而形成的一种流行语言,“如果该标准的某些部分或实现的文档描述了某些操作的行为,而另一些部分将其归类为未定义,前一部分占主导。”,以及“变量”的定义与其他语言对该术语的使用保持一致。

用这种语言,可以将每个存储位置视为一个带编号的邮箱,该邮箱始终包含一定数量(通常为八个)的位,每个位可以独立为零或一。内存位置通常以两行,四行或八行的形式组织。并且某些操作会同时处理多个连续的内存位置。取决于机器,在两个,四个或八个内存位置的组上进行操作的某些操作可能仅限于在单行内的位置上进行操作。此外,尽管某些机器可能具有一个由连续编号的邮箱组成的房间,但其他机器可能具有多个不连续的编号邮箱组。

变量标识与之唯一关联的存储位置范围,以及应解释这些存储位置的类型。读取变量将导致以与变量的类型相适应的方式解释其关联存储位置中的位,而写入变量将导致以与变量的类型和值相适应的方式来设置关联位。

地址封装了识别邮箱所需的所有信息。可以将其存储为简单数字,也可以将其与该组中邮箱的编号一起存储为某种组标识符。

&运算符应用于变量将产生一个指针,该指针封装了地址和类型。将一元*[]运算符应用于指针将导致以适合于封装类型的方式解释或设置从封装地址开始的邮箱位。


听起来您好像在想这个问题。
罗伯特·哈维

0

我参加这个聚会迟到了,但我忍不住要投入2美分。

在这些时候,这些存储位置中存储的值之间有什么区别?

时间1

在此处输入图片说明

时间2

在此处输入图片说明

正确答案:什么都没有。它们都是相同的值,但对它们的含义有不同的解释。

我怎么知道 因为我是弥补这一点的人。您还真的不知道。

您遇到了我称之为带外问题的问题。这里没有存储如何正确解释这些值的含义。该知识存储在其他位置。但是,当您在纸上提出这些价值观时,您会做出这种解释。这意味着您已经添加了这些内存位置中根本不存在的信息。

例如,这里的值是相同的,但是只有当您假设我是第一个获得ASCII / UTF-8字符编码的方式时才知道是正确的,而不是说EBCDIC。而且,您还必须假设第二个是存储在这些内存位置的数值的十六进制表达式,它们全都可以是指向其他地址的指针,而不是说对所有以“ 0x”开头的字符串的引用。:P

这些内存位置中存储的任何内容都不会告诉您这些假设是正确的。该信息可以存储。但是它将存储在其他位置。

这是表示问题。您必须先同意如何呈现数字,才能表示任何数字。您可以依靠假设,约定和上下文,但是如果您对其进行了深入研究,则在未明确定义演示文稿时,唯一真正正确的答案是“信息不足”。


当相同的内存同时用于不同的常量事物时,这会更加有趣。
重复数据删除器

@Deduplicator是的。这总是让我想到c ++的reinterpret cast。相同的位以不同的方式出现。
candied_orange

@Deduplicator或者,想起来,在c中的联合
candied_orange
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.