C/C++之指针(上)

C/C++之指针(上)

上节介绍了二维数组。C/C++之二维数组

本节介绍劝退大部分同学的指针!(重点)。

由于比内容比较多,篇幅比较长,所以分为三个部分。本篇为上。

文章目录

  • C/C++之指针(上)
    • 1、指针的定义
    • 2、指针的访问
    • 3、空指针,坏指针
    • 4、指向数组的指针(指针的自增与自减)
    • 5、指针与指针的减法

  • 引入几个问题:

    1. 之前在讲函数的时候,提到过普通变量是无法通过调用函数来改变的。
      例如:

      #include   
      
      using namespace std;
        
      void add(int a, int b){
               
      	a = a + b;
      }
      
      int main(){
               
      	int x = 0;
          add(x, 5);
          cout << x << endl;
          
          return 0;
      }
      

      显然,这里的结果不会改变。x依旧是0,看输出:

      0
      
    2. 如果一个函数需要返回多个值又该怎么做?
      例如:一个函数要返回一个int值,同时要判断是否成功执行,还要返回bool类型值。这时候肯定不行的,因为一个函数只有一个返回值。

    3. 如果一次性传入太多的参数,无疑会大幅度拖慢速度,执行效率会被降低,这时候怎么做呢?还是使用指针。

  • 上面引入的问题都对应了一个新的变量,指针变量

  • 本节来介绍听起来就比较难的指针(其实很简单。)

1、指针的定义

  • 引入:scanf()函数,大家在用scanf()输入值的时候,后面的变量列表都加了取地址符&。这是为什么呢?
    很明显,scanf()函数传递变量只是普通的值传递,并不会改变变量的值,所以此处传递的是一个指针,一个地址
    scanf("%d", &a);类似的情况。
    实质:scanf()把输入的常量,保存到地址对应的值中。

  • 指针的定义: 使用变量类型* 变量名;来定义一个指针。实例:

    #include   
    
    using namespace std;
    
    int main(){
           
    	int age = 18;
        //整数类型的指针,指向一个整数的地址。此处p的值就是age的地址。
        int* p = &age;
        
        //调用scanf(),此处可以直接调用指针变量而不需要&(取地址),因为p的值本身就是一个地址
        scanf("%d", p);
        
        return 0;
    }
    

    如图:(此处假设age的地址为8016,值为18)。
    C/C++之指针(上)_第1张图片

  • 定义:变量类型*变量名;来定义,具体方式有如下几种。差别不大,建议用第一种和第二种。

    • 第一种:int *p;
    • 第二种:int* p;
    • 第三种:int * p;
    • 第四种:int*p;(不建议)
  • 误区:int *p1, *p2;这是两个指针变量,int* p1, p2;这是一个指针变量p1和一个普通变量p2;(*连着int没啥特别的地方)。

  • 强调:指针也有自己的地址。它的值为指向的变量的地址(不是指针的地址!)。
    详解:

    #include   
    
    using namespace std;
    
    int main() {
           
        int age = 18;
        //整数类型的指针,指向一个整数的地址。此处p的值就是age的地址。
        int* p = &age;//等价为int *p;  p = &age;
    
        //获取指针变量p的地址。
        printf("指针p的地址为:%p\n", &p);
        //获取指针p指向的地址。
        printf("指针p所指向地址为:%p\n", p);
        //获取指针p指向的地址的值。
        printf("指针p所指向地址的值为:%d\n", *p);
        //获取普通变量age的地址。
        printf("普通变量p的地址为:%p\n", &age);
        //获取普通变量age地址中的值。
        printf("普通变量p的地址对应的值为:%d\n", *(&age)); //等同于先取地址,再取地址中的值
    
        return 0;
    }
    

    输出结果:(每次调试都不同, 原因:程序结束自动释放内存)

    指针p的地址为:010FFB10
    指针p所指向地址为:010FFB1C
    指针p所指向地址的值为:18
    普通变量p的地址为:010FFB1C
    普通变量p的地址对应的值为:18
    

    分析: 此处获得了两个不同的地址值,指针的地址和指针指向的地址(分别为010FFB10与010FFB1C)。指针指向的地址和指向的变量的地址相同(此处为010FFB1C),同时指向的地址的值与指向的变量的值也相同。
    用图表示为:
    C/C++之指针(上)_第2张图片

