在C ++中,名字实际上是指针 - 引擎盖下?例如:
// num is a name that points to the address of the
// memory location containing 32.53
double num (32.53);
*num; // so can we deference it and get 32.53?
num; // same as *num but the compiler automatically
// dereferences the name for us?
这个问题有点奇怪,“机器级发生了什么?”还有关于指针的C ++语言语义。因此,我可以看出为什么答案是肯定/否定。 “是”因为在语言语义之外,标识符可以被认为是指内存中的位置;和“否”,因为它仍然不是C ++指针,并且如果代码中显示了非指针双精度则不正确。
所以我认为两个阵营都成功回答了我的问题。它可能被重述为“由于名称指的是内存位置,为什么它们不能被视为隐式指针?”但是这样的问题可能会产生模糊的争论或者不值得回答。我会仔细检查答案,并尝试找到(我觉得)从两个角度最好地回答问题的答案。换句话说,一个不只是说“那不是C ++指针假人!”或“当然这个名字指向记忆以某种方式”。
答案 0 :(得分:4)
如果你将指针的定义扩展为“任何引用内存中某些信息的概念设备”那么,是的,绝对。
然而,没有人这样做。
如果您使用术语句柄,您可以更接近这笔钱,该术语可变地用于表示“指针”,“参考”,“变量”,“资源对象”,源代码中的“名称”,“访问者”,“标识符”以及无数其他内容。
通常得出的结论是,一般术语过于模糊,最终会坚持使用特定于语言的术语(例如C ++的“指针”,具有非常特定的语义,不包括你所假设的那些,或者在行业范围内明确且普遍接受的那些。这些人很少。
答案 1 :(得分:4)
指针是程序员可见的值,它保存某个对象的位置(或者是一个不指向对象的空值,或者是一个不确定的值)。
尽管在运行时解析名称引用涉及寻址,但变量名称不是C ++中的指针。这是因为变量名不是表示位置的运行时值;它们是编译时(在带有链接的外部名称的情况下,链接时间)符号表示位置。
“指针”和“地址”不是一回事。例如,当我们调用一个函数时,假设它不是尾调用优化或内联函数,通常会在某处存储“返回地址”,以便函数可以返回给调用者。这个地址不是指针;或者至少不是C ++指针。它是一个“无用户可维护组件”,由实现后台管理,就像堆栈指针(如果有这样的东西),堆栈帧指针和其他机器级功能一样。至少在某些情况下,C ++引用也是使用运行时地址实现的。 C ++引用也不是指针。
有一个与变量名相关联的运行时指针值,您可以使用address-of运算符访问此属性:
double *pnum = #
但不是这样的:
*num; // so can we deference it and get 32.53?
你试过吗?一元解引用运算符需要指针类型的表达式;它不适用于double
类型的表达式。 (它可以使用类类型的表达式,如果适当地重载,这是其他的。)
虽然通过&
运算符,我们可以获得指向num
命名的存储位置的指针,但名称num
本身并不是指针。
当我们评估像num = 3
这样的代码时,很可能涉及一个地址。或者可能不是。例如,如果将num
优化为寄存器,则只需将3
加载到该寄存器中。即使num
具有内存地址,在该情况下该地址也不是程序员可见的。指针是程序员可见的:程序员创建它们,替换它们,取消引用它们,将它们存储在变量中,将它们传递给函数,等等。
实际上,C ++中的名称不是什么;名称不需要在运行时保留,也不能以任何便携方式访问。 (为了进行符号调试,实现方法可以在编译后保留有关名称的信息,支持动态链接的平台可以通过字符串查找动态符号。)
答案 2 :(得分:1)
没有
在这种情况下,num
是double
类型对象的名称。它的声明没有显式或隐式地创建指针。它创建一个浮点对象,初始化为32.53
。
您可以通过获取对象的地址来获取指针值(而不是指针对象),如&num
中所示,但您可以为任何对象获取。
至于*num
,如果num
的类型为double
,则属于非法行为。
对象的名称是引用对象本身的东西;从这个意义上讲,如果我真的很眯眼,我可以把它想象成一种“指针”。但是C ++意义上的指针是一个包含内存地址的值或对象,它在程序执行时存在;标识符仅存在于C ++源代码中。 C ++编译器将有一些内部编译时数据结构引用一个声明的变量,它将包含(或引用)有关其名称和类型的信息,但是恕我直言,将该数据结构称为“指针”是不合理的。它可能比内存地址复杂得多。并且在函数内部声明的变量的名称将在程序执行期间的不同时间引用不同的对象,或者根本不引用任何对象。
答案 3 :(得分:1)
没有
根据C标准的指针类型的定义(§6.2.5/ 20):
指针类型可以从函数类型或对象类型派生,称为引用类型。指针类型描述一个对象,其值提供对引用类型的实体的引用。
(强调我的)。
在你的情况下:
double num (32.53);
您有double
,标识符为num
,其值为32.53
,不打算用作指针值。甚至num
的类型都不是指针类型。
因此没有,num
不是指针,正如编译器会告诉你的那样,如果你曾尝试编译:
*num;
你无法取消引用它。
答案 4 :(得分:0)
是的,就像指针一样,标识符是对象的句柄,而不是对象本身。但它的间接级别仍然比指针变量少一个(其名称是地址的句柄,它是对象的句柄)。
实际上,编译器将维护一个符号表,它是从标识符到对象位置的映射(这里的位置通常不是内存地址,而是从堆栈底部或从数据段的开头偏移 - 但是C ++指针又不是虚拟内存系统上的物理地址。通常,这个符号表由编译器输出,以便在调试期间使用。动态语言实际上会在执行期间使用符号表,以支持后期绑定和eval()
。但是C ++在运行时不使用标识符。
答案 5 :(得分:0)
我认为汇编中的变量就是这样,即名称实际上是地址的人类可读标签。 (本文将指出如此,至少:http://www.friedspace.com/assembly/memory.php)但是,在C / C ++中,变量的名称实际上是一个解除引用的地址。要获取地址,您必须使用&
运算符。
C / C ++中的指针实际上是包含地址的变量 - 很可能有自己的地址(尽管如果编译器选择将指针存储在CPU寄存器中则不需要试着得到它的地址 - 如果你这样做,那么,你肯定得到一个。)
答案 6 :(得分:0)
你所说的实际上归结为“左值”的定义。
让我们考虑一个像你在问题中给出的简单变量:
double num = 32.53;
num
是一个lvalue
,大致可以转化为它指的是内存中的某个位置。
当它在表达式中使用时,左值可以转换为右值,它可以(大致)转换为从该内存位置检索值。例如,给出如下表达式:
num = num + 1.5;
num
以左值开始,但是在赋值的右侧使用它,它被转换为右值。这基本上意味着获取内存中该位置的值,然后向其添加1.5,然后将结果值写回到num
引用的内存中的位置。
然而不意味着num
是一个指针。 num
确实指的是内存中的位置。指针是不同的:它是一个引用内存中某些其他位置的变量。指针变量本身就是一个左值。它在内存中有一个位置,其名称指的是内存中的位置。但是,存储在内存中该位置的值本身就是对内存中某个位置的引用(通常是某些位置其他而不是指针本身)。
也许一张照片会有所帮助:
这里我使用实线箭头表示无法更改的关联。矩形表示变量的名称,横向表示存储位置。因此,num
与内存中的一个位置不可分割地相关联。它不能被修改为引用任何其他位置,也不能被解除引用。
如果我们定义类似的东西:
double num2 = 12.34;
double *ptr = &num2;
然后我们粗略地了解图片第二部分描述的情况。我们已将num2
定义为double,就像num
一样(但保持不同的值)。然后我们定义了一个指向pointer
的指针(名为num2
)。换句话说,当我们取消引用pointer
时,我们会得到num2
。从pointer
到num2
的连接虽然是虚线 - 表示如果我们选择,我们可以更改此设置 - 我们可以指定其他地址{ {1}}到double
,这会使pointer
引用其他变量的位置。
虽然你还没有问过它(但是)图片的第三部分显示了如果我们定义了类似的东西我们会得到什么:
pointer
在这种情况下,我们在内存中创建了第三个对象(double num3 = 98.76;
double &reference = num3;
),并且我们创建了一个引用num3
的引用(名为reference
)。我已经用较浅的颜色绘制了num3
的位置,以表示存储器中可能存在或者可能没有用于存储引用本身的实际位置 - 它可能只是引用的第二个名称(或多或少)直接到reference
。与指针不同,它从引用到它引用的对象有一条实线,因为它无法更改 - 一旦创建引用,它总是引用同一位置。
要回答您的其他问题,请使用您提供的定义(num3
),否则无法取消引用double num = 32.53;
。像num
这样的表达式根本不会编译。由于变量的名称始终引用该变量的位置,因此不需要,支持或允许使用*num = 10.2;
来引用其位置。
答案 7 :(得分:0)
没有。对于这样一个简单的例子,值num
可能存储在寄存器中。在今天的CPU上,寄存器没有地址。
num
是对象的名称。 C ++假装对象存在于内存中的特定位置,但这在现代体系结构上并不是非常有效。大多数操作都要求值在寄存器中,因此如果编译器可以将它保存在寄存器中,那么它就会。如果不是,编译器可以从将值放入缓存友好位置中受益。这可能不是每次都是相同的位置,因此值可能会在内存中移动。
因此,这个名称远比指针强大得多:它在内存中移动时跟随值。创建和存储指针&num
不允许这样的移动,因为移动num
会使指针无效。