指针(一) (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]分别是这三个一维数组的数组名。如下所示:

    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(即'\0')结尾

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

✅ +

Coursera课程笔记----C程序设计进阶----Week 4的更多相关文章

  1. Coursera课程笔记----C程序设计进阶----Week 5

    指针(二) (Week 5) 字符串与指针 指向数组的指针 int a[10]; int *p; p = a; 指向字符串的指针 指向字符串的指针变量 char a[10]; char *p; p = ...

  2. Coursera课程笔记----C程序设计进阶----Week 3

    函数的递归(Week 3) 什么是递归 引入 函数可以嵌套调用:无论嵌套多少层,原理都一样 函数不能嵌套定义:不能在一个函数里再定义另一个函数,因为所有函数一律平等 问题:一个函数能调用它自己吗? 举 ...

  3. Coursera课程笔记----C程序设计进阶----Week 1&2

    C程序中的函数(Week 1&2) 函数 函数的定义 对函数的普遍认识:y=f(x) C语言中的常用函数: 平方根: r = sqrt(100.0) 底数x的y次幂:k = pow(x,y) ...

  4. Coursera课程笔记----C++程序设计----Week3

    类和对象(Week 3) 内联成员函数和重载成员函数 内联成员函数 inline + 成员函数 整个函数题出现在类定义内部 class B{ inline void func1(); //方式1 vo ...

  5. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  6. Coursera课程笔记----Write Professional Emails in English----Week 3

    Introduction and Announcement Emails (Week 3) Overview of Introduction & Announcement Emails Bas ...

  7. Coursera课程笔记----Write Professional Emails in English----Week 1

    Get to Know Basic Email Writing Structures(Week 1) Introduction to Course Email and Editing Basics S ...

  8. Coursera课程笔记----计算导论与C语言基础----Week 6

    理性认识C程序 导论(Week 6) 明确学习进度 讲课内容 感性➡️理性➡️函数➡️指针等 作业练习 初级阶段 ➡️正常作业练习 C语言的由来 程序设计语言的分类 低级语言之机器语言 0010101 ...

  9. Coursera课程笔记----计算导论与C语言基础----Week 4

    感性认识计算机程序(Week 4) 引入 编程序 = 给计算机设计好运行步骤 程序 = 人们用来告诉计算机应该做什么的东西 问题➡️该告诉计算机什么?用什么形式告诉? 如果要创造一门"程序设 ...

随机推荐

  1. 2019-05-19 Python之第一个爬虫和测试

    一.使用request和get访问某个网页20次并且打印返回状态,内容   扩展:常见状态码含义 200 - 服务器成功返回网页,404 - 请求的网页不存在,403(禁止)服务器拒绝请求,404(未 ...

  2. ActiveMQ支持的消息协议

    ActiveMQ支持哪些协议 ActiveMQ支持多种协议传输和传输方式,允许客户端使用多种协议连接ActiveMQ支持的协议:AUTO,OpenWire,AMQP,Stomp,MQTT等Active ...

  3. 5个有趣的Python小知识,结果令人意外

    1 字符串驻留 如果上面例子返回True,但是下面例子为什么是False: 这与Cpython 编译优化相关,行为称为字符串驻留,但驻留的字符串中只包含字母,数字或下划线. 2 相同值的不可变对象 这 ...

  4. Video tagging systems based on DNNs

    Need: With the ever-growth large-scale video in the mobile phone, so what will everyone get from the ...

  5. Python 七步捉虫法

    了解一些技巧助你减少代码查错时间. -- Maria Mckinley 在周五的下午三点钟(为什么是这个时间?因为事情总会在周五下午三点钟发生),你收到一条通知,客户发现你的软件出现一个错误.在有了初 ...

  6. Nagios基本搭建

    Nagios简述: 1.一款用来监视系统和网络的开源软件 2.利用其从多的插件实现对本机和远端服务的监控 3.当被监控对象异常时,回及时向管理员警告 4.提供一批预设好的监控插件,用户可以直接调用 5 ...

  7. Zabbix磁盘性能监控

    iostat统计磁盘信息的时候,使用的是/proc/diskstats ,cat /proc/diskstats显示如下 ram0 ram1 ram2 ram3 ram4 ram5 ram6 ram7 ...

  8. Spring Boot Starters介绍

    文章目录 Web Start Test Starter Data JPA Starter Mail Starter 结论 对于任何一个复杂项目来说,依赖关系都是一个非常需要注意和消息的方面,虽然重要, ...

  9. .net多线程归并排序

    一.概述 在了解排序算法的同时,想到用多线程排序减少排序的时间,所以写了一个简单的示例,加深印象.下面是具体代码 二.内容 环境:vs2017,.net  core 2.2 控制台程序. 运行时使用r ...

  10. mac OS 安装 Eclipse

    安装Eclipse前先确认你的Mac上是否已安装Java运行环境.进入终端,输入"java -version",如果返回了java版本号则说明已安装 访问Eclipse官方首页ht ...