2、指针的访问

  • 指针和其他的普通变量一样,都可以访问和修改。
    例如:(&取地址符)

    #include   
    
    using namespace std;
    
    int main() {
           
        int age1 = 18;
        int age2 = 21;
        int age3 = 24;
    
        int* p = &age1;
        //使用%d打印地址可能会出现负数。
        printf("p的地址为: %p\t age1的地址为: %p\n", p, &age1);
    
        //改变p指向的变量。
        p = &age2;
        printf("p的地址为: %p\t age2的地址为: %p\n", p, &age2);
    
        //改变p指向的变量。
        p = &age3;
        printf("p的地址为: %p\t age3的地址为: %p\n", p, &age3);
    
        return 0;
    }
    

    输出内容:

    p的地址为: 00F1F9A4      age1的地址为: 00F1F9A4
    p的地址为: 00F1F998      age2的地址为: 00F1F998
    p的地址为: 00F1F98C      age3的地址为: 00F1F98C
    

    发现:改变指针所指向的对象后,它的值变成了指向的对象的地址。(使用&取出变量的地址)
    访问指针所指向的地址的值。(*为间接访问符,指针运算符)

    #include   
    
    using namespace std;
    
    int main() {
           
        int age = 18;
    
        int* p = &age;
    
        int x = 0;
    
        //间接访问指针p指向地址所对应的值
        x = *p;
        //使用%d打印地址可能会出现负数。
        *p = 13;
        
        printf("x的值为:%d\n", x);
        printf("*p的值为:%d\n", *p);
    
        return 0;
    }
    

    输出结果:

    x的值为:18
    *p的值为:13    
    

    可以通过 *(指针运算符)来取出指针指向的变量的地址的值,并且和别的非指针变量一样能够进行相同的运算。

3、空指针,坏指针

  • 看下面的例子:

    #include   
    
    using namespace std;
    
    int main() {
           
        int age;
            
        int* p;
        
        cin >> age;
        if(age == 18){
           
            p = &age;
        }else if(age == 40){
           
            p = &age;
        }
    
        cout << "p指向的地址为:" << p << endl;
        
        return 0;
    }
    

    上面的代码存在很大的问题:可能会没有初始化指针p的值。而且会报错。

  • 在定义指针的时候就初始化来解除警报。int* p = 0;或者int* p = NULL;初始化为空指针。
    但0地址是不可访问的,所以需要将代码变得更加健全!

    #include   
    
    using namespace std;
    
    int main() {
           
        int age;
            
        int* p;
        
        cin >> age;
        if(age == 18){
           
            p = &age;
        }else if(age == 40){
           
            p = &age;
        }
    
        if(p){
           
        	cout << "p指向的地址为:" << p << endl;
        }else{
           
            cout << "p没有指向任何地址" << endl;
        }
        
        return 0;
    }
    

    注意:一般使用NULL而不使用0来初始化指针

  • 坏指针:没有初始化就使用的指针

