《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; 
} 
相关推荐
一点媛艺1 小时前
Kotlin函数由易到难
开发语言·python·kotlin
姑苏风1 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生2 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
Nu11PointerException2 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
闲晨2 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程2 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
亦枫Leonlew3 小时前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go