C++指针详解

来源:互联网 发布:win10重置网络cmd命令 编辑:程序博客网 时间:2024/04/30 03:11

先来说一下指针的命名:我们声明指针变量的时候,建议用“ptr”来开头,这样是一种好的习惯,可以让你看到这个变                                         量就知道他是一个指针变量。

也要注意给指针添加必要的注释。

先来看看内存:  内存是以字节为单位的一片连续的存储空间,这些单元都有自己的编号,这就是内存地址。

操作系统通过内存地址来实现对内存的管理。

而这里指针他就是一个变量,指针就是一个标尺,来告诉程序内存在那一片区域可以找到。

地址就是内存的编号,而只这点你就是用于存放内存地址的。

而地址的单位是字节,他来唯一标识内存中的某个特定的单元。

         这里要注意的是:无论指针指向什么数据类型,32位系统中的指针变量都是4字节。因为所有的内存地址都是4 

字节的十六进制编码。


在程序中使用指针的好处:1、可以提高程序的编译效率和执行速度。

                                           2、能够实现动态地存储分配。

   3、通过指针可以实现主调函数和北调函数之间的变量共享,便于双向的数据通信。

   4、可以指向各种数据结构,这有助于编写高质量的代码。


来区分一下指针变量和变量指针

指针变量:  要让指针的值可以被使用,要将它存储在一个对象中,这个对象就是指针变量。

变量指针: 变量的地址,  指针是一种值,它是另一个对象在内存中的存储地址的抽象。



而对于指针,他也可以来比较,看下面的代码:

[cpp] view plaincopyprint?
  1. class First{...};  
  2. class Second{...};  
  3. class Third:public First, public Second{...};  
  4.   
  5.   
  6. Third *t = new Third;  
  7. First *f = t;  
  8. Second *s = t;  

当我们进行  if(t == f).....的时候,他是返回true的。

因为继承树是以基类为定点的。    而  f 和 s 是不能比较的,因为他们不具有继承的关系。


下面的代码解释了一下指针和地址,取值这些,还有就是指针和数组,和结构的使用:

[cpp] view plaincopyprint?
  1. #include<iostream>   
  2. using namespace std;  
  3.   
  4. int main(void)  
  5. {  
  6.     /*********************************************************** 
  7.     指针,内存地址,指针取值,指针是啥 
  8.     指针是一个存储计算机内存地址的变量。   这里引用是计算机内存地址。 
  9.     从指针指向的内存读取数据叫:指针取值。 
  10.     指针可以为void类型,也可以是其他的内置数据类型等。 
  11.     也有NULL指针和未初始化指针。 
  12.     “*”操作符可以用来声明一个指针变量,也可以是解引用操作符,当然也可以是 
  13.     乘法操作符。 
  14.     “&”是地址操作符。  通过在变量前加上&我们可以得到这个变量的地址 
  15.     ***********************************************************/  
  16.   
  17.   
  18.     int *ptr; //声明一个int类型的指针  
  19.     int value = 1;//声明一个int类型的值,值为1  
  20.     ptr = &value;//给指针分配一个int类型的值的引用   
  21.     int *p = ptr;//对指针进行取值,这里可以直接获得指针指向的内存地址中的数据  
  22.     cout << p << endl;  
  23.     /****************************************************************** 
  24.     类比一下,指针比作信封     引用比作邮箱     值比作房子 
  25.     ok,我们在信封上面写上邮寄的地址(引用地址),  我们来取值就相当于地址 
  26.     对应的房子。我们也可以把信封上的地址涂掉,然后写上其他的我们想要的地址 
  27.     房子是在哪里不动的,所以不受影响。 
  28.     *******************************************************************/  
  29.   
  30.   
  31.     /********************************************************************* 
  32.     下面是void指针    NULL指针    未初始化指针 
  33.     *********************************************************************/  
  34.     int *ptrs;//未初始化  
  35.     int *ptring = NULL;//NULL指针  
  36.     void *vptr;//void指针  未初始化  
  37.     int *iptr;  
  38.     int *vastptr;  
  39.     //void类型可以存储任意类型的指针或者引用   
  40.     iptr = &value;  
  41.     vptr = &value;  
  42.     cout << iptr << vptr << endl;  
  43.     //显示类型转换  把一个void指针转换成int指针,并取值。  
  44.     vastptr = static_cast<int*>(vptr);  //(int*)vptr;  
  45.     cout << *vastptr << endl;  
  46.   
  47.     //cout << ptrs << ptring << endl;  
  48.     /****************************************************************** 
  49.     要知道,未初始化的指针也是有内存地址的,但是是一个垃圾地址。 
  50.     所以我们不能对未初始化的指针取值。 
  51.     最好的情况是你去到的是垃圾地址,接下来你需要对程序进行调试 
  52.     最坏情况就会导致程序崩溃。 
  53.     *******************************************************************/  
  54.   
  55.   
  56.     /******************************************************************** 
  57.     数组是一断连续的内存空间,来存储多个特定类型的对象。 
  58.     指针用来存储单个内存地址 
  59.     所以数组和指针不是相同的结构,不能互相转换。 
  60.     数组变量是一个常量,就算指针变量指向相同的地址或者一个不同的数组, 
  61.     也不能把指针赋值给数组变量。 
  62.     我们可以把数组变量赋值给指针时,世界上把指向数组第一个元素的地址赋值给指针。 
  63.  
  64.     要注意的是:指针需要和数组元素类型保持一致,除非指针是void类型。 
  65.     ********************************************************************/  
  66.     int myarray[4] = {1, 2, 3, 0};  
  67.     int *ptrarray = myarray;//*ptrarray = &myarray[0];  
  68.     cout << *ptr << endl;  
  69.     //ok上面的操作时正确的。来看下面的错误   
  70.     //myarray = ptrarray;   
  71.     //myarray = myarrays;   
  72.     //myarray = &myarrays[0];   
  73.   
  74.     /*******************************************************************  
  75.     结构体和指针。   与数组类似,指向结构体的指针存储了结构体第一个元素的  
  76.     内存地址。    结构体的指针必须声明和结构体类型保持一致 或者为void  
  77.     *******************************************************************/  
  78.     struct person{  
  79.         int age;  
  80.         char *name;  
  81.     };  
  82.     struct person first;  
  83.     struct person *ptrstruct;  
  84.   
  85.     first.age = 22;  
  86.     char *fullname = "full name";  
  87.     first.name = fullname;  
  88.     ptrstruct = &first;  
  89.   
  90.     cout << first.age << ptrstruct->name << endl;  
  91.   
  92.   
  93.     return 0;  
  94. }  


