精华内容
下载资源
问答
  • c++指针

    千次阅读 2019-08-08 22:20:04
    c++指针 --------侵删-------- Null 指针 在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。 NULL 指针是一个定义在标准库中的值为...

    --------侵删--------
    https://www.runoob.com/cplusplus/cpp-pointers.html

    Null 指针

    在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

    NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:

    #include <iostream>
    using namespace std;
    int main ()
    {
        int  *ptr = NULL;
    	cout << "ptr 的值是 " << ptr ;
        return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    ptr 的值是 0
    

    在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

    如需检查一个空指针,您可以使用 if 语句,如下所示:

    if(ptr)     /* 如果 p 非空,则完成 */
    if(!ptr)    /* 如果 p 为空,则完成 */
    

    因此,如果所有未使用的指针都被赋予空值,同时避免使用空指针,就可以防止误用一个未初始化的指针。很多时候,未初始化的变量存有一些垃圾值,导致程序难以调试。

    指针的算术运算

    指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-。

    假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

    ptr++
    

    在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

    递增一个指针

    我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:

    #include <iostream>
    using namespace std;
    const int MAX = 3;
    
    int main ()
    {
       int  var[MAX] = {10, 100, 200};
       int  *ptr;
       // 指针中的数组地址
       ptr = var;
       for (int i = 0; i < MAX; i++)
       {
          cout << "Address of var[" << i << "] = ";
          cout << ptr << endl;
          cout << "Value of var[" << i << "] = ";
          cout << *ptr << endl;
          // 移动到下一个位置
          ptr++;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Address of var[0] = 0xbfa088b0
    Value of var[0] = 10
    Address of var[1] = 0xbfa088b4
    Value of var[1] = 100
    Address of var[2] = 0xbfa088b8
    Value of var[2] = 200
    
    递减一个指针

    同样地,对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:

    #include <iostream>
    using namespace std;
    const int MAX = 3;
    
    int main ()
    {
       int  var[MAX] = {10, 100, 200};
       int  *ptr;
       // 指针中最后一个元素的地址
       ptr = &var[MAX-1];
       for (int i = MAX; i > 0; i--)
       {
          cout << "Address of var[" << i << "] = ";
          cout << ptr << endl;
          cout << "Value of var[" << i << "] = ";
          cout << *ptr << endl;
          // 移动到下一个位置
          ptr--;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Address of var[3] = 0xbfdb70f8
    Value of var[3] = 200
    Address of var[2] = 0xbfdb70f4
    Value of var[2] = 100
    Address of var[1] = 0xbfdb70f0
    Value of var[1] = 10
    
    指针的比较

    指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

    下面的程序修改了上面的实例,只要变量指针所指向的地址小于或等于数组的最后一个元素的地址 &var[MAX - 1],则把变量指针进行递增:

    #include <iostream>
    using namespace std;
    const int MAX = 3;
    
    int main ()
    {
       int  var[MAX] = {10, 100, 200};
       int  *ptr;
       // 指针中第一个元素的地址
       ptr = var;
       int i = 0;
       while ( ptr <= &var[MAX - 1] )
       {
          cout << "Address of var[" << i << "] = ";
          cout << ptr << endl;
          cout << "Value of var[" << i << "] = ";
          cout << *ptr << endl;
          // 指向上一个位置
          ptr++;
          i++;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Address of var[0] = 0xbfce42d0
    Value of var[0] = 10
    Address of var[1] = 0xbfce42d4
    Value of var[1] = 100
    Address of var[2] = 0xbfce42d8
    Value of var[2] = 200
    

    指针 vs 数组

    指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。请看下面的程序:

    #include <iostream> 
    using namespace std;
    const int MAX = 3;
     
    int main ()
    {
       int  var[MAX] = {10, 100, 200};
       int  *ptr;
       // 指针中的数组地址
       ptr = var;
       for (int i = 0; i < MAX; i++)
       {
          cout << "Address of var[" << i << "] = ";
          cout << ptr << endl; 
          cout << "Value of var[" << i << "] = ";
          cout << *ptr << endl; 
          // 移动到下一个位置
          ptr++;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Address of var[0] = 0xbfa088b0
    Value of var[0] = 10
    Address of var[1] = 0xbfa088b4
    Value of var[1] = 100
    Address of var[2] = 0xbfa088b8
    Value of var[2] = 200
    

    然而,指针和数组并不是完全互换的。例如,请看下面的程序:

    #include <iostream>
    using namespace std;
    const int MAX = 3;
     
    int main ()
    {
       int  var[MAX] = {10, 100, 200}; 
       for (int i = 0; i < MAX; i++)
       {
          *var = i;    // 这是正确的语法
          var++;       // 这是不正确的
       }
       return 0;
    }
    

    把指针运算符 * 应用到 var 上是完全可以接受的,但修改 var 的值是非法的。这是因为 var 是一个指向数组开头的常量,不能作为左值。

    由于一个数组名对应一个指针常量,只要不改变数组的值,仍然可以用指针形式的表达式。例如,下面是一个有效的语句,把 var[2] 赋值为 500:

    *(var + 2) = 500;
    

    上面的语句是有效的,且能成功编译,因为 var 未改变。

    C++ 指针数组

    在我们讲解指针数组的概念之前,先让我们来看一个实例,它用到了一个由 3 个整数组成的数组:

    #include <iostream> 
    using namespace std;
    const int MAX = 3;
     
    int main ()
    {
       int  var[MAX] = {10, 100, 200};
       for (int i = 0; i < MAX; i++)
       {
          cout << "Value of var[" << i << "] = ";
          cout << var[i] << endl;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Value of var[0] = 10
    Value of var[1] = 100
    Value of var[2] = 200
    

    可能有一种情况,我们想要让数组存储指向 int 或 char 或其他数据类型的指针。下面是一个指向整数的指针数组的声明:

    int *ptr[MAX];
    

    在这里,把 ptr 声明为一个数组,由 MAX 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

    #include <iostream> 
    using namespace std;
    const int MAX = 3;
     
    int main ()
    {
       int  var[MAX] = {10, 100, 200};
       int *ptr[MAX]; 
       for (int i = 0; i < MAX; i++)
       {
          ptr[i] = &var[i]; // 赋值为整数的地址
       }
       for (int i = 0; i < MAX; i++)
       {
          cout << "Value of var[" << i << "] = ";
          cout << *ptr[i] << endl;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Value of var[0] = 10
    Value of var[1] = 100
    Value of var[2] = 200
    

    您也可以用一个指向字符的指针数组来存储一个字符串列表,如下:

    #include <iostream> 
    using namespace std;
    const int MAX = 4;
     
    int main ()
    {
     const char *names[MAX] = {
                       "Zara Ali",
                       "Hina Ali",
                       "Nuha Ali",
                       "Sara Ali",
       };
       for (int i = 0; i < MAX; i++)
       {
          cout << "Value of names[" << i << "] = ";
          cout << names[i] << endl;
       }
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Value of names[0] = Zara Ali
    Value of names[1] = Hina Ali
    Value of names[2] = Nuha Ali
    Value of names[3] = Sara Ali
    

    指向指针的指针(多级间接寻址)

    指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

    [外链图片转存失败(img-wTv1rPZJ-1565273764869)(E:\Typora\c++\c++指针\1564735566317.png)]

    int **var;
    

    当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:

    #include <iostream>
    using namespace std;
     
    int main ()
    {
       int  var;
       int  *ptr;
       int  **pptr;
       var = 3000;
       // 获取 var 的地址
       ptr = &var;
       // 使用运算符 & 获取 ptr 的地址
       pptr = &ptr;
       // 使用 pptr 获取值
       cout << "Value of var :" << var << endl;
       cout << "Value available at *ptr :" << *ptr << endl;
       cout << "Value available at **pptr :" << **pptr << endl;
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Value of var = 3000
    Value available at *ptr = 3000
    Value available at **pptr = 3000
    

    传递指针给函数

    C++ 允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

    下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

    #include <iostream>
    #include <ctime> 
    using namespace std;
    void getSeconds(unsigned long *par);
    
    int main ()
    {
       unsigned long sec;
       getSeconds( &sec );
       // 输出实际值
       cout << "Number of seconds :" << sec << endl;
       return 0;
    }
    void getSeconds(unsigned long *par)
    {
       // 获取当前的秒数
       *par = time( NULL );
       return;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Number of seconds :1294450468
    

    能接受指针作为参数的函数,也能接受数组作为参数,如下所示:

    #include <iostream>
    using namespace std;
    // 函数声明
    double getAverage(int *arr, int size); 
    int main ()
    {
       // 带有 5 个元素的整型数组
       int balance[5] = {1000, 2, 3, 17, 50};
       double avg; 
       // 传递一个指向数组的指针作为参数
       avg = getAverage( balance, 5 ) ; 
       // 输出返回值
       cout << "Average value is: " << avg << endl;     
       return 0;
    }
    double getAverage(int *arr, int size)
    {
      int    i, sum = 0;       
      double avg;          
      for (i = 0; i < size; ++i)
      {
        sum += arr[i];
       }
      avg = double(sum) / size; 
      return avg;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    Average value is: 214.4
    

    从函数返回指针

    在上一章中,我们已经了解了 C++ 中如何从函数返回数组,类似地,C++ 允许您从函数返回指针。为了做到这点,您必须声明一个返回指针的函数,如下所示:

    int * myFunction()
    {
    	...
    }
    

    另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。

    现在,让我们来看下面的函数,它会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们,具体如下:

    #include <iostream>
    #include <ctime>
    using namespace std;
     
    // 要生成和返回随机数的函数
    int * getRandom( )
    {
      static int  r[10]; 
      // 设置种子
      srand( (unsigned)time( NULL ) );
      for (int i = 0; i < 10; ++i)
      {
        r[i] = rand();
        cout << r[i] << endl;
      }
      return r;
    } 
    // 要调用上面定义函数的主函数
    int main ()
    {
       // 一个指向整数的指针
       int *p;
       p = getRandom();
       for ( int i = 0; i < 10; i++ )
       {
           cout << "*(p + " << i << ") : ";
           cout << *(p + i) << endl;
       } 
       return 0;
    }
    

    当上面的代码被编译和执行时,它会产生下列结果:

    624723190
    1468735695
    807113585
    976495677
    613357504
    1377296355
    1530315259
    1778906708
    1820354158
    667126415
    *(p + 0) : 624723190
    *(p + 1) : 1468735695
    *(p + 2) : 807113585
    *(p + 3) : 976495677
    *(p + 4) : 613357504
    *(p + 5) : 1377296355
    *(p + 6) : 1530315259
    *(p + 7) : 1778906708
    *(p + 8) : 1820354158
    *(p + 9) : 667126415
    
    展开全文
  • C++指针

    千次阅读 2015-08-12 11:57:34
    华电北风吹 天津大学认知计算与应用...2、指针指针包含两个内容,一个是内存地址,这个大小和机器长度有关;另一个是指针类型,这个跟指针定义的类型有关。 int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; int (*p

    华电北风吹
    最后修改日期:2015/8/20

    一、指针与数组
    1、数组:数组是一段连续的空间,数组名表示空间第一个元素的地址。
    2、指针:指针包含两个内容,一个是内存地址,这个大小和机器长度有关;另一个是指针类型,这个跟指针定义的类型有关。

        int arr[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
        int (*p1)[3] = arr;
        int (*p2)[2][3] = &arr;
    
        int * p3 = (int *)(arr + 1);    // p3[0]=4
        int * p4 = (int *)(&arr + 1);   // p4[-1]=6

    arr是一个int(*)[3]类型,&arr是一个int(*)[2][3]类型(返回指针类型为整个数组),两者指向的地址相同。数组指针可以强制转化为指针类型(指向地址不变,只是修改了指针类型)。
    上例可以看出数组名实质上也是指针,储存的地址是数组第一个元素,指针类型是除过第一维的数组指针。

    arr[1]执行过程:arr[1]=*(arr+1)

    二、数组指针和指针数组
    1、数组指针:指向数组的指针。首先来说数组指针也是指针,只不过指针的类型是数组。例如int (*p)[3],其中()优先级大于[],表述指向一个1*3的数组指针。

        int a[3] = { 1, 2, 3 };
        int(*ptr)[3] = &a;
        cout << ptr[0][2] << endl;
        int * p=*ptr;
        cout << p[2] << endl;

    这里首先要注意第2行语句,对数组a取址,因为a本身是一个int*,而不是int (*p)[3],然后需要注意的就是第三行ptr[0]代表的是&a整体的地址(同理ptr+1,表示a后面sizeof(a)空间的首地址),所以访问a中第3个元素的时候需要ptr[0][2]。
    2、指针数组:首先来说指针数组是一个数组,数组里面的每个元素是一个指针。例如int *ptr[3],其中[]优先级大于*。

        int *ptr[2];
        int a[3] = { 1, 2, 3 };
        int x = 5;
        ptr[0] = &x;
        ptr[1] = &a[2];
        cout << *ptr[0] << endl;
        cout << *(ptr[1]-1) << endl;

    三、指针与函数
    1、函数指针:可以指向函数的指针,实质是指向函数所在的内存区域

    int add(int a, int b){
        return a + b;
    }
    
    void main()
    {
        int(*p)(int, int);
        p = add;
        printf("%d\n", p(1,2));
    }

    2、指针型函数
    函数的返回值是一个指针,这里需要注意返回的指针初始化的时候最好使用new分类内存空间,否则容易出现返回的指针指向的内存空间被释放问题。

    四、指向指针的指针
    *表示指向内存的一个地址
    **当然就表示一个指向指针的指针,这个的一个用途:如果指针指向的是一个不可变变量,而这时候需要交换两个指针所值的变量的时候,对函数传参就需要**

    int main(int argc, char* argv[])
    {
        char *a="hello world";
        char *b="i love liyani";
        StrSwap(a,b);
        puts(a);
        puts(b);
        return 0;
    }
    void StrSwap(char *pa,char *pb)
    {   
        char * temp;
        temp = pa;
        pa = pb;
        pb = temp;
    }

    五、总结:
    1、数组名表示降第一维后的数组指针。
    2、数组指针是一个指针,它指向一个数组。
    3、指针数组是一个数组,它的元素是指针。
    4、&a(a是一个数组)是整个数组a的地址。
    5、指针数组可以强制转化为指针,其中地址不变,只改变指针类型
    参考资料:
    http://www.jb51.net/article/54220.htm
    http://www.cnblogs.com/kaituorensheng/p/3722570.html

    感兴趣的可以试着解决下面文章中一位博主遇到的问题。
    http://blog.csdn.net/iot_hept/article/details/47719685

    展开全文
  • 二级指针指针指针) 3.1 定义与初始化 3.2间接数据访问 3.2.1.改变一级指针指向 3.2.2改变 N-1 级指针的指向 3.2.3二级指针的步长 四. 指针与数组 4.1 指针与数组名 4.1.1 通过数组名访问数组元素 4.....

    目录

    一.变量的内存实质到

    1.1变量的实质

    1.2 赋值给变量

    1.3 变量在哪里?

    二. 指针是个什么东西?

     三. 二级指针(指针的指针)

    3.1 定义与初始化

    3.2 间接数据访问

    3.2.1 .改变一级指针指向

    3.2.2 改变 N-1 级指针的指向

    3.2.3 二级指针的步长

    四. 指针与数组

    4.1 指针与数组名

    4.1.1 通过数组名访问数组元素

    4.1.2 通过指针访问数组元素

    4.1.3 数组名与指针变量的区别

    4.2 指针数组( 字符指针数组 )

    4.2.1 定义

     4.2.2 代码实例

    4.3 二级指针与指针数组 

    4.3.1 .指针数组名赋给二级指针的合理性

    4.3.2 完美匹配的前提(小尾巴 NULL)


    在风起云涌的编程世界中,C/C++作为编程界的扛把子,以霸主地位而屹立不倒,究其原因,它有其他语言无法相媲美的“底牌”而存在,那就是——指针。指针被称为是C/C++中的精髓,也有人说由于指针的存在让C/C++变得更加难学,难懂,难消化。果真是这样吗?本篇文章让我们一起来揭开指针的真正面纱。

    一.变量的内存实质到

    1.1变量的实质

    要理解指针,首先就要理解“变量”的存储实质,如果你已经深入理解“变量”的存储实质,直接跳过本小节……

    首先我们来看一下内存空间图:

    如图所示,内存只不过是一个存放数据的空间,可以理解为装鸡蛋的篮子,装水果的箱子,或是装RMB的钱包,随便啦,反正就是这么一个很随意的玩意……现在我们把它想象成电影院的座位,电影院中的每个座位都要编号,而我们的内存要存放各种各样的数据,当然我们要知道我们的这些数据存放在什么位置吧!所以内存也要象座位一样进行编号了,这就是我们所说的内存编址(为内存进行地址编码)。座位可以是遵循“一个座位对应一个号码”的原则,从“第 1 号”开始编号。而内存则是按一个字节接着一个字节的次序进行编址,如上图所示。每个字节都有个编号,我们称之为内存地址

    内存编址:

    当我们在程序中写下了这样的语言声明:

    int i;
    char a;

    时,它其实是内存中申请了一个名为 i 的整型变量宽度空间(DOS 下的 16 位编程中其宽度为 2 个字节),和一个名为 a 的字符型变量宽度的空间(占 1 个字节)。

    内存中的映象如下图所示:

    图中可看出,i 在内存起始地址为 6 上申请了两个字节的空间(我这里假设
    了 int 的宽度为 16 位,不同系统中 int 的宽度可能是不一样的,最常用的win32环境下为4个字节),并命名为 i。
    a 在内存地址为 8 上申请了一字节的空间,并命名为 a。阿欧……这样我们就有两个不同类型的变量了。变量有了接下来我们考虑的就是如何给变量进行赋啦。

    1.2 赋值给变量

    再看下面赋值:
    i = 30;
    a = ’t’;
    你当然知道个两个语句是将 30 存入 i 变量的内存空间中,将“t”字符存入 a 变量的内存空间中。我们可以利用这样来形象理解:

    我们将30存在了以地址6(真正的地址可不是这样子哦,真正的地址如:0016FD14)为起始地址的两个字节空间里,a 在内存地址为 8 上申请了一字节的空间存入了‘t’,那么变量i和a在哪呢???

    1.3 变量在哪里?

    接下来我们来看看&i 是什么意思?是取 i 变量所在的地址编号嘛!我们可以这样大声读出来:返回 i 变量的地址编号。你记住了吗?如果没有,在读一遍:返回 i 变量的地址编号

    以一小段简单代码来弱弱看下:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	std::cout << "&i = "<< &i << std::endl;
    	std::cout << "i =  " << i << std::endl;
    
    	system("pause");
    	return 0;
    }

    输出结果为:

    输出的 &i 的值 0016FD14就是我们图示中内存空间编码为6的内存地址。接下来就进入我们真正的主题——指针。

    二. 指针是个什么东西?

    指针,想说弄懂你不容易啊!我常常在思索它,为什么呢?其实生活中处处都有指针,我们也处处在使用它。有了它
    我们的生活才更加方便了。没有指针,那生活才不方便。不信?你看下面的例子。

    比如有天你说你要学习C++,要借我的这本 C++ Primer Plus,我把书给你送过去发现你已经跑出去打篮球了,于是我把书放在了你桌子上书架的第三层四号的位置。并写了一张纸条:你要的书在第 三 层 四号的书架上。贴在你门上。当你回来时,看到这张纸条,你就知道了我借与你的书放在哪了。你想想看,这张纸条的作用,纸条本身不是书,它上面也没有放着书。那么你又如何知道书的位置呢?因为纸条上写着书的位置嘛!聪明!!!其实这张纸条就是一个指针了。它上面的内容不是书本身,而是
    书的地址,你通过纸条这个指针找到了我借给你的这本书。

    那么我们 C/C++中的指针又戴上了啥面具呢?让我们拭目以待。下面看一条声明一个指向整型变量的指针的语句:
    int *pi;

    pi 是一个指针,当然我们知道啦,但是这样说,你就以为 pi 一定是个多么特别的东西了。其实,它也只过是一个变量而已。与上一篇中说的变量并没有实质的区别。好了,这就是指针。仅此而已,就这么简单。不信你看下图:

    (说明:这里我假设了指针只占 2 个字节宽度,实际上在 32 位系统中,指针的宽度是 4 个字节宽的,即 32 位。)
    由图示中可以看出,我们使用“int *pi”声明指针变量 —— 其实是在内存的某处声明一个一定宽度的内存空间,并把它命名为 pi。你能在图中看出pi 与前面的 i、a 变量有什么本质区别吗?没有,当然没有!肯定没有!!真的没有!!!pi 也只不过是一个变量而已嘛!那么它又为什么会被称为“指针”?关键是我们要让这个变量所存储的内容是什么。现在我要让 pi 成为具有真正“指针”意义的变量。请接着看下面语句:
    pi = &i;
    你应该知道 &i 是什么意思吧!刚刚大声读的那句话,如果忘了,回头去在大声读,记住了!!!那这句代码怎么读呢?这样大声读:把 i 地址的编号赋值给 pi。并记下来。也就是你在 pi 里面写上 i 的地址编号。结果如下图所示:

    你看,执行完 pi=&i 后,在图示中的内存中,pi 的值是 6。这个 6 就是i 变量的地址编号,这样 pi 就指向了变量 i 了。你看,pi 与那张纸条有什么区别?pi 不就是那张纸条嘛!上面写着 i 的地址,而 i 就是那本厚书C++ Primer Plus。你现在看懂了吗?因此,我们就把 pi 称为指针。所以你要牢牢记住:指针变量所存的内容就是内存的地址编号 ! 本篇文章看完啥都可以没记住,这18个红字切记,切记,切记要牢牢刻在脑子里,也许可能或许大概在将来某个面试中人家问你指针是啥?这18个字足以得满分。也会随着你不断的学习对这句话会理解的越来越深。切记记住这18个红字!!好了废话太多了,现在我们就可以通过这个指针 pi 来访问到 i 这个变量了,请看代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	std::cout << "&i = "<< &i << std::endl;
    	std::cout << "i =  " << i << std::endl;
    
    	int *pi = &i;
    	std::cout << "*pi = " << *pi << std::endl;
    
    	system("pause");
    	return 0;
    }
    

    输出结果如下: 

    那么程序中*pi 什么意思呢?你只要这样大声读它:pi 内容所指的地址的内容(读上去好像在绕口令了),就是 pi 这张“纸条”上所写的位置上的那本 “书”—— i 。你看,Pi 的内容是 6,也就是说 pi 指向内存编号为 6 的地址。*pi嘛,就是它所指地址的内容,即地址编号 6 上的内容了,当然就是 30 这个“值”了。所以这条语句会在屏幕上显示 30。请结合上图好好体会吧!由于本人水平有限,对“指针是个什么东西?”的理解仅限于此,有问题我们在随时探讨喽。真想说:指针是个什么东西,这么难理解,脑仁疼疼的。
    到此为止,你已经能大声读出类似&i、*pi 写法的含义了。也知道了具体的相关操作。总结一句话:我们的纸条就是我们的指针,同样我们的 pi 也就是我们的纸条!剩下的就是我们如何应用这张纸条了。如何用?大声读出下面的代码并正确理解含义。

    char a,*pa;
    a = 10;
    pa = &a;
    *pa = 20;

    假设你已经完全掌握在此之前的所有内容,我们接着看看“指针的指针”它是个什么东西?即对int **ppa;中 ppa 的理解。

     三. 二级指针(指针的指针)

    二级指针,是一种指向指针的指针。我们可以通过它实现间接访问数据,和改变一级指针的指向问题。

    3.1 定义与初始化

    以一张简图说明问题:(看不懂看第二节)

    例子代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	std::cout << "&i = "<< &i << std::endl;
    	std::cout << "i =  " << i << std::endl;
    
    	int *pi = &i;
    	std::cout << "*pi = " << *pi << std::endl;
    
    	int **ppi = &pi;
    	std::cout << "**ppi = " << **ppi << std::endl;
    
    	system("pause");
    	return 0;
    }

     输出结果:

     思考:**pi 怎么读?代表的含义是什么?

    3.2 间接数据访问

    3.2.1 .改变一级指针指向

    例子代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	int *pi = &i;
    	std::cout << "一级指针*pi = " << *pi << std::endl;       //一级指针
    
    	int **ppi = &pi;
    	std::cout << "二级指针**ppi = " << **ppi << std::endl;   //二级指针
    
    	*pi = 20;
    	std::cout << "改变一级指针内容: *pi = " << *pi << std::endl;  //改变一级指针值
    	std::cout << "一级指针*pi = " << *pi << std::endl;       //二级指针
    
    	int b = 10;
    	*ppi = &b;
    	std::cout << "改变一级指针指向*pi = " << *pi << std::endl;   //改变一级指针的指向
    	std::cout << "二级指针**ppi = " << **ppi << std::endl;   
    
    	system("pause");
    	return 0;
    }
    

    输出结果:

    3.2.2 改变 N-1 级指针的指向

    • 可以通过一级指针,修改 0  级指针(变量)的内容。
    • 可以通过二级指针,修改一级指针的指向。
    • 可以通过三级指针,修改二级指针的指向。
    •  ·····
    • 可以通过 n  级指针,修改 n-1 

    3.2.3 二级指针的步长

    所有类型的二级指针,由于均指向一级指针类型,一级指针类型大小是 4,所以二级指针的步长也是 4,这个信息很重要

    四. 指针与数组

    4.1 指针与数组名

    4.1.1 通过数组名访问数组元素

    看下面代码:
    int i, a[] = {3,4,5,6,7,3,7,4,4,6};
    for (i = 0; i <= 9; i++)
    {
        std::cout << a[i] std::endl;
    }
    很显然,它是显示 a 数组的各元素值。
    我们还可以这样访问元素,如下:
    int i, a[] = {3,4,5,6,7,3,7,4,4,6};
    for (i = 0; i <= 9; i++)
    {
    std::cout << *(a+i) << std<<endl;;
    }
    它的结果和作用完全一样。

    4.1.2 通过指针访问数组元素

    int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
    pa = a; /*请注意数组名 a 直接赋值给指针 pa*/
    for (i = 0; i <= 9; i++)

    {
     std::cout <<  pa[i] << std::endl;
    }
    很显然,它也是显示 a 数组的各元素值。

    另外与数组名一样也可如下:
    int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
    pa = a;
    for (i = 0; i <= 9; i++)
    {
       std::cout <<  *(pa+i) << std::endl;
    }

    4.1.3 数组名与指针变量的区别

    请看下面的代码:
    int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
    pa = a;
    for (i = 0; i <= 9; i++)
    {
    printf("%d\n", *pa);
    pa++; /*注意这里,指针值被修改*/
    }

    可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的 pa改成 a 试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而 数组名只是一个指针常量。

    4.2 指针数组( 字符指针数组 )

    4.2.1 定义

    指针数组的本质是数组,数组中每一个成员是一个指针。定义形式如下:
    char * pArray[10];
    语法解析:pArray 先与“[ ]”结合,构成一个数组的定义,char *修饰的是数组的内容,即数组的每个元素。

    图示:

     4.2.2 代码实例

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
    	for(int i=0; i<sizeof(pArray)/sizeof(*pArray); i++)
    	{
    		std::cout << pArray[i] << std::endl;
    	} 
    
    	system("pause");
    	return 0;
    }

    输出结果:

    4.3 二级指针与指针数组 

    4.3.1 .指针数组名赋给二级指针的合理性

    二级指针与指针数组名等价的原因:
    char **p 是二级指针;
    char* array[N]; array = &array[0]; array[0] 本身是 char*型;
    char **p = array;

    例子代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
    	std::cout << "**********pArray[i]************" << std::endl;
    	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
    	{
    		std::cout << pArray[i] << std::endl;
    	}
    		
    	char **pArr = pArray;
    	std::cout << "**********pArr[i]************" << std::endl;
    	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
    	{
    		std::cout << pArr[i] << std::endl;
    	}
    	system("pause");
    	return 0;
    }

    输出结果:

    4.3.2 完美匹配的前提(小尾巴 NULL)

    数组名,赋给指针以后,就少了维度这个概念,所以用二级指针访问指针数组,需要维度,当然了,也可以不用需要。

    实例代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	//演绎 1
    	std::cout << "******演绎 1*****" << std::endl;
    	int arr[10] = {1};
    	for(int i=0; i<10; i++)
    	{
    		std::cout << arr[i] << std::endl;
    	}
    
    	int *parr = arr;
    	for(int i=0; i<10; i++)
    	{
    		std::cout << *parr++ << std::endl;
    	}
    
    
    	//演绎 2
    	std::cout << "*****演绎 2*****"<<std::endl;
    	char *str = "china";
    	while(*str)
    	{
    		std::cout << *str++ << std::endl;
    	}
    
    	char * pArray[] ={"apple","pear","banana","orange","pineApple",NULL};
    	char **pa = pArray;
    	while(*pa != NULL)
    	{
    		std::cout << *pa++ << std::endl;
    	}
    	system("pause");
    	return 0;
    }

    输出结果:

     

    【下一篇:】C/C++指针详解之提高篇(史上最全最易懂指针学习指南!!!!)

    展开全文
  • C/C++ 指针的13份资料

    万次下载 热门讨论 2008-11-13 11:16:36
    C/C++ 指针的13份资料 内容丰富,让编程人员不再害怕指针
  • C++野指针及c++指针使用注意点

    千次阅读 多人点赞 2019-08-29 16:11:33
    野指针及c++指针使用注意点 避免野指针的产生 “野指针”的成因主要有: 1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时...

    内容转自(部分已被本人编辑):https://www.cnblogs.com/mrlsx/p/5419030.html

    野指针及c++指针使用注意点

    避免野指针的产生

    “野指针”的成因主要有:

    1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

    char *p; //此时p为野指针
    

    2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针.

    char *p=new char[10];  //指向堆中分配的内存首地址,p存储在栈区
    cin>> p;
    delete []p; //p重新变为野指针
    

    3)指针操作超越了变量的作用范围。          

    char *p=new char[10]; //指向堆中分配的内存首地址
    cin>> p;
    cout<<*(p+10); //可能输出未知数据
    

    指针的注意点:

    a.指针指向常量存储区对象

    char *p="abc";

    此时p指向的是一个字符串常量,不能对*p的内容进行写操作,如srtcpy(p,s)是错误的,因为p的内容为“abc”字符串常量,该数据存储在常量存储区,但可以对指针p进行操作,让其指向其他的内存空间。

    b.资源泄漏

    问题:

    1 #include<iostream>
    2 using namespace std;
    3 void main()
    4 {
    5     char *p=new char[3];  //分配三个字符空间,p指向该内存空间
    6     p="ab";             //此时改变p的指向,p指向了常量“ab”,而不再是new char分配的内存空间了,new char[3]开辟的三个字符空间没有被释放,从而造成了资源泄漏。
    7     delete []p;         //释放时报错,此时p指向的是字符串常量
    8 }

    结果:卡死

     

    改进:

    1 #include<iostream>
    2 using namespace std;
    3 void main()
    4 {
    5     char *p=new char[3];  //分配三个字符空间,p指向该内存空间
    6     strcpy(p,"ab");      //将"ab"存储到p指向的内存空间,而不是改变p的指向
    7     delete []p;         //ok
    8 }

    结果:正确

     

    c.内存越界

    1 char *p=new char[3];  //分配三个字符空间,p指向该内存空间
    2 strcpy(p,"abcd");    //将abcd存处在分配的内存空间中,由于strlen("abcd")=4>3,越界
    3 delete []p;         //ok
    

    d.返回值是指针

    问题:数组p[]中的内容为“hello world”,存储在栈区,函数结束时内容被清除,p变为野指针,可能导致乱码

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     char p[]="abc";
     6     return p;
     7 }
     8 void main()
     9 {
    10     cout<<f()<<endl;
    11 }

    结果:

     改进:

    1.加static限定,延长数组生存期

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     static char p[]="abc";  //此时数组为静态数组,存储在全局/静态区,生存期到程序结束,因此函数结束时不会销毁p
     6     return p;
     7 }
     8 void main()
     9 {
    10     cout<<f()<<endl;
    11 }

    结果:

     

    2.定义成指针型数组

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     char *p="abc";    //"abc"存储在文字常量区,p是指向常量的指针,生存期到程序结束
     6     return p;
     7 }
     8 void main()
     9 {
    10     cout<<f()<<endl;
    11 }

    结果:

     

    3.动态分配存储空间,存储在堆区

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     char *p=new char[5];  //动态分配存储空间,p指向堆区
     6     strcpy(p,"abc");   // 这里不能用p="abc",前面已经说明
     7     return p;
     8 }
     9 void main()
    10 {
    11     cout<<f()<<endl;
    12 }

    结果:

     e.指针做形参

    即所谓的地址传递,我们都知道地址传递的方式,形参的改变会导致实参的改变,但要注意的是,这里的改变是指指针所指内容的改变,而不是指针值的改变。因此,当形参改变会导致实参改变时,指针所指的内容是非const类型的,否则会出错。

    1.改变指针内容:

    void swap(int *a,int *b)   //交换的是*a,*b,即指针的内容,而不是指针a,b
    {
       int t;
       t=*a;
       *a=*b;
       *b=t;
    }

    2.改变指针值:

    #include<iostream>
    using namespace std;
    void fun(char *p)
    {
        p="cba";    //“cba”存放在文字常量区,让p指向常量"abc",这里改变的是指针值,实参并不会改变
    }
    void main()
    {
        char *p="abc";  //“abc”存放在文字常量区,p指向常量"abc"
        fun(p);
        cout<<p<<endl;   //输出"abc",而不是"cba"
    }

    结果:

    如果对于这里不容易理解:笔者作如下解释:

    改写代码:(注意看注释)

    #include<iostream>
    using namespace std;
    void fun(char *a)  //给形参换个名字
    {
        cout<<a<<endl;   //abc
        a="cba";         //“cba”存放在文字常量区,让a指向常量"cba"
        cout<<a<<endl;   //cba
    
        //证明这里只改变了指针a的指向,即改变的是指针值a的值,先前是“abc”的地址,后来是“cba”的地址。实参并不会改变。
    }
    void main()
    {
        char *p="abc";  //“abc”存放在文字常量区,p指向常量"abc"
        fun(p);
        cout<<p<<endl;   //输出"abc",而不是"cba"
    
        p="cba";
    	cout<<p<<endl;  //输出“cba”    实参p发生改变
        这就证明:在fun函数中,只是改变了形参的值。
    }

    main函数和fun函数中的"abc"、"cba"都是存放在文字常量区,是具有各自的编号地址的。可以自己调试用printf("%p\n",p);打印出地址,可以看到两个不同的地址。

     

    继续看下面的情况,修改指针的内容:

    #include<iostream>
    using namespace std;
    void fun(char *p)
    {
        p[0]='c';    //改变p的内容,即修改p[0]
    }
    void main()
    {
        char *p="abc";  //p指向常量"abc"
        fun(p);
        cout<<p<<endl;   //error,p所指内容为常量,不能修改
    }

    结果:

    注:p="ab"和strcpy(p,"ab"),含义不一样,前者指针p指向常量“ab”存储区域的首地址,改变了p最开始指向的new申请的内存空间;而后者是将“ab”分配到new申请的内存空间中;

    展开全文
  • C++指针详解

    万次阅读 多人点赞 2018-12-08 22:55:09
    有好的一面,必然会有坏的一面,指针的灵活导致了它的难以控制,所以C/C++程序员的很多bug是基于指针问题上的。今天就对指针进行详细的整理。 1、指针是什么? 指针是“指向(point to)”另外一种类型的复合类型...
  • C++指针地址和指针的值

    千次阅读 2018-12-28 19:57:49
    C++指针 学过C++ 都知道,指针是一个很重要的概念,他是C++的灵魂,在上学的时候从书山了解到,指针就是指向内存的地址,然,今天和同事讨论指针传递和引用传递的时候,我说:指针传递其实就是引用传递。打脸了。...
  • c++指针加数字

    千次阅读 2017-02-21 10:01:14
    c++指针加数字
  • C++指针引用和指向指针的指针传输结构体 #include <iostream> using namespace std; struct teacher { int id; char name[64]; }; int tea(struct teacher* &a) {//指针引用,a是c的别名 a = ...
  • C++ 指针常量、常量指针和常指针常量 2014年11月26日 22:12:41 To-Big_Fish 阅读数:4324 https://blog.csdn.net/yujin753/article/details/41523005 1、指针常量 如果在定义指针变量时候,指针变量前用const...
  • C++指针取值!

    千次阅读 2019-10-19 14:30:14
    c++ 指针取值,一般为以下三种情景: 1. 保存一个特定的对象地址 2. 用另一个指针对其进行赋值 3. 用0值,注意别的值是不行的 接下来我们来看下面的一个例子: #include<iostream> using namespace std;...
  • c++指针(一)——指针的定义以及写法

    万次阅读 多人点赞 2018-05-08 08:26:31
    一、c++中指针的定义以及写法二、定义变量时的内存分配三、指针与数组名的关系四、常量指针与常量指针五、指针的指针六、指针和引用好了,按照这个顺序开启探索c++指针之旅~一、c++中指针的定义int *p; //*号前有...
  • c++指针和内存结构总结

    千次阅读 2017-07-04 16:05:14
    c++指针、内存模型、函数指针

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 105,527
精华内容 42,210
关键字:

c++指针

c++ 订阅