“取消引用”指针是什么意思?

请包括一个例子和解释

复习基本术语

除非您正在编写汇编程序,否则设想一个指针包含一个数字内存地址,其中1表示进程内存中的第二个字节,2表示第三个字节,3表示第四个字节,依此类推,通常就足够了

  • 0和第一个字节发生了什么?好吧,我们稍后再讨论-请参见下面的空指针
  • 要更准确地定义指针存储的内容以及内存和地址的关系,请参阅本答案末尾的“关于内存地址的更多信息,以及您可能不需要知道的原因”

当您想要访问指针指向的内存中的数据/值时(即带有该数字索引的地址的内容),您可以取消对指针的引用

不同的计算机语言有不同的符号,告诉编译器或解释程序,你现在对指向对象的(当前)值感兴趣。我集中在C和C++上。

指针场景

考虑在C中,给定一个指针,例如下面的p

const char*p=“abc”;

…四个字节的数值用于编码字母“a”、“b”、“c”,一个0字节表示文本数据的结尾,存储在内存中的某个位置,该数据的数字地址存储在p中。这种C在内存中编码文本的方式称为ASCIIZ

例如,如果字符串文字恰好位于地址0x1000处,并且p32位指针位于0x2000处,则内存内容将是:

内存地址(十六进制)变量名内容
1000‘a’==97(ASCII)
1001‘b’==98
1002‘c’==99
1003                                     0
...
2000-2003 p 1000十六进制

请注意,地址0x1000没有变量名/标识符,但我们可以使用存储其地址的指针间接引用字符串文字:p

取消对指针的引用

为了引用p所指向的字符,我们使用其中一种符号(同样,对于C)取消引用p

断言(*p==“a”);//地址p的第一个字符将是“a”
断言(p[1]=“b”);//p[1]实际上取消引用通过添加
//p和p所指物体大小的1倍:
//在这种情况下,它们是字符,在C中为1字节。。。
断言(*(p+1)='b');//p[1]的另一种表示法

您还可以在指向的数据中移动指针,并在移动过程中取消对它们的引用:

++p;//增加p,现在是0x1001
断言(*p=='b');//p==0x1001,这是“b”所在的位置。。。

如果您有一些可以写入的数据,则可以执行以下操作:

intx=2;
int*p_x=&x、 //将x变量的地址放入指针p_x
*p_x=4;//将p_x中地址处的内存更改为4
断言(x==4);//检查x现在是4

如上所述,您必须在编译时知道您需要一个名为x的变量,并且该代码要求编译器安排它应该存储的位置,确保通过&x

取消引用和访问结构数据成员

在C语言中,如果有一个变量是指向具有数据成员的结构的指针,则可以使用-&gt取消引用运算符:

typedef结构X{int i_u;;double d_;}X;
X;
X*p=&x;
p->d=3.14159;//解引用和访问数据成员x.d_
(*p).d_*=-1;//另一种访问x.d的等效符号_

多字节数据类型

要使用指针,计算机程序还需要深入了解所指向的数据类型-如果该数据类型需要多个字节来表示,则指针通常指向数据中编号最低的字节

那么,看一个稍微复杂一点的例子:

双码[]={10.3,13.4,11.2,19.4};
双*p=尺寸;
断言(p[0]==10.3);//知道查看第一个双精度值中的所有字节
断言(p[1]==13.4);//实际查看地址p+1*sizeof(双精度)中的字节
//(sizeof(double)几乎总是8个字节)
++p、 //按sizeof预付p(双倍)
断言(*p==13.4);//从地址p开始的双精度存储器的值为13.4
*(p+2)=29.8;//将尺寸[3]从19.4更改为29.8
//注意前面的+p和+2=>尺寸[3]

指向动态分配内存的指针

有时你不知道你需要多少内存,直到你的程序运行,并看到什么数据抛出它。。。然后可以使用malloc动态分配内存。通常的做法是将地址存储在指针中

