--- config: look: handDrawn themeVariables: fontSize: 28px --- mindmap Root(变量) 变量名:标识符 类型:数据类型 值:存储的数据 地址:内存中的位置 作用域:变量的可见范围 生存周期:变量的有效时间
《计算机程序设计》
计算机学院计算机研究所编译系统研究室
~/course
~/course/main.cpp
main.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节
q
int *
&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
)
nullptr
nullptr
考考你
能否使用一个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 指针 指针的内涵 取地址运算符 指针的类型 指针的存储 指针的六个属性 使用指针 解引用运算符 空指针与野指针 指针运算 算术 关系 指针参数 指针应用 动态类型
学习目标
计算机程序设计