Presentation is loading. Please wait.

Presentation is loading. Please wait.

第 6 章 第 6 章 指 针 指 针 1.

Similar presentations


Presentation on theme: "第 6 章 第 6 章 指 针 指 针 1."— Presentation transcript:

1 第 6 章 第 6 章 指 针 指 针 1

2 教学内容 什么是指针 变量与指针 数组与指针 字符串与指针 6.5 堆内存分配(补充) 6.1 6.2 6.3 6.4 6.5

3 教学内容 返回指针值的函数 指针数组和指向指针的指针* const指针* void指针类型* 指针数据类型和运算的小结 引用 6.6 6.7
6.8 const指针* 6.9 void指针类型* 6.10 指针数据类型和运算的小结 6.11 引用

4 6.1 什么是指针 4

5 6.1 指针的概念 内存中每个字节有一个编号-----地址 内存 程序中: int i; float k; … 2000 2001 i
程序中: int i; float k; 2000 2001 i 2002 2003 2004 2005 k 2006 2007 2008

6 6.1 指针的概念 在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值的存取都是通过地址进行的。例如: 2000 2001 2002 2006 2003 i k 2004 2007 2008 2005 100 10 i=10; k=k+1; mov add 2.23 1.23 这种按变量地址存取变量值的方式称为直接存取方式,或直接访问方式。

7 如果有一个变量是专门用来存放另一变量地址(即指针)的,则它称为指针变量。
6.1 指针的概念 …... 2000 2012 2016 整型变量i 100 变量 i_pointer 2004 变量的值 一个变量的地址称为该变量的指针 2000 2000 如果有一个变量是专门用来存放另一变量地址(即指针)的,则它称为指针变量。

8 通过一个指针变量中存放的地址,对该地址处存放的另一个变量进行访问,就称为间接访问。
6.1 指针的概念 指针变量 变量地址(指针) 地址存入 指针变量 指向 变量 变量值 通过一个指针变量中存放的地址,对该地址处存放的另一个变量进行访问,就称为间接访问。

9 6.2 变量与指针 9

10 6.2.1 定义指针变量 定义指针变量的一般形式为 基类型 *指针变量名; 说明: 基类型定义了指针变量可以指向的变量的数据类型
在变量名前加一个*表示该变量是指针变量

11 通过&操作符可以获取一个变量的地址,将这个变量地址赋给一个指针变量,就说指针变量指向了变量。
6.2.1 定义指针变量 通过&操作符可以获取一个变量的地址,将这个变量地址赋给一个指针变量,就说指针变量指向了变量。 例: int *p; int num=18; p=# 注意:指针变量只能 指向定义时基类型规 定类型的变量。 …… 1234 2df0 int * p; 2df0 18 int num;

12 6.2.1 定义指针变量 一般的C++编译系统为每一个指针变量分配4个字节的存储单元,用来存放其他变量的地址。 定义指针变量时还要注意:
不能用一个整数给一个指针变量赋初值。只能通过&操作符取得某个变量的地址赋给指针变量。例:int *pointer_1=2000; //错误 (2) 在定义指针变量时必须指定基类型。例: int *pointer_1; //定义一个指向整型数据的指针变量 float *pointer_2; //定义一个指向单精度型数据的指针变量

13 6.2.2 引用指针变量 *放在指针定义中,称为指针定义符,放在可执行语句中的指针前,称为指针运算符,或称为间接访问运算符。
间接引用指针时,可获得由该指针指向的变量内容,如下代码: int *p; // *为指针定义符 int num=18; p=&num; cout<<*p<<endl; //*为间接访问运算符,*p == num cout<<*num<<endl; //wrong,*不能放非指针变量前 对于以上代码有: num = *p = *(&num) p = &num = &(*p)

