第6讲 指针与引用 6.1 指针 6.2 引用
学习目标 理解指针的概念。 掌握指针的定义、使用。 理解指针与数组、字符串之间的关系,掌握通 过指针访问数组和字符串。 理解并掌握动态内存分配。 理解引用的概念,掌握引用变量的定义和使用。
第5讲 指针与引用 5.1.1 指针的定义与使用 5.1.2 指针与数组 5.1.3 指针与字符串 5.1.4 动态存储分配
数据的存储方式 1Byte=8bit 2000 1 2001 2002 2003 2004 2005 地址 存储单元 内存
指针的概念 指针 一个数据的地址被称为指向该数据的指针。 地址 存储单元 a的指针 2000 1 char a b的指针 2001 2002 1 char a b的指针 2001 2002 int b 2003 2004 2005 地址 存储单元
指针的概念 指针变量 指针变量的特点 存储指针的变量 与普通变量的相同点 与普通变量的不同点 x p x 2000 10 2000 10 int型变量 指针变量 int型变量 指针变量的特点 与普通变量的相同点 占用存储空间 可以参与运算 与普通变量的不同点 储存的是其他变量的地址 可以间接访问数据
指针的定义 指针定义的格式为 类型标识符 * 指针变量名 用户自定义的标识符,命名规则和普通变量相同 表示所定义的变量为指针变量 指针指向的数据的类型 可以是基本数据类型,如整型、实型等 也可以是构造数据类型,如结构体、共用体等
例1: int * p1; double * p2, * p3; 注: 定义了一个指向int型数据的指针变量p1 “*”只是表示所定义的变量为指针变量,而不是指针变 量名的一部分。 一个指针变量只能指向同一种类型的数据。
指针的使用 问题: 两个常用的运算符: 如何将一个指针变量指向另一个变量? 如何通过指针变量来访问所指向的数据? & 取地址运算符 * 指针运算符
& 取地址运算符 单目运算符,其作用是返回操作对象的地址 int x=10, * p; p=&x; 定义了一个int型变量x和一个指向int型数据的指针变量p p=&x; 通过运算符“&”取得x的地址并赋给指针变量p,即指针p指向变量x
* 指针运算符 单目运算符,后面的操作数是一个指针 其作用是返回该指针所指向的对象。 int x=10, * p; p=&x; cout<<*p<<endl; cout<<x<<endl; 间接访问 直接访问 10 10
例2: void main(void) { int a, b; int *p1, *p2; p1=&a; p2=&b; cout<<a<<‘\t’<<b<<endl; cout<<*p1<<‘\t’<<*p2<<endl; } b a 100 10 指针变量赋值 通过指针对变量赋值 &b &a p1 p2 指针变量使用
例3: void main(void) { int a, b; int *p1, *p2; p1=&a; p2=&b; cout<<a<<‘\t’<<b<<endl; cout<<*p1<<‘\t’<<*p2<<endl; } b a 随机 随机 p1 p2 随机 随机 变量a,b未赋值 指针变量未赋值,即指针指向未知地址
核心概念 重点 指针 指针变量 两个运算符 是一种特殊的数据类型 提供一种直观的地址操作手段 一个数据的内存地址即为指向该数据的指针 存储指针的变量称为指针变量 实际上存放的是另一个变量的地址 两个运算符 & 取地址运算符获取变量的地址 * 指针运算符返回指针变量所指向的变量的值 重点
6.1.2 指针的定义与使用 指针的运算:可参与赋值、算术及关系运算。 算术运算 指针可以进行的算术运算有如下几种: 6.1.2 指针的定义与使用 指针的运算:可参与赋值、算术及关系运算。 算术运算 指针可以进行的算术运算有如下几种: p+n p-n p++ p-- ++p --p p-q 地址的运算,先使n乘以一个比例因子,再与地址进行加减运算。该比例因子就是指针所指向变量的数据类型在存储空间实际所占字节数 p+n实际为p+n*比例因子
6.1.2 指针的定义与使用 指针的运算:可参与赋值、算术及关系运算。 关系运算 必须指向同一连续存储空间。 例如: int a; 6.1.2 指针的定义与使用 指针的运算:可参与赋值、算术及关系运算。 关系运算 必须指向同一连续存储空间。 指针的关系运算一般在指向相同类型变量的指针之间进行,表示它们所指向的变量在内存中的位置关系。 例如: int a; int * p1=&a; * p2=p1; 两指针做p1==p2关系运算,其结果为1(true)
指针使用举例 输入a, b两个整数,按大小输出这两个数 void main(void) { int *p1, *p2, *p, a,b; cin>>a>>b; p1=&a; p2=&b; if (a<b) { p=p1; p1=p2; p2=p; } cout<<a<<‘\t’<<b<<endl; cout<<*p1<<‘\t’<<*p2<<endl; } a b 10 100 交换地址 &a &b &a &b &a p1 p2 p 10 100 100 10 虽然变量不变,但指向变量的指针发生变化
第6讲 指针与引用 6.1.1 指针的概念 6.1.2 指针的定义与使用 6.1.3 指针与数组 6.1.4 指针与字符串 第6讲 指针与引用 6.1.1 指针的概念 6.1.2 指针的定义与使用 6.1.3 指针与数组 6.1.4 指针与字符串 6.1.5 动态存储分配
6.1.3 指针与数组 数组的指针 数组元素的指针 数组元素可以用两种方式访问: 下标访问; 指针访问 6.1.3 指针与数组 数组的指针 数组元素的指针 数组元素可以用两种方式访问: 下标访问; 指针访问 数组与变量一样,在内存中占据单元,有地址,一样可以 用指针来表示。 C++规定:数组名就是数组的起始地址;又规定:数组的 指针就是数组的起始地址。数组元素的指针就是数组元素 的地址。
6.1.3 指针与数组 一维数组与指针 指向数组的指针变量、指向数组元素的指针变 量的定义方式与普通的指针变量的定义方式一 致。 6.1.3 指针与数组 一维数组与指针 指向数组的指针变量、指向数组元素的指针变 量的定义方式与普通的指针变量的定义方式一 致。 int a[10]; int *p; p=&a[0]; p=a; 由于C++语言规定数组名代表数组的首地址,也就是数组第一个元素的地址。
指向数组元素的指针变量的定义与赋值 int a[10], *p; p=&a[0]; a[0] p=a; a[1] a[2] 数组第一个元素的地址 2024H 2020H 201CH 2018H 2014H 2010H 200CH 2008H 2004H 2000H a[9] a[8] a[7] a[6] a[5] a[4] a[3] a[2] a[1] a[0] a p &a[0] 直接用数组名赋值 p是变量,a为常量。 若数组元素为int型,则指向其的指针变量也应定义为int型。 这两种情况均为赋初值 int a[10]; int *p=a; int *p=&a[0];
通过指针使用数组元素 int a[10]; int *p=a; *p=1; C++规定,p+1指向数组的下一个元素,而不是下一个字节。 2024H 2020H 201CH 2018H 2014H 2010H 200CH 2008H 2004H 2000H a[9] a[8] a[7] a[6] a[5] a[4] a[3] a[2] a[1] a[0] a p &a[0] 1 int a[10]; int *p=a; 2 为指针变量赋初值 通过指针变量为数组元素赋值 a[0]=1; *p=1; C++规定,p+1指向数组的下一个元素,而不是下一个字节。 *(p+1)=2; a[1]=2; *++p=2; p=p+1; *p=2; p=2004H 指针变量也重新赋值
p+i 或 a+i 均表示 a[i] 的地址 &a[i] 2024H 2020H 201CH 2018H 2014H 2010H 200CH 2008H 2004H 2000H a[9] a[8] a[7] a[6] a[5] a[4] a[3] a[2] a[1] a[0] a p &a[0] 1 *(a+1)=2; 2 *(a+1)与a[1]等同。 p+i 或 a+i 均表示 a[i] 的地址 &a[i] *(a+i) a[i] *(p+i) p[i]
用指向数组的指针变量输出数组的全部元素 void main(void) void main(void) { int a[10], i; int *p=a; for (i=0; i<10; i++) cin>>a[i]; cout<<*p++<<‘\t’; } void main(void) { int a[10], i; int *p; for (i=0; i<10; i++) cin>>a[i]; for (p=a; p<a+10; p++) cout<<*p<<‘\t’; } 输入数组元素 指针变量赋初值 指向下一元素 *p, p=p+1 输出指针指向的数据 输出数据后指针加1
例: void main(void) { int x[ ]={1,2,3}; int s, i, *p; s=1; p=x; for (i=0; i<3; i++) s*=*(p+i); cout<<s<<endl; } p p+1 p+2 i=0 s=s*(*(p+0)) =s*1=1 i=1 s=s*(*(p+1)) =s*2=2 i=2 s=s*(*(p+2)) =s*3=6 6
多维数组与指针 a 多维数组与指针 一维数组名! a[0] a[0][0] a[0][1] a[0][2] a[0][3] a[1]
多维数组与指针 int a[3][4]; //首地址为2000H 用指针变量也可以指向多维数组,表示的同样是多维数组的首地址。 int a[3][4]; //首地址为2000H 2000H 2008H 2010H 2014H 201cH 2020H 2028H 202cH a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] a a[2] a[1] a[0] a[2][3] a[2][2] a[2][1] a[2][0] a[1][3] a[1][2] a[1][1] a[1][0] a[0][3] a[0][2] a[0][1] a[0][0] 200CH 2008H 2004H 2000H 2000H 2010H 2020H 可以将a数组看作一个一维数组,这个一维数组的每个元素又是一个具有4个int型数据的一维数组,这样,我们就可以利用一维数组的概念来标记一些写法。
6.1.3 指针与数组 多维数组与指针 a[0]=&a[0][0] int a[3][4] a[1]=&a[1][0] 6.1.3 指针与数组 多维数组与指针 a[0]=&a[0][0] int a[3][4] a[1]=&a[1][0] a[2]=&a[2][0] a代表二维数组的首地址 a=&a[0][0]
6.1.3 指针与数组 5.1.3 指针与数组 多维数组与指针 1、*(a+0)=*a=a[0]=&a[0][0]。 6.1.3 指针与数组 5.1.3 指针与数组 多维数组与指针 1、*(a+0)=*a=a[0]=&a[0][0]。 2、*(a+0)+1=*a+1=a[0]+1=&a[0][1]。 3、*(a+i)+j=a[i]+j=&a[i][j]。 4、a[i][j]=*(a[i]+j) =(*(a+i))[j] = *(*(a+i)+j)
6.1.3 指针与数组 多维数组与指针 定义指向二维数组的指针,需要将指针定义 成一个指向一维数组的指针。 6.1.3 指针与数组 多维数组与指针 定义指向二维数组的指针,需要将指针定义 成一个指向一维数组的指针。 int a[3][4]; int (*p)[4]; p=a; 课本P96,例5.3 列数要相同! p和a的值具有相同的指针类型, 均为 int(*)[4]类型
第6讲 指针与引用 6.1.1 指针的概念 6.1.2 指针的定义与使用 6.1.3 指针与数组 6.1.4 指针与字符串 第6讲 指针与引用 6.1.1 指针的概念 6.1.2 指针的定义与使用 6.1.3 指针与数组 6.1.4 指针与字符串 6.1.5 动态存储分配
6.1.4 指针与字符串 字符串的指针:字符串在内存中的起始地 址。 可以定义一个字符型指针变量来指向一个 字符串。 例如: 6.1.4 指针与字符串 字符串的指针:字符串在内存中的起始地 址。 可以定义一个字符型指针变量来指向一个 字符串。 例如: char str1[10]=“abcdefg”; char *p1=str1; char *p2=“Welcome to Beijing!”;
6.1.4 指针与字符串 字符串的表示形式 string 1、用字符数组实现 I l o v e C h i n a \0 6.1.4 指针与字符串 数组首地址 字符串的表示形式 string 1、用字符数组实现 I l o v e C h i n a \0 void main(void ) { char string[ ]=“I love China”; cout<<string; } string为数组名,代表数组的首地址,是常量。
string=“I love China”; 6.1.4 指针与字符串 \0 a n i h C e v o l I string char string[20]; string=“I love China”; 错误!常量不能赋值 strcpy(string, “I love China”); 正确赋值形式 cin.getline(string); //从键盘输入
6.1.4 指针与字符串 2、用字符指针表示字符串 指针变量 void main(void) 6.1.4 指针与字符串 2、用字符指针表示字符串 指针变量 void main(void) { char *string=“I love China”; cout<<string; } 字符串常量 I l o v e C h i n a \0 string 将内存中字符串常量的首地址赋给一个指针变量
内存中的指向: string[0] string string string[1] string[12] H e l o W r d ! \0 H e l o W r d ! \0 string[1] string[12]
例: main() {char string[15]=“I love China!”; cout<<string<<endl; } 结果: {char * string=“I love China!”; cout<<* string<<endl; } 结果: I love China! I I love China!
例: main() {char a[]="I am a boy", b[20], * p1, * p2; int i; p1=a; p2=b; for (;*p1!='\0'; p1++, p2++) *p2=*p1; *p2='\0'; cout<<"string a is:"<<a<<endl; cout<<"string b is:"<<b<<endl; } I am a boy I am a boy
{ char a[ ]=“I am a boy”, b[20]; char *p1, *p2; int i; p1=a; p2=b; void main(void) { char a[ ]=“I am a boy”, b[20]; char *p1, *p2; int i; p1=a; p2=b; for(; *p1!=‘\0’; p1++,p2++) *p2=*p1; *p2=‘\0’; cout<<a<<endl; cout<<b<<endl; } *p2=*p1 a I a m b o y \0 必须以\0结束 p1 p1 p1 a a+1 y b I \0 p2 p2 p2 b b+1
第5讲 指针与引用 5.1.1 指针的概念 5.1.2 指针的定义与使用 5.1.3 指针与数组 5.1.4 指针与字符串 第5讲 指针与引用 5.1.1 指针的概念 5.1.2 指针的定义与使用 5.1.3 指针与数组 5.1.4 指针与字符串 5.1.5 动态存储分配
静态存储空间的开辟 在定义变量或数组的同时即在内存为其开辟了指定的固定空间。 一经定义,即为固定地址的空间,在内存不能被别的变量所占用。 int n, a[10]; char str[100]; 在程序内我们有时需要根据实际需要开辟空间,如输入学生成绩,但每个班的学生人数不同,一般将人数定得很大,这样占用内存。
静态存贮空间的开辟 无论班级中有多少个学生,程序均在内存中开辟100×5个实型数空间存放学生成绩,造成内存空间的浪费。 #define N 100 ...... float score[N][5]; cin>>n; for(int i=0;i<n;i++) for(j=0;j<5;j++) cin>>score[i][j]; 无论班级中有多少个学生,程序均在内存中开辟100×5个实型数空间存放学生成绩,造成内存空间的浪费。
5.1.5 动态存储分配 在实际编写程序时,需要使用的数组大小 并不是固定的。 例如: 5.1.5 动态存储分配 在实际编写程序时,需要使用的数组大小 并不是固定的。 例如: int score[40]; //存某班学生的某门课成绩 通常不同班级的人数是不一样的! C++语言提供动态存储分配方法。
6.1.5 动态存储分配 静态存储分配:编译器在编译程序时,对 程序中变量、数组等分配内存空间,这种 分配方式。 6.1.5 动态存储分配 静态存储分配:编译器在编译程序时,对 程序中变量、数组等分配内存空间,这种 分配方式。 动态存储分配:在程序运行期间,根据实 际需要随时申请内存空间,这种分配方式 。所分配空间不需要时,要释放。 C++中用new和delete运算符实现动态存储分 配。在C语言中用malloc和free。
如何动态分配空间? new 数据类型[单位数]; new int[4]; int i; cin>>i; new int[i]; int n; cin>>n; float score[n][5]; 错误!数组的维数必须是常量 利用 new 运算符可以在程序中动态开辟内存空间。 new 数据类型[单位数]; new int[4]; int i; cin>>i; new int[i]; 在内存中开辟了4个int型的数据空间,即16个字节
动态分配一个整型内存单元,并将其初始化为10 6.1.5 动态存储分配 new的使用 格式1: 指针变量= new 类型标识符(初始值) 例如: int * p1=new int (10); double * pd; pd=new double; 作用:从堆中为程序分配指定数据类型所需的内存单元。若分配成功,则返回其首地址;否则,返回一个空指针。 动态分配一个整型内存单元,并将其初始化为10
6.1.5 动态存储分配 new的使用 格式2: 例:int * p=new int[5] //申请存储长度为5的一个整型数组的空间 6.1.5 动态存储分配 new的使用 格式2: 指针变量 = new 数据类型[下标表达式] 例:int * p=new int[5] //申请存储长度为5的一个整型数组的空间 作用:动态申请一个数组空间。分配成功, 则返回所分配的空间的首地址;不成功,则 返回空指针。
动态开辟空间的收回 用 new 运算符分配的空间,不能在分配空间时进行初始化。 例如: int *p=new int[5]={1,2,3,4,5} 错误!不可初始化 同样,用new开辟的内存单元如果程序不“主动”收回,那么这段空间就一直存在,直到重新开机为止。 delete运算符用来将动态分配到的内存空间归还给系统,使用格式为: delete p;
例: 课本P100,例5.5 int *point=new int(10); ...... delete point; delete也可以收回用new开辟的连续的空间。 int *point; cin>>n; point=new int[n]; ....... delete [ ]point; 只有用new开辟的空间才能用delete收回。 课本P100,例5.5
第6讲 指针与引用 6.1 指针 6.2 引用
6.2 引用的定义 引用的定义: 格式 例如: int a=10; int &b=a; 对变量起另外一个名字 (外号),这个名字称为该变量的 引用。 格式 <类型> &<引用变量名> = <原变量名>; 其中原变量名必须是一个已定义过的变量。如: int max ; int &refmax=max; 例如: int a=10; int &b=a; “&”表示所定义的变量为引用变量,不是取地址运算符“&” 其中,refmax并没有重新在内存中开 辟单元,只是引用max的单元。max 与refmax在内存中占用同一地址,即 同一地址两个名字。
6.2 引用的定义 <类型> &<引用变量名> = <原变量名>; 引用变量定义后不能再指向其它变量 <类型> &<引用变量名> = <原变量名>; 引用变量定义后不能再指向其它变量 定义引用的同时对其进行初始化指向一个已经定义并类型相同的变量。
引用的使用 int v,k,h; int &rv=v; rv=3; //引用 v=5; k=rv+2; //此时v的值也同时变成了3。 rv=h; v=?; k=? //引用 //此时v的值也同时变成了3。 //此时k=5+2=7。 引用是C++的一种新的变量类型,是对C语言的重要扩充。它的作用是为变量起一个别名。当声明一个引用时,应该把它初始化为另一个对象名,即目标变量。从这时起,引用就成为目标的替代名,所有对引用的操作都相当于对目标变量的操作。下面通过一个实例来说明别名和变量的关系。 v=12; k=7
引用的使用 max 5 20 10 refmax int max ; int &refmax=max; max=5 ; refmax=10; refmax=max+refmax;
引用的几点说明 1、引用在定义的时候要初始化。 int &refmax; int &refmax=max; 错误,没有具体的引用对象 int &refmax=max; max是已定义过的变量 2、对引用的操作就是对被引用的变量的操作。 3、 引用类型变量的初始化值不能是一个常数。 如:int &ref1 = 5; // 是错误的。 int &ref=i;
指针与引用的比较 共同点: 不同点: 它们都是对某个内存空间进行间接操作的手段。 指针是独立的,而引用是不独立的。指针有自己 的内存空间存放指针值,而引用只是一个符号而 已。 引用必须初始化,而一旦被初始化后不得再作为 其它变量的别名。
总结 掌握指针的定义和使用 掌握 “&”和“*”的作用 掌握利用指针对数组进行操作 掌握利用指针对字符串进行操作 掌握引用的定义和使用 掌握new和delete的使用
作业: 用指针重新改写上节课的作业,实现矩阵的乘 法和输出。