《C语言程序设计 第4版》笔记和代码 第九章 指针

第九章 指针

9.1 变量的内存地址

1 **取地址符&**可以取出变量的地址。

例9.1见文末

2 内存的地址是按字节编号,变量的地址 实际指变量在内存中所占存储空间的首地址变量的值 是变量在存储空间中存放的数据;变量的名字可以看成对程序中数据存储空间的一种随机。

3 声明变量时若没有赋初值,则他们的内容就是随机的、不确定的

9.2 指针变量的定义和初始化

1 指针 是一种特殊类型的变量 ,专门用于存储 变量的地址值的变量。定义形式为

类型关键字 * 指针变量名

2 指针一定要记得初始化,这非常重要。可以将其初始化为NULL(stdio.h中零值的宏)。

例9.2见文末

3 指向某变量的指针常被简称为某变量的指针。 变量的地址是一个常量 ,不能对其赋值;变量的指针是一个变量,可以对其赋值。

4 指针变量只能指向同一基类型的变量,如int *pa,只能指向int型。

9.3 间接寻址运算符

1 直接寻址 是直接按变量名或变量的地址存取变量的内容的访问(如前几章的方法);间接寻址是通过指针变量间接存取它所指向的变量的访问方式。

2 间接寻址是先通过指针变量 去获得变量的地址值 ,如何再到地址值对应的存储单元中访问变量。

3 指针运算符 (也称间接寻址运算符或解应用运算符),也就是***** ,用来访问指针变量指向的变量的值

例9.3见文末

4 *在不同的位置具有不同的功能。可以是指针类型说明符 ,也可以作为间接引用符,这两者之间并无关系。

5 指针的解引用是指引用指针所指向的变量的值。

6 使用指针一定要注意(1)清楚每个指针指向了哪里 (指针是指向一个地址,通过地址文末可以找到对应地址上的变量),必需指向一块有意义的内存。(2)清楚每个指针指向的对象 的内容是什么(即指向地址上的变量是什么)。(3)永远不要用未初始化的指针变量。

9.4 按值调用与模拟按引用调用

1 用普通变量作函数参数的方法属于一种按值调用的方法,即程序将函数调用语句中的实参的一份副本传给函数的形参。按值调用不会改变实参的值。

例9.4见文末

2 指针作为函数参数传递时,本质上传递的不是变量的值,而是变量的地址 ,可以实现在函数中 改变实参的值。尽管这种方法实际上也是按值调用 (C语言中所有函数调用都是按值调用),但是被称为模拟按引用调用

例9.5和例9.6见文末

9.5 用指针变量作函数参数的程序实例

例9.7见文末

1 指针形参所指向的变量的值在调用结束后才被确定,则该指针形参被称为函数的出口参数 ;对应的,在函数调用前必需确定值的被称为函数的入口参数

9.6 函数指针及其应用

1 函数指针 (指向函数的指针)中存储的是一个函数在内存中的入口地址 ,而函数的入口地址也就是指向存储这个函数的第一条指令 的地址。

2 函数名是这个函数的源代码在内存中的起始位置。

例9.8和例9.9见文末

3 注意int *pa 和int (*pa)表示的是两个含义。

代码:

9.1

使用取地址符&取出变量的地址,然后将其显示在屏幕上。

cs 复制代码
//例9.1 使用取地址符&取出变量的地址,然后将其显示在屏幕上。
#include<stdio.h>
int main(void)
{
	int a=0,b=1;
	char c='A';
	printf("a is %d,&a is %p\n",a,&a);//%p可以用于输出格式是十六进制的无符号整型
	printf("b is %d,&b is %p\n",b,&b);
	printf("c is %c,&c is %p\n",c,&c);
	return 0;
 }

9.2(错误示范版一)

使用指针变量在屏幕上显示变量的地址值(错误示范版)

cs 复制代码
//例9.2 使用指针变量在屏幕上显示变量的地址值(错误示范版) 
#include<stdio.h>
int main(void)
{
	int a=0,b=1;
	char c='A';
	int *pa,*pb;//指向int型的指针变量pa和pb 
	char *pc;//指向char型的指针变量pc
	printf("a is %d,&a is %p,pa is %p\n",a,&a,pa);
	printf("b is %d,&b is %p,pb is %p\n",b,&b,pb);
	printf("c is %c,&c is %p,pc is %p\n",c,&c,pc);
	return 0;
 } 

