目录
14.1 指针概述
- 指针
对每一个变量,计算机都指定一段内存用以存储变量值。变量所使用的这段内存的开始地址称为变量的地址,存储这一地址的变量称为指针变量,简称指针。简单的说,指针就是变量的地址。
- 指针变量
所谓指针变量,就是专门用来保存指针的一类变量,它的值是其他变量的地址。指针变量是指向其他变量的变量。通过指针变量可以实现对其他变量的访问。
- 直接访问数据
直接访问数据是指通过变量名直接使用变量数据,我们以前编写的程序,都是用这种方式访问数据。例如:
int i; scanf("%d",&i); printf("%d",i);
- 间接访问数据
间接访问数据是指通过指针变量去访问其他变量数据,这是C语言中大量使用的一种数据访问方式。
指针变量指向的目标可以是任何数据类型,例如可以是基本类型,也可以是数组、结构体等。因此,任何类型的数据,都可以通过指针变量实现间接访问。
14.2 指针变量的定义和使用
与其他变量一样,指针变量也必须先定义、后使用。 现在详细介绍定义和使用指针变量的方法。
14.2.1 定义指针变量
一般定义格式如下:
数据类型 *指针变量名1,*指针变量名2┅;说明:
(1)变量名前的“*” 是指针类型变量的标志,在定义时不能省略。
(2)“数据类型”是指针变量指向的目标的数据类型。例如,下面语句分别定义了指向整型变量的指针变量p和指向实型变量的指针变量q。
int *p ;
float *q;
(3)其他类型的变量允许和指针变量在同一个语句中定义。如:int m,n,*p,*q;
14.2.2 使用指针变量
- 指针变量的初始化
指针变量的初始化,就是在定义指针变量的同时为其赋初值。由于指针变量是指针类型,所赋初值应是一个地址值。一般格式如下:
数据类型 *指针变量名1=地址1,*指针变量名2=地址2,┅;其中,地址的表示形式有多种,如&变量名、数组名、另外的指针变量等。 例如:
int m;
int *p=&m;
char string[20];
char *str=string;说明:
(1)不能用尚未定义的变量给指针赋初值。
(2)当用一个变量地址为指针变量赋初值时,该变量的数据类型必须与指针变量指向的数据类型一致。
(3)除0之外,一般不把其他整数作为初值赋给指针变量。程序中变量的地址是由计算机分配的,当用一个整数为一个指针变量赋初值后,可能会造成难以预料的后果。当用0对指针变量赋初值时,系统会将该指针变量初始化为一个空指针,不指向任何目标。C语言的空指针也可使用NULL表示,它是在头文件“studio.h中定义的一个宏”。
- 指针变量的赋值
在程序执行过程中,可以使用赋值运算为指针变量赋值。一般格式如下:
指针变量=地址;例如:
int m=196,*p,*q;
p=&m; /* 将变量m的地址赋给指针变量p */
q=p; /* 利用指针变量p为指针变量q赋值 */
- 使用指针变量输入数据
当指针变量有了明确的指向后。即可使用该指针变量为指向的目标输入数据。
例如:
int score,*p;
p=&score;
scanf("%d",p);
该程序段中scanf语句的功能与下列语句等价:
scanf("%d",&score);
- 指针运算符与目标访问
对于指针变量,访问其指向的目标时,使用指向运算“*”。一般格式为:*指针变量
例如,以下语句输出上述变量score的值:
printf("%d",*p); //*p表示p指向的目标score。该语句与“printf("%d",score);”语句等价C语言中的指针运算符是“*”运算符,它有两种作用:在定义指针时,它的作用是用来说明一个指针变量;在使用指针运算的时候,它表示取该指针指向的单元的值。
“&”符是一种取地址运算符,其作用是把其后变量的地址取出来,“&变量名”也可以直接理解为变量的地址。
- 指针运算举例
输入a和b两个整数,然后按先大后小的顺序输出。程序如下:
void main(void)
{
int *p1,*p2,*p,a,b;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
if(a<b)
{
p=p1;
p1=p2;
p2=p;
} /* 交换指针值 */
printf("a=%d,b=%d\n",a,b);
printf("Max=%d,Min=%d\n",*p1,*p2);
}
14.3 指针与数组
14.3.1 指针与一维数组
- 指针与一维数组的关系
一维数组在内存存储时,自首元素开始连续占用存储空间。 因此,对于长度为N的一维数组a,当使用指针p指向其首元素后,即可通过指针p访问数组的各个元素。指针p与各个元素的对应关系如下图所示。
假如a数组为简单类型数组,则a[0]可以用“*p”表示、a[1]可以用“*(p+1)”表示,对于任意元素a[i],则用“*(p+i)”表示。
- 定义与使用指向一维数组的指针变量
我们用如下的方法就能定义指向一维数组的指针变量:
int a[6]={10,20,30,40,50,60};
int *p=a;//数组名代表数组的首地址
或者:
int *p=&a[0];通过指针p引用数组元素的方法如下:
⑴ 对第i个元素,指向它的指针为p+i,其数组元素表示为*(p+i)。
⑵ 可以使用带下标的指针变量表示数组元素,p[i]与arr[i]等价,也与*(p+i)等价。说明:
(1)用指针指向数组元素时,只与元素序号有关,并不考虑每个数组元素占用的存储单元数量,即对任何类型的一维数组arr,当用arr(或&arr[0])为指针变量p赋值后,如下表达式都成立:p+i==&arr[i]
(2)必须注意,在定义指向数组的指针变量时,指针变量的数据类型要与数组的数据类型一致。
- 程序示例
(1)用指针实现一维数组的输入输出。
//程序1
void main()
{
int a[10];
int *p=a, i;
for(i=0;i<10;i++)
scanf("%d",p+i);
for(i=0;i<10;i++)
printf("%d ",*p++);
}
//程序2
void main()
{
int a[10];
int *p=a, i;
for(i=0;i<10;i++)
scanf("%d",p++);
p=a;
for(i=0;i<10;i++)
printf("%d ",*(p+i));
}
//程序3
void main()
{
int a[10];
int *p;
for(p=a;p<(a+10);p++)
scanf("%d",p);
for(p=a;p<(a+10);p++)
printf("%d ",*p);
}
//程序4
main()
{
int i,a[10];
for(i=0;i<10;i++)
scanf("%d",a+i);
for(i=0;i<10;i++)
printf("%d",*(a+i));
}
(2)用指针编写一维数组的排序程序,其中排序数据由随机数函数生成。
#define N 10
#include"stdlib.h"
void main()
{
int a[N],i,j,temp,*p;
printf("data: ");
for(p=a;p<a+N;p++)
{
*p=rand()%100;
printf("%4d",*p);
}
for(i=1;i<N;i++)
for(p=a;p<a+N-i;p++)
if(*p>*(p+1))
{
temp=*p;
*p=*(p+1);
*(p+1)=temp;
}
printf("\nresult:");
for(p=a;p<a+N;p++)
printf("%4d",*p);
printf("\n");
}
14.3.2 指针与二维数组
- 指针与二维数组的关系
我们知道,一个二维数组在计算机中存储时,是按照先行后列的顺序依次存储的,当把每一行看作一个整体,即视为一个大的数组元素时,这个存储的二维数组也就变成了一个一维数组了。而每个大数组元素对应二维数组的一行,我们就称之为行数组元素,显然每个行数组元素都是一个一维数组。因此,用两级数组的观点透视二维数组时,一个M×N的二维数组a,可分解为如图所示的二维数组。
设p是指向数组a的指针变量,若有“p=a[0];”,则p+j将指向a[0]数组中的元素a[0][j]。
由于a[0]、a[1]┅a[M-1]等各个行数组依次连续存储,则对于a数组中的任一元素a[i][j],将由指针p+i*N+j指向,元素a[i][j]相应的指针表示为:*(p+i*N+j)。同样,a[i][j]也可使用指针下标法表示:p[i*N+j]。
- 用二维数组名作地址表示数组元素
对于二维数组a,其a[0]数组由a指向,a[1]数组则由a+1指向,a[2]数组由a+2指向,以此类推。因此,*a与a[0]等价、*(a+1)与a[1]等价、*(a+2)与a[2]等价,┅,即对于a[i]数组,由*(a+i)指向。由此,对于数组元素a[i][j],用数组名a的表示形式为:*(*(a+i)+j),指向该元素的指针为:*(a+i)+j
说明:
对二维数组a,虽然a[0]、a都是数组首地址,但二者指向的对象不同:
①a[0]是一维数组名,代表a[0]数组的首元素地址,对其进行“*”运算,得到的是一个数组元素值,即a[0]数组的首元素值,因此,*a[0]==a[0][0]==*(*a)。
②a是一个二维数组名,该数组由a[0]、a[1]、a[2]...m个行数组元素构成,a代表首个行数组元素的地址,它的指针移动单元是“行”,所以a+i指向的行数组i,即指向a[i]。对a进行“*”运算,得到的是一维数组a[0]的首地址,即:*a==a[0]==&a[0][0]。
- 程序示例
求二维数组元素的最大值。
该问题只需对数组元素遍历,即可求解。因此,可以通过顺序移动数组指针的方法实现。程序如下:
void main()
{
int a[3][4]={{3,17,8,11},{66,7,8,19},{12,88,7,16}};
int *p,max;
for(p=a[0],max=*p;p<a[0]+12;p++)
if(*p>max)
max=*p;
printf("MAX=%d\n",max);
}
14.3.3 指针与字符串
- 使用字符指针处理字符串
字符串的处理方法有两种:一种方法是使用字符数组处理字符串,相关知识前面已进行了专门介绍;另一种方法是使用字符指针处理字符串,这是C语言中更为常用的一种方法。 它首先通过一定的方式,用字符指针指向字符串,然后通过字符指针来访问字符串存贮区域,从而实现对字符串的操作。
使字符型指针指向字符串的方法通常有两种:
(1)通过定义指针变量时初始化指向字符串。例如:
char *p="a string";
"a string"是一个字符串常量,它保存在计算机的一个内存区域。用该字符串对字符指针变量p初始化,就是把字符串首字符的地址赋给p。
(2)利用赋值语句使指针变量指向字符串。例如:
char ch[20],*s;
char *str1=ch,*str2;
s="string";
str2=str1;
- 程序示例
用指针复制字符串。程序如下:
void main()
{
char a[]="I am a student.";
char b[30],*p1,*p2;
int i;
for(p1=a,p2=b;*p1!='\0';p1++,p2++)
*p2=*p1;
*p2='\0';
printf("string a is: %s\n",a);
printf("string b is: ");
for(i=0;b[i]!='\0';i++)
printf("%c",b[i]);
printf("\n");
}
14.3.4 指针数组
- 一维指针数组的定义
数组元素为指针类型的数组称为指针数组,指针数组中的每一个元素都是指针变量,它们指向相同类型的数据。
一维指针数组的定义形式如下:
数据类型 *数组名[数组长度];
例如:
char *days[7];//该语句定义了一个名为days的字符型指针数组,它有7个指向字符数据的指针变量,即它能存储7个指针。与其他数组一样,指针数组可以在定义时初始化,也可以通过赋值语句对其赋指针值。由于指针数组的每个元素是指针变量,只能存储地址值,所以对指向字符串的指针数组赋初值时,要把字符串的首地址赋给指针数组的对应元素。
例如:
Char*days[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};//初始化后,days的每一个元素都指向一个字符串,元素值是对应字符串的首地址。
- 程序示例
分行输出days数组指向的7个字符串。程序如下:
#include "stdio.h"
void main()
{
int i;
char *days[7]={"Sunday","Monday","Tuesday", "Wednesday","Thursday","Friday","Saturday"};
for(i=0;i<7;i++)
puts(days[i]);
}
14.4 指针作函数参数
指针可以指向任何类型的数据,因此通过指针能够实现任何类型的数据处理。函数使用指针参数,就可以使函数处理各种类型的数据。与基本数据类型的变量作函数参数的最大区别是:在函数间传递的不是变量的数值,而是变量的地址,这样可以通过函数直接处理实参指针指向的数据。
14.4.1 简单指针变量作函数参数
简单变量指针作为函数参数,是指针作函数参数中最基本的内容,它能够实现简单变量的地址向函数的传递。
用swap()函数实现交换两个变量的值。 程序如下:
void swap(int *p1,int *p2) { int temp; temp=*p1; *p1=*p2; *p2=temp; } void main() { int x,y; scanf("%d,%d",&x,&y); if(x<y) swap(&x,&y); //交换变量x、y的值。注意,此处必须是地址,若使用数据,不会有交换结果 printf("%d,%d\n",x,y); }
14.4.2 指向数组的指针作函数参数
由于数组名是数组的首地址,与指针具有一些相同的性质。因此,用指向数组的指针作函数的参数和用数组名作函数参数也有许多相同之处。
现给出相关程序实例:
(1)求一维数组的最大元素值。
我们用三个函数实现这个程序:用输入函数input()建立数组;用max_a()函数求最大值;用main()函数作为主调函数。为了方便对函数编写,先假定数组长度为n,指针p指向该数组。void input(int *p,int n) { int i; for(i=0;i<n;i++) scanf("%d",p+i); return; } int max_a(int *p,int n) { int i,max=*p; for(i=1;i<n;i++) if(*(p+i)>max) max=*(p+i); return(max); } void main() { int a[10]; input(a,10); printf("MAX=%d\n",max_a(a,10)); }
(2)利用一维数组的排序函数对数组排序。
void p_sort(int *,int ); void output(int *,int); void main() { int a[10]={3,-5,8,16,7,19,11,6,17,5}; p_sort(a,10); printf("result: "); output(a,10); } void p_sort(int *p,int n) { int i,temp,*q; for(i=1;i<n;i++) for(q=p;q<p+n-1;q++) if(*q>*(q+1)) { temp=*q; *q=*(q+1); *(q+1)=temp; } } void output(int *p,int n) { int *q; for(q=p;q<p+n;q++) printf("%d ",*q); printf("\n"); }
14.4.3 字符串指针作函数参数
字符串指针作函数的参数与之前介绍的数组指针作函数参数没有本质区别,函数间传递的都是地址值,仅仅是指针指向对象的类型不同而已。
现给出相关程序实例:
用字符串指针作函数参数,将输入的一个字符串复制到另一个字符串中。 程序如下:
#include "stdio.h" void main() { void copy_s(char *,char *); char a[20],b[30]; gets(a); copy_s(a,b); puts(b); } void copy_s(char *str1,char *str2) { while((*str2=*str1)!='\0') { str1++; str2++; } }
14.4.4 指针数组作函数参数
指针数组的元素是指针变量,用指针数组能够实现一组字符串的处理。当用指针数组作函数参数时,就可以设计通用的多字符串操作函数。
现给出相关程序实例:
将一组字符串按字典顺序排序后输出。程序如下:
#include "stdio.h" #include "string.h" void main() { void string_sort(char *[],int); void string_out(char *[],int); char*days[7]={"Sunday","Monday","Tuesday", "Wednesday","Thursday","Friday","Saturday"}; string_sort(days,7); string_out(days,7); } void string_sort(char *string[],int n) { char *temp; int i,j; for(i=1;i<n;i++) { for(j=0;j<n-i-1;j++) if(strcmp(string[j],string[j+1])>0) { temp= string [j]; string [j]= string [j+1]; string [j+1]=temp; } } } void string_out(char * string [],int n) { int i; for(i=0;i<n;i++) printf("%s ", string[i]); }
14.4.5 使用带参数的main()函数
- 概念
指针数组的一个重要应用是作为main()函数的形参,带参数的main函数的一般形式如下:
main (int argc, char *argv[ ])
其中,argc表示命令行参数个数,argv是指向命令行参数的指针数组。在操作系统下运行C程序时,可以以命令行参数形式向main()函数传递参数。命令行参数的一般形式是:
运行文件名 参数1 参数2 …… 参数n
运行文件名和参数之间、各个参数之间要用一个空格分隔。指针argv[0]指向的字符串是运行文件名,argv[1]指向的字符串是命令行参数1,argv[2]指向的字符串是命令行参数2,……,等等。
- 程序示例
#include "stdio.h"
main(int argc,char *argv[])
{
int i;
printf("argc=%d\n",argc); //输出参数argc的值
for(i=1;i<argc;i++)
printf("%s\n",argv[i]); //输出作为命令行参数的各个字符串
}
14.5 指针函数
函数返回值是指针类型的函数称为指针函数。指针函数的定义与其他函数有一定区别,需要在函数名前使用“*”符。一般格式如下:
数据类型 *函数名(形参表)
{
函数体
}需要特别注意的是:对于指针函数,用return返回的值必须是一个指针值。
现给出相关程序实例:
将一组字符串中长度最大的找出来,并输出这个字符串。程序如下:#include "stdio.h" #include "string.h" void main() { char *max_lenth(char *[],int); char *p_string[4]={"Sydney2000","Beijing2008","Athens1996","Korea1992"}; char *p; p=max_lenth(p_string,4) puts(p); } char *max_lenth(char *string[],int n) { int i,posion,max_l; posion=0; max_l=strlen(string[0]); for(i=1;i<n;i++) if(strlen(string[i])>max_l) { max_l=strlen(string[i]); posion=i; } return(string[posion]); }