• volcanol的工控博客


      C语言最富有迷幻色彩的部分当属指针部分,无论是指针的定义还是指针的意义都可算是C语言中最复杂的内容。指针不但提供给了程序员直接操作硬件部分的操作接口,还提供给了程序员更多灵活的用法。C++继承这一高效的机制,同时引入了另一个与指针相似但不相同的机制: 引用。

    一、引用

      简单的来说,引用就是变量的别名(alias), 通过别名我们可以操作引用代表的变量。 定义一个引用的语法如下所示:

        变量类型   &引用标识符 = 变量名。

    Exp:

      int  iVar=10;
    
      int  &iRef = iVar;
    
      iRef = 20 ;
    
      cout<<iVar<<endl;

      这段程序执行的结果就是输出: 20 ;

      程序通过引用 iRef 改变了变量iVar的值。

    要点:

      1、在定义引用的同事必须初始化,指出引用代表的是哪一个变量,而且这种“指向关系”不能改变。

      2、引用只是对象的另一个名字,可以通过对象的原标识符访问对象,也可以通过对象的引用访问对象。

      3、在一个语句定义多个引用的时候,每个引用标识符(引用名)的前面必须都加上&符号,否则就是错误。

    1、const引用

      const引用是指向const对象的引用, 不能通过const引用改变原对象的值。如下所示:

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 #include <bitset>
     5 
     6 using std::cin;
     7 using std::cout;
     8 using std::endl;
     9 using std::string;
    10 using std::vector;
    11 using std::bitset;
    12 
    13 int main()
    14 {
    15     const int iVar=10;
    16     const int &iRef = iVar;
    17     iRef = 20; 
    18     cout<<iVar<<endl;
    19 
    20     return 0;
    21 }

    上面的程序编译的结果如下所示:

    [root@localhost cpp_src]# g++ test.cpp 
    test.cpp: In function ‘int main()’:
    test.cpp:17: 错误:assignment of read-only reference ‘iRef’

    可以发现在第17行,试图对一个指向const对象的const引用赋值,结果编译报错。

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 #include <bitset>
     5 
     6 using std::cin;
     7 using std::cout;
     8 using std::endl;
     9 using std::string;
    10 using std::vector;
    11 using std::bitset;
    12 
    13 int main()
    14 {
    15     const int iVar=10;
    16     const int &iRef = iVar;
    17     iRef = 20;
    18 
    19     int &iRef1 = iVar;
    20     cout<<iVar<<endl;
    21 
    22     return 0;
    23 }

    程序编译结果如下:

    [root@localhost cpp_src]# g++ test.cpp 
    test.cpp: In function ‘int main()’:
    test.cpp:17: 错误:assignment of read-only reference ‘iRef’
    test.cpp:19: 错误:将类型为 ‘int&’ 的引用初始化为类型为 ‘const int’ 的表达式无效

    我们发现在程序编译的时候第19行也报错啦,报错的类型是: 将 类型int &的引用初始化类型const int的表达式无效。

    2、字面值引用

       可以定义const引用代表字面值。实例如下:

    int main()
    {
        int const &iRef = 100;
        const string &strRef = "volcanol";
        cout << iRef <<endl;
        cout << strRef <<endl;
    
        return 0;
    }

    程序的执行结果如下:

    [root@localhost cpp_src]# g++ test.cpp 
    [root@localhost cpp_src]# ./a.out 
    100
    volcanol

    上面的实例注意一点: 要对字面值定义别名引用,则必须将别名引用定义为const型的,否则将出现编译错误。

    二、指针

      指针是什么,有的地方说是指针是一个地址。这里我们不对指针的复杂用法进行讨论,如果想了解指针的复杂用法可以产考我在园子里的另外一篇随笔,链接地址

    为:http://www.cnblogs.com/volcanol/archive/2011/06/05/2073042.html

    1、指针的定义

      在C++中定义指针,很简单,在定义的变量的时候,在变量的前面加上一个 * 就表示要定义一个指针变量。语法如下:

    指针要指向的数据类型  * 指针变量名;

    Exp:

      int  *pInt;  定义了一个指向整型变量的指针变量pInt;

      string *pStr;  定义了一个指向string类型的对象的指针pStr;

      vector<int>  *pVectorInt; 定义一个指向vector<int> 容器的指针。 

      bitset<5>     *pBitset5;  定义一个指向bitset<5>类型的对象的指针。

    2、指针变量赋值和初始化

      指针变量在使用前必须有一个确定的指向,否则就会造成一个游离的指针,操作的游离指针会得到一个意想不到的的结果。通过取得一个变量的地址然后赋值给

    指针变量或者初始化指针变量使指针变量有一个确定的指向。  通过操作符 & 取得一个变量/对象的地址或者(指针)。

      指针变量初始化:

          int  iVar = 10;
          int  *pInt = &iVar;

      指针变量赋值:

          int iVar = 10;
          int *pInt1;
          int *pInt2;
          pInt1 = &iVar;
          pInt2 = pInt1;

    3、指针的引用

      通过解引用操作符 * 可以引用指针指向的变量。

      int iVar = 20;
    
      int *pInt = NULL;
    
      pInt = &iVar;
    
      cout<< * pInt<<endl;

    Exp:

    int main()
    {
        int iVar = 100;
        int *pInt = &iVar;
        cout<<(*pInt)<<endl;
    
        string strVar = "volcanol";
        string *pStr = &strVar;
        cout<<(*pStr)<<endl;
    
        vector<int> vInt(1);
        vector<int> *pVecInt=&vInt;
        cout<<(*pVecInt)[0]<<endl;
    
        bitset<5> bitVar(5);
        bitset<5> *pBitset5 = &bitVar;
        cout<< (*pBitset5) <<endl;
    
        return 0;
    }

    程序的执行结果如下所示:

    [root@localhost cpp_src]# g++ test.cpp 
    [root@localhost cpp_src]# ./a.out 
    100
    volcanol
    0
    00101

    要点:

      在定义指针变量的时候,必须在每个指针变量的前面都加上 * ,否则定义的就是一个非指针变量。

      int  *pInt1,pInt2; //pInt1 为指针变量,  pInt2为整型变量。

      在定义指针变量的时候,有两种风格的格式:   int  *pInt 和  int*  pInt; 这两种格式没有对错之分,两种格式C++都是接受的,只是在理解的时候可能会引起

    误解。为了避免误解,在一个程序里面,最好选取一种格式一直保持下去。

     

    4、指针的指针

      指针变量也是一种对象,同样可以给指针变量定义一个指向它的指针,就是指针的指针。定义语法如下:

        指针的指针变量指向的对象类型  **指针的指针变量标识符;

    Exp:

      int iVar = 10 ;
    
      int *pInt = &iVar;
    
      int **ppInt = &pInt;

    如上就定义了一个指向整型指针变量的指针变量ppInt;   ppInt指向的对象的类型为 int* 类型的对象。

    int main()
    {
        int iVar = 100;
        int *pInt = &iVar;
        int **ppInt = &pInt;
    
        cout <<"iVar ="<< iVar<<endl;
        cout <<"int *pInt = &iVar,then *pInt ="<<*pInt<<endl;
        cout <<"int **ppInt = &pInt,then *ppInt="<<*ppInt;
        cout <<";and then **ppInt="<<**ppInt<<endl;
    
        return 0;
    }

    程序的执行结果如下所示:

    [root@localhost cpp_src]# g++ test.cpp 
    [root@localhost cpp_src]# ./a.out 
    iVar =100
    int *pInt = &iVar,then *pInt =100
    int **ppInt = &pInt,then *ppInt=0xbfb949f8;and then **ppInt=100

    5、通过指针访问数组元素

      这里需要说明一个细节:  某一个数组的数组名是一个常量,而且数组名表示的是数组的第一个元素的首地址,同时数组元素在内存中是连续存放的。

    正是因为数组具有上述的特点,才能方便的通过指针来访问数组的元素。

      通过指针访问数组元素的例子如下:

    int main()
    {
        int iArray[5] = {1,2,3,4,5};
        int *pInt = iArray;
    
        cout << *pInt << endl;  // 1
        cout << pInt[0]<<endl;  // 1
        cout << *++pInt<<endl;  // 2
        cout << *pInt++<<endl;  // 2
        cout << *pInt<<endl  ;  // 3
    
        return
    }

    程序的执行结果如下所示:

    [root@localhost cpp_src]# ./a.out 
    1
    1
    2
    2
    3

    不但可以通过++运算符来改变指针的指向,指针还支持加整数和减整数运算,同时支持两个指针的减法运算。

    int main()
    {
        int iArray[5] = {1,2,3,4,5};
        int *pInt1 = iArray;
        int *pInt2= &iArray[4];
    
        cout <<*(pInt1 + 2)<<endl;  // 3
        cout <<*(pInt2 - 1)<<endl;  // 4
        cout << pInt2 - pInt1 <<endl;
    
        return 0;
    }

    程序的执行结果如下:

    [root@localhost cpp_src]# g++ test.cpp 
    [root@localhost cpp_src]# ./a.out 
    3
    4
    4

    要点:

      可以发现这个地方  pInt2 - pInt1 的结果是4, 这个结果与C语言的输出是存在差别的。这一点要非常注意,在指针与数组结合使用的过程中,两个指针相减

    是经常见到的操作,因此这个地方需要注意。

      

      通过上面的实例,我们可知利用指针可以很方便的访问数组的元素,因此我们可以通过指针遍历整个数组。

    int main()
    {
        int iArray[5] = {1,2,3,4,5};
    
        for(int *pBegin=iArray,*pEnd=iArray+5; pBegin != pEnd; ++pBegin)
            cout<<*pBegin<<endl;
    
        return 0;
    }

    程序的执行结果如下所示:

    [root@localhost cpp_src]# g++ test.cpp 
    [root@localhost cpp_src]# ./a.out 
    1
    2
    3
    4
    5

      指针和数组之间的定义还包括* 和 [] 符号同时在定义中出现的情况,

    Exp:

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 #include <bitset>
     5 
     6 using std::cin;
     7 using std::cout;
     8 using std::endl;
     9 using std::string;
    10 using std::vector;
    11 using std::bitset;
    12 
    13 int main()
    14 {
    15     int iArray_1[5] = {1,2,3,4,5};
    16     int iArray_2[3] = {1};
    17     int *pInt1[5] ={iArray_1, iArray_2};
    18     int (*pInt2)[5] = iArray_1; //error
    19     pInt2 = iArray_2;   //error 
    20 
    21 
    22     return 0;
    23 }

         上面的代码中, 我标出了两处错误,错误的原因是, pInt2 是一个二维的指针,而iArray_1 和 iArray_2 都是int * 类型的指针, 如果将程序修改一下就可以

    得到如下的结果。

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 #include <bitset>
     5 
     6 using std::cin;
     7 using std::cout;
     8 using std::endl;
     9 using std::string;
    10 using std::vector;
    11 using std::bitset;
    12 
    13 int main()
    14 {
    15     //int iArray_1[5] = {1,2,3,4,5};
    16     //int iArray_2[3]= {1};
    17     //int *pInt1[5] ={iArray_1, iArray_2};
    18     //int (*pInt2)[5] = iArray_1; //error
    19     //pInt2 = iArray_2;   //error 
    20 
    21     int iArray_1[5]={1,2,3,4,5};
    22     int iArray_2[3][5]={{1}};
    23     int iArray_3[5][3]={{2}};
    24     int (*pInt)[5] = iArray_2;
    25     pInt=iArray_3;  //error 
    26 
    27 
    28     return 0;
    29 }

    上面的代码中,我们可以知道  25行的语法是错误的,错误的原因是二维数组的第二维的指针长度不一致。通过上面的例子我们可以知道,* 和 [] 在一起定义指针变量

    的时候,需要注意 * 和 [] 符号的优先级,同事需要知道加上括号后,定义的时候[] 的维度的扩展。这个地方是C语言当中经常会使用的,而且是属于较复杂的用法,因

    此需要因此特别的重视。

    6、 指针 和 const限定符/修饰符

      指针和const的结合使用没有太多的说头,主要是注意const修饰的 *p 还是 p, 只要分清楚修饰对象的不同就很好理解。

    int main()
    {
        int iVar1 = 10;
        int iVar2 = 20;
        const int *pInt1 = &iVar1;
        int const *pInt2 = &iVar1;
        int * const pInt3 = &iVar1;
        const int * const pInt4 = &iVar1;
        int const * const pInt5 = &iVar2;
    
    
        return 0;
    }

      关于const限定符需要知道的就是上面的各个定义的意义,只要知道 const是修饰 *pInt 还是修饰pInt就可以准确的分辨各个定义的意义,具体可以关注我前面

    给出的关于C语言趣事相关的链接文章。

      这里还有一个需要注意的地方,就是对于const对象如何定义指向其的指针,下面是一个例子:

    int main()
    {
        const int iVar = 10;
        //int *pInt1 = &iVar;  //error
    
        int const *pInt1 = &iVar;
        const int *pInt2 = &iVar;
    
        return 0;
    }

      这里要注意加了注释部分错误的原因。这里就不解释了,这个与const对象与引用的关系是一样的。

    7、指针和typedef的使用

      在C语言中进程会做这样的预处理指令。

    #define  PINT  int*

      这样定义宏以后,就可以通过这个宏来定义指针变量,如下所示:

    #define PINT int*
    
    int iVar = 0;
    PINT pInt = &iVar;

      这样是可以通过的,但是这样会存在一个漏洞,如果同时定义两个指针变量的话,就会出现错误。

    #define  PINT int*
    
    int iVar1 = 0;
    int iVar2 = 0;
    PINT pInt1 = &iVar1,  pInt2 = &iVar2;

        很显然上面的代码存在漏洞,  第二个变量 pInt2 不是指针变量,而是一个整型的变量, 好在这样的错误编译器在编译的时候会检查出来,这里需要引起注意。

        我们可以利用typedef机制来规避上述的风险,  typedef 的作用就是为数据类型取一个别名,尤其在数据类型比较长时是一个非常有效的机制, typedef的语法

    如下:

      typedef   数据类型   数据类型别名;

    例如:

      typedef  int*  PINT;

      这就为 int* 这种类型定义了一个新的别名 PINT,在使用的时候PINT就表示 int*。

    Exp:

    typedef  int*  PINT;
    
    int iVar1 = 0;
    int iVar2 = 0;
    PINT pInt1 = &iVar1,  pInt2 = &iVar2;

      上面的代码定义了两个整型变量 iVar1、iVar2, 同时定义了两个指针变量pInt1 和 pInt2; 

    要点:

      通过上面两个例子,就可以清楚 typedef和#define 之间的差别。

         注意typedef是语句,因此后面必须有个分号结尾。  这个点是经常容易忘记的,好在编译器一般可以检测出这样的错误。

      

      typedef和指针的结合还有一个值得注意的地方,就是 typedef 、const和指针同时出现。

    typedef  int* PINT
    const PINT pInt;  //error

      这里定义的指针对象pInt是const指针对象, 这个指针对象在定义的时候必须初始化。因此要注意上面的这个错误。

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 #include <bitset>
     5 
     6 using std::cin;
     7 using std::cout;
     8 using std::endl;
     9 using std::string;
    10 using std::vector;
    11 using std::bitset;
    12 
    13 int main()
    14 {
    15     typedef int* PINT;
    16     const PINT pInt;
    17 
    18     return 0;
    19 }

    程序编译的结果如下所示:

    [root@localhost cpp_src]# g++ test.cpp 
    test.cpp: In function ‘int main()’:
    test.cpp:16: 错误:未初始化的常量 ‘pInt’

    将程序改成下面的形式则正确:

    #include <iostream>
    #include <string>
    #include <vector>
    #include <bitset>
    
    using std::cin;
    using std::cout;
    using std::endl;
    using std::string;
    using std::vector;
    using std::bitset;
    
    int main()
    {
        typedef int* PINT;
        //const PINT pInt;
        int iVar = 0;
        const PINT pInt = &iVar;  //初始化const指针
    
        return 0;
    }

      当然还可以定义更加复杂的数据类型,这里就不再进行描述,后面如果碰到会进行相关的描述。

      指针的操作基本上就是这些,在C++语言中,大部分的人倾向于不使用指针, 但是指针确实是一种非常高效的机制,但是如果能把指针用好,则会对

    程序的性能的提升具有很好的提高作用。 

      

      关于指针和引用暂时就说到这, 接下来将要对C语言风格和C++风格的字符串进行一番讨论, 待续......

  • 相关阅读:
    软件工程二人组队开发第一周
    软件工程第五周
    这学期的目标
    软件工程第四周的总结
    二维数组的最大子数组和 时间复杂度:O(n的四次方)
    10.tesseract
    mysql存储过程和函数
    mysql触发器
    9.selenium
    mysql练习
  • 原文地址:https://www.cnblogs.com/volcanol/p/4006404.html
Copyright © 2020-2023  润新知