---
config:
look: handDrawn
themeVariables:
fontSize: 28px
---
mindmap
Root(变量)
变量名:标识符
类型:数据类型
值:存储的数据
地址:内存中的位置
作用域:变量的可见范围
生存周期:变量的有效时间
《计算机程序设计》
计算机学院计算机研究所编译系统研究室
~/course~/course/main.cppmain.cpp,随堂编程练习的代码请直接在此文件中编辑考考你
阅读下列程序,判断程序输出是什么?
#include <iostream>
#include <cstdlib>
double range(double numbers[], int n) {
double ub = numbers[0], lb = numbers[0];
for (int i = 1; i < n; i++) {
if (numbers[i] > ub) ub = numbers[i];
if (numbers[i] < lb) lb = numbers[i];
}
return ub - lb;
}
int main() {
int n;
std::cin >> n;
double numbers[n];
for (int i = 0; i < n; i++)
numbers[i] = (double)std::rand() / RAND_MAX; // 生成随机数
std::cout << range(numbers, n) << std::endl;
return 0;
}编程求解
C++是一种静态强类型语言,可否在C++中实现一个“动态类型”,即支持在同一内存空间中存储不同类型的数据?
dynamictype.cpp
#include <iostream>
void writeI(void *buffer, int i) { /* store i to buffer */ }
void writeD(void *buffer, double d) { /* store d to buffer */ }
void writeC(void *buffer, char c) { /* store c to buffer */ }
int main() {
int type;
char buffer[sizeof(double)];
int i; double d;
std::cin >> type;
// read in a value and write the value to buffer according to type
switch (type) { // 0: int, 1: double
case 0: std::cin >> i; writeI(buffer, i); break;
case 1: std::cin >> d; writeD(buffer, d); break;
case 2: std::cin >> c; writeC(buffer, c); break;
default: std::cerr << "Invalid type" << std::endl; return 1;
}
return 0;
}学习目标
指针是C/C++语言的精华!
思考
地址的本质就是一个整数,那么能否把变量的地址存储在另一个变量中?
---
config:
look: handDrawn
themeVariables:
fontSize: 28px
---
mindmap
Root(变量)
变量名:标识符
类型:数据类型
值:存储的数据
地址:内存中的位置
作用域:变量的可见范围
生存周期:变量的有效时间
两个问题
&&可以获取变量的地址
&a是一个表达式,其值即为变量a的地址考考你
地址本质上就是一个整数,那么&a可否赋值给一个整型变量?
code/intaddr.cpp: In function ‘int main()’:
code/intaddr.cpp:3:14: error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
3 | int addr = &a;
| ^~
| |
| int*
教材7.2.1节第1点
明察秋毫
&a的类型为int *,这就是指针类型!指针,是带有类型的地址
考考你
使用指针来存储地址,与使用整型变量有什么区别?
明察秋毫
关键的区别在于指针带有类型!通过指针,可以准确地刻画一块内存区域的性质:首地址+长度+对齐
教材7.1节
指针定义
考考你
若上述代码第5行的p4前忘记加*,会发生什么?
教材7.1节
qint *&b---
config:
look: handDrawn
themeVariables:
fontSize: 28px
---
mindmap
Root(变量)
变量名:标识符
类型:数据类型
值:存储的数据
地址:内存中的位置
作用域:变量的可见范围
生存周期:变量的有效时间
考考你
指针本身的大小与对齐与什么有关?与它指向的类型有关吗?
ptrstorage.cpp
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int i = 0;
int *pi = &i;
double *pd = nullptr;
cout << left << setw(16) << "pointer" << setw(16) << "address" << setw(8) << "size" << setw(8) << "align" << endl;
cout << setw(16) << pi << setw(16) << &pi << setw(8) << sizeof(int *) << setw(8) << alignof(int *) << endl;
cout << setw(16) << pd << setw(16) << &pd << setw(8) << sizeof(double *) << setw(8) << alignof(double *) << endl;
return 0;
}pointer address size align
0x7ffe256c55d4 0x7ffe256c55d8 8 8
0 0x7ffe256c55e0 8 8
明察秋毫
指针变量本身也需要内存空间,其大小与对齐与指向的类型无关!在32/64位系统中,指针变量大小固定为4/8字节
char *c;int *p = &a;double *q = nullptr;---
config:
look: handDrawn
themeVariables:
fontSize: 28px
---
mindmap
指针的内涵
取地址运算符
指针是带有*类型*的*地址*
指针变量的定义与初始化
指针的存储
测一测
表达式&(a+b)中取地址运算符的使用是否恰当?
*p保存了变量a的地址,称指针p指向变量a*,指针p可以访问其指向的变量,称为间接访问indaccess.cpp
10
20
考考你
间接访问的“间接”体现在哪里?
明察秋毫
教材7.2.1节第2点
*与取地址运算符&互为逆运算
*(&a)等价于a&(*p)等价于p明察秋毫
*与&优先级相同,故*(&a)中的括号可以省略
考考你
空指针是值为nullptr(0地址)的指针,野指针是指向未知内存的指针,使用它们会发生什么?
invalidptr.cpp
0x7f63f14a6934 0
/bin/bash: line 1: 21011 Segmentation fault (core dumped) ./code/invalidptr 2>&1
避坑
C/C++中最常见也是最致命的软件BUG之一:通过空指针或野指针访问内存!
nullptr)
nullptrnullptr考考你
能否使用一个double *指针指向一个int变量?
code/ptrcast.cpp: In function ‘int main()’:
code/ptrcast.cpp:4:16: error: cannot convert ‘int*’ to ‘double*’ in initialization
4 | double *pd = &a;
| ^~
| |
| int*
明察秋毫
int *和double *是两种不同的指针类型,彼此不能隐式转换考考你
下列程序可以通过编译,请预测输出内容
explicitptrcast.cpp
ptrarith.cpp
#include <iostream>
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p1 = &arr[0]; // p1指向arr的低一个元素
int *p3 = p1 + 5; // p3由p1向高地址移动5个int得到
int *p2 = p3 - 4; // p2由p3向低地址移动4个int得到
std::cout << p1 << ' ' << p2 << ' ' << p3 << std::endl;
std::cout << *p1 << ' ' << *p2 << ' ' << *p3 << std::endl;
std::cout << p2 - p1 << ' ' << p1 - p2 << std::endl; // p2和p1之间的距离,以int为单位
return 0;
}| 表达式 | 类型 | 含义 |
|---|---|---|
p + n |
指针 | 指向p之后n个元素的指针 |
p - n |
指针 | 指向p之前n个元素的指针 |
p1 - p2 |
整数 | p1和p2之间的元素个数 |
p++/p-- |
指针 | 改变p的值使其指向后一个元素,返回更新前的指针 |
++p/--p |
指针 | 改变p的值使其指向后一个元素,返回更新后的指针 |
p+=n/p-=n |
指针 | 改变p的值使其指向p之后/之前n个元素,返回更新后的指针 |
教材7.2.1节第3点
==、!=、>、<、>=、<=等关系运算ptrrel.cpp
#include <iostream>
#include <iomanip>
int main() {
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p1 = &arr[0]; // p1指向arr的低一个元素
int *p2 = p1 + 5; // p3由p1向高地址移动5个int得到
std::cout << std::boolalpha << (p1 < p2) << ' ' << ( p1 > p2) << std::endl;
std::cout << (p2 == &arr[5]) << ' ' << (p2 != p1) << std::endl;
return 0;
}ptrparam.cpp
*:间接访问
&互逆---
config:
look: handDrawn
themeVariables:
fontSize: 20px
---
mindmap
使用指针
解引用运算符
空指针与野指针
指针运算
算术
关系
指针参数
测一测
判断下列程序的输出
ptrquiz.cpp
17
编程求解
C++是一种静态强类型语言,可否在C++中实现一个“动态类型”,即支持在同一内存空间中存储不同类型的数据?
dtypemain.cpp
#include <iostream>
#include "dtype.h"
int main() {
int type;
char buffer[sizeof(double)];
int i; double d;
std::cin >> type;
// read in a value and write to buffer according to type
switch (type) { // 0: int, 1: double
case 0: std::cin >> i; writeI(buffer, i); break;
case 1: std::cin >> d; writeD(buffer, d); break;
}
// read out the value from buffer according to type
switch (type) {
case 0: std::cout << readI(buffer) << std::endl; break;
case 1: std::cout << readD(buffer) << std::endl; break;
}
return 0;
}dtype.h
dtype.cpp
---
config:
look: handDrawn
themeVariables:
fontSize: 20px
---
mindmap
指针
指针的内涵
取地址运算符
指针的类型
指针的存储
指针的六个属性
使用指针
解引用运算符
空指针与野指针
指针运算
算术
关系
指针参数
指针应用
动态类型
学习目标
计算机程序设计