14 例 题 例6.1 通过指针变量访问整型变量。 int main( ) { int a,b; int *pointer_1,*pointer_2; a=100;b=10; pointer_1=&a; pointer_2=&b; cout<<a<<″ ″<<b<<endl; cout<<*pointer_1<<″ ″<<*pointer_2<<endl; //输出pointer_1和pointer_2指向的变量,也就是a和b的值 return 0; }

15 例 题 例6.2 输入a和b两个整数,按先大后小的顺序输出a和b(用指针变量处理)。 int main( )
{ int *p1,*p2,*p,a,b; cin>>a>>b; p1=&a; p2=&b; if(a<b) {p=p1;p1=p2;p2=p;} cout<<″a=″<<a<<″ b=″<<b<<endl; cout<<″max=″<<*p1<<″ min=″<<*p2<<endl; return 0; }

16 6.2.2 引用指针变量 注意:指针变量必须先赋值,再使用 重要数据 例1: main( ) { int i=10,k; int *p;
p=&k; *p=i; cout<<i<<k<<*p; } …... 2000 2004 2006 2005 整型变量i 10 指针变量p 2001 2002 2003 随机 重要数据 例2: main( ) { int i=10; int *p; *p=i; cout<<i<<*p; } 危险!

17 函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。
6.2.3 指针作为函数参数 函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型。 如果定义一个函数的参数为指针类型,那么调用这个函数时,必须将一个变量的地址作为实参传送给被调用函数的形参。

18 6.2.3 指针作为函数参数 例:编写函数swap实现交换两个变量的值。
void swap(float a,float b); int main( ) { float num1,num2; cin>>num1>>num2; cout<<"before swap:"<<endl; cout<<"num1="<<num1<<" num2="<<num2<<endl; swap(num1,num2); cout<<"after swap:"<<endl; return 0; } void swap(float a,float b) { float temp; temp=b; b=a; a=temp; cout<<"a="<<a<<" b="<<b<<endl; 说明:实参与形参各自占据不同的内存空间,函数调用时,实参将值传给形参后二者便再无关联,swap函数只交换了形参的值,对实参无影响

19 6.2.3 指针作为函数参数 将参数定义为指针便可实现两数交换。 void swap(float *a,float *b);
int main( ) { float num1,num2; cin>>num1>>num2; cout<<"before swap:"<<endl; cout<<"num1="<<num1<<" num2="<<num2<<endl; swap(&num1,&num2); cout<<"after swap:"<<endl; return 0; } void swap(float *a,float *b) { float temp; temp=*b; *b=*a; *a=temp; cout<<"a="<<*a<<" b="<<*b<<endl;

20 6.2.3 指针作为函数参数 a num1 temp=*b; *b=*a; *a=temp; 10 &num1 b num2 20
为什么将参数定义成指针就可以实现两数的交换? 思考:如果将swap内部代码改成以下代码行不行 float *temp=b; b=a; a=temp; a num1 temp=*b; *b=*a; *a=temp; 10 &num1 b num2 20 &num2 说明:不行,这样只是交换了两个形参的值,等于交换了两个行参的指向,但它们指向的两个实参本身没有发生交换

21 例6.3: 对输入的两个整数按大小顺序输出 int main( ) { void swap(int *p1,int *p2); int *pointer_1,*pointer_2,a,b; cin>>a>>b; pointer_1=&a; pointer_2=&b; if(a<b) swap(pointer_1,pointer_2); cout<<″max=″<<a<<″ min=″<<b<<endl; return 0; } void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp;

22 例6.4 输入a,b,c 3个整数,按由大到小的顺序输出。
int main( ) { void exchange(int *,int *,int *); int a,b,c,*p1,*p2,*p3; cin>>a>>b>>c; p1=&a;p2=&b;p3=&c; exchange(p1,p2,p3); cout<<a<<″ ″<<b<<″ ″<<c<<endl; } void swap(int *pt1,int *pt2) { int temp; temp=*pt1; *pt1=*pt2; *pt2=temp; void exchange(int *q1,int *q2,int *q3) { if(*q1<*q2) swap(q1,q2); if(*q1<*q3) swap(q1,q3); if(*q2<*q3) swap(q2,q3);

23 由于函数只能具有一个返回值,如果需要将被调函数中的多个运行结果带回到主调函数中,就可以通过将参数定义为指针来实现。
补充例题 由于函数只能具有一个返回值,如果需要将被调函数中的多个运行结果带回到主调函数中,就可以通过将参数定义为指针来实现。 因为如果参数定义为指针,便可在被调函数中通过变量的间接访问直接对主调函数中的实参赋值,即等同于将函数的运行结果返回给了主调函数中的实参变量。

24 补充例题 举例,自定义函数max找出一个数组中值最大元素 思考,若函数max不仅要找出数组中值最大的元素,还要知道该元素的下标,如何实现?
int max(int[],int); void main(void) { int a[10]={34,74,23,75,45,15,87,46,97,26}; for(int i=0;i<10;i++) cout<<a[i]<<" "; int m=max(a,10); cout<<endl<<"数组元素的最大值为:"<<m<<endl; } int max(int arr[],int n) { int max=arr[0]; for(int i=1;i<n;i++) if(arr[i]>max) max=arr[i]; return max; 思考,若函数max不仅要找出数组中值最大的元素,还要知道该元素的下标,如何实现?

25 补充例题 int max(int[],int,int *); void main(void) { int a[10]={34,74,23,75,45,15,87,46,97,26}; int k; //定义变量k记录最大元素的下标 for(int i=0;i<10;i++) cout<<a[i]<<" "; int m=max(a,10,&k); cout<<endl<<"数组中最大元素为:a["<<k<<"],值为:"<<m<<endl; } int max(int arr[],int n,int *p ) { int max=arr[0]; for(int i=1;i<n;i++) if(max>arr[i]) { max=arr[i]; *p=i; //通过间接访问变量给main中的实参k赋值 return max;

26 习 题 void f(int s[],int *y) { static int t=3; *y=s[t]; s[t]*=2; t--; } void main() { int a[]={1,2,3,4},i,x=0; for(i=0;i<4;i++) { f(a,&x); cout<<x<<endl; for(i=0;i<4;i++) cout<<a[i]<<'\t'; cout<<'\n';

27 6.3 数组与指针 27

28 6.3.1 指向数组元素的指针 指针变量既可以指向变量,也可以指向数组元素(把某一元素的地址存放到一个指针变量中)。
数组元素的地址就是数组元素的指针。 若要定义一个指针变量指向一个数组的首地址,可以如下定义: int a[10]; int *p; p=&a[0]; //将元素a[0]的地址赋给指针变量p,使p指向a[0] C++中,数组名代表数组中第1个元素的地址,因此第三行代码也可以这样写: p=a;

29 6.3.1 指向数组元素的指针 可以通过指针来引用数组元素。例: int a[10]; int *p=a;
如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。(注意,不是将p中存放的地址进行加1操作)。例如,一个整型数组中每个元素占4个字节,则p+1意味着使p的值加4个字节,以使它指向下一个元素。

30 6.3.1 指向数组元素的指针 如果p的初值为&a[0],则:
(1) p+i和a+i就是a[i]的地址,或者说,它们指向a数组下标为i的元素。 (2) *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。例如,*(p+5)或*(a+5)就是a[5]。实际上,在编译时,对数组元素a[i]就是按*(a+5)处理的。 (3) 指向数组元素的指针变量也可带下标,如p[i]与*(p+i)等价

31 根据以上叙述,引用一个数组元素,以下两种方法都可使用: (1) 下标法,如a[i]形式;
6.3.1 指向数组元素的指针 根据以上叙述,引用一个数组元素,以下两种方法都可使用: (1) 下标法,如a[i]形式; (2) 指针法,如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量。如果已给p赋值为a,在访问数组元素时,数组名a与指针变量名p是可以换用的。

32 例6.5 输出数组中的全部元素。 cin>>*(a+i); cout<<*(a+i);
int main( ) { int a[10]; int i; for(i=0;i<10;i++) cin>>a[i]; //引用元素a[i] cout<<endl; cout<<a[i]<<″ ″; //引用元素a[i] return 0; } 1.下标法 最直观 2.指针法 3.用指针变量指向数组元素 ,*p=a; cin>>*(p+i); cin>>*(a+i); for(p=a;p<(a+10);p++) cout<<*p; cout<<*(a+i); 执行效率最高

33 6.3.1 指向数组元素的指针 在用指针变量指向数组元素时要注意: 指针变量p可以指向有效的数组元素,也可以指向数组以后的内存单元。在使用指针变量访问数组元素时,应避免通过指针访问数组以外的内存空间。例如: int a[10],*p=a; cout<<*(p+10); //输出a[10]的值

34 若使p指向数组a的首元素(即p=a),则: (1) p++(或p+=1)的作用是使p指向下一个元素,即a[1]。
6.3.1 指向数组元素的指针 若使p指向数组a的首元素(即p=a),则: (1) p++(或p+=1)的作用是使p指向下一个元素,即a[1]。 (2) *p++。由于++和*同优先级,结合方向为自右而左,因此它等价于*(p++)。作用是: 先得到p指向的变量的值(即a[0]),然后再使p的值加1。 (3) (*p)++表示将p指向的元素值加1,指针的位置不变,仍然指向a[0]。

35 6.3.1 指向数组元素的指针 续: (4) *(p++)与*(++p)作用不同。前者是先取*p值,再使p加1。后者是先使p加1,再取*p。前者返回a[0],后者返回a[1],但p随后都会指向a[1]。 (5) 如果p当前指向a[i],则: *(p--) //先对p进行“*”运算,得到a[i],再使p减1,p指向a[i-1]。 *(++p) //先使p自加1,再做*运算,得到a[i+1]。 *(--p) //先使p自减1,再做*运算,得到a[i-1]。

36 设有说明语句“int hh[4]={2,3,4},*p=hh;”,则以下选项中,存在语法错误的是()。
习 题 设有说明语句“int hh[4]={2,3,4},*p=hh;”,则以下选项中,存在语法错误的是()。 A. p++; B. hh++; C. (*p)++; D.(*hh)++; 答案:B 说明:数组名代表一个固定的地址,或者说是指针型常量,因此不能改变数组名的值。

37 6.3.2 用指针变量作函数参数接收数组地址 用指针变量作函数形参,可以接收从实参传递来的数组首元素的地址(此时,实参是数组名)。
例6.6: 将10个整数按由小到大的顺序排列。

38 6.3.2 用指针变量作函数参数接收数组地址 C++编译系统将形参数组名一律作为指针变量来处理。
数组做函数参数,实参与形参的结合,可以有以下4种形式: 实 参 形 参 数组名 数组名 数组名 指针变量 指针变量 数组名 指针变量 指针变量

39 6.3.2 用指针变量作函数参数接收数组地址 注意:实参数组名a代表一个固定的地址,或者说是指针型常量,因此不能改变a的值。而形参数组名是指针变量,不是固定的地址值,其值是可改变的,可以再被赋值。例: void print_array(int arr[],int n); void main( ) { int a[]={34,22,64,23,74,36,98,85}; int num=sizeof(a)/sizeof(int); //求数组元素个数 cout<<*(a++); //报错 print_array(a,num); } void print_array(int arr[],int n) {cout<<*(arr++); //正确,arr指向a[1] for(int i=0;i<n;i++) cout<<arr[i]<<" "; cout<<endl; }

40 习 题 void fun(int x[],int n) { int i,max,min,k,p,t; for(k=p=0,max=min=x[0],i=1;i<n;i++) { if(max<x[i])max=x[i],k=i; if(min>x[i])min=x[i],p=i; } t=x[0];x[0]=x[p];x[p]=t; if(k==0)k=p; t=x[n-1];x[n-1]=x[k];x[k]=t; } void print(int *x,int n) { for(int k=0;k<n;k++)cout<<x[k]<<endl;} void main(void) { int a[5]={12,16,19,15,11}; fun(a,5); print(a,5);

41 用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。 1. 多维数组元素的地址 设有一个二维数组a,它有3行4列。它的定义为:
6.3.3 多维数组与指针* 用指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。 1. 多维数组元素的地址 设有一个二维数组a,它有3行4列。它的定义为: int a[3][4]={{1,3,5,7},{9,11,13,15}, {17,18,21,23}}; 可以将二维数组a看做是由3个一维数组所组成的。

42 a代表的是首行的起始地址(即第a[0]行的起始地址,&a[0]),a+1代表a[1]行的首地址,即&a[1]。
6.3.3 多维数组与指针* a代表的是首行的起始地址(即第a[0]行的起始地址,&a[0]),a+1代表a[1]行的首地址,即&a[1]。 a[0]则代表一维数组a[0]中0列元素的地址,即&a[0][0]。a[1]的值是&a[1][0],a[2]的值是&a[2][0]。 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]

43 6.3.3 多维数组与指针* a[0]+1是a[0][1]元素的地址,那么,*(a[0]+1) 就是a[0][1]元素的值。由于a[0]又是和*(a+0) 等价的,因此也可用*(*(a+0)+1)表示a[0][1]元素的值。依此类推,*(a[i]+j)或*(*(a+i)+j)是a[i][j]的值.

44 2. 指向多维数组元素的指针变量* 例6.7 输出二维数组各元素的值。 int main( )
{ int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int *p; //p是基类型为整型的指针变量 for(p=a[0];p<a[0]+12;p++) cout<<*p<<″ ″; cout<<endl; return 0; }

45 2. 指向多维数组元素的指针变量* 说明: ① p是指向整型数据的指针变量,在for语句中对p赋初值a[0],也可以写成“p=&a[0][0]”,但不能写成p=a,因为a指向的不是一个元素,而是一个一维数组。 ② 循环结束的条件是“p<a[0]+12”,不能写成p<a+12,因为a+12表示指针移动12行。 ③ 执行“cout<<*p;”输出p当前所指的数组元素的值,然后执行p++,使p指向下一个数组元素。

46 6.4 字符串与指针 46

47 #include <iostream> using namespace std; int main( )
6.4 字符串与指针 C++中可以用3种方法访问一个字符串: 用字符数组存放一个字符串 #include <iostream> using namespace std; int main( ) { char str[]=″I love CHINA!″; cout<<str<<endl; return 0; }

48 6.4 字符串与指针 2. 用字符串变量存放字符串 #include <string> #include <iostream> using namespace std; int main( ) { string str=″I love CHINA!″; cout<<str<<endl; return 0; }

49 6.4 字符串与指针 3. 用字符指针指向一个字符串 #include <iostream>
using namespace std; int main( ) { char *str=″I love CHINA!″; cout<<str<<endl; return 0; } 说明:对字符指针变量str的初始化,实际上是把字符串第1个元素的地址赋给str。输出时,系统先输出str指向的第1个字符,然后使str自动加1,输出下一个字符,如此直至遇到’\0’为止。

50 //用字符串函数实现,要#include<string>
例6.10 将字符串str1复制为字符串str2 int main( ) { char str1[]="I love CHINA!",str2[20],*p1,*p2; p1=str1;p2=str2; for(;*p1!='\0';p1++,p2++) *p2=*p1; *p2='\0'; p1=str1;p2=str2;//指针返回字符串头部 cout<<"str1 is: "<<p1<<endl; cout<<"str2 is: "<<p2<<endl; return 0; } //用字符串函数实现,要#include<string> char str1[]="I love CHINA!",str2[20]; strcpy(str2,str1); cout<<"str1 is: "<<str1<<endl; cout<<"str2 is: "<<str2<<endl; //用字符串变量实现,也要#include<string> string str1="I love CHINA!",str2; str2=str1; cout<<"str1 is: "<<str1<<endl; cout<<"str2 is: "<<str2<<endl; //指针实现的精简做法 char str1[]="I love CHINA!",str2[20]; char *p1=str1; char *p2=str2; while( *p2++=*p1++); cout<<"str1 is: "<<str1<<endl; cout<<"str2 is: "<<str2<<endl;

51 设有语句:“char *p=“Hello”;”,下列语句存在语法错误的是()。 A. char &a=*p; B. char *s=*p;
习题 设有语句:“char *p=“Hello”;”,下列语句存在语法错误的是()。 A. char &a=*p; B. char *s=*p; C. char *q=p+2; D. p=p+2; 答案:B

52 习题 char st[]="Your friend!"; void f2(int); void f1(int i) { cout<<(st+i)<<endl; if(i<3) {i+=2; f2(i);} } void f2(int i) { cout<<st[i]<<endl; if(i<3) {i+=2; f1(i);} void main(void) { int i=0; f1(i);

53 6.5 堆内存分配(补充) 53

54 6.5 堆内存分配 堆是区别于栈区、全局数据区和代码区的一块内存区域。堆允许程序在运行时(而不是在编译时),动态申请某个大小的内存空间。
普通变量和数组都是在编译时便知道它们占多大内存的,如果编写程序时无法确定一个或一组数据的大小,便可以在程序运行时使用new或delete操作符从堆中动态获取或释放内存。 new和delete是C++的运算符,不用头文件声明。

55 6.5 堆内存分配 new运算符使用的一般格式为: new 类型 [变量个数] 例如:int *p = new int[10];
说明:new的作用是从堆内存中切下一块指定大小的内存(上例为40字节),将指向该内存的地址返回。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL。用户可以根据返回指针的值判断分配空间是否成功。

56 6.5 堆内存分配 delete运算符使用的一般格式为 delete [ ] 指针变量 例如:delete []p;
说明:delete的作用是将先前new分配得到的内存释放回堆以供程序的其他部分使用,delete的操作数是之前new返回的指针,不能是指向其他内存区域(堆以外)的指针。指针变量前的方括号表示是对数组空间的操作,如果释放的是普通变量的空间,则不需要加方括号。

57 举 例 *(array+count) *(array+count) int main(void)
{ int arraysize,count,*array; cout<<"please input the number of array:\n"; cin>>arraysize; if((array=new int[arraysize])==NULL)//分配堆内存 { cout<<"can't allocate more memory,terminating.\n"; return -1; } for(count=0;count<arraysize;count++) array[count]=count*2; cout<<array[count]<<" "; cout<<endl; delete[]array; //释放堆内存 return 0; } *(array+count) *(array+count)

58 6.6 返回指针值的函数 58

59 6.6 返回指针值的函数 一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即一个地址。其概念与以前类似,只是返回的值的类型是指针类型而已。返回指针值的函数简称为指针函数。 定义指针函数的一般形式为 类型名 *函数名(参数表列); 例如 int *fun(int x,int y); 注意:指针函数不能把在它内部说明的具有局部作用域的数据地址作为返回值,但可以返回堆地址、全局变量或静态变量的地址。

60 举 例 int *getint(char *str) { int value=20; cout<<str<<endl; return &value; //warning:返回局部变量地址 } int main(void) { int *pr=getint("input a value:"); cout<<*pr<<endl; return 0;

61 习题 char *str(char *p1,char *p2) { char *p=p1; while(*p) p++; *p++=' '; while(*p++=*p2++); return p1; } void main(void) { char s1[200]={"NanJing"}; char s2[200]={"is"}; char s3[]={"good"}; cout<<str(s2,s3)<<'\n'; cout<<str(s1,s2)<<'\n'; cout<<s1<<'!'<<s2<<'!'<<s3<<'\n';

62 6.7 指针数组和指向指针的指针* 62

63 如果一个数组,其元素均为指针类型数据,该数组称为指针数组,指针数组中的每一个元素相当于一个指针变量,它的值都是地址。
6.7.1 指针数组的概念* 如果一个数组,其元素均为指针类型数据,该数组称为指针数组,指针数组中的每一个元素相当于一个指针变量,它的值都是地址。 一维指针数组的定义形式为 类型名*数组名[数组长度];

64 例6.15 若干字符串按字母顺序(由小到大)输出*

65 例6.15*

66 习题 设有说明语句:char*language[]={“1234”,”5678”,”9012”,”3456”,”7890”};则表达式:*language[1]>*language[3]中,关系运算符两侧比较的是()。 A. 字符串“1234”和“9012” B. 整数5和3 C. 字符‘5’和‘3’ D. 字符串“5678”和“3456” 答案:C

67 6.7.2 指向指针的指针* 指针数组name的每一个元素是一个指针型数据(其值为地址),分别指向不同的字符串。但指针数组中的每个元素也有自己的地址,如果将指针数组中某个元素的地址赋给一个变量,这个变量就是一个指向指针的指针。 指向指针的指针这样来定义: char *(*p); 或char **p;

68 6.7.2 指向指针的指针* 例如: p1地址 a地址 a的值 指针变量p2 指针变量p1 变量a int main(void)
{ int a=10; int *p1=&a; //p1为指向变量a的指针 int **p2=&p1; //p2为指向指针p1的指针 cout<<*p1<<endl; //输出变量a的值 cout<<**p2<<endl; //输出变量a的值 } p1地址 a地址 a的值 指针变量p2 指针变量p1 变量a

69 例6.16 指向字符型数据的指针变量* int main( ) { char **p; //定义指向字符指针数据的指针变量p
char *name[]={″BASIC″,″FORTRAN″, ″C++″,″Pascal″,″COBOL″}; p=name+2; //指向第三个数组元素 cout<<*p<<endl; //输出name[2]指向的字符串 cout<<**p<<endl; //输出name[2]指向的字符串中的第一个字符 } 运行结果为 : C++ C

70 设有说明语句:int *(*funp)(int);以下叙述中正确的是()。 A. funp是函数名,其返回值为整型指针
习题 设有说明语句:int *(*funp)(int);以下叙述中正确的是()。 A. funp是函数名,其返回值为整型指针 B. funp是一个指向函数的指针,所指向函数的返回值是一个整数 C. funp是一个指向函数的指针,所指向函数的返回值为指向整数的指针 D.该语句存在语法错

71 6.8 const指针* 71

72 6.8 const指针* 1. 指向常量的指针变量 即不允许通过指针变量改变它指向的对象的值,定义这种指针变量的一般形式为:
例如: int a=12,b=15; const int *p=&a; *p=15; //非法 注意:用指向常量的指针变量只是限制了通过指针变量改变它指向的对象的值。指针值可以变,指针指向的变量的值也可以变。

73 6.8 const指针* 2.常指针 指定指针变量的值是常量,即指针变量的指向不能改变。定义这种指针变量的一般形式是:
例如: int a=3,b=4; int *const p=&a; p=&b; //非法 说明:常指针必须在定义时初始化,指定其指向。指针变量的指向不能改变,但指针变量指向的变量值可以改变。

74 6.10 有关指针的数据类型 和指针运算的小结 74

75 6.8.1 有关指针的数据类型的小结 有关指针的数据类型 定义 含义 int i; 定义整型变量 int *p; p为指向整型数据的指针变量
int a[n]; 定义整型数组a,它有n个元素 int *p[n]; 定义指针数组p,它由n个指向整型数据的指针元素组成 int (*p)[n]; p为指向含n个元素的一维数组的指针变量 int f( ); f为带回整型函数值的函数 int *p( ); p为返回一个指针的函数,该指针指向整型数据 int **p; p是一个指向指针的指针变量,它指向一个指向整型数据的指针变量

76 6.8.2 指针运算小结 (1) 指针变量加/减 一个整数 例如: p++,p--,p+i,p-i,p+=i,p-=i等。 说明:指针的运算是以指针指向的变量为单位改变指针地址的。如p+i代表这样的地址计算: p+i*d,d为p所指向的变量所占用的字节数。因此p+i指向p下面的第i个元素。 (2) 指针变量赋值 int a,*p,array[10],*p1,*p2; p=&a; //将变量a的地址赋给p p=array; //将数组array首元素的地址赋给p p=&array[i];//将数组array第i个元素地址赋给p p1=p2; //p1和p2是同类型指针变量,将p2的值赋给p1

77 6.8.2 指针运算小结 (3) 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示: p=NULL; 说明:系统已先定义了#define NULL 0,p=NULL就是使p指向地址为0的单元。这样可以使指针不指向任何有效的单元。应注意,p的值等于NULL和p未被赋值是两个不同的概念。任何指针变量或地址都可以与NULL作相等或不相等的比较,如: if(p==NULL) p=p1;

78 6.8.2 指针运算小结 (4) 两个指针变量可以相减 如果两个指针变量指向同一个数组的元素,则两个指针变量值之差是两个指针之间的元素个数。 假如p1指向a[1],p2指向a[4],则p2-p1=(a+4)-(a+1)=4-1=3。但p1+p2并无实际意义。 (5) 两个指针变量比较 若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量小于指向后面元素的指针变量,即p1<p2。注意,如果p1和p2不指向同一数组则比较无意义。

79 6.8.2 指针运算小结 (6) 只有同类型的指针才能够互相赋值,如果指针的类型不同需要进行强制类型转换。例如: int *p1; char *p2; float *p3; p1=(int *)p2; p2=(char *)p3; p3=(float *)p1; 但尽量不要进行这样的赋值操作。

80 习 题 int fun(char *s) { char *p=s; while(*p) p++; return (p-s); } void main() { char s1[]={"China"}; char s2[]={"Apple\nPear\nCat\0Bye"}; cout<<sizeof(s1)<<'\t'<<fun(s1)<<endl; cout<<sizeof(s2)<<'\t'<<fun(s2)<<endl;

81 习题 void mystrcat(char *p1,char *p2,int len=-1) { while(*p1 != '\0') p1++; if(len==-1) len=strlen(p2); while(len--) *p1++=*p2++; *p1='\0'; } void main(void) { char s1[80]="abcde",s2[80]="fghij"; mystrcat(s1,s2,3); cout<<s1<<endl; strcpy(s1,"12345"); mystrcat(s1,s2); cout<<s1<<endl;

82 6.11 引用 82

83 6.9.1 什么是变量的引用 引用就是为变量起一个别名。
建立引用时,程序用另一个变量或对象的名字初 始化它,从那时起,引用便可以作为目标的别名 而使用,对引用的改动实际就是对目标的改动。 假如有一个变量a,想给它起一个别名b,可以这样写: int a; //定义a是整型变量 int &b=a; //声明b是a的引用 说明:声明了b是a的引用,b便可看做是a的别名,a或b都代表同一变量。 注意: &是引用声明符,不是取地址。 b和a占内存中的同一个存储单元,它们具有同一地址。

84 6.9.1 什么是变量的引用 在声明一个引用类型变量时,必须同时使之初始化,即声明它代表哪一个变量。
在声明变量b是变量a的引用后,在它们所在函数执行期间,该引用类型变量b始终与其代表的变量a相联系,不能再作为其他变量的引用。下面的用法不对: int a1,a2; int &b=a1; int &b=a2; //不能使b又变成a2的引用 引用不是一种独立的数据类型,不能定义引用数组,不能定义指向引用的指针。

85 6.9.2 引用的简单使用 例6.17 引用和变量的关系。 int main( ) { int a=10; int &b=a; a=a*a; cout<<a<<setw(6)<<b<<endl; b=b/5; cout<<b<<setw(6)<<a<<endl; return 0; } 运行记录如下: (a和b的值都是100) (a和b的值都是20)

86 6.9.3 引用作为函数参数 C++增加引用机制的主要目的是想把它作为函数参数,以扩充函数传递数据的功能。
到目前为止,函数参数传递有两种情况: (1) 将变量名作为实参和形参。这时传给形参的是变量的值,传递是单向的。如果在执行函数期间形参的值发生变化,并不传回给实参。因为在调用函数时,形参和实参不是同一个存储单元。 (2) 传递变量的指针。形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。这样通过形参指针变量便能访问主函数中的实参变量,并改变它们的值。 C++提供了向函数传递数据的第(3)种方法,即传送变量的别名。

87 例6.20 利用“引用形参”实现两个变量的值互换 void swap(int &,int &); //必须在此声明 int main( ) { int i=3,j=5; swap(i,j); cout<<″i=″<<i<<″ ″<<″j=″<<j<<endl; return 0; } void swap(int &a,int &b) { int temp; temp=a; a=b; b=temp;

88 习 题 int f(int a,int &b,int *c) { a+=b;b+=*c;*c+=a; return (a+b+*c); } void main(void) { int x=5,y=5,z=6,sum; for(int i=0;i<2;i++) { sum=f(x,y,&z); cout<<x<<'\t'<<y<<'\t'<<z<<'\t'<<sum<<'\n';

89 习 题 int f(int &x,int &y) { x+=y; y+=x; return(x>y?x:y); } void main(void) { int x=20,y=30,z; z=f(x,y); cout<<x<<'\t'<<y<<'\t'<<z<<endl; z=f(y,x);


Download ppt "第 6 章 第 6 章 指 针 指 针 1."

Similar presentations


Ads by Google