int*p=(int*)malloc(sizeof(int));//在某处获得一些记忆。。。
*p=10;//取消对内存指针的引用,然后在内存中写入一个值
fn(*p);//调用函数,在地址p处传递值
(*p)+=3;//更改该值,向其添加3
自由(p);//将内存释放回堆分配库

在C++中,内存分配通常是用新运算符,并且用 Dele> :

int*p=new int(10);//初始值为10的一个int的内存
删除p;
p=新整数[10];//具有未指定初始值的十个整数的内存
删除[]p;
p=新整数[10]();//值初始化的十个整数的内存(到0)
删除[]p;

另请参见下面的C++智能指针

丢失和泄漏地址

通常,指针可能是内存中某些数据或缓冲区存在的唯一指示。如果需要继续使用该数据/缓冲区,或调用free()delete以避免内存泄漏,则程序员必须操作指针的副本

const char*p=asprintf(“名称:%s”,名称);//堆上常见但非标准的printf
//用下划线替换不可打印的字符。。。。
for(常量字符*q=p;*q;++q)
如果(!isprint(*q))
*q='u';
printf(“%s\n”,p);//只修改了q
自由基(p);

…或仔细安排任何更改的反转

常量大小n=。。。;
p+=n;
...
p-=n;//还原早期值。。。
自由基(p);

C++智能指针

在C++中,最好使用智能指针对象来存储和管理指针,当智能指针析构函数运行时自动释放它们。由于C++11标准库提供了两个,唯一的\u ptr,当分配的对象只有一个所有者时

{
std::unique_ptr<T>p{new T(42,“意思”)};
调用函数(p);
//上面的函数可能会抛出,因此此处删除不可靠,但是。。。
}//p的析构函数保证运行“here”,调用delete

…和shared_ptr用于股份所有权(使用参考计数)

{
自动p=std::使_共享<T>(3.14,“pi”);
数字_storage1.may _add(p);//可以将p复制到其容器中
number_storage2.may_add(p);//may将p复制到其容器中}//p的析构函数只会删除T,如果两者都不能添加或复制它

空指针

代码>空NU/COD>和 0 ,另外在C++ NulLPTR 中,可以用来指示指针当前不保存变量的内存地址,不应该在指针算术中被取消或使用。例如:

const char*p_filename=NULL;//或“= 0”,或“= null pTR”在C++中
INTC;
而((c=getopt(argc,argv,f:)!=-1)
开关(c){
案例f:p_filename=optarg;中断;
}
if(p_filename)//仅NULL转换为false
...   // 仅当指定了-f标志时才能到达此处

在C和C++中,正如内置的数字类型不一定默认为 0 ,也不是布尔 > false >,指针不总是设置为 null 。当它们是静态变量或(仅限C++)静态对象或其基的直接或间接成员变量时,或进行零初始化(例如new T();new T(x,y,z))时,所有这些变量都设置为0/false/NULL;对T的成员(包括指针)执行零初始化,而新建T;不执行)

此外,当您将0NULLnullptr分配给指针时,指针中的位不一定全部重置:指针在硬件级别可能不包含“0”,或者在虚拟地址空间中引用地址0。如果编译器有理由,它可以在那里存储其他内容,但不管它做什么-如果您来比较指向0NULLnullptr的指针或分配给这些指针中任何一个的另一个指针,比较必须按预期进行。因此,在编译器级别的源代码下面,“null”在C语言和C++语言中可能有点“神奇”……

更多关于内存地址的信息,以及您可能不需要知道的原因

更严格地说,初始化指针存储一个位模式,标识NULL或一个(通常是虚拟的)内存地址

简单的情况是,这是进程整个虚拟地址空间的数字偏移量;在更复杂的情况下,指针可能与某个特定的内存区域有关,CPU可以根据CPU“段”寄存器或某种编码的段id来选择该区域

发表评论