目录
善于利用指针-上
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使用字符指针变量和字符数组的比较
如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。编译系统根据程序中定义的变量类型,分配一定长度的空间。内存区的每一个字节有一个编号,这就是“地址”。 由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元,将地址形象化地称为“指针”。
C语言中的地址(指针)包括位置信息(内存编号,或称纯地址)和它所指向的数据的类型信息,或者说它是“带类型的地址”。比如说:若有int型变量a与float变量b,如果先后把他们分配在2000开始的储存单元中,&a与&b的信息也不完全相同。
总之指针就是带有类型信息的编号。
注意指针与指针变量的区别与联系。指针是一个地址,而指针变量是存放地址的变量,我们可以给指针变量赋值,可是不能给指针赋值(这句话非常重要,等大家进一步学习后就会有所理解,请大家务必注意,这里有大坑)。
例子:通过指针变量访问整型变量
#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;
}
例子中&a就是一个指针,pointer_1就是指针变量,pointer_1可以自加:pointer_1++,但是&a++就不可以,因为&a是常量无法进行自加。
一般形式:
类型名 * 指针变量名;
如:
int * point;
说明:
(1) 指针变量前面的“*”表示该变量为指针型变量。指针变量名则不包含“*”。
(2) 在定义指针变量时必须指定基类型。一个变量的指针的含义包括两个方面,一是以存储单元编号表示的纯地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int,char,float等)。
(3) 如何表示指针类型。指向整型数据的指针类型表示为“int *”,读作“指向int的指针”或简称“int指针”。
(4) 指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量。
引用指针变量时有以下三种情况:
① 给指针变量赋值。 ② 引用指针变量指向的变量。 ③引用指针变量的值。
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
函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。如果不用指针变量是难以做到这一点的。要善于利用指针法。
如果想通过函数调用得到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
所谓数组元素的指针就是数组元素的地址。可以用一个指针变量指向一个数组元素。
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]; 等价。
在指针已指向一个数组元素时,可以对指针进行以下运算:
加一个整数(用+或+=),如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,而是根据定义的基类型加上一个数组元素所占用的字节数。
执行运算前一定要清楚指针变量在运算前的指向地址,否则无法确定运算后的指向。
例子:通过指针变量输出整型数组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) 要注意指针变量的当前值。
例子:用指针方法对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语言中并没有字符串类型,字符串实际上是存放在字符数组中的一串字符。
(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′。
例子:用函数调用实现字符串的复制。
(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';
}
字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),绝不是将字符串放到字符指针变量中。
赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。(数组名是常量)
存储单元的内容。编译时为字符数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元(Visual C++为指针变量分配4个字节)。
指针变量的值是可以改变的,而字符数组名代表一个固定的值(数组首元素的地址),不能改变。
字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再赋值)。
引用数组元素。对字符数组可以用下标法(用数组名和下标)引用一个数组元素(如a[5]),也可以用地址法(如*(a+5))引用数组元素a[5]。如果定义了字符指针变量p,并使它指向数组a的首元素,则可以用指针变量带下标的形式引用数组元素(如p[5]),同样,可以用地址法(如*(p+5))引用数组元素a[5]。
用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。