9.2(错误示范版二)

使用指针变量在屏幕上显示变量的地址值(错误示范版)

cs 复制代码
//例9.2 使用指针变量在屏幕上显示变量的地址值(错误示范版) 
#include<stdio.h>
int main(void)
{
	int a=0,b=1;
	char c='A';
	int *pa=NULL,*pb=NULL;//注意初始化,初始化后指向地址0,所以依旧不能得到我们需要的指向对应变量的地址的效果 
	char *pc=NULL;
	printf("a is %d,&a is %p,pa is %p\n",a,&a,pa);
	printf("b is %d,&b is %p,pb is %p\n",b,&b,pb);
	printf("c is %c,&c is %p,pc is %p\n",c,&c,pc);
	return 0;
 } 

9.2(正确版本)

使用指针变量在屏幕上显示变量的地址值(正确示范版)

cs 复制代码
//例9.2 使用指针变量在屏幕上显示变量的地址值(正确示范版) 
#include<stdio.h>
int main(void)
{
	int a=0,b=1;
	char c='A';
	int *pa=&a,*pb=&b;//注意这里的变量a,b,c前面都要有&,因为指针变量的本质是指向一个地址 
	char *pc=&c;
	printf("a is %d,&a is %p,pa is %p\n",a,&a,pa);
	printf("b is %d,&b is %p,pb is %p\n",b,&b,pb);
	printf("c is %c,&c is %p,pc is %p\n",c,&c,pc);
	return 0;
 }

9.3(版本一)

使用指针变量,通过间接寻址输出变量的值(版本一)

cs 复制代码
//例9.3 使用指针变量,通过间接寻址输出变量的值(版本一)
#include<stdio.h>
int main(void)
{
	int a=0,b=1;
	char c='A';
	int *pa=&a,*pb=&b;//定义指针时就初始化
	char *pc=&c;
	printf("a is %d,&a is %p,*pa is %d\n",a,&a,*pa);
	printf("b is %d,&b is %p,*pb is %d\n",b,&b,*pb);
	printf("c is %c,&c is %p,*pc is %c\n",c,&c,*pc);//这里的输出与例9.2不同,属于解引用,此处的*含义也不同于定义时的*
	return 0;
 } 

9.3(版本二)

使用指针变量,通过间接寻址输出变量的值(版本二,修改指针变量)

cs 复制代码
//例9.3 使用指针变量,通过间接寻址输出变量的值(版本二,修改指针变量)
#include<stdio.h>
int main(void)
{
	int a=0,b=1;
	char c='A';
	int *pa=&a,*pb=&b;//定义指针时就初始化
	char *pc=&c;
	*pa=9;//这里属于将指针变量里对应的内容改为9 
	printf("a is %d,&a is %p,*pa is %d\n",a,&a,*pa);
	printf("b is %d,&b is %p,*pb is %d\n",b,&b,*pb);
	printf("c is %c,&c is %p,*pc is %c\n",c,&c,*pc);
	return 0;
 } 

9.4

演示程序按值调用

cs 复制代码
//例9.4 演示程序按值调用
#include<stdio.h>
void Fun(int par);
int main(void)
{
	int arg=1;
	printf("arg=%d\n",arg);
	Fun(arg);//传递实参的副本给函数中的形参
	printf("arg=%d\n",arg); 
	return 0;
 } 
void Fun(int par)
{
	printf("par=%d\n",par); 
	par=2;
 } 

9.5

演示程序模拟按引用调用的例子

cs 复制代码
//例9.5 演示程序模拟按引用调用的例子 
#include<stdio.h>
void Fun(int *par);
int main(void)
{
	int arg=1;
	printf("arg=%d\n",arg);
	Fun(&arg);//注意函数要求的形参类型是指针类型,因此arg前必须加&
	printf("arg=%d\n",arg); 
	return 0;
 } 
void Fun(int *par)
{
	printf("par=%d\n",*par);//别忘记用也必须加上*
	*par=2;//直接改变形参指向的变量的值 
}

9.5补充

扩充,按值调用,但是可以正确返回在函数中改变的值

