我对运算符有疑问:C编程中的地址(&)和间接(*)。
如果
TODO
是类型var
int
是指向ptr
的指针,并指向int
那么var
的值是什么?
结果是否指示ptr
的基地址或整个4字节(在我的平台中)?如果它仅指向基址,那么为什么var
求出*ptr
的全部内容?它不是应该仅显示var
的基地址的内容吗?
答案 0 :(得分:4)
ptr
是int *
,它指向整个int
,或者正如您所说的那样,指向整个sizeof(int)
字节。
(unsigned char *)ptr
指向您所输入的“基本地址”。
ptr
和(unsigned char *)ptr
在所有常见的CPU体系结构上将具有相同的数值,这表明指向“整个”整数和指向“整数”之间的区别“基地址”完全取决于指针的类型。了解至关重要的是,具有不同类型的两个变量仍可以具有相同的数值。
答案 1 :(得分:2)
指针变量ptr
将包含var
开始的地址,即var
的第一个字节的地址。如果将ptr
取消引用为*ptr
,则将得到var
的值。
假设int
是4个字节,由于指针的类型,使用*ptr
“知道”来读取接下来的4个字节。由于ptr
的类型为int *
,这意味着*ptr
的类型为int
,因此接下来的4个字节将作为int
读取。
例如:
int var = 4;
int *ptr = &var;
printf("ptr = %p\n", (void *)ptr);
printf("*ptr = %d\n", *ptr);
printf("&var = %p\n", (void *)&var);
printf("var = %d\n", var);
输出:
ptr = 0x7ffc330b4484
*ptr = 4
&var = 0x7ffc330b4484
var = 4
答案 2 :(得分:1)
记住表达式 *ptr
的类型为int
可能会有所帮助。也就是说,根据声明
int var = 5;
int *ptr = &var;
那么以下关系是正确的:
ptr == &var // int * == int *
*ptr == var == 5 // int == int == int
是的,ptr
的 value 是var
的第一个字节 1 的地址。但是,表达式 *ptr
是指存储在int
中的整个var
值。
指针是具有附加类型语义的内存地址的抽象,因此指针操作在所有类型上的工作方式相同。如果ptr
指向4字节的int
,则*ptr
的值等于该4字节的int
的值。如果它指向8字节的double
,则*ptr
的值等于该8字节的double
的值。
答案 3 :(得分:0)
与其他任何类型一样,指针也是一种类型。
有资格被取消引用的指针必须指向完整的类型。每种类型都有一个定义的大小(pre或user),因此取消引用要考虑指针指向的对象类型的大小。
引用C11
,第6.5.3.2章
一元
*
运算符表示间接。如果操作数指向一个函数,则结果为 功能指示符;如果它指向一个对象,则结果是一个左值,表示 宾语。如果操作数的类型为“要输入的指针”,则结果的类型为“类型”。如果 无效的值已分配给指针,一元*
运算符的行为是 未定义。
让我们看下面的图片可以更好地理解它。
说
int x = 10;
int * px = &a;
,在该平台上为sizeof(int) == 4
。
现在,在内存中,看起来就像
+-------------------------------------------+
| |
| int x = 10; |
| |
+-------------------------------------------+
0x1000 0x1004
+------------------------------------------+
| |
| pointer * px = &x; (0x1000) |
| |
+------------------------------------------+
0x1000
类型的变量0x1003
分配了0x1004
至x
至地址int
的整个内存块。px
是指针,指向该内存块的开始。另外,在定义px
时,我们告诉编译器它将指向int
,因此编译器知道,存储在px
的内存块可以存储4个字节,并且要在间接过程中(使用*
运算符获取数据,我们需要读取所有4个字节,然后返回结果。因此,在编写*px
时,编译器将读取所有4个字节并返回值。
答案 4 :(得分:0)
结果是否指示var的基地址或整个4个字节(在我的平台中)?如果它仅指向基址,那么为什么* ptr评估为var的全部内容?是不是应该只显示var基址的内容?
如前所述,您使用的平台的字节大小为4
int
。
var
是类型int
的变量:
100 101 102 103
var -------------------------
| | | | |
-------------------------
byte1 byte2 byte3 byte4
where, 100 - 104 are the address of byte1-byte4 respectively.
ptr
是指向int
的指针,并指向var
。
当您执行int *ptr = &var;
时,它的含义如下:
ptr 100 101 102 103
-------- var -------------------------
| &var |-------->| | | | |
-------- -------------------------
byte1 byte2 byte3 byte4
ptr
的类型为int *
,即指向整数的指针。因此,*ptr
的类型为int
。这意味着,当您取消引用ptr
时,它会提供指向地址ptr
所指向的值,并且其类型指示值的类型,在您的情况下为int
。因此,*ptr
会求整个int
的值,而不仅仅是基地址。
请注意,如果您这样做
char *c_ptr = (char *)&var;
它更改了var
地址的解释,当使用c_ptr
访问时,*c_ptr
将被解释为char
。不过,ptr
和c_ptr
指向的地址在数字上是相同的。
检查:
#include <stdio.h>
int main() {
int var = 50;
int *i_ptr = &var;
char *c_ptr = (char *)&var;
printf ("address of var: %p\n", (void *)&var);
printf ("i_ptr: %p\n", (void *)i_ptr);
printf ("i_char: %p\n\n", (void *)c_ptr);
printf ("value of var: %d\n", var);
printf ("value of *i_ptr: %d\n", *i_ptr);
for (size_t i = 0; i < sizeof(int); i++) {
printf ("Address of byte[%zu]: %p, ", i, (void *)&c_ptr[i]);
printf ("byte[%zu]: %c\n", i, c_ptr[i]);
}
return 0;
}
小端架构的输出:
address of var: 0x7ffeea3ac9f8
i_ptr: 0x7ffeea3ac9f8 <========\
i_char: 0x7ffeea3ac9f8 <========/ the address pointing to is same
value of var: 50
value of *i_ptr: 50
Address of byte[0]: 0x7ffeea3ac9f8, byte[0]: 2 <========= 50th character of ascii
Address of byte[1]: 0x7ffeea3ac9f9, byte[1]:
Address of byte[2]: 0x7ffeea3ac9fa, byte[2]:
Address of byte[3]: 0x7ffeea3ac9fb, byte[3]:
big-endian架构上的输出:
address of var: ffbffbd0
i_ptr: ffbffbd0
i_char: ffbffbd0
value of var: 50
value of *i_ptr: 50
Address of byte[0]: ffbffbd0, byte[0]:
Address of byte[1]: ffbffbd1, byte[1]:
Address of byte[2]: ffbffbd2, byte[2]:
Address of byte[3]: ffbffbd3, byte[3]: 2
答案 5 :(得分:0)
ptr
的内部值/表示形式通常未指定。规范指出,只有当你取消对它的引用,价值有一定的意义。可以告诉您的一切是,某些信息使计算机能够找到所引用类型的值。
编译器使用指针类型存储该指针类型的值的引用类型。我们的想法是能够做到指针运算(一个C特定强大想法)和能够使用的值的指向在表达式。
但是标准并没有说明实际指针值的内部表示。
这样做的目的是为了允许不同的体系结构自由地尽可能地实现 的地址。
这使得可以实现例如通常,英特尔32位处理器将指针作为segment:offset
数据类型,因此没有有效的解释为单个数字。在非透明的虚拟内存体系结构中,指针可能需要指定磁盘设备,块编号和块中存储指向的值的偏移量,因此使用磁盘设备,块编号和偏移量值可以使指针很难将整个事情解释为一个数字。人们可以认为,segment:offset
的值可以被解释为一个数字(因为它是在老8086个处理器,与你可以通过移位左4位的段选择得到线性地址,然后添加偏移量)。但是,如果你这样做,在现代的虚拟内存架构,你会发现,还有,如果你认为某些信息是由操作系统隐藏计算实际内存地址中没有简单的方法。的segment
选择器仅仅是一个描述符,以一个可能大量的OS隐藏元信息(其中,该段位于线性地址空间,它多少延伸,如果你有权限解除分配指针作为数据或可执行代码等)
实际上,两个不同的指针可以取消引用相同的最终指向数据。假设已经通过不同的方式生成了两个不同的指针,这些指针由不同的段选择器和相同或不同的偏移量组成,但是在转换后映射到最终指向同一物理内存位置的重叠段。在那种情况下,甚至无法比较这些指针,但是当您访问一个引用的值时,实际上是从两个指针获取相同的数据。处理此类指针可能会让您感到痛苦,并且迫使您在使用此类指针进行算术运算时必须非常小心(请参阅旧的Microsoft编译器中的far
指针),但这并不妨碍在这种架构上实现C编译器。