实验9指针 9.1指针的概念及基本运算 【实验目的】 1.理解指针的概念,掌握指针变量的定义和使用: 2.理解指针和地址间的关系: 3.掌握指针的基本运算: 4.掌握指针作为函数参数的方法: 【相关知识点】 1.指针变量的定义方式 指针变量的定义方式一般为: int *p; ∥定义单个整型指针变量 char *ch; ∥定义单个字符指针变量 2.指针变量的初始化 指针被定义后,必须将指针和一个特定的变量进行关联后,才可以使用指针。也就是说, 指针变量也要先赋值再使用,当然指针变量被赋的值应该是地址。假设有定义: int i,*p; 下面的语句可以对指针变量p赋值: p=&i; 该语句中的指针p被看做是指向变量ⅰ或包含变量i的地址,也就是将指针p和普通的整 型变量ⅰ关联起来,这是指针最常用的赋值方法。 另外,可使用语句: p=NULL; 来对指针赋初值,此时指针的值为NULL。常量NULL在系统文件stdio.h中定义,其值 为0,将它赋给指针时,代表空指针。C语言中的空指针不指向任何存储单元。 -167-
实验 9 指针 9.1 指针的概念及基本运算 【实验目的】 1. 理解指针的概念,掌握指针变量的定义和使用; 2. 理解指针和地址间的关系; 3. 掌握指针的基本运算; 4. 掌握指针作为函数参数的方法; 【相关知识点】 1. 指针变量的定义方式 指针变量的定义方式一般为: int *p; //定义单个整型指针变量 char *ch; //定义单个字符指针变量 2. 指针变量的初始化 指针被定义后,必须将指针和一个特定的变量进行关联后,才可以使用指针。也就是说, 指针变量也要先赋值再使用,当然指针变量被赋的值应该是地址。假设有定义: int i,*p; 下面的语句可以对指针变量 p 赋值: p = &i; 该语句中的指针 p 被看做是指向变量 i 或包含变量 i 的地址,也就是将指针 p 和普通的整 型变量 i 关联起来,这是指针最常用的赋值方法。 另外,可使用语句: p = NULL; 来对指针赋初值,此时指针的值为 NULL。常量 NULL 在系统文件 stdio.h 中定义,其值 为 0,将它赋给指针时,代表空指针。C 语言中的空指针不指向任何存储单元。 - 167 -
指针除了指向己有变量或常量的地址外,还可以通过malloc函数动态申请分配内存。 malloc函数的原型是: void *malloc(unsigned size) 其功能是在内存的动态存储区域中分配一连续空间,长度为sz心。若申请成功,则返回一 个指向所分配内存空间的起始地址的指针:若申请内存空间不成功,则返回NULL。 3.指针的基本运算 如果指针的值是某个变量的地址,通过指针就能间接访问那个变量,这些操作由取地址 运算符&和间接访问运算符*完成。此外,相同类型的指针还能进行赋值、比较和算术运算。 示例1: int *p,a=99; p=&a; printf("%d\n",*p); *p=100: printf("%d n",a), 在该示例中,语句 p=&a; 是将整型变量a的地址赋给指针变量p(注意使用了取地址运算符&),那么,*p就是取 得p中所保存的地址所指向的存储单元的值(注意使用了间接访问运算符*),也就是变量 的值。语句 *p=100 的作用是将p中所保存的地址所指向的存储单元的值改为100,也即变量a的值改为100。 另外,指针被定义并赋值后,就可以如同其他类型的变量一样进行赋值运算。 示例2: int a=99,*pl,*p2; pl=&a; p2=pl店 该示例将指针变量p1存放的地址值赋给指针变量p2。注意,只能将一个指针的值赋给另 一个相同类型的指针。 -168-
指针除了指向已有变量或常量的地址外,还可以通过 malloc 函数动态申请分配内存。 malloc 函数的原型是: void *malloc(unsigned size) 其功能是在内存的动态存储区域中分配一连续空间,长度为 size。若申请成功,则返回一 个指向所分配内存空间的起始地址的指针;若申请内存空间不成功,则返回 NULL。 3. 指针的基本运算 如果指针的值是某个变量的地址,通过指针就能间接访问那个变量,这些操作由取地址 运算符&和间接访问运算符*完成。此外,相同类型的指针还能进行赋值、比较和算术运算。 示例 1: int *p,a=99; p=&a; printf("%d\n",*p); *p=100; printf("%d\n",a); 在该示例中,语句 p=&a; 是将整型变量 a 的地址赋给指针变量 p(注意使用了取地址运算符&),那么,*p 就是取 得 p 中所保存的地址所指向的存储单元的值(注意使用了间接访问运算符*),也就是变量 a 的值。语句 *p=100; 的作用是将 p 中所保存的地址所指向的存储单元的值改为 100,也即变量 a 的值改为 100。 另外,指针被定义并赋值后,就可以如同其他类型的变量一样进行赋值运算。 示例 2: int a=99,*p1,*p2; p1=&a; p2=p1; 该示例将指针变量 p1 存放的地址值赋给指针变量 p2。注意,只能将一个指针的值赋给另 一个相同类型的指针。 - 168 -
4.指针作为函数参数的方法 指针作为函数的参数,主要是在函数定义时将指针变量作为形式参数,在函数调用时, 实际参数必须是地址表达式。 示例: #include void main(void) { inta=99,b=100: void swap(int *pl,int *p2); swap(&a,&b); printf("%d %d n",a,b); } void swap(int *pl,int *p2) int temp; temp=*pl; *pl=*p2; *p2 =temp; } 该示例中的函数swp使用指针做参数,实现两个数的交换,其形式参数有2个,均为指 针变量。在mai中调用该函数时,使用两个整型变量的地址作为实际参数。使用指针做函数 的参数,传递的是变量的地址:不使用指针做参数时,传递的是变量的值。 5.指针与地址之间的关系 要学会使用指针,最重要的一点是必须深刻理解指针其实就是地址。仔细阅读下面的几 个示例程序,并输入到VC中编译、运行,查看程序的输出结果。 示例1:该程序简明地说明了变量在内存中的位置。 #include -169-
4. 指针作为函数参数的方法 指针作为函数的参数,主要是在函数定义时将指针变量作为形式参数,在函数调用时, 实际参数必须是地址表达式。 示例: #include void main(void) { int a=99,b=100; void swap(int *p1,int *p2); swap(&a,&b); printf("%d %d\n",a,b); } void swap(int *p1,int *p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } 该示例中的函数 swap 使用指针做参数,实现两个数的交换,其形式参数有 2 个,均为指 针变量。在 main 中调用该函数时,使用两个整型变量的地址作为实际参数。使用指针做函数 的参数,传递的是变量的地址;不使用指针做参数时,传递的是变量的值。 5. 指针与地址之间的关系 要学会使用指针,最重要的一点是必须深刻理解指针其实就是地址。仔细阅读下面的几 个示例程序,并输入到 VC 中编译、运行,查看程序的输出结果。 示例 1:该程序简明地说明了变量在内存中的位置。 #include - 169 -
void main(void) { int i=10,j=20; char chl='a'.ch2='b': f1 loat f1=99,2=100; printf("整型变量i的地址:%d;整型变量j的地址:%dn",&i,&j)方 printf("字符变量chl的地址:%d;字符变量ch2的地址:%dn",&chl,&ch2; printf("浮点变量f1的地址:%d;浮点变量f2的地址:%dn",&f1,&f2); 示例2:该程序演示了指针变量的实质。首先,系统也对指针变量本身分配存储空间,其 次,它保存了某个存储单元的地址,通过指针变量可以访问该地址代表的存储单元。 #include void main(void) { char ch='a',*pch; int i=10,*pi; pch=&ch; pi=&i; printf"字符变量ch的地址:%d;指针变量pch的地址:%dn",&ch,&pch; printf("整型变量i的地址:%d,指针变量pi的地址:%dn",&i,&pi); printf("字符变量ch的值:%c,指针变量pch的值:%dn",ch,pch: printf("整型变量i的值:%d;指针变量pi的值:%dn",i,pi); printf("指针变量pch所指向的存储单元的值:%cln",*pch); printf("指针变量pi所指向的存储单元的值:%dn",*pi); 示例3:该程序解释了函数参数值传递的原理。 #include -170-
void main(void) { int i=10,j=20; char ch1='a',ch2='b'; float f1=99,f2=100; printf("整型变量 i 的地址:%d ;整型变量 j 的地址:%d \n",&i,&j); printf("字符变量 ch1 的地址:%d ;字符变量 ch2 的地址:%d\n",&ch1,&ch2); printf("浮点变量 f1 的地址:%d ;浮点变量 f2 的地址:%d\n",&f1,&f2); } 示例 2:该程序演示了指针变量的实质。首先,系统也对指针变量本身分配存储空间,其 次,它保存了某个存储单元的地址,通过指针变量可以访问该地址代表的存储单元。 #include void main(void) { char ch='a',*pch; int i=10,*pi; pch=&ch; pi=&i; printf("字符变量 ch 的地址: %d; 指针变量 pch 的地址: %d\n",&ch,&pch); printf("整型变量 i 的地址: %d; 指针变量 pi 的地址: %d\n",&i,&pi); printf("字符变量 ch 的值: %c; 指针变量 pch 的值: %d\n",ch,pch); printf("整型变量 i 的值: %d; 指针变量 pi 的值: %d\n",i,pi); printf("指针变量 pch 所指向的存储单元的值: %c\n",*pch); printf("指针变量 pi 所指向的存储单元的值: %d\n",*pi); } 示例 3:该程序解释了函数参数值传递的原理。 #include - 170 -
void swap(int x,int y) { int temp; printf("函数swap中,变量x的地址:%d;变量y的地址:%dn",&x,&y)方 printf("函数swap中,交换前:X=%d,y=%dn",Xy);方 temp-x,x可y,y=temp, printf("函数swap中,交换后:X=%d,y=%dn",X,y)方 void main(void) int a-3,b-4; printf("main函数中,变量a的地址:%d,变量b的地址:%dn",&a,&b printf("调用swap函数交换前:a=%d,b=%dn",a,b方 swap(a,b); printf("调用swap函数交换后:a=%d,b=%dn",a,b方 示例4:该程序解释了使用指针作为参数进行地址传递的过程及产生的结果。 #include void swap(int *x,int *y) int temp: printf("函数swap中,指针变量x的地址:%d;指针变量y的地址:%dn",&x,&y)方 printf("函数swap中,指针变量x的值:%d,指针变量y的值:%dn",x,y): printf("函数swap中,交换前:*x=%d,*y=%dn",*x,*y); temp=*x;*x=*y;*y=temp; printf("函数swap中,交换后:*x=%d,*y=%dn",*x,*y)店 -171-
void swap(int x,int y) { int temp; printf("函数 swap 中,变量 x 的地址:%d; 变量 y 的地址:%d\n",&x,&y); printf("函数 swap 中,交换前:x=%d,y=%d\n",x,y); temp=x;x=y;y=temp; printf("函数 swap 中,交换后:x=%d,y=%d\n",x,y); } void main(void) { int a=3,b=4; printf("main 函数中,变量 a 的地址:%d; 变量 b 的地址:%d\n",&a,&b); printf("调用 swap 函数交换前:a=%d,b=%d\n",a,b); swap(a,b); printf("调用 swap 函数交换后:a=%d,b=%d\n",a,b); } 示例 4:该程序解释了使用指针作为参数进行地址传递的过程及产生的结果。 #include void swap(int *x,int *y) { int temp; printf("函数 swap 中,指针变量 x 的地址:%d; 指针变量 y 的地址:%d\n",&x,&y); printf("函数 swap 中,指针变量 x 的值:%d; 指针变量 y 的值:%d\n",x,y); printf("函数 swap 中,交换前:*x=%d,*y=%d\n",*x,*y); temp=*x;*x=*y;*y=temp; printf("函数 swap 中,交换后:*x=%d,*y=%d\n",*x,*y); } - 171 -
void main(void) int a=3,b=4; printf"main函数中,变量a的地址:%d变量b的地址:%dn",&a,&b; printf("调用swap函数交换前:a=%d,b=%dn",a,b); swap(&a,&b): printf"调用swap函数交换后:a=%d,b=%dn",a,b)方 } 【实验任务】 一、程序改错 该部分实验主要训练学生读简单程序的能力,并在理解程序的基础上,改正程序中出现 的问题,这些问题都是初学者经常容易犯的错误。针对指针的概念及基本运算这一部分,主 要有如下问题: 1)不能正确使用&及*运算符: 2)指针作为函数参数时,不能正确地定义形式参数,调用时也不能正确给出实在参数。 针对上面的问题,查找下面程序中存在的问题,并改正调试。 1.打开c:lc-programming\9pg9101.cpp,该程序使用指针变量输入并输出一个整数。程序 中有一个错误,请修改,使程序能正确运行。修改后的程序以pg9101ok.cpp保存。该源程序 内容如下: #include void main(void) { int i,*p; p=&i; printf("请输入一个整数:")方 scanf("%d",p), printf("你输入的整数为:%dn",p; -172-
void main(void) { int a=3,b=4; printf("main 函数中,变量 a 的地址:%d; 变量 b 的地址:%d\n",&a,&b); printf("调用 swap 函数交换前:a=%d,b=%d\n",a,b); swap(&a,&b); printf("调用 swap 函数交换后:a=%d,b=%d\n",a,b); } 【实验任务】 一、程序改错 该部分实验主要训练学生读简单程序的能力,并在理解程序的基础上,改正程序中出现 的问题,这些问题都是初学者经常容易犯的错误。针对指针的概念及基本运算这一部分,主 要有如下问题: 1) 不能正确使用&及*运算符; 2) 指针作为函数参数时,不能正确地定义形式参数,调用时也不能正确给出实在参数。 针对上面的问题,查找下面程序中存在的问题,并改正调试。 1.打开 c:\c-programming\9\pg9101.cpp,该程序使用指针变量输入并输出一个整数。程序 中有一个错误,请修改,使程序能正确运行。修改后的程序以 pg9101_ok.cpp 保存。该源程序 内容如下: #include void main(void) { int i,*p; p=&i; printf("请输入一个整数:"); scanf("%d",p); printf("你输入的整数为: %d\n",p); - 172 -
了提示:如何发现问题? 编译、链接该程序,没有错误,可以直接运行。运行时,输入一个数字,例如99,但是 输出的结果却是1245052(不同的电脑上运行时该数字可能有所不同)。从前面的示例程序可 以看到,1245052这样的数字,其实是某个变量的地址(此处为变量i的地址)。而该程序的 目的是要输出刚输入的数字,并非要输出地址。所以在输出时应该使用间接访问运算符*来取 得指针变量p所指向的存储单元的值。 改正程序后的运行结果示例如图9.1.1所示 四"C:\e-programming\9\Debug\pg9I0lok.exe"-回☒ 请输入一个整数:99 你输入的整数为:99 Press any key to continue 图9.1.1pg9101_ok.cpp的运行结果 2.打开c.c-programming\9列pg9102.cpp,该程序定义了一个swap函数,其功能是实现两 个数的交换。在main函数中,输入两个数,然后调用swap函数,再依次输出交换后的这两 个数。程序中有一处错误,请修改,使程序能正确运行。修改后的程序以pg9102ok.cpp保存。 该源程序内容如下: #include void main(void) int a,b; void swap(int *pl,int *p2); printf("请输入两个整数:")方 scanf("%d%d",&a,&b); swap(a,b); printf("这两个数交换后的值是:%d%dn",a,b), } void swap(int *pl,int *p2) -173-
} 提示:如何发现问题? 编译、链接该程序,没有错误,可以直接运行。运行时,输入一个数字,例如 99,但是 输出的结果却是 1245052(不同的电脑上运行时该数字可能有所不同)。从前面的示例程序可 以看到,1245052 这样的数字,其实是某个变量的地址(此处为变量 i 的地址)。而该程序的 目的是要输出刚输入的数字,并非要输出地址。所以在输出时应该使用间接访问运算符*来取 得指针变量 p 所指向的存储单元的值。 改正程序后的运行结果示例如图 9.1.1 所示 图 9.1.1 pg9101_ok.cpp 的运行结果 2.打开 c:\c-programming\9\pg9102.cpp,该程序定义了一个 swap 函数,其功能是实现两 个数的交换。在 main 函数中,输入两个数,然后调用 swap 函数,再依次输出交换后的这两 个数。程序中有一处错误,请修改,使程序能正确运行。修改后的程序以 pg9102_ok.cpp 保存。 该源程序内容如下: #include void main(void) { int a,b; void swap(int *p1,int *p2); printf("请输入两个整数:"); scanf("%d%d",&a,&b); swap(a,b); printf("这两个数交换后的值是:%d %d\n",a,b); } void swap(int *p1,int *p2) { - 173 -
int temp; temp=*pl; *pl=*p2; *p2 temp; 了提示:如何发现问题? 编译该程序后,发现有一个错误: error C2664:'swap':cannot convert parameter 1 from'int'to 'int 错误发生在调用swap函数的那一行。 从错误信息可知,swp函数的形式参数是指针,但是调用的时候却用了整数变量作为实 在参数,系统的出错信息指出不能将整数转换为指针。改正的方法是将实在参数改成变量、 b的地址。 改正程序后的运行结果示例如图9.1.2所示。 "C:\c-programming\9叭Debug1pg9102k.exe”-回☒ 输入两个整数:,34 这两个数交换后的值是:43 Press any key to continue 图9.1.2pg9102_ok.cpp的运行结果 二、程序扩展 1.打开c:lc-programming\9pg201.cpp,该程序对输入的2个整数按从大到小的顺序输出。 该源程序内容如下: #include void main(void) int a.b; void swap(int *pl,int *p2); printf"请输入两个整数:")方 scanf("%d%d",&a,&b); if(a<b)swap(&a,&b)月 -174-
int temp; temp = *p1; *p1 = *p2; *p2 = temp; } 提示:如何发现问题? 编译该程序后,发现有一个错误: error C2664: 'swap' : cannot convert parameter 1 from 'int' to 'int *' 错误发生在调用 swap 函数的那一行。 从错误信息可知,swap 函数的形式参数是指针,但是调用的时候却用了整数变量作为实 在参数,系统的出错信息指出不能将整数转换为指针。改正的方法是将实在参数改成变量 a、 b 的地址。 改正程序后的运行结果示例如图 9.1.2 所示。 图 9.1.2 pg9102_ok.cpp 的运行结果 二、程序扩展 1.打开 c:\c-programming\9\pg9201.cpp,该程序对输入的 2 个整数按从大到小的顺序输出。 该源程序内容如下: #include void main(void) { int a,b; void swap(int *p1,int *p2); printf("请输入两个整数:"); scanf("%d%d",&a,&b); if (a<b) swap(&a,&b); - 174 -
printf"这两个数从大到小的顺序是:%d%dn",a,b: void swap(int *pl,int *p2) int temp; temp=*pl店 *pl=*p2; *p2 temp; } 要求:修改上面的程序,要求输入3个数,实现从大到小输出这3个数。修改结果源程 序以pg9201la.cpp保存。注意,只需要修改main函数,swap函数不能修改,并要求在main 函数中仍然要调用swap函数。并且,假定3个数输入时的顺序是分别赋值给变量a、b、c, 打印输出时的顺序依然是变量a、b、c。 训练要点:理解并掌握指针作为函数的参数,在此基础上,能够按照修改要求,实现多 次函数调用。 修改后程序的运行结果如图9.1.3所示。 x "C:\c-programming\9\Debug\pg9201a.exe" 回 请输入3个整数:,231 这3个数从大到小的顺序是:321 Press any key to continue 图9.1.3pg9201a.cpp的运行结果 三、程序编写 1.设计一个程序计算输入的两个数的和与差,要求自定义一个函数sum diff(float opl,foat op2,foat*psum,float*pdif),其中opl和op2是输入的两个数,*psum和*pdif是计算得出的 和与差。编写的源程序以pg9301.cpp做文件名保存。 训练要点:使用指针做函数的参数进行编程。 程序运行结果参考示例如图9.1.4所示。 -175-
printf("这两个数从大到小的顺序是:%d %d\n",a,b); } void swap(int *p1,int *p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } 要求:修改上面的程序,要求输入 3 个数,实现从大到小输出这 3 个数。修改结果源程 序以 pg9201a.cpp 保存。注意,只需要修改 main 函数,swap 函数不能修改,并要求在 main 函数中仍然要调用 swap 函数。并且,假定 3 个数输入时的顺序是分别赋值给变量 a、b、c, 打印输出时的顺序依然是变量 a、b、c。 训练要点:理解并掌握指针作为函数的参数,在此基础上,能够按照修改要求,实现多 次函数调用。 修改后程序的运行结果如图 9.1.3 所示。 图 9.1.3 pg9201a.cpp 的运行结果 三、程序编写 1.设计一个程序计算输入的两个数的和与差,要求自定义一个函数 sum_diff(float op1,float op2, float *psum, float *pdiff),其中 op1 和 op2 是输入的两个数,*psum 和*pdiff 是计算得出的 和与差。编写的源程序以 pg9301.cpp 做文件名保存。 训练要点:使用指针做函数的参数进行编程。 程序运行结果参考示例如图 9.1.4 所示。 - 175 -
可"C:\c-progranming\9叭Debug\pgs9301.exe”-曰☒ 请输入两个数:98.662.4 这两个数的和是:161.0;差是:36.28 Press any key to continue 图9.1.4pg9301.cpp的运行结果 2.设计一个程序,输入年份和天数,输出对应的年、月、日。要求设计和调用函数 month day(int year,,int yearday,,int*pmonth,int*pday),其中year是年,yearday是天数,*pmonth 和*pday是计算得出的月和日。例如输入2008和65,输出2008-3-5,即2008年的第65天是 3月5日。编写的源程序以pg9302.cpp做文件名保存。 训练要点:使用指针做函数的参数进行编程。 程序运行结果参考示例如图9.1.5所示。 的"C:\e-programming\9八Debug\pg9302.exe 回☒ 请输人年份和天数:200865 对应的年月白为:28-3-5 Press any key to continue 图9.1.5pg9302.cpp的运行结果 9.2指针与数组 【实验目的】 1.理解指针、地址和数组间的关系: 2.掌握通过指针操作数组元素的方法: 3.掌握数组名作为函数参数的编程方式: 4.掌握通过指针操作字符串的方法: 5.理解指针数组的概念,掌握指针数组的基本应用和编程方法: 6.理解结构指针的概念,掌握结构指针的基本应用和编程方法。 7.理解和掌握指针作为函数返回值的基本应用和编程方法。 【相关知识点】 1.指针、地址和数组间的关系 在定义数组时,编译器必须为数组分配基地址和足够的存储空间,以存储数组的所有元 -176-
图 9.1.4 pg9301.cpp 的运行结果 2.设计一个程序,输入年份和天数,输出对应的年、月、日。要求设计和调用函数 month_day(int year, int yearday, int *pmonth, int *pday),其中 year 是年,yearday 是天数,*pmonth 和*pday 是计算得出的月和日。例如输入 2008 和 65,输出 2008-3-5,即 2008 年的第 65 天是 3 月 5 日。编写的源程序以 pg9302.cpp 做文件名保存。 训练要点:使用指针做函数的参数进行编程。 程序运行结果参考示例如图 9.1.5 所示。 图 9.1.5 pg9302.cpp 的运行结果 9.2 指针与数组 【实验目的】 1. 理解指针、地址和数组间的关系; 2. 掌握通过指针操作数组元素的方法; 3. 掌握数组名作为函数参数的编程方式; 4. 掌握通过指针操作字符串的方法; 5. 理解指针数组的概念,掌握指针数组的基本应用和编程方法; 6. 理解结构指针的概念,掌握结构指针的基本应用和编程方法。 7. 理解和掌握指针作为函数返回值的基本应用和编程方法。 【相关知识点】 1. 指针、地址和数组间的关系 在定义数组时,编译器必须为数组分配基地址和足够的存储空间,以存储数组的所有元 - 176 -