cs 复制代码
//例9.5扩充,按值调用,但是可以正确返回在函数中改变的值 
#include<stdio.h>
void Fun(int par);
int main(void)
{
	int arg=1;
	printf("arg=%d\n",arg);
	Fun(arg);//传递实参的副本给函数中的形参
	printf("arg=%d\n",arg); 
	return 0;
 } 
void Fun(int par)
{
	printf("par=%d\n",par); 
	par=2;
	return par;//注意这是与之前的按值调用的不同之处,你可以理解成返回了这个值后函数对应的内存才释放,但是这种方法只能返回一个值,不在函数中改变多个值 
 } 

9.6(错误版本)

从键盘上任意输入两个整数,编程实现将其交换后再重新输出。试一试下面程序能否实现该功能。(答案是不能)

cs 复制代码
//例9.6 从键盘上任意输入两个整数,编程实现将其交换后再重新输出。试一试下面程序能否实现该功能。(答案是不能)
#include<stdio.h>
void Swap(int x,int y);
int main(void)
{
	int a,b;
	printf("Please enter a,b:");
	scanf("%d,%d",&a,&b);
	printf("Before swap:a=%d,b=%d\n",a,b);//启用调用函数前
	Swap(a,b);
	printf("After swap:a=%d,b=%d\n",a,b);//验证实参是否交换 
	return 0;
  } 
void Swap(int x,int y)
{
	int temp;//用temp作为交换辅助变量
	temp=x;
	x=y;
	y=temp;//在这个函数里的确实现了交换,但是只是相当于副本改变,对实参不会产生影响,和例9.4是一样的道理 
 } 

9.6(正确示范)

从键盘上任意输入两个整数,编程实现将其交换后再重新输出。试一试下面程序能否实现该功能。(正确方法)

cs 复制代码
//例9.6 从键盘上任意输入两个整数,编程实现将其交换后再重新输出。试一试下面程序能否实现该功能。(正确方法)
#include<stdio.h>
void Swap(int *x,int *y);
int main(void)
{
	int a,b;
	printf("Please enter a,b:");
	scanf("%d,%d",&a,&b);
	printf("Before swap:a=%d,b=%d\n",a,b);//启用调用函数前
	Swap(&a,&b);//注意这里是传递地址给指针变量,因此需要& 
	printf("After swap:a=%d,b=%d\n",a,b);//验证实参是否交换 
	return 0;
  } 
void Swap(int *x,int *y)//形参是两个指向int型的指针变量 
{
	int temp;//用temp作为交换辅助变量
	temp=*x;
	*x=*y;
	*y=temp;//也不要忘记*哦 
 } 

9.7(错误版本)

从键盘输入某班学生某门课的成绩(不超过40人,具体人数由键盘输入),试分析下面的程序能否实现计算并输出最高分及相应学生的学号。

(答案是不能,因为同样是按值调用)

cs 复制代码
//例9.7 从键盘输入某班学生某门课的成绩(不超过40人,具体人数由键盘输入),试分析下面的程序能否实现计算并输出最高分及相应学生的学号。
//(答案是不能,因为同样是按值调用) 
#include<stdio.h>
#define N 40
void FindMax(int score[],long num[],int n,int pMaxScore,long pMaxNum);
int main(void)
{
	int score[N],maxScore;
	int n,i;
	long num[N],maxNum;
	printf("How many students:");
	scanf("%d",&n);
	printf("Input student's ID and score:\n");
	for(i=0;i<n;i++)
	{
		scanf("%ld %d",&num[i],&score[i]);//输入是字母l,字母d,不是1d 
	}//完成输入,学号和成绩之间用空格隔开 
	FindMax(score,num,n,maxScore,maxNum);//这里是按值调用 
	printf("maxScore=%d,maxNum=%ld\n",maxScore,maxNum);
	return 0;
 } 
void FindMax(int score[],long num[],int n,int pMaxScore,long pMaxNum)
{
	int i;
	pMaxScore=score[0];//把第一个成绩先当成最大成绩 
	pMaxNum=num[0];
	for(i=1;i<n;i++)//i,从1开始,其实相当于是和第二个成绩开始比较 
	{
		if(score[i]>pMaxScore){
		pMaxScore=score[i];
		pMaxNum=num[i];//成绩和学号都要改变	
		} 
	}
}

9.7(正确方法)

从键盘输入某班学生某门课的成绩(不超过40人,具体人数由键盘输入)(正确方法)

