学习 C 语言的指针既简单又有趣。通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。
正如您所知道的,每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。请看下面的实例,它将输出定义的变量地址:
#include <stdio.h> int main () { int var1; char var2[10]; printf("var1 变量的地址: %p ", &var1 ); printf("var2 变量的地址: %p ", &var2 ); return 0; } |
当上面的代码被编译和执行时,它会产生下列结果:
var1 变量的地址: 0x7fff5cc109d4 var2 变量的地址: 0x7fff5cc109de |
通过上面的实例,我们了解了什么是内存地址以及如何访问它。接下来让我们看看什么是指针。
8.1什么是指针?
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。
指针变量声明的一般形式为:type *var-name;
在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */ double *dp; /* 一个 double 型的指针 */ float *fp; /* 一个浮点型的指针 */ char *ch; /* 一个字符型的指针 */ |
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
8.2指针变量
8.2.1 使用指针变量的例子
#include <stdio.h> 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 ",a,b); //输出变量a和b的值 printf("*pointer_1=%d,*pointer_2=%d ",*pointer_1,*pointer_2); //输出变量a和b的值 return 0; } |
运行结果:
上面变量对应的关系,如图:
注:定义指针变量时,左侧应有类型名,否则就不是定义指针变量。也称为基类型
8.2.2 怎样定义指针变量
定义指针的一般形式为:
类型名 *指针变量名;
如:
int *pointer_1;
注意:左端的int是在定义指针变量时必须指定的"基类型".换个说法就是类型名一定要有,否则是错误的定义。
如:
*pointer_1; //企图定义pointer_1为指针变量。出错
int *pointer_1; //正确,必须指定指针变量的基类型
说明:在定义指针变量时要注意一下几点:
(1) 指针变量前面的"*"表示该变量的类型为指针型变量。
(2) 在定义指针变量时必须制定基类型(即类型名)
一个变量的指针的含义包括两个方面,一是以存储单元编号表示的地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int,char,float等)。
(3)如何表示指针类型
指向整型数据的指针类型表示为"int*",读作"指向int的指针"或简称"int指针"
(4) 指针变量中只能存放地址(地址),不要讲一个整数赋给一个指针变量。如:
*pointer_1 = 100; //pointer_1是指针变量,100是整数,不合法的赋值
8.2.3 怎样引用指针变量
(1) 给指针变量赋值。如:
p = &a; //把a的地址赋给指针变量a
(2) 引用指针变量指向的变量
如果已执行"p = &a;",即指针变量p指向了整型变量a,则
printf("%d",*p);
作用:以整数形式输出指针变量p所指向的变量的值,即变量a的值
注:一定要带'*',否则输出地址
也可以用指针的形式对a重新赋值
*p = 1;
表示将1赋值给p当前p指向的变量,因为p指向变量a,则相当于把1赋值给a,即和"a = 1"等价。
(3) 引用指针变量的值。如:
printf("%o",p);
作用:以八进制数形式输出指针变量p的值,如果p指向了a,就是输出了a的地址,即&a.
printf("%o",&a); <==> printf("%o",p); //两者等价
例:
#include <stdio.h> int main() { int *p,a; a = 100; p = &a; printf("%o ",p); printf("%o ",&a); } |
要熟练掌握两个有关的运算符:
(1) &取地址运算符。&a是变量a的地址。
(2) * 指针运算符(或称“间接访问”运算符),*p代表指针变量p指向的对象。
#include <stdio.h> 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<b) //如果a<b { p=p1;p1=p2;p2=p;} //使p1与p2的值互换 printf("a=%d,b=%d ",a,b); //输出a,b printf("max=%d,min=%d ",*p1,*p2); //输出p1和p2所指向的变量的值 return 0; } |
运行结果:
指针在过程中的变化:
8.2.4 指针变量作为函数参数
例:对输入的两个整数按大小顺序输出。现用函数处理,而且用指针类型的数据作函数参数。
#include <stdio.h> void swap(int *p1,int *p2); //对swap函数的声明 int main() { 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<b) swap(pointer_1,pointer_2); //如果a<b,调用swap函数 printf("max=%d,min=%d ",a,b); //输出结果 return 0; } void swap(int *p1,int *p2) //定义swap函数 { int temp; temp=*p1; //使*p1和*p2互换 *p1=*p2; *p2=temp; } |
运行结果:
指针变量pointer_1 pointer_2的变量过程
注:要注意区分以下三种形式,可自己调试,增加理解
void swap(int *p1,int *p2)//定义swap函数 { int temp; temp=*p1;//使*p1和*p2互换 *p1=*p2; *p2=temp; } |
void swap(int *p1,int *p2) { int *temp; *temp=*p1; *p1=*p2; *p2=*temp; } |
void swap(int x,int y) { int temp; temp=x; x=y; y=temp; } |
下面有一个问题需要注意,也可以直接跳过,如果想提升可以理解,以理解为主,不需要死记硬背。
指针变量作为函数参数函数的调用可以(而且只可以)得到一个返回值(即函数值),而使用指针变量作参数,可以得到多个变化了的值。如果不用指针变量是难以做到这一点的。要善于利用指针法。 如果想通过函数调用得到n个要改变的值,可以这样做: 1.在主调函数中设n个变量,用n个指针变量指向它们; 2.设计一个函数,有n个指针形参。在这个函数中改变这n个形参的值; 3.在主调函数中调用这个函数,在调用时将这n个指针变量作实参,将它们的值,也就是相关变量的地址传给该函数的形参; 3.在执行该函数的过程中,通过形参指针变量,改变它们所指向的n个变量的值; 5.主调函数中就可以使用这些改变了值的变量。 |
例:输入3个整数a,b,c,要求按由大到小的顺序将它们输出。用函数实现。
#include <stdio.h> int main() { void exchange(int *q1, int *q2, int *q3); //函数声明 int a,b,c,*p1,*p2,*p3; printf("please enter three numbers:"); scanf("%d,%d,%d",&a,&b,&c); p1=&a;p2=&b;p3=&c; exchange(p1,p2,p3); printf("The order is:%d,%d,%d ",a,b,c); return 0; } void exchange(int *q1, int *q2, int *q3) //将3个变量的值交换的函数 { void swap(int *pt1, int *pt2); //函数声明 if(*q1<*q2) swap(q1,q2); //如果a<b,交换a和b的值 if(*q1<*q3) swap(q1,q3); //如果a<c,交换a和c的值 if(*q2<*q3) swap(q2,q3); //如果b<c,交换b和c的值 } void swap(int *pt1, int *pt2) //交换2个变量的值的函数 { int temp; temp=*pt1; //交换*pt1和*pt2变量的值 *pt1=*pt2; *pt2=temp; } |
运行结果:
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 |
引用数组元素的方式
(1) 下标法
(2) 指针法
p=&a[0]; //p的值是a[0]的地址
等价于
p=a; //p的值是数组a首元素(即a[0])的地址
注:程序中的数组名不代表整个数组,只代表数组首元素的地址。
以下是指针变量的初始化方式:
(1) 在定义后再单独进行初始化
int *p;
p=&a[0]; //不应写成*p=&a[0];
(2) 在定义时直接进行初始化
int *p=&a[0];
等价于
int *p=a;
以上三种的作用:将a数组首元素(即a[0])的地址赋值给指针变量p(而不是赋给*p)
8.3.2 在引用数组元素时指针的运算
在指针已指向一个数组元素时,可以对指针进行以下运算:
1.加一个整数(用+或+=),如p+1,表示指向同一数组中的下一个元素;
2.减一个整数(用-或-=),如p-1,表示指向同一数组中的上一个元素;
3.自加运算,如p++,++p;
4.自减运算,如p--,--p。
两个指针相减,如p1-p2(p1和p2都指向同一数组中的元素时才有意义),结果为两个地址之差除以数组元素的长度。
注:两个地址不能相加,如p1+p2是无实际意义的。
说明:
*(p+i)或*(a+i)是p+i或a+i所指向的数组元素,即a[i]。
p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址
[]实际上是变址运算符,即将a[i]按a+i计算地址,然后找出此地址单元中的值。
例:有一个整型数组a,有10个元素,要求输出数组中的全部元素。
//下标法 #include <stdio.h> int main() { int a[10]; int i; printf("please enter 10 integer numbers:"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(i=0;i<10;i++) printf("%d ",a[i]); //数组元素用数组名和下标表示 printf("% "); return 0; } |
//通过数组名计算数组元素地址,找出元素的值 #include <stdio.h> int main() { int a[10]; int i; printf("please enter 10 integer numbers:"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(i=0;i<10;i++) printf("%d ",*(a+i)); //通过数组名和元素序号计算元素地址找到该元素 printf(" "); return 0; } |
//用指针变量指向数组元素 #include <stdio.h> int main() { int a[10]; int *p,i; printf("please enter 10 integer numbers:"); for(i=0;i<10;i++) scanf("%d",&a[i]); for(p=a;p<(a+10);p++) printf("%d ",*p); //用指针指向当前的数组元素 printf(" "); return 0; } |
说明:
第(1)和第(2)种方法执行效率是相同的。C编译系统是将a[i]转换为*(a+i)处理的,即先计算元素地址。因此用第(1)和第(2)种方法找数组元素费时较多。 第(3)种方法比第(1)、第(2)种方法快,用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的。这种有规律地改变地址值(p++)能大大提高执行效率。 |
例:通过指针变量输出整型数组a的10个元素。
#include <stdio.h> 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(" "); return 0; } |
#include <stdio.h> 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(" "); return 0; } |
运行上面两个代码看看有何区别,造成这种区别的原因是什么?
答:第一种是因为指针已经到了数组的末尾,继续循环则超出了数组的范围,则会直接输出P对应的地址值。如图所示:
技巧:
设p开始时指向数组a的首元素(即p=a)
(1)
p++; //使p指向下一元素a[1]
*p; //得到下一个元素a[1]的值
(2)
*p++; /*由于++和*同优先级,结合方向自右而左,因此它等价于*(p++)。先引用p的值,实现*p的运算,然后再使p自增1*/
(3)
*(p++); //先取*p值,然后使p加1
*(++p); //先使p加1,再取*p
(4)
++(*p); /*表示p所指向的元素值加1,如果p=a, 则相当于++a[0],若a[0]的值为3,则a[0]的值为4。注意: 是元素a[0]的值加1,而不是指针p的值加1*/
(5)
如果p当前指向a数组中第i个元素a[i],则:
*(p--) //相当于a[i--],先对p进行“*”运算,再使p自减
*(++p) //相当于a[++i],先使p自加,再进行“*”运算
*(--p) //相当于a[--i],先使p自减,再进行“*”运算
8.3.4 用数组名作函数参数
以变量名和数组名作为函数参数的比较
C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。
注意:实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。
例:将数组a中n个整数按相反顺序存放
#include <stdio.h> int main() { void inv(int x[],int n); //inv函数声明 int i,a[10]={3,7,9,11,0,6,7,5,4,2}; printf("The original array: "); for(i=0;i<10;i++) printf("%d ",a[i]); //输出未交换时数组各元素的值 printf(" "); inv(a,10); //调用inv函数,进行交换 printf("The array has been inverted: "); for(i=0;i<10;i++) printf("%d ",a[i]); //输出交换后数组各元素的值 printf(" "); return 0; } void inv(int x[],int n) //形参x是数组名 { int temp,i,j,m=(n-1)/2; for(i=0;i<=m;i++) { j=n-1-i; temp=x[i]; x[i]=x[j]; x[j]=temp; //把x[i]和x[j]交换 } return; } |
#include <stdio.h> int main() { void inv(int *x,int n); int i,a[10]={3,7,9,11,0,6,7,5,4,2}; printf("The original array: "); for(i=0;i<10;i++) printf("%d ",a[i]); printf(" "); inv(a,10); printf("The array has been inverted: "); for(i=0;i<10;i++) printf("%d ",a[i]); printf(" "); return 0; } void inv(int *x,int n) //形参x是指针变量 { int *p,temp,*i,*j,m=(n-1)/2; i=x; j=x+n-1; p=x+m; for(;i<=p;i++,j--) { temp=*i; *i=*j; *j=temp;} //*i与*j交换 return; } |
以上两种方式所得结果是一致的,只是实现的形式有区别。所得结果都为下图所示:
说明:以下的形式要认识,以下四种都是等价的
例:将数组a中n个整数按相反顺序存放,用指针变量作实参。
#include <stdio.h> int main() { void inv(int *x,int n); //inv函数声明 int i,arr[10],*p=arr; //指针变量p指向arr[0] printf("The original array: "); for(i=0;i<10;i++,p++) scanf("%d",p); //输入arr数组的元素 printf(" "); p=arr; //指针变量p重新指向arr[0] inv(p,10); //调用inv函数,实参p是指针变量 printf("The array has been inverted: "); for(p=arr;p<arr+10;p++) printf("%d ",*p); printf(" "); return 0; } void inv(int *x,int n) //定义inv函数,形参x是指针变量 { int *p,m,temp,*i,*j; m=(n-1)/2; i=x;j=x+n-1;p=x+m; for(;i<=p;i++,j--) { temp=*i;*i=*j;*j=temp;} return; } |
注:如果用指针变量作实参,必须先使指针变量有确定值,指向一个已定义的对象。
例:用指针方法对10个整数按由大到小顺序排序。(选择排序法)
//形参是数组 #include <stdio.h> 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(" "); return 0; } void sort(int x[],int n)//x是形参数组名 { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(x[j]>x[k]) k=j; if(k!=i) { t=x[i]; x[i]=x[k]; x[k]=t;} } } |
//形参是指针 #include <stdio.h> 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(" "); return 0; } void sort(int *x,int n) //形参x是指针变量 { int i,j,k,t; for(i=0;i<n-1;i++) { k=i; for(j=i+1;j<n;j++) if(*(x+j)>*(x+k)) k=j; //*(x+j)就是x[j],其他亦然 if(k!=i) { t=*(x+i); *(x+i)=*(x+k); *(x+k)=t;} } } |
8.3.5 通过指针引用多维数组
注:这章节了解即可,如果想提升自己,不仅需要理解还需要会使用。
理解以下这张图便可了解多维数组:
说明:我们在这里说明一下,在计算机底层是不存在二维数组这种存储方式的,都是以一维数组为基础逻辑化出二维数组,而一维数组不仅是逻辑化更是物理上也是可以实现的。总的来说,二维数组是一维数组的逻辑化,本质上二维数组还是一维数组。只是为了方便人们理解,才把二维数组分为行和列的方式显示。
如果用一个指针变量pt来指向此一维数组:
int (*pt)[4];
//表示pt指向由4个整型元素组成的一维数组,此时指针变量pt的基类型是由4个整型元素组成的一维数组
例:输出二维数组的有关数据(地址和元素的值)。
#include <stdio.h> int main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; printf("%d,%d ",a,*a); //0行起始地址和0行0列元素地址 printf("%d,%d ",a[0],*(a+0)); //0行0列元素地址 printf("%d,%d ",&a[0],&a[0][0]); //0行起始地址和0行0列元素地址 printf("%d,%d ",a[1],a+1); //1行0列元素地址和1行起始地址 printf("%d,%d ",&a[1][0],*(a+1)+0); //1行0列元素地址 printf("%d,%d ",a[2],*(a+2)); //2行0列元素地址 printf("%d,%d ",&a[2],a+2); //2行起始地址 printf("%d,%d ",a[1][0],*(*(a+1)+0)); //1行0列元素的值 printf("%d,%d ",*a[2],*(*(a+2)+0)); //2行0列元素的值 return 0; } |
结果如下:
说明:要注意看懂以上的格式,区分各种形式的联系和区别。如a和*a在输出上是等价的
例:有一个3×4的二维数组,要求用指向元素的指针变量输出二维数组各元素的值。
#include <stdio.h> int main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; int *p; //p是int *型指针变量 for(p=a[0];p<a[0]+12;p++) //使p依次指向下一个元素 { if((p-a[0])%4==0) printf(" "); //p移动4次后换行 printf("%4d",*p); //输出p指向的元素的值 } printf(" "); return 0; } |
运行结果:
例:输出二维数组任一行任一列元素的值。
#include <stdio.h> int main() { int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; //定义二维数组a并初始化 int (*p)[4],i,j; //指针变量p指向包含4个整型元素的一维数组 p=a; //p指向二维数组的0行 printf("please enter row and colum:"); scanf("%d,%d",&i,&j); //输入要求输出的元素的行列号 printf("a[%d,%d]=%d ",i,j,*(*(p+i)+j)); //输出a[i][j]的值 return 0; } |
#include <stdio.h> int main() { int a[4]={1,3,5,7}; //定义一维数组a,包含4个元素 int (*p)[4]; //定义指向包含4个元素的一维数组的指针变量中 p=&a; //使p指向一维数组 printf("%d ",(*p)[3]); //输出a[3],输出整数7 return 0; } |
比较: ① int a[4];(a有4个元素,每个元素为整型) ② int (*p)[4]; 第②种形式表示(*p)有4个元素,每个元素为整型。也就是p所指的对象是有4个整型元素的数组,即p是指向一维数组的指针,见图8.24。应该记住,此时p只能指向一个包含4个元素的一维数组,不能指向一维数组中的某一元素。p的值是该一维数组的起始地址。虽然这个地址(指纯地址)与该一维数组首元素的地址相同,但它们的基类型是不同的。 |
指向由m个元素组成的一维数组的指针变量 要注意指针变量的类型,从“int (*p)[4];”可以看到,p的类型不是int *型,而是int (*)[4]型,p被定义为指向一维整型数组的指针变量,一维数组有4个元素,因此p的基类型是一维数组,其长度是16字节。“*(p+2)+3”括号中的2是以p的基类型(一维整型数组)的长度为单位的,即p每加1,地址就增加16个字节(4个元素,每个元素4个字节),而“*(p+2)+3”括号外的数字3,不是以p的基类型的长度为单位的。由于经过*(p+2)的运算,得到a[2],即&a[2][0],它已经转化为指向列元素的指针了,因此加3是以元素的长度为单位的,加3就是加(3×4)个字节。虽然p+2和*(p+2)具有相同的值,但由于它们所指向的对象的长度不同,因此(p+2)+3和*(p+2)+3的值就不相同了。 |
一维数组名可以作为函数参数,多维数组名也可作函数参数。
用指针变量作形参,以接受实参数组名传递来的地址。可以有两种方法:
① 用指向变量的指针变量;
② 用指向一维数组的指针变量。
例8:有一个班,3个学生,各学4门课,计算总平均分数以及第n个学生的成绩。
#include <stdio.h> int main() { void average(float *p,int n); void search(float (*p)[4],int n); float score[3][4]={{65,67,70,60},{80,87,90,81},{90,99,100,98}}; average(*score,12); //求12个分数的平均分 search(score,2); //求序号为2的学生的成绩 return 0; } void average(float *p,int n) //定义求平均成绩的函数 { float *p_end; float sum=0,aver; p_end=p+n-1; //n的值为12时,p_end的值是p+11,指向最后一个元素 for(;p<=p_end;p++) sum=sum+(*p); aver=sum/n; printf("average=%5.2f ",aver); } void search(float (*p)[4],int n) //p是指向具有4个元素的一维数组的指针 { int i; printf("The score of No.%d are: ",n); for(i=0;i<4;i++) printf("%5.2f ",*(*(p+n)+i)); printf(" "); } |
运行结果:
注:实参与形参如果是指针类型,应当注意它们的基类型必须一致。不应把int *型的指针(即数组元素的地址)传给int (*)[4] 型(指向一维数组)的指针变量,反之亦然。
例:在上例的基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩。
#include <stdio.h> int main() { void search(float (*p)[4],int n); //函数声明 float score[3][4]={{65,57,70,60},{58,87,90,81},{90,99,100,98}}; //定义二维数组函数score search(score,3); //调用search函数 return 0; } void search(float (*p)[4],int n) //形参p是指向包含4个float型元素的一维数组的指针变量 { int i,j,flag; for(j=0;j<n;j++) { flag=0; for(i=0;i<4;i++) if(*(*(p+j)+i)<60) flag=1; //*(*(p+j)+i)就是score[j][i] if(flag==1) { printf("No.%d fails,his scores are: ",j+1); for(i=0;i<4;i++) printf("%5.1f ",*(*(p+j)+i)); //输出*(*(p+j)+i)就是输出score[j][i]的值 printf(" "); } } } |
运行结果:
8.4 通过指针引用字符串
8.4.1 字符串的引用方式
(1)用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明“%s”输出该字符串。
(2)用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
例:定义一个字符数组,在其中存放字符串″I love China!″,输出该字符串和第8个字符。
#include <stdio.h> int main() { char string[]="I love China!"; //定义字符数组sting printf("%s ",string); //用%s格式声明输出string,可以输出整个字符串 printf("%c ",string[7]); //用%c格式输出一个字符数组元素 return 0; } |
运行结果:
例:通过字符指针变量输出一个字符串。
#include <stdio.h> int main() { char *string="I love China!"; //定义字符指针变量string并初始化 printf("%s ",string); //输出字符串 return 0; } |
运行结果:
在C语言中只有字符变量,没有字符串变量。
char *string="I love China!";
等价于
char *string; //定义一个char *型变量
string=″I love China!″;
//把字符串第1个元素的地址赋给字符指针变量string
注:string被定义为一个指针变量,基类型为字符型。它只能指向一个字符类型数据,而不能同时指向多个字符数据,更不是把″I love China!″这些字符存放到string中(指针变量只能存放地址),也不是把字符串赋给*string。只是把″I love China!″的第1个字符的地址赋给指针变量string。
可以对指针变量进行再赋值,string=″I am a student.″; //对指针变量string重新赋值
可以通过字符指针变量输出它所指向的字符串,printf(″%s ″,string); //%s可对字符串进行整体的输入输出
说明:%s是输出字符串时所用的格式符,在输出项中给出字符指针变量名string,则系统会输出string所指向的字符串第1个字符,然后自动使string加1,使之指向下一个字符,再输出该字符……如此直到遇到字符串结束标志′