• 指针 (整理版)


    指针是C/C++编程中的重要概念之中的一个,也是最easy产生困惑并导致程序出错的问题之中的一个。

    利用指针编程可以表示各种数据结构,通过指针可使用主调函数和被调函数之间共享变量或数据结构。便于实现双向数据通讯。指针可以灵活的操作内存,合理的操作内存可以使程序更高效。

    1.指针的概念 
    本质上讲指针也是一种变量,普通的变量包括的是实际的数据,而指针变量包括的是内存中的一块地址,这块地址指向某个变量或者函数,指针就是地址。指针是一个指示器,它告诉程序在内存的哪块区域能够找到数据。

    2.指针的内容 
    指针的内容包括4部分:指针的类型,指针所指向的类型,指针的值,指针本身所占有的内存区。在初学指针时,指针的类型和指针所指向的类型是极easy搞混淆的,弄清楚这些概念。有助于我们正确的使用指针。

    3.指针的类型和指针所指向的类型 
    从语法上讲,指针的类型是指把指针声明语句中的指针名字去掉所剩下的部分。

     
    指针指向的是一块内存区域,指针所指向的类型取决于这块内存在编译时是什么类型,比方一个int*类型指针所指向的类型是int。

     
    以下我来就一些样例来对这两个概念进行说明。

    1. int p;//这不过一个普通的变量
    2.  
    3. intp;//int*也表示一种数据类型:int指针类型。所以p的类型为:int*类型,p所指向的类型为int型

    到这里,略微暂停一下。教大家一种怎样看待指针类型和指针所指向的类型的方法。

    (我自己的理解) 
    就上面这个int*p样例来说。它能够写成int* p,也能够写成int *p

    第一种的理解偏向于地址。就是p是一个地址变量。p表示一个十六进制地址;另外一种的写法偏向于值。*p是一个整型变量,它可以表示一个整型值。 
    这两种写法都正确,仅仅是理解上不同,可是我觉得,在理解指针类型和指针所指向的类型这两个概念时,全然能够把它们两个结合起来。

     
    想想我们使用指针的步骤:声明指针,为指针赋值。然后使用指针所指向的值。 
    我们都知道指针是一种复合数据类型。它必须和主要的类型结合才干构成指针类型。

     
    那么int*就是一种复合的数据类型——整型指针类型。 
    这样就好解释第一种写法了,在声明时,int* p,直接声明p变量为整型指针类型,这是第一步。

     
    第二步就是为指针赋值了,p是指针类型,它存放的是地址,这里如果我这样为它赋值:p = &普通变量;(比方int a = 5;p=&a;)。 
    第三步使用指针。在C++ Primer中具体的解释了*是解除引用的运算符(我的理解是地址解析运算符),假设p是地址,那么*p就是实际的值(比方上面相应的*p = 5)。对于刚開始学习的人来说。在理解它的含义时,全然能够跨过这一步,上面说了在声明指针时int* pint *p这两种写法都能够。在声明时我偏向第一种理解,在使用时我偏向另外一种理解:毕竟我们使用的是值,而*p就是这个值。 
    我的结论:对于int* pint *p的理解(也是对于指针类型和指针所指向的类型的理解),一个指针包括两部分。地址和值。指针声明时声明的是一个地址变量(指针就是地址),在使用时使用的是指针所指向的值。或者说指针包括两个类型:指针类型和指针所指向的类型。声明时是声明指针类型,使用时是使用指针所指向的类型。

    1. int p[3];//p先和[]结合,说明p是一个数组,再和int结合。所以p是一个int型数组
    2.  
    3. intp[3];//优先级[]比*高,p是数组,再加上int*,能够称它为指针数组。数组的每个元素的值都为指针(地址)
    4.  
    5. int (*p)[3];//*p能够看作是普通变量,就回到第三种情况(int p[3]),可是这里p是指针变量,它指向的是一个包括3个整型数值的数组。能够称它为数组指针,数组中每个元素的值为普通整型值。
    6.  
    7. int** p;//int*代表指针类型,*是指针类型,所以p是指向int型指针的指针。p指向的类型是int*类型(int型指针)
    8.  
    9. int p(int);//这非常明显是一个返回类型为int,而且带一个int型參数的函数
    10.  
    11. int (*p)(int);//p是函数指针,指向的是返回值为int而且带一个int參数的函数。这个声明包括两部分:函数变量+函数地址变量(姑且把函数也看做是变量)
    12.  
    13. int* (*p(int))[3];//这个有点复杂。它仍然是一个函数指针。从*p(int)看,它是函数指针,带一个int參数;然后看[],说明函数返回值为数组,然后返回类型为int*。

      所以p是一个指向返回值为int*型指针数组,而且带一个int型參数的函数的指针

    在寻常使用中。我们仅仅须要理解前面几个就能够了,太复杂的指针基本用不到,可读性也不好。

    以下是一些指针的简单样例。

    Demo1 &和*操作符

    1. #include
    2. int main()
    3. {
    4.     using namespace std;
    5.     int updates = 6;
    6.     int *p_updates;
    7.     p_updates = &updates;
    8.  
    9.     cout<<"Values: updates = "<<updates;
    10.     cout<<", *p_updates = "<<*p_updates<<endl;
    11.  
    12.     cout<<"Address: &updates = "<<&updates;
    13.     cout<<",p_updates = "<<p_updates<<endl;
    14.  
    15.     *p_updates = *p_updates + 1;
    16.     cout<<"Now updates = "<<updates<<endl;
    17. }

    Demo2

    1. #include
    2. int main()
    3. {
    4.     using namespace std;
    5.     int higgens = 5;
    6.     intpt = &higgens;//是对pt进行赋值。而不是*pt。

      等价于int* pt; pt = &higgens;

    7.  
    8.     cout<<"Value of higgens = "<<higgens<<endl;
    9.     cout<<"Addnress of higgens = "<<&higgens<<endl;
    10.     cout<<"Value of *pt = "<<*pt<<endl;
    11.     cout<<"Value of pt = "<<pt<<endl;
    12.  
    13.     return 0;
    14. }

    Demo3  new和delete操作符

    1. #include
    2. int main()
    3. {
    4.     using namespace std;
    5.     intpt = new int;
    6.     *pt = 1001;
    7.     cout<<"int value = "<<*pt<<",and location = "<<pt<<endl;
    8.     doublepd = new double;
    9.     *pd = 5.234;
    10.     cout<<"double value = "<<*pd<<",and location = "<<pd<<endl;
    11.     delete pt;
    12.     delete pd;
    13.     return 0;
    14. }

    Demo4 动态数组

    1. #include
    2. int main()
    3. {
    4.     using namespace std;
    5.     doublep3 =  new double[3];
    6.     p3[0] = 0.2;
    7.     p3[1] = 0.5;
    8.     p3[2] = 0.8;
    9.  
    10.     cout<<sizeof(p3)<<endl;
    11.     cout<<"p3[1] is "<<p3[1]<<". ";
    12.     p3 = p3 + 1;//添加一位地址。这里的一位的单位标准是依照数组的一个元素占内存的字节数。

    13.     cout<<"Now p3[0] is "<<p3[0]<<" and p3[1] is "<<p3[1]<<". ";
    14.     cout<<sizeof(p3)<<endl;
    15.     p3 = p3 -1;
    16.     delete[] p3;
    17.     return 0;
    18. }

    Demo 5 数组和指针

    1. #include
    2. int main()
    3. {
    4.     using namespace std;
    5.     double wages[3] = {10000.0,20000.0,30000.0};
    6.     short stacks[3] = {3,2,1};
    7.     //两种方式去获取数组的地址——数组名或对数组首元素进行求址运算
    8.     doublepw = wages;
    9.     shortps = &stacks[0];
    10.  
    11.     cout<<"pw = "<<pw<<",*pw = "<<*pw<<endl;//打印出数组第一个元素的值和地址
    12.     pw = pw + 1;
    13.     cout<<"add 1 to the pw pointer: ";
    14.     cout<<"pw = "<<pw<<",*pw = "<<*pw<<" ";
    15.  
    16.     cout<<"ps = "<<ps<<",*ps = "<<*ps<<endl;//打印出数组第一个元素的值和地址
    17.     ps = ps + 1;
    18.     cout<<"add 1 to the ps pointer: ";
    19.     cout<<"pw = "<<ps<<",*ps = "<<*ps<<" ";    
    20.  
    21.     //stacks[1] 等同于 *(stacks + 1)
    22.     cout<<"access two elements with array notation ";
    23.     cout<<" stacks[0] = "<<stacks[0]<<", stack[1] = "<<stacks[1]<<endl;
    24.     cout<<"access two elements with pointer notation ";
    25.     cout<<"*stacks = "<<*stacks<<",*(stacks + 1) = "<<*(stacks + 1)<<endl;
    26.  
    27.     cout<<sizeof(wages)<<" = size of wages array ";    //24字节
    28.     cout<<sizeof(pw)<<" = size of pw pointer ";            //4字节
    29.  
    30.     return 0;
    31. }

    Demo6 字符串和地址

    1. #include
    2. int main()
    3. {
    4.     using namespace std;
    5.     char flower[10] = "rose";
    6.     //字符串发送地址给cout对象。

    7.     //在cout和多数C++表达式中,char数组名、指向char的指针以及用引號括起来的字符串常量都被解释为字符串第一个字符的地址
    8.     cout<<flower<<"s are red. ";
    9.     return 0;
    10. }

    Demo7 字符串和地址

    1. #include
    2. #include
    3. int main()
    4. {
    5.     using namespace std;
    6.     char animal[20] = "bear";
    7.     const charbird = "wren";//bird存放字符串的地址
    8.     charps;
    9.  
    10.     cout<<animal<<" and ";
    11.     cout<<bird<<" ";
    12.  
    13.     cout<<"Enter a kind of animal:";
    14.     cin>>animal;
    15.     //cin>>ps;//没有为ps分配内存空间。这样做是错误的
    16.  
    17.     ps = animal;//ps和animal指向同一个string和内存单元
    18.     cout<<ps<<"s! ";
    19.     cout<<"Before using strcpy(): ";
    20.     cout<<animal<<" at "<<(int*)animal<<endl;//计算animal的地址,假设是char*类型,则打印出字符串
    21.     cout<<ps<<" at "<<(int*)ps<<endl;
    22.  
    23.     ps = new char[strlen(animal) + 1];//分配新的内存空间
    24.     strcpy(ps,animal);//数组拷贝,假设这里把animal赋给ps,这样将改变ps的地址。我们将无法操作新分配的内存
    25.     cout<<"After using strcpy(): ";
    26.  
    27.     //尽管animal和ps指向的是同一个string。可是它们的内存地址是不同的
    28.     cout<<animal<<" at "<<(int*)animal<<endl;
    29.     cout<<ps<<" at "<<(int*)ps<<endl;
    30.     delete[] ps;
    31.  
    32.     return 0;
    33. }

    Demo8 动态结构 ->指向操作符

    1. #include
    2. struct inflatable
    3. {
    4.     char name[20];
    5.     float volume;
    6.     double price;
    7. };
    8. int main()
    9. {
    10.     using namespace std;
    11.     inflatableps = new inflatable;//在执行时分配内存
    12.     cout<<"Enter name of inflatable item:";
    13.     cin.get(ps->name, 20);
    14.     cout<<"Enter volume in cubic feet:";
    15.     cin>>(*ps).volume;
    16.     cout<<"Enter price:$";
    17.     cin>>ps->price;
    18.     cout<<"Name:"<<(*ps).name<<endl;
    19.     cout<<"Volume:"<<ps->volume<<" cubic feet ";
    20.     cout<<"Price:$"<<ps->price<<endl;
    21.     delete ps;
    22.     return 0;
    23. }

    4.指针的值(或称指针所指向的内存区)

    指针的值或者叫指针所指向的内存区或地址,是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。

    在32位程序里,全部类型的指针的值都是一个32位整数,由于32位程序里内存地址全都是32位长。 指针所指向的内存区就是从指针的值所代表的那个内存地址開始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX。就相当于说该指针指向了以XX为首地址的一片内存区域。我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

    指针所指向的内存区和指针所指向的类型是两个全然不同的概念。

    在上例中,指针所指向的类型已经有了,但因为指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

    以后,每遇到一个指针。都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

    5.指针本身所占有的内存区

    指针本身所占有的内存区是指针本身占内存的大小,这个你仅仅要用函数sizeof(指针的类型)測一下就知道了。

    在32位平台里,指针本身占领了4个字节的长度。

    指针本身占领的内存这个概念在推断一个指针表达式是否是左值时非常实用。

  • 相关阅读:
    Redis集群(一)
    Mysql分库分表
    Redisson分布式锁
    Spring Cloud Eureka 高可用
    Spring RestTemplate具备负载均衡功能
    Redis分布式锁
    B树/B-树/B+树/B*树的数据库应用
    ABAC访问控制模型
    Maven自定义打包的包名
    Versions maven plugin 修改版本
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/7144075.html
Copyright © 2020-2023  润新知