cs 复制代码
//例9.7 从键盘输入某班学生某门课的成绩(不超过40人,具体人数由键盘输入)(正确方法) 
#include<stdio.h>
#define N 40
void FindMax(int score[],long num[],int n,int *pMaxScore,long *pMaxNum);
int main(void)
{
	int score[N],maxScore;
	int n,i;
	long num[N],maxNum;
	printf("How many students:");
	scanf("%d",&n);
	printf("Input student's ID and score:\n");
	for(i=0;i<n;i++)
	{
		scanf("%ld %d",&num[i],&score[i]);
	}//完成输入,学号和成绩之间用空格隔开 
	FindMax(score,num,n,&maxScore,&maxNum);//同样不要忘记& 
	printf("maxScore=%d,maxNum=%ld\n",maxScore,maxNum);
	return 0;
 } 
void FindMax(int score[],long num[],int n,int *pMaxScore,long *pMaxNum)//注意同样是以指针变量作为形参 
{
	int i;
	*pMaxScore=score[0];//把第一个成绩先当成最大成绩 
	*pMaxNum=num[0];//不要忘记解引用 
	for(i=1;i<n;i++)
	{
		if(score[i]>*pMaxScore){
		*pMaxScore=score[i];
		*pMaxNum=num[i];//不要忘记解引用 用* 
		} 
	}
}

9.8

修改例8.7中的排序函数,使其既能实现对学生成绩的升序排序,又能实现对学生成绩的降序排序。

cs 复制代码
//例9.8 修改例8.7中的排序函数,使其既能实现对学生成绩的升序排序,又能实现对学生成绩的降序排序。
#include<stdio.h>
#define N 40
int ReadScore(int score[]);
void PrintScore(int score[],int n);
void AscendingSort(int a[],int n);//升序排序函数 
void DescendingSort(int a[],int n);//降序排序函数 
void Swap(int *x,int *y);//交换两数的函数 
int main(void){
	int score[N],n;
	int order;//值为1时表示选择升序,值为2表示选择降序 
	n=ReadScore(score);
	printf("%Total students are %d\n",n);
	printf("Enter 1 to sort in ascending order,\n");
	printf("Enter 2 to sort in descending order:");
	scanf("%d",&order);//让用户选择升序还是降序
	printf("Data items in original order\n");
	PrintScore(score,n); 
	if(order==1)
	{
		AscendingSort(score,n);
		printf("Data items in ascending order\n");
	}
	else
	{
		DescendingSort(score,n);
		printf("Data items in descending order\n");
		
	}
	PrintScore(score,n);
	return 0;
} 
int ReadScore(int score[]){
	int i=-1;//初始为-1,才能在do-while循环体中加一变成0,作为下标
	do{
		i++;
		printf("Input score:");
		scanf("%d",&score[i]);
	}while(score[i]>=0);
	return i;//返回 的i就是学生人数 
}

void PrintScore(int score[],int n){
	int i;
	for(i=0;i<n;i++){
		printf("%4d",score[i]);
	}
	printf("\n");
} 
//选择法实现数组a的升序排序 
void AscendingSort(int a[],int n)
{
	int i,j,k;
	for(i=0;i<n-1;i++)
	{
		k=i;//在每次的外层循环中,将i值赋给k,再把k值带到内存循环中比较 
		for(j=i+1;j<n;j++)//相当于是从当前位置数组的后一个开始,数组的最后一个结束 
		{
			if(a[j]<a[k])//如果后面的值小于前面的k值 
				k=j;//那么就把小的值赋值给k ,相当于找到后面的数中,最小k是多少,由于k代表的是下标,所以现在只是找下标阶段,数还没交换 
		}
		if(k!=i)//k不等于i,就说明内层循环发生过数的改变,那么就交换当前k为下标的数和i为下标的数,实现数的位置改变 
			Swap(&a[k],&a[i]);
	 } 
	
}
void DescendingSort(int a[],int n)
{
	int i,j,k;
	for(i=0;i<n-1;i++)
	{
		k=i;//在每次的外层循环中,将i值赋给k,再把k值带到内存循环中比较 
		for(j=i+1;j<n;j++)//相当于是从当前位置数组的后一个开始,数组的最后一个结束 
		{
			if(a[j]>a[k])//与升序同理,只不过现在是找后面数里面最大的值 
				k=j;
		}
		if(k!=i)//k不等于i,就说明内层循环发生过数的改变,那么就交换当前k为下标的数和i为下标的数,实现数的位置改变 
			Swap(&a[k],&a[i]);
	 }
	
}
void Swap(int *x,int *y)
{
	//这里用模拟按引用调用才能实现两个数在函数内的实参交换
	int temp;
	temp=*x;
	*x=*y;
	*y=temp; 
}

