• Coursera课程笔记----C程序设计进阶----Week 4


    指针(一) (Week 4)

    什么是“指针”

    • 互联网上的资源——地址

      当获得一个地址,就能得到该地址对应的资源,所以可以把“网址”称为指向资源的“指针”

    • 内存中的位置——地址

      • 变量的三要素:变量的地址,变量的,变量的名字
      • 通常把某个变量的地址称为“指向该变量的指针
    • 如何拿到or看到一个变量的地址

      • 可以利用 取地址运算符“&” 实现
        • cout<<&c<<endl; //结果:0041FE24
        • cout<<sizeof(&c)<<endl; //结果:4
    • 变量地址(指针)的作用

      • 我们可以通过资源地址(指针)访问网络资源

      • 计算机通过变量地址(指针)操作变量

        • 可以用指针运算符*实现

          cout<<*&c<<endl; // 等价于cout<<c<<endl;

    什么是“指针变量”

    • 我们可以设置一个变量,来存放网络资源的地址
    • 我们可以设置一个变量,来存放变量的地址(指针)

    指针变量

    • 专门用于存放指针(某个变量地址)的变量

      • 例:若指针pointer为指针变量,且存储着变量c的指针(地址),则称pointer为指向变量c的“指针变量”
    • 定义一个指针变量

      • int *****pointer;

        指针变量的基类型/指针运算符pointer的类型/指针变量的名字

        基类型:指针变量指向的变量的类型

    int c = 76;
    int *pointer; //定义名字为pointer的指针变量
    pointer = &c; //将变量c的地址赋给指针变量pointer;
    //赋值后,称指针变量pointer指向了变量c
    
    • 使用指针变量
      • 既然指针变量中存放的是“某个变量的地址”,可否通过“指针变量”访问“它所指向的变量”呢?
      • 可以用指针运算符*****实现
    int c = 76;
    int *pointer = &c;
    //*pointer为“pointer所指向的存储单元的内容”————也就是变量c
    
    • 一些说明
      • 指针变量也是变量,是变量就有地址
      • &与*的运算符优先级比较高(优先级基本上跟着逻辑走就行了)
    • 指针变量的++ --
      • 根据基类型来确定++和--
      • 假设:iPtr当前所存地址是0x00000100
        • 若iPtr指向一个整型元素(占4个字节),则iPtr++等于iPtr+1*4 = 0x00000104
        • 若iPtr指向一个实型元素(占4个字节),则iPtr++等于iPtr+1*4 = 0x00000104
        • 若iPtr指向一个字符元素(占1个字节),则iPtr++等于iPtr+1*1 = 0x00000101

    数组与指针

    • 指向数组元素的指针
      • 指向数组元素和指向普通变量没有区别
      • 数组名是数组首元素的地址
    • 数组的地址
      • 数组名代表数组首元素的地址
      • 数组名相当于指向数组第一个元素的指针
        • 数组名不是变量,不能赋值
    • 利用指针变量引用数组元素
      • 若定义
        • 数组int a[10]; 指针int *pointer;
      • 则:
        • pointer = a; 等价于pointer = &a[10];
      • 数组访问:
        • pointer + i ; 等价于a + i; 等价于&a[i];
        • *(pointer + i) 等价于*(a+i)等价于a[i];
      • 表示形式
        • pointer[i] 等价于*(pointer + i)
    • 需要注意的问题
      • int *p = &a[0]
        • a++是没有意义的,但p++会引起p变化
        • p可以指向数组最后一个元素以后的元素
      • 所以指针做加减运算时一定注意有效的范围
    • 指向二维数组的指针
      • 因为存储方式是把二维数组“拉直”,所以使用方法和一位数组区别不大
    int main()
    {
      int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23};
      int _____,i,j; //填空:完成变量p的定义
      p = a;
      cin >> i >> j;
      cout<<setw(4)<<*(*(p+i)+j); //正误判断,访问元素a[i][j]
      return 0;
    }
    
    • 从p = a开始

      • a相当于指向a[3][4]的“第一个元素”的指针
      • 所谓“第一个元素”是指一个”包含4个int型元素的一维数组“
      • 所以,a相当于一个”包含4个int型元素的一维数组“的地址
      • 因此,p的基类型是:“包含4个int型元素的一维数组”
    • 问题:如何定义一个指向 “包含4个int型元素的一维数组”的指针变量?

    • 答案:变量定义语句:int (*p)[4];

    • *(*(p+i)+j)是什么?

      • p指向一个“包含4个int型元素的一维数组”
      • p+i是第i+1个“包含4个int型元素的一维数组”的地址
      • p + i 等价于&a[i];
      • *(p+i)等价于a[i];
      • *(p+i)+j等价于 a[i] + j
      • 因为:a[i] + j 等价于&a[i][j]
      • *(*(p+i)+j)等价于a[i][j]
      • 结果正确
    • 详解

      指针变量可以指向一维数组中的元素,当然也就可以指向二维数组中的元素。但是在概念和使用方法上,二维数组的指针比一维数组的指针要复杂一些。要理解指针和二维数组的关系首先要记住一句话:二维数组就是一维数组,这句话该怎么理解呢?

      假如有一个二维数组:

      int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
      

      其中,a 是二维数组名。a 数组包含 3 行,即 3 个行元素:a[0],a[1],a[2]。每个行元素都可以看成含有 4 个元素的一维数组。而且 C 语言规定,a[0]、a[1]、a[2]分别是这三个一维数组的数组名。如下所示:

      img

      a[0]、a[1]、a[2] 既然是一维数组名,一维数组的数组名表示的就是数组第一个元素的地址,所以 a[0] 表示的就是元素 a[0][0] 的地址,即 a[0]&a[0][0];a[1] 表示的就是元素 a[1][0] 的地址,即 a[1]&a[1][0];a[2] 表示的就是元素 a[2][0] 的地址,即 a[2]==&a[2][0]。

      所以二维数组a[M][N]中,a[i]表示的就是元素a[i][0]的地址,即(式一)

      a[i] == &a[i][0]; 
      

      我们知道,在一维数组 b 中,数组名 b 代表数组的首地址,即数组第一个元素的地址,b+1 代表数组第二个元素的地址,…,b+n 代表数组第 n+1 个元素的地址。所以既然 a[0]、a[1]、a[2]、…、a[M–1] 分别表示二维数组 a[M][N] 第 0 行、第 1 行、第 2 行、…、第 M–1 行各一维数组的首地址,那么同样的道理,a[0]+1 就表示元素 a[0][1] 的地址,a[0]+2 就表示元素 a[0][2] 的地址,a[1]+1 就表示元素 a[1][1] 的地址,a[1]+2 就表示元素 a[1][2] 的地址……a[i]+j 就表示 a[i][j] 的地址,即(式二)

      a[i]+j == &a[i][j];
      

      将式一代入式二得(式三)

      &a[i][0]+j == &a[i][j]
      

      在一维数组中 a[i] 和 *(a+i) 等价,即(式四):

      a[i] == *(a+i);
      

      这个关系在二维数组中同样适用,二维数组 a[M][N] 就是有 M 个元素 a[0]、a[1]、…、a[M–1] 的一维数组。将式四代入式二得(式五)

      *(a+i)+j == &a[i][j];
      

      由式二和式五可知,a[i]+j 和 *(a+i)+j 等价,都表示元素 a[i][j] 的地址。

      上面几个公式很“绕”,理清楚了也很简单,关键是把式二和式五记住。

    习题选解

    注:只分析较难的答案,不代表是唯一的答案

    1. 有double num = 3.14; double * pi = & num; 现在pi指向的地址的内容为3.14。然而我们又想要提高精度,将它变成3.14159。在不考虑代码优美性、易读性的情况下,以下操作正确的有哪些?

    ✅ pi[0] = 3.14159

    解析:虽然pi看上去不是指向数组,但确实可以通过pi[0]引用到我们需要修改的数。但是,这不是一种良好的代码风格,因为一旦使用pi[1]就会出现不可预料的后果。

    1. 已知字符串char str[] = "hello,world"; 现在我们想输出字符串的后半部分,即",world",以下操作正确的包括哪些?

    ✅cout << str + 5 << endl;

    解析:str+5指向',',并且是字符指针,对它使用cout,输出从str+5开始的字符串

    1. 以下函数的输出结果是:

      int fun( ){

      char a[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', 0}, *p;

      int i = 8;

      p = a + i;

      cout << p - 3 << endl;

      return 0;

      }

    ✅ 6789

    解析:p - 3指向a+5,即内容为'6'的那个地址。由于p-3为字符指针,所以使用cout时输出字符串,以0(即'')结尾

    4.对于基类型相同的两个指针变量之间,以下哪一项操作缺乏有价值的语义?

    ✅ +

  • 相关阅读:
    拓扑排序模板
    杭电 2066 一个人的旅行
    杭电 1596 find the safest road (最小路径变形求最大安全度)
    【状态压缩DP】【BZOJ1087】【SCOI2005】互不侵犯king
    【bzoj1001】【最短路】【对偶图】【最大流转最小割】狼抓兔子题解
    [BZOJ1179][APIO2009][强连通分量Tarjan+spfa]ATM
    [BZOJ1194][HNOI2006][强连通分量Tarjan+dfs]潘多拉的盒子
    说一说ST表 讲一讲水题
    【强连通分量】tarjan算法及kosaraju算法+例题
    对拍程序讲解及源码举例
  • 原文地址:https://www.cnblogs.com/maimai-d/p/12831004.html
Copyright © 2020-2023  润新知