C程序设计(第五版)谭浩强 学习笔记—重点及易错点(第8章指针-上)

善于利用指针-上

目录

善于利用指针-上

8.1指针是什么

8.2指针变量

8.2.1使用指针变量的例子

8.2.2 怎么定义指针变量

8.2.3怎么引用指针变量

8.2.4指针变量作为函数参数

8.3通过指针引用数组

8.3.1数组元素的指针

8.3.2在引用数组元素时指针的运算

8.3.3通过指针引用数组元素

8.3.4用数组名作函数参数

8.4通过指针引用字符串

8.4.1字符串的引用方式

8.4.2字符指针作函数参数

8.4.3使用字符指针变量和字符数组的比较


8.1指针是什么

如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。编译系统根据程序中定义的变量类型,分配一定长度的空间内存区的每一个字节有一个编号这就是“地址”。 由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元,将地址形象化地称为“指针”。

C语言中的地址(指针)包括位置信息(内存编号,或称纯地址)和它所指向的数据的类型信息,或者说它是“带类型的地址”。比如说:若有int型变量a与float变量b,如果先后把他们分配在2000开始的储存单元中,&a与&b的信息也不完全相同。

总之指针就是带有类型信息的编号。

8.2指针变量

注意指针与指针变量的区别与联系。指针是一个地址,而指针变量是存放地址的变量,我们可以给指针变量赋值,可是不能给指针赋值(这句话非常重要,等大家进一步学习后就会有所理解,请大家务必注意,这里有大坑

8.2.1使用指针变量的例子

例子:通过指针变量访问整型变量

#include 
int main()
{	int a=100,b=10;
	//定义整型变量a,b,并初始化
	int *pointer_1,*pointer_2;
	//定义指向整型数据的指针变量pointer_1, pointer_2
	pointer_1=&a;	//把变量a的地址赋给指针变量pointer_1
	pointer_2=&b;	//把变量b的地址赋给指针变量pointer_2 
	printf("a=%d,b=%d\n",a,b);	//输出变量a和b的值
	printf("*pointer_1=%d,*pointer_2=%d\n",*pointer_1,*pointer_2);
	//输出变量a和b的值
	return 0;
}

C程序设计(第五版)谭浩强 学习笔记—重点及易错点(第8章指针-上)_第1张图片

例子中&a就是一个指针,pointer_1就是指针变量,pointer_1可以自加:pointer_1++,但是&a++就不可以,因为&a是常量无法进行自加。

8.2.2 怎么定义指针变量

一般形式:

类型名 * 指针变量名;

如:

int * point;

说明:

(1) 指针变量前面的“*”表示该变量为指针型变量。指针变量名则不包含“*”。

(2) 在定义指针变量时必须指定基类型。一个变量的指针的含义包括两个方面,一是以存储单元编号表示的纯地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int,char,float等)。

(3) 如何表示指针类型。指向整型数据的指针类型表示为“int *”,读作“指向int的指针”或简称“int指针”。

(4) 指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量。

8.2.3怎么引用指针变量

引用指针变量时有以下三种情况:

① 给指针变量赋值。 ② 引用指针变量指向的变量。 ③引用指针变量的值。

int a, *p;
p=&a;				//把a的地址赋给指针变量p	①
printf("%d",*p);		//以整数形式输出指针变量p所指向的变量的值,即a的值	②
*p=1;				//将整数1赋给p当前所指向的变量,由于p指向变量a,相当于把1赋给a,即a=1 ②
printf("%o",p);		//以八进制形式输出指针变量p的值,由于p指向a,相当于输出a的地址,即&a ③

要熟练掌握两个有关的运算符:

(1) &取地址运算符。&a是变量a的地址。

(2) * 指针运算符(或称“间接访问”运算符),*p代表指针变量p指向的对象。

例子:输入a和b两个整数,按先大后小的顺序输出a和b。