9.9

修改例9.8中的程序实例,用函数指针编程实现一个通用的排序指针,既能实现对学生成绩的升序排序,又能实现对学生成绩的降序排序

cs 复制代码
//例9.9 修改例9.8中的程序实例,用函数指针编程实现一个通用的排序指针,既能实现对学生成绩的升序排序,又能实现对学生成绩的降序排序

#include<stdio.h>
#define N 40
int ReadScore(int score[]);
void PrintScore(int score[],int n);
void SelectionSort(int a[],int n,int (*compare)(int a,int b));
int Ascending(int a,int b);
int Descending(int a,int b);
void Swap(int *x,int *y);
int main(void){
	int score[N],n;
	int order;
	n=ReadScore(score);
	printf("%Total students are %d\n",n);
	printf("Enter 1 to sort in ascending order,\n");
	printf("Enter 2 to sort in descending order:");
	scanf("%d",&order);//让用户选择升序还是降序
	printf("Data items in original order\n");
	PrintScore(score,n); 
	if(order==1)
	{
		SelectionSort(score,n,Ascending);//第三个形参是一个函数,所以相当于定义里一长串的本质是函数 
		printf("Data items in ascending order\n");
	}
	else
	{
		SelectionSort(score,n,Descending);
		printf("Data items in descending order\n");
		
	}
	PrintScore(score,n);
	return 0;
} 
int ReadScore(int score[]){
	int i=-1;//初始为-1,才能在do-while循环体中加一变成0,作为下标
	do{
		i++;
		printf("Input score:");
		scanf("%d",&score[i]);
	}while(score[i]>=0);
	return i;//返回 的i就是学生人数 
}

void PrintScore(int score[],int n){
	int i;
	for(i=0;i<n;i++){
		printf("%4d",score[i]);
	}
	printf("\n");
}

void SelectionSort(int a[],int n,int (*compare)(int a,int b))
//重点在这个函数的理解,你可以理解成该compare是一个指针变量,这个指针变量指向一个形参为两个int型并且返回值为int型的函数 
{
	int i,j,k;
	for(i=0;i<n-1;i++)
	{
		k=i;
		for(j=i+1;j<n;j++)
		{
			if((*compare)(a[j],a[k]))//相当于是调用了一个函数,返回结果是对应的比较 
				k=j;
		}
		if(k!=i)
			Swap(&a[k],&a[i]);
	}
}

int Ascending(int a,int b)
{
	return a<b;
}
int Descending(int a,int b)
{
	return a>b;
}

void Swap(int *x,int *y)
{
	//这里用模拟按引用调用才能实现两个数在函数内的实参交换
	int temp;
	temp=*x;
	*x=*y;
	*y=temp; 
} 
相关推荐
CoderCodingNo29 分钟前
【GESP】C++五级考试大纲知识点梳理, (5) 算法复杂度估算(多项式、对数)
开发语言·c++·算法
_李小白1 小时前
【Android Gradle学习笔记】第八天:NDK的使用
android·笔记·学习
ftpeak1 小时前
JavaScript性能优化实战
开发语言·javascript·性能优化
一个很帅的帅哥1 小时前
JavaScript事件循环
开发语言·前端·javascript
驰羽2 小时前
[GO]gin框架:ShouldBindJSON与其他常见绑定方法
开发语言·golang·gin
摇滚侠2 小时前
Spring Boot 3零基础教程,WEB 开发 自定义静态资源目录 笔记31
spring boot·笔记·后端·spring
摇滚侠2 小时前
Spring Boot 3零基础教程,WEB 开发 Thymeleaf 遍历 笔记40
spring boot·笔记·thymeleaf
程序员大雄学编程2 小时前
「用Python来学微积分」5. 曲线的极坐标方程
开发语言·python·微积分
Code小翊2 小时前
C语言bsearch的使用
java·c语言·前端
Jose_lz2 小时前
C#开发学习杂笔(更新中)
开发语言·学习·c#