4、指向数组的指针(指针的自增与自减)

  • 数组和普通变量不同,前面通过普通函数发现能够改变数组中成员的值。C/C++之自定义函数
    原理:传递整个数组到函数中,不是单纯的传递值,而是传递数组的第零个成员的地址!!

  • 对数组的地址进行分析:

    #include   
    
    using namespace std;
    
    int main() {
           
        //定义两个数组,一个为int型,一个为char型
        int a[3] = {
           1, 2, 3};
        char b[3] = {
           'a', 'b', 'c'};
        
        //获取a数组的所有成员的地址。
        printf("a[0]的地址为:%p\n", &a[0]); //等效于printf("a[0]的地址为:%p\n", a);
        printf("a[1]的地址为:%p\n", &a[1]); //等效于printf("a[0]的地址为:%p\n", a + 1);
        printf("a[2]的地址为:%p\n", &a[2]); //等效于printf("a[0]的地址为:%p\n", a + 2);
        //使用第二种取地址的方法取值
        printf("a[0]的值为:%d\n", *a);
        printf("a[1]的值为:%d\n", *(a + 1));
        printf("a[2]的值为:%d\n", *(a + 2));
        //获取b数组的所有成员的地址。
        printf("b[0]的地址为:%p\n", &b[0]); //等效于printf("b[0]的地址为:%p\n", b);
        printf("b[1]的地址为:%p\n", &b[1]); //等效于printf("b[0]的地址为:%p\n", b + 1);
        printf("b[2]的地址为:%p\n", &b[2]); //等效于printf("b[0]的地址为:%p\n", b + 2);
        //使用第二种取地址的方法取值
        printf("b[0]的值为:%c\n", *b);
        printf("b[1]的值为:%c\n", *(b + 1));
        printf("b[2]的值为:%c\n", *(b + 2));
      
        return 0;
    }
    

    输出:

    a[0]的地址为:012FF904
    a[1]的地址为:012FF908
    a[2]的地址为:012FF90C
    a[0]的值为:1
    a[1]的值为:2
    a[2]的值为:3
    b[0]的地址为:012FF8F8
    b[1]的地址为:012FF8F9
    b[2]的地址为:012FF8FA
    b[0]的值为:a
    b[1]的值为:b
    b[2]的值为:c
    

    大家发现没有:之前讲指针的定义的时候int *p的地址占了四个字节(010FFB10)。
    这里也相同,int a[3]数组,每个成员占四个字节,而char b[3]数组,每个成员占1个字节。

  • 总结:int* p = a;表示指向数组的指针,访问数组的第i个元素可以用*(p + i)来访问!

C/C++之指针(上)_第3张图片

  • 指针的自增:(自增与自减++,–的优先级高于*)

    #include   
    
    using namespace std;
    
    int main() {
           
        int a[5] = {
           1, 2, 3, 4, 5};
        int* p = a;
        
        //用指针输出值
        for(int i = 0; i < 5; i++){
           
    		printf("%d ", *p++);
        }
      
        return 0;
    }
    

    输出结果:

    1 2 3 4 5
    
  • 指针的自减:

    #include   
    
    using namespace std;
    
    int main() {
           
        int a[5] = {
           1, 2, 3, 4, 5};
        //让p指向最后一个元素的地址
        int* p = &a[sizeof(a) / sizeof(a[0]) - 1];
        
        //用指针逆序输出
        for(int i = 0; i < 5; i++){
           
    		printf("%d ", *p--);
        }
      
        return 0;
    }
    

    输出结果:

    5 4 3 2 1
    
  • 注意:+,-运算符的优先级低于*()(取值运算符)所以在具体访问数组中某个值时需要*(p + i);
    例:

    #include   
    
    using namespace std;
    
    int main() {
           
        int a[5] = {
            21, 12, 66, 44, 25 };
        //让p指向最后一个元素的地址
        int* p = &a[0];
    
        //用指针输出第四个*(p + 3)
        printf("第四个元素的值为:%d\n", *(p + 3));
        //注意区别
        printf("*p + 3的值为:%d", *p + 3);
    
        return 0;
    }
    

    输出结果:

    第四个元素的值为:44
    *p + 3的值为:24
    

    分析: *(p + 3) 等同于 a[4], *p + 3 等同于 a[0] + 3;

    总结: 进行指针对数组中的元素进行取值时:p + i实际指向的地址为p + i * sizeof(数据类型))

5、指针与指针的减法

  • 先来讲指针的减法:
    用处:计算数组元素的偏移值,计算字符串元素的偏移值

    #include   
    
    using namespace std;
    
    int main() {
           
        int a[5] = {
            21, 12, 66, 44, 25 };
        
        int* p1 = &a[0];
        int* p2 = &a[4];
    
        printf("p2 - p1 = %d\n", p2 - p1);
        printf("p1 - p2 = %d", p1 - p2);
    
        return 0;
    }
    

    输出结果:

    p2 - p1 = 4
    p1 - p2 = -4
    

    相减的指针如果不是指向同一个数组,那就没有意义。

  • 指针的相加:(无任何意义,故在此不做研究。)

本节到这里,下一节继续。

你可能感兴趣的:(C++,c++,指针)