看运行结果:



上面的代码里面说到了数组指针,下面我们来看一下指针数组:

指针数组: 数组里的元素都是某一类型的指针,这个数组就是指针数组。

int b[2][4];

int *a[2] = {b[0], b[1]};   a是由两个整形的指针组成,分别指向b[0],b[1]。

指针数组最常用的地方就是  字符串数组了,  一个数组,他的每个元素都是一个字符串。


而指针不一定是一级的,也可以是多级的,来看看多级指针是怎么样的:

二级指针:

也就是指向指针的指针。

[cpp] view plaincopyprint?
  1. int **p;\  
来用代码解释一下:

[cpp] view plaincopyprint?
  1. int **p, *p1, x = 10;  
  2. p1 = &x;  
  3. p = &p1;  
  4. cout << **p << endl;  
  5. cout << *p1 << endl;  
这段代码的结果是10  10。



多级指针:

我们还是用代码来解释:

[cpp] view plaincopyprint?
  1. int ****p, ***p1, **p2, *p3, x = 23;  
  2. p = &p1;  
  3. p1 = &p2;  
  4. p2 = &p3;  
  5. p3 = &x;  
  6. cout << ****p << ***p1 <<  **p2 << *p3 << endl;  
最后输出的结果是:23232323。


除此之外,在指针中我们也可以用const限定符,他能出现在两个位置,下面就来看一下:

1、const char *s

这个声明是一个常量指针,指针指向的对象是一个常量。  s所指的内存单元的内容不可修改。

我们也可以char const *s; 这样使用。

2、char *const s

这个声明是一个指针常量,指针所标识的地址是不能改变的。

要注意的是,const * int a; 这个语句是没有意义的。



最后,我们来看看使用指针时候要注意的:

1、指针未初始化

在你使用指针前,一定要进行初始化,  如果你未初始化,指针变量中所存储的是一个未知的值。

初始化指针的时候也可以设为NULL, 这样程序中就可以通过判断指针是否等于NULL,来确定指针的有效性。

2、指针越界

这个错误是比较难捕获的,使用的时候要谨慎。

3、指向局部变量的指针

如果你要想让指针有效,那么你必须让你的指针指向的那一块内存单元有效。

3、指着你呢指向的转移

这里要说的是野指针,他不是NULL指针,他指向的是一块不可用的内存区域。会导致内存泄露。

下面来通过代码来看看野指针:

[cpp] view plaincopyprint?
  1. char *pChar = new char;  
  2. char chs;  
  3. pChar = &chs;  
  4. delete pChar;  

OK,通过上述代码,我们想把chs的内容给pChar指针所指的内存空间,这样我们就吧pChar先前指向的空间变成了垃圾地址,不能获取了,野指针出现。
0 0
原创粉丝点击