#include 
int main()
{	int *p1,*p2,*p,a,b;					//p1,p2的类型是int *类型
	printf("please enter two integer numbers:");
	scanf("%d,%d",&a,&b);				//输入两个整数 
	p1=&a;							//使p1指向变量a
	p2=&b;							//使p2指向变量b
	if(a

8.2.4指针变量作为函数参数

函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。如果不用指针变量是难以做到这一点的。要善于利用指针法。

如果想通过函数调用得到n个要改变的值,可以这样做: 在主调函数中设n个变量,用n个指针变量指向它们; 设计一个函数,有n个指针形参。在这个函数中改变这n个形参的值; 在主调函数中调用这个函数,在调用时将这n个指针变量作实参,将它们的值,也就是相关变量的地址传给该函数的形参; 在执行该函数的过程中,通过形参指针变量,改变它们所指向的n个变量的值; 主调函数中就可以使用这些改变了值的变量。

例子:对输入的两个整数按大小顺序输出。现用函数处理,而且用指针类型的数据作函数参数。

#include 
int main()
{	void swap(int *p1,int *p2);	//对swap函数的声明 
	int a,b;
	int *pointer_1,*pointer_2;	//定义两个int *型的指针变量
	printf("please enter a and b:");
	scanf("%d,%d",&a,&b);		//输入两个整数
	pointer_1=&a;			//使pointer_1指向a
	pointer_2=&b;			//使pointer_2指向b 
	if(a

8.3通过指针引用数组

8.3.1数组元素的指针

所谓数组元素的指针就是数组元素的地址。可以用一个指针变量指向一个数组元素。

int a[10]={1,3,5,7,9,11,13,15,17,19};		//定义a为包含10个整型数据的数组
int *p;									//定义p为指向整型变量的指针变量
p=&a[0];								//把a[0]元素的地址赋给指针变量p

引用数组元素可以用下标法,也可以用指针法,即通过指向数组元素的指针找到所需的元素。

p=a;	//p的值是数组a首元素(即a[0])的地址

这句与p=&a[0];    等价。

8.3.2在引用数组元素时指针的运算

在指针已指向一个数组元素时,可以对指针进行以下运算:

加一个整数(用+或+=),如p+1,表示指向同一数组中的下一个元素;

减一个整数(用-或-=),如p-1,表示指向同一数组中的上一个元素;

自加运算,如p++,++p; 自减运算,如p--,--p。

两个指针相减,如p1-p2(只有p1和p2都指向同一数组中的元素时才有意义),结果是两个地址之差除以数组元素的长度。注意: 两个地址不能相加,如p1+p2是无实际意义的。 如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,它们指向a数组序号为i的元素。 *(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。[]实际上是变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值。

注意:

执行p+1时并不是将p的值(地址)简单地加1,而是根据定义的基类型加上一个数组元素所占用的字节数。

执行运算前一定要清楚指针变量在运算前的指向地址,否则无法确定运算后的指向。

8.3.3通过指针引用数组元素

例子:通过指针变量输出整型数组a的10个元素。

错误代码:

#include 
int main()
{	int *p,i,a[10];
	p=a;				//p指向a[0]		①
	printf("please enter 10 integer numbers:");
	for(i=0;i<10;i++)
	scanf("%d",p++);	//输入10个整数给a[0]~a[9]
	for(i=0;i<10;i++,p++)
	printf("%d ",*p);	//想输出a[0]~a[9]	②
	printf("\n");
	return 0;
}

正确代码:

#include 
int main()
{	int i,a[10],*p=a;	//p的初值是a,p指向a[0]
	printf("please enter 10 integer numbers:");
	for(i=0;i<10;i++)
	scanf("%d",p++);
	p=a;				//重新使p指向a[0]
	for(i=0;i<10;i++,p++)
	printf("%d ",*p);
	printf("\n");
	return 0;
}

p=a;                //重新使p指向a[0]。这句请大家好好体会,一定搞明白。执行运算前一定要清楚指针变量在运算前的指向地址,否则无法确定运算后的指向。

注意:

(1) 可以通过改变指针变量的值指向不同的元素。 如果不用p变化的方法而用数组名a变化的方法(例如,用a++)行不行呢? 因为数组名a代表数组首元素的地址,它是一个指针型常量,它的值在程序运行期间是固定不变的。既然a是常量,所以a++是无法实现的。 (2) 要注意指针变量的当前值。

8.3.4用数组名作函数参数

例子:用指针方法对10个整数按由大到小顺序排序。(选择排序法)

#include 
int main()
{	void sort(int x[],int n);	//sort函数声明
	int i,*p,a[10];
	p=a;					//指针变量p指向a[0]
	printf("please enter 10 integer numbers:");
	for(i=0;i<10;i++)
		scanf("%d",p++);	//输入10个整数
	p=a;	//指针变量p重新指向a[0]
	sort(p,10);			//调用sort函数
	for(p=a,i=0;i<10;i++)
	{	printf("%d ",*p);	//输出排序后的10个数组元素
		p++;
	}
	printf("\n");
	return 0;
}
void sort(int x[],int n)			//定义sort函数,x是形参数组名 
{	int i,j,k,t;
	for(i=0;ix[k]) k=j;
		if(k!=i)
		{	t=x[i]; x[i]=x[k]; x[k]=t;}
	}
}

如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况。

① 形参和实参都用数组名

C程序设计(第五版)谭浩强 学习笔记—重点及易错点(第8章指针-上)_第2张图片

② 实参用数组名,形参用指针变量。

C程序设计(第五版)谭浩强 学习笔记—重点及易错点(第8章指针-上)_第3张图片

③ 实参形参都用指针变量。

C程序设计(第五版)谭浩强 学习笔记—重点及易错点(第8章指针-上)_第4张图片

④ 实参为指针变量,形参为数组名。

C程序设计(第五版)谭浩强 学习笔记—重点及易错点(第8章指针-上)_第5张图片

8.4通过指针引用字符串

在C语言中并没有字符串类型,字符串实际上是存放在字符数组中的一串字符。

8.4.1字符串的引用方式

(1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明“%s”输出该字符串。

(2)用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。

例子:定义一个字符数组,在其中存放字符串″I love China!″,输出该字符串和第8个字符。

#include 
int main()
{	char string[]="I love China!";	//定义字符数组sting
	printf("%s\n",string);		//用%s格式声明输出string,可以输出整个字符串
	printf("%c\n",string[7]);		//用%c格式输出一个字符数组元素 
	return 0;
}

定义一个字符指针变量,在其中存放字符串″I love China!″,输出该字符串和第8个字符。

#include 
int main()
{	char *string="I love China!";	//定义字符指针变量string并初始化
	printf("%s\n",string);		//输出字符串
	return 0;
}

注意:

string被定义为一个指针变量,基类型为字符型。它只能指向一个字符类型数据,而不能同时指向多个字符数据,更不是把″I love China!″这些字符存放到string中(指针变量只能存放地址),也不是把字符串赋给*string。只是把″I love China!″的第1个字符的地址赋给指针变量string。在内存中,字符串的最后被自动加了一个′\0′。

8.4.2字符指针作函数参数

例子:用函数调用实现字符串的复制。

(1) 用字符数组名作为函数参数

#include 
int main()
{	void copy_string(char from[], char to[]);
	char a[]="I am a teacher.";
	char b[]="You are a student.";
	printf("string a=%s\nstring b=%s\n",a,b);
	printf("copy string a to string b:\n");
	copy_string(a,b);		//用字符数组名作为函数实参
	printf("\nstring a=%s\nstring b=%s\n",a,b);
	return 0;
}

void copy_string(char from[], char to[])	//形参为字符数组 
{	int i=0;
	while(from[i]!='\0')
	{	to[i]=from[i]; i++;}
	to[i]='\0';
}

(2) 用字符型指针变量作实参

#include 
int main()
{	void copy_string(char from[], char to[]);	//函数声明
	char a[]="I am a teacher.";		//定义字符数组a并初始化
	char b[]="You are a student.";	//定义字符数组b并初始化
	char *from=a,*to=b;	//from指向a数组首元素,to指向b数组首元素 
	printf("string a=%s\nstring b=%s\n",a,b);
	printf("copy string a to string b:\n");
	copy_string(from,to);	//实参为字符指针变量
	printf("\nstring a=%s\nstring b=%s\n",a,b);
	return 0;
}
void copy_string(char from[], char to[]) 		//形参为字符数组
{	int i=0;
	while(from[i]!='\0')
	{	to[i]=from[i]; i++;}
	to[i]='\0';
}

(3) 用字符指针变量作形参和实参

#include 
int main()
{	void copy_string(char *from, char *to);
	char *a="I am a teacher.";		//a是char*型指针变量
	char b[]="You are a student.";	//b是字符数组
	char *p=b;		//使指针变量p指向b数组首元素
	printf("string a=%s\nstring b=%s\n",a,b);	//输出a串和b串
	printf("copy string a to string b:\n");
	copy_string(a,p);	//调用copy_string函数,实参为指针变量
	printf("\nstring a=%s\nstring b=%s\n",a,b);	//输出改变后的a串和b串
	return 0;
}
void copy_string(char *from, char *to)	//定义函数,形参为字符指针变量
{	for(;*from!='\0';from++,to++)
	{	*to=*from;}
	*to='\0';
}

8.4.3使用字符指针变量和字符数组的比较

字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),绝不是将字符串放到字符指针变量中。  

赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。(数组名是常量)

存储单元的内容。编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元(Visual C++为指针变量分配4个字节)。

指针变量的值是可以改变的,而字符数组名代表一个固定的值(数组首元素的地址),不能改变。

字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再赋值)。

引用数组元素。对字符数组可以用下标法(用数组名和下标)引用一个数组元素(如a[5]),也可以用地址法(如*(a+5))引用数组元素a[5]。如果定义了字符指针变量p,并使它指向数组a的首元素,则可以用指针变量带下标的形式引用数组元素(如p[5]),同样,可以用地址法(如*(p+5))引用数组元素a[5]。

用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。

你可能感兴趣的:(C语言,二级)