当我声明一个变量时,例如:
int x = 6;
究竟是x
什么?内存中的地址通常是十六进制的。
另外,当我调用 X
x = 2;
编译器如何知道在哪里x
?x
不是地址。
那是我的第一个问题。
第二:
假设我有一个对象:
Person p;
p
有 2 个数据成员:
int type1, int type2;
究竟是什么p
,为什么我需要去p
,然后是变量?
p.type1, p->type1.
当我声明一个变量时,例如:
int x = 6;
究竟是x
什么?内存中的地址通常是十六进制的。
另外,当我调用 X
x = 2;
编译器如何知道在哪里x
?x
不是地址。
那是我的第一个问题。
第二:
假设我有一个对象:
Person p;
p
有 2 个数据成员:
int type1, int type2;
究竟是什么p
,为什么我需要去p
,然后是变量?
p.type1, p->type1.
在这种情况下int x = 6
,x
只是一个帮助您编写代码和编译器编译它的名称。它基本上是内存中某个位置的别名,因此以后通过它更容易访问它x = 2
- 这告诉您和编译器您想2
在同一个位置写入值。
和以前一样,但它占用了更多的空间(sizeof(Person)
准确地说)。p->type1
仅当p
是指向 a 的指针时才有效Person
(或者如果您重载了->
运算符,但事实并非如此),p.type1
是用于对象的语法,以指定要访问的对象的哪一部分。
int x=6 ;
这x
是标识符,即name given to memory area
存储值 6 的位置。
x
是一个简单的名称,仅用于标识存储值 6 的内存区域。
当你声明变量时,那个时候编译器将你的变量名存储在标识符表中。
对于person p
,这里又p
是给内存区域起的名字,它存储了两个数据成员type1
&type2
要访问type1
&的值type2
,首先你必须找到存储它们的内存区域。这就是为什么你必须先访问内存区域p
然后你才能访问type1 * type2
x
是一个左值,表示“定位器值”。之所以这样称呼它,是因为x
编译器知道它的位置。所以x
在某种意义上是一个地址,或者可以推断出一个地址的东西。
那个东西只有在读取时才成为一个值
int a = x;
在这种情况下,编译器获取x
,读取其位置中的值,然后在该初始化中x
代表其值。不幸的是,这种“读取值”是一个隐含的过程,因此文本x
是值还是左值取决于它出现的位置。
int x = 6;
X究竟是什么?
x
是一个变量,可以定义为内存中某个位置的名称。
编译器如何知道 x 在哪里?
有一种叫做符号表的东西,编译器在内部使用它来将变量名映射到实际的内存位置。
道不是事物。
所有x
, 和p
, 和 就此而言p.type1
都是名称或标识符。所以,我猜你真的在问他们的身份。答案是它们识别出某些东西,它的行为就好像它满足了语言规范提出的某些要求。这个确定的东西实际上是什么取决于你的平台、编译器,也许你打开了哪些优化等等。
所以,让我们接受声明
int x = 6;
您是说x
现在(在此范围内和可能的嵌套范围内)将引用一个int
(无论是什么),并为其赋予初始整数文字值6
。
那么,什么是int
? 它可以在range内保存单个(正或负)整数值[std::numeric_limits<int>::min(),std::numeric_limits<int>::max()]
。它的确切大小取决于架构,但它至少与 a 一样大char
且不大于 a long
(IIRC)。
这件事的可能候选人:
int
平台上的任何内容,如果它是全局声明,则与链接器符号(可能在运行时重定位)相关联,因此所有引用的代码都x
使用相同的内存位置int
: 如果你从不获取它的地址,x
它可能会在一个寄存器中度过它的整个生命,但是如果你要么获取地址,要么编译器用完所有变量的寄存器(或需要在函数调用中保存它们),x
可能会在您使用它时加载到寄存器中,并溢出回堆栈。同样,由编译器来记住当前何时 x
是寄存器(以及哪个寄存器),以及何时是内存位置如您所见,“事物”x
标识实际上可以是很多事物,在程序执行期间的不同点。
除非您正在优化某些内容或仔细布置内存,否则更有用的问题是x 的属性是什么?
标准(或任何语言规范,实际上)的重点是对类型、变量和行为做出一些有用的保证,然后让编译器选择如何实现这些保证。
x
是一个变量。或者更准确地说,是存储该变量所引用的那条信息(数据)的位置的名称或标签。您需要标签来区分不同的变量。它只是为您(程序员)提供帮助。
p
又是变量,但类型为Person
。并且Person
是数据结构 - 它包含您通过结构访问的数据。并且p
是这种类型的对象(ei 是结构的实例化)。您可以拥有两个相同类型的对象
Person p;
Person someone;
为了知道您想要访问您使用的数据/成员p.data
someone.data
为什么p->data
?
p->data
语法等价于(*p).data
这就解释了什么是指针。正如我所说,变量是您存储一些信息(数据)的地方。简单地说,指针是一个存储内存地址的变量。换句话说,它指向存储在内存某处的另一个值
例如:
Person p; // variable of type Person
Person *pointer_to_p = &p;
// a pointer that points to the variable p
// it holds the address of p - that's what &p does it returns the address of a varible
*p
in(*p).data
称为取消引用指针(访问指针指向的值)
有关更多信息,最好使用谷歌指针,甚至更好地阅读一本书
让我们退后一步……重要的是要理解所有编程语言(除了汇编语言)都用程序员应该容易思考的术语来描述问题的抽象。编译器的工作是读取该抽象描述并生成与硬件直接对应的较低级别的描述。
当你写:
int x = 6;
你是在告诉编译器你想使用一个整数变量,你想调用那个变量 x,并且你希望这个变量的值是 6。
编译器的工作之一是决定如何存储该变量。C++ 语言描述了各种变量范围,帮助编译器决定哪种存储是合适的。
局部变量(在函数内部声明)通常存储在堆栈的内存中,但可以将一个小值(例如整数)保存在寄存器中。
全局或静态变量将存储在内存中。
编译器记住它选择存储值的位置,以便它可以再次找到它——对于一个本地变量,它将是寄存器名称或相对于堆栈顶部的地址;对于全局或静态,它将是相对于所有程序数据开始的地址。
在程序被编译、链接和加载到内存之前,内存中的实际地址是未知的(因为操作系统可能并不总是将程序加载到相同的地址)——重要的是编译器知道变量在哪里将是,并且它可以生成代码来访问它。
如果像第二个问题一样,变量的类型是某种数据结构,编译器会以完全相同的方式选择将其放置在内存中的位置。当您的程序访问该结构的成员时,编译器可以计算出该成员的地址,因为它知道该结构的地址以及该成员在该结构中的偏移量。
因此,在您的person p
示例中,当程序引用时p1.type2
,编译器获取地址p
并添加偏移量type2
(可能为 4,因为您的第一部分struct person
占用了type1
整数,即 4字节(在大多数 32 位架构上))。
您必须同时指定两者p
,type2
因为您可能有另一个人(例如q
),并且需要告诉编译器您要操纵哪个人。p.type2
不是同一个变量q.type2
,并且会有不同的地址。
x
是一个变量标识符。变量与地址的不同之处在于它有大小,并且不能为空。的地址x
可以通过 获取&x
。考虑这段代码:
int x = 5;
int* p = &x; // the pointer p now refers to x
x = 6;
现在,即使您只更改了 的值x
,如果您获得 的值*p
(称为“取消引用”指针),您还将获得 6.p
也是一个变量,但它的类型为“指向 int 的指针”而不是'int',这是类型x
。
可以自信地说,这是一个变量。
如果优化器将它放入寄存器,它可能没有内存地址。如果它确实有一个内存地址,它可以被赋予一个固定的绝对地址(对于不可重定位代码中的全局和静态),一个相对于其他对象的地址(对于非静态成员),或者一个相对于堆栈的地址框架(用于自动局部变量)。
如果编译器确定它总是可以预测值并在每次使用时替换该值,它甚至可能不是存储位置。
如果你&
在变量上使用地址操作符,那么编译器将不得不给它一个内存位置。
阅读堆栈和堆。
x 将被推送到具有唯一地址的堆栈中。但是你只需要调用 x。
但试试 cout << &x;
这是真实地址
当你声明你的变量时,你的编译器将它映射到一个变量属性表。该表包含地址和大小,类型等。因此编译器在需要时从该表中获取内容。