第十二章 指针第十二章 指针
12.1 指针与指针变量如,int a=1,b=2;
float x=3.4,y = 4.5 ;
double m=3.124;
char ch1='a?,ch2='b';
1
2
4.5
3.4
3.124
a
b
2000
2002
2004
2008
2012
2020
2021
变量 a
变量 b
变量 x
变量 y
变量 m
变量 ch1
变量 ch2
一个地址唯一指向一个内存变量,我们称这个地址为变量的指针。如果将变量的地址保存在内存的特定区域,用变量来存放这些地址,这样的变量就是指针变量,通过指针对所指向变量的访问,也就是一种对变量的“间接访问”。
第十二章 指针
1
2
4.5
3.4
3.124
a
b
2000
2002
2004
2008
2012
2020
2021
变量 a
变量 b
变量 x
变量 y
变量 m
变量 ch1
变量 ch2
1012
pa
1004
2012
2000
2002
2004
2008
2020
2021
1000
1002
1006
1008
1010
1014
1016
pb
px
py
pm
pch1
pch2
设一组指针变量 pa,pb,px,py,pm,pch1,pch2,
分别指向上述的变量 a,b,x,y,m,ch1,ch2,指针变量也同样被存放在内存,二者的关系如图:
12.1 指针与指针变量第十二章 指针
指针定义的格式,类型说明符 *指针名;
*表示这是一个指针变量,
变量名即为定义的指针变量名;
类型说明符表示本指针变量所指向的变量的数据类型,也就是说一个指针变量只能指向同一类型的变量。
如,int *p1;
表示 p1是一个指针变量,它的值是某个整型变量的地址。或者说 p1指向一个整型变量。至于 p1究竟指向哪一个整型变量,应由向 p1赋予的地址来决定。
12.2 指针变量的定义与引用一、指针变量的定义第十二章 指针
赋值形式,& 变量名;
&:取地址符
&a表示变量 a的地址,&b表示变量 b的地址。变量本身必须预先说明。
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机。
指针变量的赋值只能赋予地址,决不能赋予任何其它数据,否则将引起错误。
12.2 指针变量的定义与引用二、指针变量的赋值第十二章 指针设有指向整型变量的指针变量 p,如要把整型变量 a 的地址赋予 p可以有以下两种方式:
1)指针变量初始化的方法
int a; int *p=&a;
2)赋值语句的方法
int a; int *p;
p=&a;
不允许把一个数赋予指针变量,下面的赋值是错误的:
int *p; p=1000;
被赋值的指针变量前不能再加,*”说明符,如写为
*p=&a 也是错误的。
12.2 指针变量的定义与引用二、指针变量的赋值第十二章 指针
指针变量只能进行赋值运算和部分算术运算及关系运算。
(1)取地址运算符 &:是单目运算符,其结合性为自右至左,其功能是取变量的地址。
(2)取内容运算符 *:是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在,*”
运算符之后跟的变量必须是指针变量。
注意:在指针变量说明中,,*”是类型说明符,
表示其后的变量是指针类型。而表达式中出现的,*”则是一个运算符用以表示指针变量所指的变量。
12.2 指针变量的定义与引用二、指针变量的运算第十二章 指针
[例 12.1]指针变量程序举例 。
main( )
{ int *p1,*p2,i1,i2;
scanf(“%d,%d”,&i1,&i2);
p1=&i1; p2=&i2;
printf(“%d,%d\n”,*p1,*p2);
p2=p1; printf(“%d,%d\n”,*p1,*p2);
}
若输入,3,5
则输出,3,5
3,3
12.2 指针变量的定义与引用二、指针变量的运算第十二章 指针
[例 12.2] 从键盘输入两个整数,按由大到小的顺序输出。
main( )
{ int *p1,*p2,a,b,t;
scanf("%d,%d ",&a,&b) ;
p1=&a; p2=&b;
if(*p1<*p2)
{ t=*p1; *p1=*p2; *p2=t; }
printf("%d,%d\n",a,b) ;
}
12.2 指针变量的定义与引用二、指针变量的运算第十二章 指针
使用指针类型做函数的参数,实际向函数传递的是变量的地址。由于函数中获得了所传递变量的地址,在该地址空间的数据当函数调用结束后被物理地保留下来。因此,如果希望函数间传递的是地址,实参用变量的地址或指针变量,
形参用指针变量。
12.3 指针变量做函数的参数第十二章 指针
[例 12.3] 利用指针变量作为函数的参数,用函数的方法再次实现上述功能。
main ( )
{ void change(); int *p1,*p2,a,b,*t;
scanf("%d,%d",&a,&b) ;
p1=&a; p2=&b;
change(p1,p2); / *函数调用 * /
printf("%d,%d\n",*p1,*p2);
}
void change(int *pt1,int *pt2)
{ int t;
if(*pt1<*pt2)
{ t=*pt1; *pt1=*pt2; *pt2=t; }
return;
}
12.3 指针变量做函数的参数第十二章 指针
指向数组元素的指针变量的定义与指向变量的指针变量的定义相同如,int a[10]; int *p;
对该指针元素赋值,p=&a[0];
把 a[0]元素的地址赋给指针变量 p,即 p指向 a数组的第 0号元素,
数组名代表数组的首地址,也就是第一个元素的地址。因此,p=&a[0];与 p=a;等价。
12.4 指针与数组一、指向数组元素的指针变量的定义与赋值第十二章 指针
在定义指针变量时可以赋给初值:
int *p=&a[0];它等效于:
int *p; p=&a[0];
定义时也可以写成:
int *p=a;
它的作用是将 a的首地址(即 a[0]的地址)赋给指针变量 p(而不是 *p)。
12.4 指针与数组一、指向数组元素的指针变量的定义与赋值第十二章 指针
设 p已定义为指针变量,并已给它赋了一个地址,使它指向某一个数组元素。如果有以下赋值语句:
*p=1;
表示对 p当前所指向的数组元素赋以一个值(值为 1)。
C规定 p+ 1指向数组的下一个元素 (而不是将 p值简单地加 1)。
如,数组元素是实型,每个元素占 4个字节,则 p+ 1意味着使 p的原值(地址)加 4个字节,以使它指向下一元素。 p+ 1所代表的地址值实际上是 p+ 1?d,d是一个数组元素所占的字节数(对整型,d=2;对实型,d= 4;
对字符型,d= 1)。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
如果 p的初值为& a[0],则:
(1) p+ i和 a+ i就是 a[i]的地址,或者说,它们指向 a数组的第 i个元素。
(2) *(p+ i)或 *(a+ i)是 p+ i或 a+ i所指向的数组元素,即
a[i]。
(3)指向数组的指针变量也可以带下标,如 p[i]与 *(p+ i)等价。
(4)当指针指向一串连续的存储单元时,可以对指针进行加上或减去一个整数,这种操作称为指针的移动。
(5)指针不允许进行乘、除运算,移动指针时,不允许加上或减去一个非整数,对指向同一串连续存储单元的两个指针只能进行相减操作。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
[例 12.4]从键盘输入 10个整数,存放在数组中,并输出数组的全部元素。用指针变量指向数组元素。
main( )
{ int i,a[10],*p;
for (i=0; i<10; i++ ) scanf("%d",&a[i]);
for(p=a; p<a+ 10; p++ )
printf("%4d",*p);
printf("\n");
}
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
注意:
(1)用 p++使 p的值不断改变,这是合法的,如果不用 p而企图使 a变化是不行的,如将上述程序的最后两行改为:
for(p=a; a<p+ 10; a++ )
printf("%d",*a);
(2) 要注意指针变量的当前值,请看下面的程序。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
[例 12.5]输出 a数组的 10个元素。
main ( )
{ int *p,i,a[10]; p=a;
for(i=0; i<10; i++ ) scanf ("%d",p++ );
for(i=0; i<10; i++,p++ )
printf ("%d",*p);
printf ("\n");
}
输入,1 2 3 4 5 6 7 8 9 0<回车 >
运行结果,
22153 234 0 0 30036 25202 11631 8259 8237 28483
12.4 指针与数组二、通过指针引用数组元素解决这个问题的办法是在第二个 for循环改为如下形式:
for(i=0,p=a; i<10; i++,p++ )
printf("%d",*p);
第十二章 指针
(3)注意指针变量的运算。如果先使 p指向数组 a(即 p=a),则:
① p++(或 p+=1),p指向下一元素 a[1],若再执行 *p,取了元素 a[1]的值。
② *p++,由于 ++和 *同优先级,是自右而左的结合方向,
因此它等价于 *(p++)。作用是先得到 p指向的变量的值
(即 *p),然后再使 p+1。
③ *( p++)和 *++p作用不同。前者是先取 *p值。然后使 p
移动,后者是先使 p移动,再取 *p。若 p的初值为 a(即
& a[0]),输出 *( p++)时,得 a[0] 的值,而输出 *
( ++p),则得到 a[1]的值。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
④ (*p)++表示 p所指向元素值加 1,即 (a[0])++,如果 a[0]=3,则执行 (a[0])++后,a[0]的值为 4。
注意,是元素值加 1,而不是指针值加 1。
⑤如果当前 p指向 a数组中第 i个元素,则:
*(p--)相当于 a[i--],先取 p值作,*” 运算,再使
p自减。
*(++p)相当于 a[++i],先使 p自加,再作 *运算。
*(--p)相当于 a[--i],先使 p自减,再作 *运算。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
数组名可以用作函数的实参传给形参。由于数组名是一个地址值,对应的形参就应该是数组名或一个指针变量,但该指针必须与数组类型一致。
12.4 指针与数组三、数组名做函数参数
main ( ) f(int *arr)
{ int array[10] {
…… ……
f(array) }
……
}
如:
第十二章 指针
数组名作为实参时,对应的函数首部可以写成如下三种形式:
① f( int *arr )
② f( int arr[10] )
③ f( int arr[ ])
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.6]有一个一维数组 score,内存放 10个学生成绩,求平均成绩。
float average(float * array)
{ int i; float aver,sum=0;
for (i=0; i<10; i++ ) sum=sum+ array[i];
aver=sum/10; return (aver);
}
main ( )
{ float score[10],ave ; int i;
printf ("Input 10 scores,\n");
for (i=0; i<10; i++ ) scanf("%f",&score[i]);
printf (“\n”); ave=average (score);
printf ("Average score is %5.1f",ave);
}
运行情况如下,Input 10 scores,100 56 78 98.5 76 87 99
67.5 75 97<回车 >
运行结果,Average score is 83.40
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.7]有程序如下:
float average (float *array,int n)
{ int i; float aver,sum=0;
for(i=0; i<n; i++ ) sum=sum+ array[i];
aver=sum/n; return (aver);
}
main ( )
{ float score_1[5]={98.5,97,91.5,60,55};
float score
_2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5};
printf ("A- average,%6.2f\n“,average (score_1,5));
printf ("B―average,%6.2f\n“,average (score_2,10));
}
运行结果如下,A- average,80.40
B- average,78.20
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.8]定义长度为 10的整型类型一维数组,并将数组中的元素按逆序存放后输出其值 (完成逆序存放操作时,只允许开辟一个临时存储单元 )。
main ( )
{ int i,a[10];
for(i=0; i<10; i++ ) scanf("%d",&a[i]);
for(i=0; i<10; i++ ) printf("%4d",a[i]);
printf(“\n”); fun(a,10);
for(i=0; i<10; i++ )printf("%4d",a[i]);
printf("\n");
}
fun(int *a,int n)
{ int i,j,t;
for(i=0,j=n- 1; i<j; i++,j-- )
{ t=a[i]; a[i]=a[j]; a=[j]=t; }
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.9]已知一维数组中存放互不相同的 10个整数,从键盘输入一个数,从数组中删除与该值相同的元素中的值。
main ( )
{ int i,t,a [10]={2,4,1,6,5,9,7,0,8,3};
for(i=0; i<10; i++ ) printf("%4d",a[i]);
printf(“\nlnput t:\n”); scanf("%d",&t);
del(a,t);
for(i=0; i<9; i++ ) printf("%4d",a[i]);
printf("\n");
}
del(int a[10],int t )
{ int i,j;
for(i=0; i<10; i++ ) if(t== a[i]) break;
for(j=i; j<9; j++ ) a[j]=a[j+ 1];
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.10]用选择法对 10个整数按由小到大顺序排列。
main( )
{ int i,a[10];
for(i=0; i<10; i++ ) scanf(“%d”,&a[i]);
sort(a,10);
for(i=0; i<10; i++ ) printf(“%d”,a[i]);
printf("\n");
}
sort (int p[ ],int n)
{ int i,j,k,temp;
for(i=0; i<n- 1; i++ )
{ k=i ;
for(j=k+ 1; j<n; j++ )
if(p[j]<p[k]) k=j;
temp=p[i]; p[i]=p[k]; p[k]=temp;
}
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.11]假定 a数组中已存放由小到大顺序排序的 10个数,
在 a数组中插入一个数,插入后数组中的数仍有序。
main ( )
{ int I,x,a [11]={1,3,5,7,9,11,13,15,17,19};
printf(“Input x,\n”); scanf("%d",&x);
for(i=0; i<10; i++ ) printf ("%4d",a[i]);
printf(“\n”); insert(a,x);
for(i=0; i<11; i++ ) printf ("%4d",a[i]);
printf("\n");
}
insert(int *a,int x)
{ int i,j; i=0;
while(i<10 &&a[i]<x) i++;
for(j=9; j>=i; j――) a[j + 1]=a[j];
a[i]=x;
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.12]求一维数组中值最大的那个元素的值,以及其所在的位置。
main ( )
{ int i,k,max; int a[10]={3,4,9,1,7,6,- 10,10,-
5,2};
for(i=0; i<10; i++ ) printf("%d",a[i]);
printf(“\n”); max=find(a,&k);
printf("max=%d,k=%d\n",max,k);
}
find(int a[10],int *k)
{ int i,max;
max=a[0]; *k=0;
for(i=0; i<10; i++ )
if(max<a[i]) { max=a[i]; *k=i; }
return max;
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
设已定义二维数组 a:
int a[3][4]={{1,3,5,7},{9,11,13,15,15},{17,19,21,23}};
( 1) a是二维数组名,是二维数组的起始地址 (设地址为
2000)。即,a指向 a数组第 0行,a也是 0行首地址。
( 2) a+ 1是 a数组第 1行首地址,或者说 a+ 1指向第 1行
(地址为 2008)。
( 3) a[0],a[1],a[2]是二维数组中三个一维数组(即三行)
的名字,因此它们也是地址 (分别是 0行,1行,2行的首地址 )。
( 4) a[i]+j是 i行 j列元素的地址,*(a[i]+j)是 i行 j列元素的值。
( 5) a[i]与 *(a+ i)无条件等价,这两种写法可以互换。
12.4 指针与数组四、指向多维数组的指针和指针变量第十二章 指针
( 6) a[i][j],*(a[i]+ j),*(*(a+ i)+ j)都是 i行 j列元素的值。
( 7)区别行指针与列指针的概念。例如 a+ 1和
a[1]都代表地址 2008。但 a+ 1是行指针。它指向一个一维数组。 a[1](即 *(a+ 1))是列指针,它指向一个元素,它是 1行 0列元素的地址。
( 8)可以定义指向一维数组的指针变量,
如,int (*p)[4]; /*称 p为行指针 */
定义 p为指向一个含 4个元素的一维数组的指针变量。
12.4 指针与数组四、指向多维数组的指针和指针变量第十二章 指针
main ( )
{ int a[3][4];
int * p1,(*p2)[4];
p1=&a[3][4]; /*p1是列指针 */
p2=a+ 1; /*p2是行指针 */
}
不能写成:
p1=a+ 1; p2=&a[3][4]; /*类型不一致 */
12.4 指针与数组四、指向多维数组的指针和指针变量第十二章 指针
12.5 字符串的指针和指向字符串的指针变量一、字符串的表示形式
可以用两种方法实现一个字符串:
1.用字符数组实现
[例 12.13]用字符数组实现的示例。
main ( )
{ char string [ ]= " I love China! ";
printf("%s\n",string);
}
string是数组名,它代表字符数组的首地址第十二章 指针
2.用字符指针实现可以不定义数组,而定义一个字符指针。用字符指针指向字符串中的字符。
[例 12.14]用字符指针实现的示例。
main ( )
{ char *string= " I love China! ";
printf("%s\n",string);
}
注意,在内存中,字符串的最后被自动加了一个 ‘ \0?,
因此在输出时能确定字符串的终止位置。通过字符数组名或字符指针变量可以输出一个字符串,而对一个数值型数组,是不能企图用数组输出它的全部元素的,只能逐个元素输出。
12.5 字符串的指针和指向字符串的指针变量一、字符串的表示形式第十二章 指针
[例 12.15]将字符串 a复制到字符串 b(用指针变量来处理)。
main ( )
{ char a[ ] = "I am a boy,",b[20],*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,%s\n",b);
}
12.5 字符串的指针和指向字符串的指针变量一、字符串的表示形式第十二章 指针字符数组与字符指针变量的区别:
( 1)字符数组由若干个元素组成,每个元素存放一个字符,而字符指针变量中存放的是地址(字符串的首地址),决不是将字符串放到字符指针变量中。
( 2)对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。
char str[14]; str="I love China!";
而对字符指针变量,可以采用下面方法赋值:
char *a; a="I love China! ";
但注意赋给 a的不是字符串,而是字符串的首地址。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
( 3)对字符指针变量赋初值时:
char *a="I love China! ";
等价于:
char *a; a="I love China! ";
而对数组初始化时:
char str[14]={ " I love China! "};
不等价于:
char str[14]; str[ ]="I love China! ";
即数组可以在变量定义时整体赋初值,但不能在赋值语句中整体赋值。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
( 4)在定义一个数组时,在编译时即已分配内存单元,有固定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以存放一个地址值,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋以一个地址值,则它并未具体指向哪一个字符数据。
如,char str [10];
scanf ("%s",str);
是可以的,而下面的形式是错误的:
char * a;
scanf(“%s”,a);
( 5)指针变量的值是可以改变的。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
[例 12.16]程序示例。
main ( )
{ char a=“I love China!,;
/*a指向存放字符 'I'的存储单元 */
a=a+ 7; /*a指向存放字符 'C'的存储单元 */
printf("%s",a); /*将输出 China! /*
}
数组名代表一个固定的地址,它的值是不能改变的。
说明,定义一个指针变量并使它们指向一个字符串后,
也可以用下标形式引用指针所指的字符串中的字符 (与前面介绍的数组类似 )。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
(6) 不要随意使用无确定指向的指针。有时为了防止无确定指向的指针破坏有用数据,给暂时不用的指针赋 NULL。
如,int *p; p=NULL;
其中 NULL是在文件 stdio.h中定义的符号常量,它的代码值为 0,这时称 p为空指针。
以下三种写法等价:
p=NULL; p='\0'; p=0;
使用 NULL时,应在程序的开头加
#include<stdio.h>。当企图通过空指针访问一个存储单元时,将显示一个出错信息。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
将一个字符串从一个函数传递到另一个函数,可以用地址传递的办法,即用字符数组名或用指向字符串的指针变量作参数。和数组一样,函数的首部有三种说明形式,而且形参也是指针变量。在被调用的函数中可以改变字符串的内容,
在主调函数中可以得到改变了字符串。
12.6 字符串指针做函数参数第十二章 指针
[例 12.17]将字符数组中的字符串按逆序存放后输出。
#include<stdio.h>
#include<string.h>
main( )
{ int i,j; char ch [80]; gets(ch); puts (ch);
j=strlen (ch)- 1; fun(ch,j); puts (ch);
}
fun (char *p,int j)
{ int i; char t,*q;
q=p+ j;
for( ; p<q ; p++,q-- )
{ t=*p; *p=*q; *q=t;}
}
运行结果是:
asdfgh<回车>
asdfgh
hgfdsa
12.6 字符串指针做函数参数第十二章 指针
[例 12.18]从键盘输入一个字符串,计算该字符串的长度。
不要用 strlen函数。
#include< stdio.h>
main ( )
{ int len; char a [80];
gets (a); puts (a);
len=lenth(a); printf("%d\n",len);
}
lenth(char *p)
{ char *q=p; /* p和 q都指向数组 a*/
while (*q!= '\0') q++; /*只移动 q,p不变 */
return q- p; /*q- p表示共移动几个存储单元 */
}
运行结果是
asdfgh<回车>
asdfgh
6
12.6 字符串指针做函数参数第十二章 指针
[例 12.19]编一程序,将两个字符串连接起来。不要用 strcat函数。
#include< stdio.h>
main ( )
{ char sl [80],s2[40]; printf("\n Input string1,");
gets(s1); printf("\n Input string2:);
gets(s2); scat(s1,s2); printf("\n New string,%s",s1);
}
scat(char *s1,char *s2)
{ while(*s1!='\0') s1++;
while(*s2!='\0') { *s1=*s2; s1++ ; s2++; }
*s1='\0';
}
运行情况,Input string 1,country<回车>
Input string 2,side <回车>
New string,countryside
12.6 字符串指针做函数参数第十二章 指针
[例 12.20]编写一个程序,将字符数组 from中的全部字符拷贝到字符数组 to中。不要用 strcpy函数。拷贝时,'\0'也要拷贝过去。 '\0'后面的字符不拷贝。
#include< stdio.h>
#include< string.h>
main ( )
{ char from [80],to[80];
printf("Input,string,"); scanf("%s,from);
strcopy(from,to); printf("Copied string,%s/n",to);
}
strcopy(char *from,char *to)
{ int i;
for(i=0; i< =strlen (from); i++ )
to[i]=from[i];
}
运行结果:
Input string,student<回车>
Copied string,student
12.6 字符串指针做函数参数第十二章 指针
[例 12.21]编写一个程序,比较字符数组 s1和 s2。不要用 strcmp函数。
#include< stdio.h>
main ( )
{ int a; char s1[80],s2[80];
gets (s1); gets(s2); puts(s1); puts(s2);
a=strcomp(s1,s2);
if(a>0) printf("(s1,%s)>(s2,%s)\n",s1,s2);
if(a= = 0) printf("(s1,%s)=(s2,%s)\n",s1,s2);
if(a<0) printf("(s1,%s)<(s2,%s)\n",s1,s2);
}
strcomp(char *s1,char *s2)
{ while(*s1= = *s2&&,*s1!= '\0') {s1++; s2++; }
return *s1- *s2;
}
运行结果是,boy<回车>
girl<回车>
boy
girl
(s1,boy)< (s2,girl)
12.6 字符串指针做函数参数第十二章 指针
可以用指针变量指向整型变量、字符串、
数组,也可以指向一个函数。一个函数在编译时被分配一个入口地址。这个入口地址就称为函数时的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
main ( )
{ int max (int,int ); int (*p) (int,int ); int a,b,c;
p=max; scanf (“%d,%d,,&a,&b); c=(*p)(a,b);
printf("max=%d\n ",c);
}
int max (int x,int y) /*定义 max函数 */
{ int z;
if(x> y) z = x;
else z=y;
return (z);
}
main ( )
{ int max (int,int ); int (*p) (int,int ); int a,b,c;
p=max; scanf (“%d,%d”,&a,&b); c=(*p)(a,b);
printf("max=%d\n ",c);
}
int max (int x,int y) /*定义 max函数 */
{ int z;
if(x> y) z = x;
else z=y;
return (z);
}
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
说明:
(1)指向函数的指针变量的一般定义形式为:
数据类型标识符 (*指针变量名 )(类型 参数 1,类型 参数 2,….)
“数据类型标识符”是指函数返回值的类型,旧版本允许省略后一括号中的内容。
(2) 函数的调用可以通过函数名调用。也可以通过函数指针调用 (即用指向函数的指针变量调用 )。
(3) (*p)(int,int )表示定义一个指向函数的指针变量,它不是固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
(4) 在给函数指针变量赋值时,只需要给出函数名而不必给出参数。如,p=max;
因为是将函数入口地址赋给 p,而不牵涉到实参与形参的结合问题。不能写成,p=max (a,
b);”形式。
(5) 用函数指针变量调用函数时,只需将( *p)代替函数名即可( p为指针变量名),在( *p)之后的括弧中根据需要写上实参,
(6)对指向函数的指针变量,象 p+n,p++,p--等运算无意义的。
(7)函数指针变量常用的用途之一是把指针作为参数传递到其它函数。
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
函数的指针变量也可以作为参数,以便实现函数地址的传递,也就是将函数名传给形参。
它的原理可以简述如下:有一个函数(假设函数名为 sub),它有两个形参( x1和 x2),定义
x1和 x2为指向函数的指针变量,在调用函数 sub
时,实参用两个函数名 f1和 f2给形参传递函数地址,这样在函数 sub中就可以调用 f1和 f2函数了。
12.6 字符串指针做函数参数二、把指向函数的指针变量作为函数参数第十二章 指针
一个函数不仅可以带回简单类型的数据,而且可以带回指针型的数据,即地址。
如:
#include < stdio.h>
char *fun (char *); /*指针类型也要原型说明 */
main ( )
{ char ch ='a',*p,*q;
p=&ch; q=fun(p); /*调用结束后 q才有确定的指向,相当于 q="bag"; */
puts (q)
}
char * fun (char *s)
{ char *t="big";
*( t+ 1 )= *s; /* *s相当于主函数中的 *p即 ch */
return t ; /*返字符串的首地址 */
}
输出结果是:
bag
12.7 返回指针值的函数第十二章 指针
一个数组,其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都是指针变量。
形式:类型标识符 *数组名 [ 数组长度说明 ];
如,int *p[4];
注意,不要写成 int ( *p ) [ 4 ] ;这是指向一维数组的指针变量。指针数组使字符串处理更方便灵活。
12.8 指针数组和指向指针的指针一、指针数组第十二章 指针
[例 12.22]在五个字符串中,找出最大者,并使下标为 0的指针数组元素指向它。
#include < stdio.h>
#include < string.h>
main ( )
{ int i,k;
char *temp,*str
[ ]={ "Follow”,"QBASIC”,"Great”,"FORTRAN”,"Computer"};
k=0;
for ( i =1; i< 5; i++ )
if(strcmp(str[k],str[i])< 0) k=i;
if (k!=0)
{ temp=str[0]; str[0]=str[k]; str[k]=temp; }
printf("The largest string is\n%s\n",str[0]); }
运行结果是,QBASIC
12.8 指针数组和指向指针的指针一、指针数组第十二章 指针
指针变量也有地址,这地址可以存放在另一个指针变量中。如果变量 p中存放了指针变量 q的地址,那末 p就指向指针变量 q。指向指针数据的指针变量,简称为指向指针的指针。
定义一个指向字符型指针数据的指针变量,形式如下,char * * p;
如果有 char *q="abc";则 p=&q;是合法的。
*p相当于 q,**p 相当于 *q,因此,**p中的值为 'a',
12.8 指针数组和指向指针的指针二、指向指针的指针第十二章 指针
[例 12.23] 写出下面程序地运行结果。
main ( )
{ char *str [ ] ={“ENGLISH”,“MATH”,“MUSIC”,
“PHYSICS”,"CHEMISTRY"};
char **q ; int num ;
q=str ;
for(num=0; num<5; num++ )
printf("%s\n",*(q++ ));
}
运行结果是:
ENGLISH
MATH
MUSIC
PHYSICS
CHEMISTRY
12.8 指针数组和指向指针的指针二、指向指针的指针第十二章 指针
main函数可以有参数,形式:
main (int argc,char **argv )
argc和 argv是 main函数形参。实参从命令行得到。
命令行形式:命令名 参数 1 参数 2 …… 参数
n
argc是指命令行中参数的个数,参数含文件名,
因此 argc≥1。 argv是指向字符指针数组的指针
(即指向指针的指针 ),它指向的指针数组的每一元素都指向一个字符串,
12.8 指针数组和指向指针的指针三,main函数的命令行参数第十二章 指针
[例 12.24] 下面程序的文件名为 file.c,写出程序的运行结果。
main ( int argc,char * argv [ ] )
{ argc--; argv++;
while(argc> 0)
{ printf("%s",*argv);
argc--; argv++;
}
}
如果命令输入:
c:> file Computer and C Language<回车>
则输出:
Computer and C Language
12.8 指针数组和指向指针的指针三,main函数的命令行参数第十二章 指针
如有命令行,filel China Beijing
argc的值等于 3,argv[0] 指向‘ filel’,
argv[1]指向‘ China’,argv[2]指向
‘ Beijing’。
12.8 指针数组和指向指针的指针三,main函数的命令行参数
12.1 指针与指针变量如,int a=1,b=2;
float x=3.4,y = 4.5 ;
double m=3.124;
char ch1='a?,ch2='b';
1
2
4.5
3.4
3.124
a
b
2000
2002
2004
2008
2012
2020
2021
变量 a
变量 b
变量 x
变量 y
变量 m
变量 ch1
变量 ch2
一个地址唯一指向一个内存变量,我们称这个地址为变量的指针。如果将变量的地址保存在内存的特定区域,用变量来存放这些地址,这样的变量就是指针变量,通过指针对所指向变量的访问,也就是一种对变量的“间接访问”。
第十二章 指针
1
2
4.5
3.4
3.124
a
b
2000
2002
2004
2008
2012
2020
2021
变量 a
变量 b
变量 x
变量 y
变量 m
变量 ch1
变量 ch2
1012
pa
1004
2012
2000
2002
2004
2008
2020
2021
1000
1002
1006
1008
1010
1014
1016
pb
px
py
pm
pch1
pch2
设一组指针变量 pa,pb,px,py,pm,pch1,pch2,
分别指向上述的变量 a,b,x,y,m,ch1,ch2,指针变量也同样被存放在内存,二者的关系如图:
12.1 指针与指针变量第十二章 指针
指针定义的格式,类型说明符 *指针名;
*表示这是一个指针变量,
变量名即为定义的指针变量名;
类型说明符表示本指针变量所指向的变量的数据类型,也就是说一个指针变量只能指向同一类型的变量。
如,int *p1;
表示 p1是一个指针变量,它的值是某个整型变量的地址。或者说 p1指向一个整型变量。至于 p1究竟指向哪一个整型变量,应由向 p1赋予的地址来决定。
12.2 指针变量的定义与引用一、指针变量的定义第十二章 指针
赋值形式,& 变量名;
&:取地址符
&a表示变量 a的地址,&b表示变量 b的地址。变量本身必须预先说明。
指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机。
指针变量的赋值只能赋予地址,决不能赋予任何其它数据,否则将引起错误。
12.2 指针变量的定义与引用二、指针变量的赋值第十二章 指针设有指向整型变量的指针变量 p,如要把整型变量 a 的地址赋予 p可以有以下两种方式:
1)指针变量初始化的方法
int a; int *p=&a;
2)赋值语句的方法
int a; int *p;
p=&a;
不允许把一个数赋予指针变量,下面的赋值是错误的:
int *p; p=1000;
被赋值的指针变量前不能再加,*”说明符,如写为
*p=&a 也是错误的。
12.2 指针变量的定义与引用二、指针变量的赋值第十二章 指针
指针变量只能进行赋值运算和部分算术运算及关系运算。
(1)取地址运算符 &:是单目运算符,其结合性为自右至左,其功能是取变量的地址。
(2)取内容运算符 *:是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在,*”
运算符之后跟的变量必须是指针变量。
注意:在指针变量说明中,,*”是类型说明符,
表示其后的变量是指针类型。而表达式中出现的,*”则是一个运算符用以表示指针变量所指的变量。
12.2 指针变量的定义与引用二、指针变量的运算第十二章 指针
[例 12.1]指针变量程序举例 。
main( )
{ int *p1,*p2,i1,i2;
scanf(“%d,%d”,&i1,&i2);
p1=&i1; p2=&i2;
printf(“%d,%d\n”,*p1,*p2);
p2=p1; printf(“%d,%d\n”,*p1,*p2);
}
若输入,3,5
则输出,3,5
3,3
12.2 指针变量的定义与引用二、指针变量的运算第十二章 指针
[例 12.2] 从键盘输入两个整数,按由大到小的顺序输出。
main( )
{ int *p1,*p2,a,b,t;
scanf("%d,%d ",&a,&b) ;
p1=&a; p2=&b;
if(*p1<*p2)
{ t=*p1; *p1=*p2; *p2=t; }
printf("%d,%d\n",a,b) ;
}
12.2 指针变量的定义与引用二、指针变量的运算第十二章 指针
使用指针类型做函数的参数,实际向函数传递的是变量的地址。由于函数中获得了所传递变量的地址,在该地址空间的数据当函数调用结束后被物理地保留下来。因此,如果希望函数间传递的是地址,实参用变量的地址或指针变量,
形参用指针变量。
12.3 指针变量做函数的参数第十二章 指针
[例 12.3] 利用指针变量作为函数的参数,用函数的方法再次实现上述功能。
main ( )
{ void change(); int *p1,*p2,a,b,*t;
scanf("%d,%d",&a,&b) ;
p1=&a; p2=&b;
change(p1,p2); / *函数调用 * /
printf("%d,%d\n",*p1,*p2);
}
void change(int *pt1,int *pt2)
{ int t;
if(*pt1<*pt2)
{ t=*pt1; *pt1=*pt2; *pt2=t; }
return;
}
12.3 指针变量做函数的参数第十二章 指针
指向数组元素的指针变量的定义与指向变量的指针变量的定义相同如,int a[10]; int *p;
对该指针元素赋值,p=&a[0];
把 a[0]元素的地址赋给指针变量 p,即 p指向 a数组的第 0号元素,
数组名代表数组的首地址,也就是第一个元素的地址。因此,p=&a[0];与 p=a;等价。
12.4 指针与数组一、指向数组元素的指针变量的定义与赋值第十二章 指针
在定义指针变量时可以赋给初值:
int *p=&a[0];它等效于:
int *p; p=&a[0];
定义时也可以写成:
int *p=a;
它的作用是将 a的首地址(即 a[0]的地址)赋给指针变量 p(而不是 *p)。
12.4 指针与数组一、指向数组元素的指针变量的定义与赋值第十二章 指针
设 p已定义为指针变量,并已给它赋了一个地址,使它指向某一个数组元素。如果有以下赋值语句:
*p=1;
表示对 p当前所指向的数组元素赋以一个值(值为 1)。
C规定 p+ 1指向数组的下一个元素 (而不是将 p值简单地加 1)。
如,数组元素是实型,每个元素占 4个字节,则 p+ 1意味着使 p的原值(地址)加 4个字节,以使它指向下一元素。 p+ 1所代表的地址值实际上是 p+ 1?d,d是一个数组元素所占的字节数(对整型,d=2;对实型,d= 4;
对字符型,d= 1)。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
如果 p的初值为& a[0],则:
(1) p+ i和 a+ i就是 a[i]的地址,或者说,它们指向 a数组的第 i个元素。
(2) *(p+ i)或 *(a+ i)是 p+ i或 a+ i所指向的数组元素,即
a[i]。
(3)指向数组的指针变量也可以带下标,如 p[i]与 *(p+ i)等价。
(4)当指针指向一串连续的存储单元时,可以对指针进行加上或减去一个整数,这种操作称为指针的移动。
(5)指针不允许进行乘、除运算,移动指针时,不允许加上或减去一个非整数,对指向同一串连续存储单元的两个指针只能进行相减操作。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
[例 12.4]从键盘输入 10个整数,存放在数组中,并输出数组的全部元素。用指针变量指向数组元素。
main( )
{ int i,a[10],*p;
for (i=0; i<10; i++ ) scanf("%d",&a[i]);
for(p=a; p<a+ 10; p++ )
printf("%4d",*p);
printf("\n");
}
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
注意:
(1)用 p++使 p的值不断改变,这是合法的,如果不用 p而企图使 a变化是不行的,如将上述程序的最后两行改为:
for(p=a; a<p+ 10; a++ )
printf("%d",*a);
(2) 要注意指针变量的当前值,请看下面的程序。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
[例 12.5]输出 a数组的 10个元素。
main ( )
{ int *p,i,a[10]; p=a;
for(i=0; i<10; i++ ) scanf ("%d",p++ );
for(i=0; i<10; i++,p++ )
printf ("%d",*p);
printf ("\n");
}
输入,1 2 3 4 5 6 7 8 9 0<回车 >
运行结果,
22153 234 0 0 30036 25202 11631 8259 8237 28483
12.4 指针与数组二、通过指针引用数组元素解决这个问题的办法是在第二个 for循环改为如下形式:
for(i=0,p=a; i<10; i++,p++ )
printf("%d",*p);
第十二章 指针
(3)注意指针变量的运算。如果先使 p指向数组 a(即 p=a),则:
① p++(或 p+=1),p指向下一元素 a[1],若再执行 *p,取了元素 a[1]的值。
② *p++,由于 ++和 *同优先级,是自右而左的结合方向,
因此它等价于 *(p++)。作用是先得到 p指向的变量的值
(即 *p),然后再使 p+1。
③ *( p++)和 *++p作用不同。前者是先取 *p值。然后使 p
移动,后者是先使 p移动,再取 *p。若 p的初值为 a(即
& a[0]),输出 *( p++)时,得 a[0] 的值,而输出 *
( ++p),则得到 a[1]的值。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
④ (*p)++表示 p所指向元素值加 1,即 (a[0])++,如果 a[0]=3,则执行 (a[0])++后,a[0]的值为 4。
注意,是元素值加 1,而不是指针值加 1。
⑤如果当前 p指向 a数组中第 i个元素,则:
*(p--)相当于 a[i--],先取 p值作,*” 运算,再使
p自减。
*(++p)相当于 a[++i],先使 p自加,再作 *运算。
*(--p)相当于 a[--i],先使 p自减,再作 *运算。
12.4 指针与数组二、通过指针引用数组元素第十二章 指针
数组名可以用作函数的实参传给形参。由于数组名是一个地址值,对应的形参就应该是数组名或一个指针变量,但该指针必须与数组类型一致。
12.4 指针与数组三、数组名做函数参数
main ( ) f(int *arr)
{ int array[10] {
…… ……
f(array) }
……
}
如:
第十二章 指针
数组名作为实参时,对应的函数首部可以写成如下三种形式:
① f( int *arr )
② f( int arr[10] )
③ f( int arr[ ])
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.6]有一个一维数组 score,内存放 10个学生成绩,求平均成绩。
float average(float * array)
{ int i; float aver,sum=0;
for (i=0; i<10; i++ ) sum=sum+ array[i];
aver=sum/10; return (aver);
}
main ( )
{ float score[10],ave ; int i;
printf ("Input 10 scores,\n");
for (i=0; i<10; i++ ) scanf("%f",&score[i]);
printf (“\n”); ave=average (score);
printf ("Average score is %5.1f",ave);
}
运行情况如下,Input 10 scores,100 56 78 98.5 76 87 99
67.5 75 97<回车 >
运行结果,Average score is 83.40
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.7]有程序如下:
float average (float *array,int n)
{ int i; float aver,sum=0;
for(i=0; i<n; i++ ) sum=sum+ array[i];
aver=sum/n; return (aver);
}
main ( )
{ float score_1[5]={98.5,97,91.5,60,55};
float score
_2[10]={67.5,89.5,99,69.5,77,89.5,76.5,54,60,99.5};
printf ("A- average,%6.2f\n“,average (score_1,5));
printf ("B―average,%6.2f\n“,average (score_2,10));
}
运行结果如下,A- average,80.40
B- average,78.20
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.8]定义长度为 10的整型类型一维数组,并将数组中的元素按逆序存放后输出其值 (完成逆序存放操作时,只允许开辟一个临时存储单元 )。
main ( )
{ int i,a[10];
for(i=0; i<10; i++ ) scanf("%d",&a[i]);
for(i=0; i<10; i++ ) printf("%4d",a[i]);
printf(“\n”); fun(a,10);
for(i=0; i<10; i++ )printf("%4d",a[i]);
printf("\n");
}
fun(int *a,int n)
{ int i,j,t;
for(i=0,j=n- 1; i<j; i++,j-- )
{ t=a[i]; a[i]=a[j]; a=[j]=t; }
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.9]已知一维数组中存放互不相同的 10个整数,从键盘输入一个数,从数组中删除与该值相同的元素中的值。
main ( )
{ int i,t,a [10]={2,4,1,6,5,9,7,0,8,3};
for(i=0; i<10; i++ ) printf("%4d",a[i]);
printf(“\nlnput t:\n”); scanf("%d",&t);
del(a,t);
for(i=0; i<9; i++ ) printf("%4d",a[i]);
printf("\n");
}
del(int a[10],int t )
{ int i,j;
for(i=0; i<10; i++ ) if(t== a[i]) break;
for(j=i; j<9; j++ ) a[j]=a[j+ 1];
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.10]用选择法对 10个整数按由小到大顺序排列。
main( )
{ int i,a[10];
for(i=0; i<10; i++ ) scanf(“%d”,&a[i]);
sort(a,10);
for(i=0; i<10; i++ ) printf(“%d”,a[i]);
printf("\n");
}
sort (int p[ ],int n)
{ int i,j,k,temp;
for(i=0; i<n- 1; i++ )
{ k=i ;
for(j=k+ 1; j<n; j++ )
if(p[j]<p[k]) k=j;
temp=p[i]; p[i]=p[k]; p[k]=temp;
}
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.11]假定 a数组中已存放由小到大顺序排序的 10个数,
在 a数组中插入一个数,插入后数组中的数仍有序。
main ( )
{ int I,x,a [11]={1,3,5,7,9,11,13,15,17,19};
printf(“Input x,\n”); scanf("%d",&x);
for(i=0; i<10; i++ ) printf ("%4d",a[i]);
printf(“\n”); insert(a,x);
for(i=0; i<11; i++ ) printf ("%4d",a[i]);
printf("\n");
}
insert(int *a,int x)
{ int i,j; i=0;
while(i<10 &&a[i]<x) i++;
for(j=9; j>=i; j――) a[j + 1]=a[j];
a[i]=x;
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
[例 12.12]求一维数组中值最大的那个元素的值,以及其所在的位置。
main ( )
{ int i,k,max; int a[10]={3,4,9,1,7,6,- 10,10,-
5,2};
for(i=0; i<10; i++ ) printf("%d",a[i]);
printf(“\n”); max=find(a,&k);
printf("max=%d,k=%d\n",max,k);
}
find(int a[10],int *k)
{ int i,max;
max=a[0]; *k=0;
for(i=0; i<10; i++ )
if(max<a[i]) { max=a[i]; *k=i; }
return max;
}
12.4 指针与数组三、数组名做函数参数第十二章 指针
设已定义二维数组 a:
int a[3][4]={{1,3,5,7},{9,11,13,15,15},{17,19,21,23}};
( 1) a是二维数组名,是二维数组的起始地址 (设地址为
2000)。即,a指向 a数组第 0行,a也是 0行首地址。
( 2) a+ 1是 a数组第 1行首地址,或者说 a+ 1指向第 1行
(地址为 2008)。
( 3) a[0],a[1],a[2]是二维数组中三个一维数组(即三行)
的名字,因此它们也是地址 (分别是 0行,1行,2行的首地址 )。
( 4) a[i]+j是 i行 j列元素的地址,*(a[i]+j)是 i行 j列元素的值。
( 5) a[i]与 *(a+ i)无条件等价,这两种写法可以互换。
12.4 指针与数组四、指向多维数组的指针和指针变量第十二章 指针
( 6) a[i][j],*(a[i]+ j),*(*(a+ i)+ j)都是 i行 j列元素的值。
( 7)区别行指针与列指针的概念。例如 a+ 1和
a[1]都代表地址 2008。但 a+ 1是行指针。它指向一个一维数组。 a[1](即 *(a+ 1))是列指针,它指向一个元素,它是 1行 0列元素的地址。
( 8)可以定义指向一维数组的指针变量,
如,int (*p)[4]; /*称 p为行指针 */
定义 p为指向一个含 4个元素的一维数组的指针变量。
12.4 指针与数组四、指向多维数组的指针和指针变量第十二章 指针
main ( )
{ int a[3][4];
int * p1,(*p2)[4];
p1=&a[3][4]; /*p1是列指针 */
p2=a+ 1; /*p2是行指针 */
}
不能写成:
p1=a+ 1; p2=&a[3][4]; /*类型不一致 */
12.4 指针与数组四、指向多维数组的指针和指针变量第十二章 指针
12.5 字符串的指针和指向字符串的指针变量一、字符串的表示形式
可以用两种方法实现一个字符串:
1.用字符数组实现
[例 12.13]用字符数组实现的示例。
main ( )
{ char string [ ]= " I love China! ";
printf("%s\n",string);
}
string是数组名,它代表字符数组的首地址第十二章 指针
2.用字符指针实现可以不定义数组,而定义一个字符指针。用字符指针指向字符串中的字符。
[例 12.14]用字符指针实现的示例。
main ( )
{ char *string= " I love China! ";
printf("%s\n",string);
}
注意,在内存中,字符串的最后被自动加了一个 ‘ \0?,
因此在输出时能确定字符串的终止位置。通过字符数组名或字符指针变量可以输出一个字符串,而对一个数值型数组,是不能企图用数组输出它的全部元素的,只能逐个元素输出。
12.5 字符串的指针和指向字符串的指针变量一、字符串的表示形式第十二章 指针
[例 12.15]将字符串 a复制到字符串 b(用指针变量来处理)。
main ( )
{ char a[ ] = "I am a boy,",b[20],*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,%s\n",b);
}
12.5 字符串的指针和指向字符串的指针变量一、字符串的表示形式第十二章 指针字符数组与字符指针变量的区别:
( 1)字符数组由若干个元素组成,每个元素存放一个字符,而字符指针变量中存放的是地址(字符串的首地址),决不是将字符串放到字符指针变量中。
( 2)对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。
char str[14]; str="I love China!";
而对字符指针变量,可以采用下面方法赋值:
char *a; a="I love China! ";
但注意赋给 a的不是字符串,而是字符串的首地址。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
( 3)对字符指针变量赋初值时:
char *a="I love China! ";
等价于:
char *a; a="I love China! ";
而对数组初始化时:
char str[14]={ " I love China! "};
不等价于:
char str[14]; str[ ]="I love China! ";
即数组可以在变量定义时整体赋初值,但不能在赋值语句中整体赋值。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
( 4)在定义一个数组时,在编译时即已分配内存单元,有固定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以存放一个地址值,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋以一个地址值,则它并未具体指向哪一个字符数据。
如,char str [10];
scanf ("%s",str);
是可以的,而下面的形式是错误的:
char * a;
scanf(“%s”,a);
( 5)指针变量的值是可以改变的。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
[例 12.16]程序示例。
main ( )
{ char a=“I love China!,;
/*a指向存放字符 'I'的存储单元 */
a=a+ 7; /*a指向存放字符 'C'的存储单元 */
printf("%s",a); /*将输出 China! /*
}
数组名代表一个固定的地址,它的值是不能改变的。
说明,定义一个指针变量并使它们指向一个字符串后,
也可以用下标形式引用指针所指的字符串中的字符 (与前面介绍的数组类似 )。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
(6) 不要随意使用无确定指向的指针。有时为了防止无确定指向的指针破坏有用数据,给暂时不用的指针赋 NULL。
如,int *p; p=NULL;
其中 NULL是在文件 stdio.h中定义的符号常量,它的代码值为 0,这时称 p为空指针。
以下三种写法等价:
p=NULL; p='\0'; p=0;
使用 NULL时,应在程序的开头加
#include<stdio.h>。当企图通过空指针访问一个存储单元时,将显示一个出错信息。
12.5 字符串的指针和指向字符串的指针变量二、字符指针变量与字符数组第十二章 指针
将一个字符串从一个函数传递到另一个函数,可以用地址传递的办法,即用字符数组名或用指向字符串的指针变量作参数。和数组一样,函数的首部有三种说明形式,而且形参也是指针变量。在被调用的函数中可以改变字符串的内容,
在主调函数中可以得到改变了字符串。
12.6 字符串指针做函数参数第十二章 指针
[例 12.17]将字符数组中的字符串按逆序存放后输出。
#include<stdio.h>
#include<string.h>
main( )
{ int i,j; char ch [80]; gets(ch); puts (ch);
j=strlen (ch)- 1; fun(ch,j); puts (ch);
}
fun (char *p,int j)
{ int i; char t,*q;
q=p+ j;
for( ; p<q ; p++,q-- )
{ t=*p; *p=*q; *q=t;}
}
运行结果是:
asdfgh<回车>
asdfgh
hgfdsa
12.6 字符串指针做函数参数第十二章 指针
[例 12.18]从键盘输入一个字符串,计算该字符串的长度。
不要用 strlen函数。
#include< stdio.h>
main ( )
{ int len; char a [80];
gets (a); puts (a);
len=lenth(a); printf("%d\n",len);
}
lenth(char *p)
{ char *q=p; /* p和 q都指向数组 a*/
while (*q!= '\0') q++; /*只移动 q,p不变 */
return q- p; /*q- p表示共移动几个存储单元 */
}
运行结果是
asdfgh<回车>
asdfgh
6
12.6 字符串指针做函数参数第十二章 指针
[例 12.19]编一程序,将两个字符串连接起来。不要用 strcat函数。
#include< stdio.h>
main ( )
{ char sl [80],s2[40]; printf("\n Input string1,");
gets(s1); printf("\n Input string2:);
gets(s2); scat(s1,s2); printf("\n New string,%s",s1);
}
scat(char *s1,char *s2)
{ while(*s1!='\0') s1++;
while(*s2!='\0') { *s1=*s2; s1++ ; s2++; }
*s1='\0';
}
运行情况,Input string 1,country<回车>
Input string 2,side <回车>
New string,countryside
12.6 字符串指针做函数参数第十二章 指针
[例 12.20]编写一个程序,将字符数组 from中的全部字符拷贝到字符数组 to中。不要用 strcpy函数。拷贝时,'\0'也要拷贝过去。 '\0'后面的字符不拷贝。
#include< stdio.h>
#include< string.h>
main ( )
{ char from [80],to[80];
printf("Input,string,"); scanf("%s,from);
strcopy(from,to); printf("Copied string,%s/n",to);
}
strcopy(char *from,char *to)
{ int i;
for(i=0; i< =strlen (from); i++ )
to[i]=from[i];
}
运行结果:
Input string,student<回车>
Copied string,student
12.6 字符串指针做函数参数第十二章 指针
[例 12.21]编写一个程序,比较字符数组 s1和 s2。不要用 strcmp函数。
#include< stdio.h>
main ( )
{ int a; char s1[80],s2[80];
gets (s1); gets(s2); puts(s1); puts(s2);
a=strcomp(s1,s2);
if(a>0) printf("(s1,%s)>(s2,%s)\n",s1,s2);
if(a= = 0) printf("(s1,%s)=(s2,%s)\n",s1,s2);
if(a<0) printf("(s1,%s)<(s2,%s)\n",s1,s2);
}
strcomp(char *s1,char *s2)
{ while(*s1= = *s2&&,*s1!= '\0') {s1++; s2++; }
return *s1- *s2;
}
运行结果是,boy<回车>
girl<回车>
boy
girl
(s1,boy)< (s2,girl)
12.6 字符串指针做函数参数第十二章 指针
可以用指针变量指向整型变量、字符串、
数组,也可以指向一个函数。一个函数在编译时被分配一个入口地址。这个入口地址就称为函数时的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
main ( )
{ int max (int,int ); int (*p) (int,int ); int a,b,c;
p=max; scanf (“%d,%d,,&a,&b); c=(*p)(a,b);
printf("max=%d\n ",c);
}
int max (int x,int y) /*定义 max函数 */
{ int z;
if(x> y) z = x;
else z=y;
return (z);
}
main ( )
{ int max (int,int ); int (*p) (int,int ); int a,b,c;
p=max; scanf (“%d,%d”,&a,&b); c=(*p)(a,b);
printf("max=%d\n ",c);
}
int max (int x,int y) /*定义 max函数 */
{ int z;
if(x> y) z = x;
else z=y;
return (z);
}
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
说明:
(1)指向函数的指针变量的一般定义形式为:
数据类型标识符 (*指针变量名 )(类型 参数 1,类型 参数 2,….)
“数据类型标识符”是指函数返回值的类型,旧版本允许省略后一括号中的内容。
(2) 函数的调用可以通过函数名调用。也可以通过函数指针调用 (即用指向函数的指针变量调用 )。
(3) (*p)(int,int )表示定义一个指向函数的指针变量,它不是固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
(4) 在给函数指针变量赋值时,只需要给出函数名而不必给出参数。如,p=max;
因为是将函数入口地址赋给 p,而不牵涉到实参与形参的结合问题。不能写成,p=max (a,
b);”形式。
(5) 用函数指针变量调用函数时,只需将( *p)代替函数名即可( p为指针变量名),在( *p)之后的括弧中根据需要写上实参,
(6)对指向函数的指针变量,象 p+n,p++,p--等运算无意义的。
(7)函数指针变量常用的用途之一是把指针作为参数传递到其它函数。
12.6 字符串指针做函数参数一、用函数指针变量调用函数第十二章 指针
函数的指针变量也可以作为参数,以便实现函数地址的传递,也就是将函数名传给形参。
它的原理可以简述如下:有一个函数(假设函数名为 sub),它有两个形参( x1和 x2),定义
x1和 x2为指向函数的指针变量,在调用函数 sub
时,实参用两个函数名 f1和 f2给形参传递函数地址,这样在函数 sub中就可以调用 f1和 f2函数了。
12.6 字符串指针做函数参数二、把指向函数的指针变量作为函数参数第十二章 指针
一个函数不仅可以带回简单类型的数据,而且可以带回指针型的数据,即地址。
如:
#include < stdio.h>
char *fun (char *); /*指针类型也要原型说明 */
main ( )
{ char ch ='a',*p,*q;
p=&ch; q=fun(p); /*调用结束后 q才有确定的指向,相当于 q="bag"; */
puts (q)
}
char * fun (char *s)
{ char *t="big";
*( t+ 1 )= *s; /* *s相当于主函数中的 *p即 ch */
return t ; /*返字符串的首地址 */
}
输出结果是:
bag
12.7 返回指针值的函数第十二章 指针
一个数组,其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都是指针变量。
形式:类型标识符 *数组名 [ 数组长度说明 ];
如,int *p[4];
注意,不要写成 int ( *p ) [ 4 ] ;这是指向一维数组的指针变量。指针数组使字符串处理更方便灵活。
12.8 指针数组和指向指针的指针一、指针数组第十二章 指针
[例 12.22]在五个字符串中,找出最大者,并使下标为 0的指针数组元素指向它。
#include < stdio.h>
#include < string.h>
main ( )
{ int i,k;
char *temp,*str
[ ]={ "Follow”,"QBASIC”,"Great”,"FORTRAN”,"Computer"};
k=0;
for ( i =1; i< 5; i++ )
if(strcmp(str[k],str[i])< 0) k=i;
if (k!=0)
{ temp=str[0]; str[0]=str[k]; str[k]=temp; }
printf("The largest string is\n%s\n",str[0]); }
运行结果是,QBASIC
12.8 指针数组和指向指针的指针一、指针数组第十二章 指针
指针变量也有地址,这地址可以存放在另一个指针变量中。如果变量 p中存放了指针变量 q的地址,那末 p就指向指针变量 q。指向指针数据的指针变量,简称为指向指针的指针。
定义一个指向字符型指针数据的指针变量,形式如下,char * * p;
如果有 char *q="abc";则 p=&q;是合法的。
*p相当于 q,**p 相当于 *q,因此,**p中的值为 'a',
12.8 指针数组和指向指针的指针二、指向指针的指针第十二章 指针
[例 12.23] 写出下面程序地运行结果。
main ( )
{ char *str [ ] ={“ENGLISH”,“MATH”,“MUSIC”,
“PHYSICS”,"CHEMISTRY"};
char **q ; int num ;
q=str ;
for(num=0; num<5; num++ )
printf("%s\n",*(q++ ));
}
运行结果是:
ENGLISH
MATH
MUSIC
PHYSICS
CHEMISTRY
12.8 指针数组和指向指针的指针二、指向指针的指针第十二章 指针
main函数可以有参数,形式:
main (int argc,char **argv )
argc和 argv是 main函数形参。实参从命令行得到。
命令行形式:命令名 参数 1 参数 2 …… 参数
n
argc是指命令行中参数的个数,参数含文件名,
因此 argc≥1。 argv是指向字符指针数组的指针
(即指向指针的指针 ),它指向的指针数组的每一元素都指向一个字符串,
12.8 指针数组和指向指针的指针三,main函数的命令行参数第十二章 指针
[例 12.24] 下面程序的文件名为 file.c,写出程序的运行结果。
main ( int argc,char * argv [ ] )
{ argc--; argv++;
while(argc> 0)
{ printf("%s",*argv);
argc--; argv++;
}
}
如果命令输入:
c:> file Computer and C Language<回车>
则输出:
Computer and C Language
12.8 指针数组和指向指针的指针三,main函数的命令行参数第十二章 指针
如有命令行,filel China Beijing
argc的值等于 3,argv[0] 指向‘ filel’,
argv[1]指向‘ China’,argv[2]指向
‘ Beijing’。
12.8 指针数组和指向指针的指针三,main函数的命令行参数