9.4
字符串的指针和指向字符串的指针变量
字符串在内存中的起始地址称为字符串的指针,可以定义一个字符指针变量指向一个字符串。
9.4.1
字符串的表示与引用
在C语言中,既可以用字符数组表示字符串,也可用字符指针变量来表示;引用时,既可以逐个字符引用,也可以整体引用。
1.逐个引用
[案例9.8] 使用字符指针变量表示和引用字符串。
/*案例代码文件名:AL9_8.C*/
main()
{char *string=”I love Beijing.”;
for(; *string!=’\0’; string++) printf(“%c”, *string);
printf(“\n”);
}
程序运行结果:
I love Beijing.
程序说明:char*string="I love Beijing.";语句
定义并初始化字符指针变量string:用串常量“I love Beijing.”的地址(由系统自动开辟、存储串常量的内存块的首地址)给string赋初值。
该语句也可分成如下所示的两条语句:
char*string;
string="I love Beijing.";
注意:字符指针变量string中,仅存储串常量的地址,而串常量的内容(即字符串本身),是存储在由系统自动开辟的内存块中,并在串尾添加一个结束标志’\0’。
2.整体引用
[案例9.9] 采取整体引用的办法,改写[案例9.8]。
/*案例代码文件名:AL9_9.C*/
/*程序功能:使用字符指针变量表示和引用字符串*/
main()
{char *string=”I love Beijing.”;
printf(“%s\n”,string);
}
[程序演示]
程序说明:printf("%s\n",string);语句
通过指向字符串的指针变量string,整体引用它所指向的字符串的原理:系统首先输出string指向的第一个字符,然后使string自动加1,使之指向下一个字符;重复上述过程,直至遇到字符串结束标志。
注意:其它类型的数组,是不能用数组名来一次性输出它的全部元素的,只能逐个元素输出。
例如:
int array[10]={……};
......
printf("%d\n",array);
/*这种用法是非法的*/
......
3.字符指针变量与字符数组之比较
虽然用字符指针变量和字符数组都能实现字符串的存储和处理,但二者是有区别的,不能混为一谈。
(1)存储内容不同。
字符指针变量中存储的是字符串的首地址,而字符数组中存储的是字符串本身(数组的每个元素存放一个字符)。
(2)赋值方式不同。
对字符指针变量,可采用下面的赋值语句赋值:
char
*pointer;
pointer="This is a example.";
而字符数组,虽然可以在定义时初始化,但不能用赋值语句整体赋值。下面的用法是非法的:
char
char_array[20];
char_array="This is a example.";
/*非法用法*/
(3)指针变量的值是可以改变的,字符指针变量也不例外;而数组名代表数组的起始地址,是一个常量,而常量是不能被改变的。
9.4.2
字符串指针作函数参数
[案例9.10] 用函数调用方式,实现字符串的复制。
/*案例代码文件名:AL9_10.C*/
/**********************************************************/
/*string_copy()函数:复制一个字符串
*/
/*形参:字符指针str_from接收源串,字符指针 str_to存储目标串 */
/*返回值:无
*/
/**********************************************************/
void string_copy(char *str_from, char*str_to)
{int i=0;
for(; (*(str_to+i)=*(str_from+i))!=’\0’; i++) ; /*循环体为空语句*/
}
main()
{char array_str1[20]=”I am a teacher.”;
char array_str2[20];
string_copy(array_str1, array_str2); /*数组名作实参*/
printf(“array_str2=%s\n”, array_str2);
}
程序运行结果:
I am a teacher.
程序说明:for(;(*(str_to+i)=*(str_from+i))!=’\0’; i++) ;
语句的执行过程为:首先将源串中的当前字符,复制到目标串中;然后判断该字符(即赋值表达式的值)是否是结束标志。如果不是,则相对位置变量i的值增1,以便复制下一个字符;如果是结束标志,则结束循环。其特点是:先复制、后判断,循环结束前,结束标志已经复制。
在C语言中,用赋值运算符、而不是赋值语句来实现赋值操作,能给某些处理带来很大的灵活性,该语句(实现字符串的复制)的用法就是最好的例证。
9.5
返回指针值的函数
一个函数可以返回一个int型、float型、char型的数据,也可以返回一个指针类型的数据。 返回指针值的函数(简称指针函数)的定义格式如下:
函数类型
*函数名([形参表])
[案例9.11] 某数理化三项竞赛训练组有3个人,找出其中至少有一项成绩不合格者。要求使用指针函数实现。
/*案例代码文件名:AL9_11.C*/
/*************************************************************/
/*seek()函数:判断是否有不合格成绩
*/
/*形参:指向由3个int型元素组成的1维数组的行指针变量
*/
/*返回值:(1)有不合格成绩,则返回指向本行首列的一个(列)指针;
*/
/*
(2)没有有不合格成绩,返回值为指向下一行的一个(列)指针 */
/*************************************************************/
int
*seek( int
(*pnt_row)[3] )
{int i=0, *pnt_col;
/*定义一个(列)指针变量pnt_col */
pnt_col=*(pnt_row+1);
/*使pnt_col指向下一行之首(作标志用)*/
for(; i<3; i++)
if(*(*pnt_row+i)<60)
/*某项成绩不合格*/
{ pnt_col=*pnt_row; /*使pnt_col指向本行之首*/
break;
/*退出循环*/
}
return(pnt_col);
}
/*主函数main()*/
main()
{int grade[3][3]={{55,65,75},{65,75,85},{75,80,90}};
int i,j,*pointer;
/*定义一个(列)指针变量pointer */
for(i=0; i<3; i++)
/*控制每个学生*/
{ pointer=seek(grade+i);
/*用行指针作实参,调用seek()函数*/
if(pointer==*(grade+i))
/*该学生至少有一项成绩不合格*/
{ /*输出该学生的序号和各项成绩*/
printf(“No.%d gradelist: ”, i+1);
for(j=0; j<3; j++)printf(“%d
”,*(pointer+j));
printf(“\n”);
}
}
}
[程序演示]
程序运行结果:
No.1 grade list: 55
65
75
程序说明:
(1)主函数中的pointer=seek(grade+i);语句
调用seek()函数时,将实参grade+i(行指针)的值,复制到形参pnt_row(行指针变量)中,使形参pnt_row指向grade数组的第i行。
(2)在指针函数seek()中:
1) pnt_col=*(pnt_row+1);语句
*(pnt_row+1)将行指针转换为列指针,指向grade数组的第i+1行第0列,并赋值给(列)指针变量pnt_col。
2) if(*(*pnt_row+i)<60)行
pnt_row是一个行指针,指向数组grade的第i行;*pnt_row使指针由行转换为列,指向数组grade的第i行0列;*pnt_row+j的值还是一个指针,指向数组的第i行第j列;*(*pnt_row+j)是一个数据(数组元素grade[j]的值)。
9.6
指针数组与主函数main()的形参
9.6.1
指针数组
1.概念
数组的每个元素都是一个指针数据。指针数组比较适合用于指向多个字符串,使字符串处理更加方便、灵活。
2.定义格式
数据类型
*数组名[元素个数]
注意:与行指针变量定义格式“<数据类型>(*行指针变量)[<元素个数>]”的差别。
[案例9.12] 有若干计算机图书,请按字母顺序,从小到大输出书名。解题要求:使用排序函数完成排序,在主函数中进行输入输出。
/*案例代码文件名:AL9_12.C*/
/*程序功能:指针数组应用示例*/
/***********************************************/
/* sort()函数:对字符指针数组进行排序
*/
/*形参:name--字符指针数组,count--元素个数*/
/*返回值:无
*/
/***********************************************/
void
sort(char *name[], int count)
{char *temp_p;
int i,j,min;
/*使用选择法排序*/
for(i=0; i<count-1; i++)
/*外循环:控制选择次数*/
{ min=i;
/*预置本次最小串的位置*/
for(j=i+1; j<count; j++)
/*内循环:选出本次的最小串*/
if(strcmp(name[min],name[j])>0)
/*存在更小的串*/
min=j;
/*保存之*/
if(min!=i)
/*存在更小的串,交换位置*/
temp_p=name,name=name[min],name[min]=temp_p;
}
}
/*主函数main()*/
main()
{char *name[5]={“BASIC”,”FORTRAN”,”PASCAL”,”C”,”FoxBASE”};
int i=0;
sort(name,5);
/*使用字符指针数组名作实参,调用排序函数sort()*/
/*输出排序结果*/
for(; i<5; i++) printf(“%s\n”,name);
}
[程序演示]
程序运行结果:
BASIC
C
FORTRAN
FoxBASE
PASCAL
程序说明:
(1)实参对形参的值传递:
sort(
name
,
5 );
↓
↓
void sort(char *name[], int count)
(2)字符串的比较只能使用strcmp()函数。形参字符指针数组name的每个元素,都是一个指向字符串的指针,所以有strcmp(name[min],name[j])。
9.6.2
主函数main()的形参
在以往的程序中,主函数main()都使用其无参形式。实际上,主函数main()也是可以指定形参的。
[案例9.13] 用同一程序实现文件的加密和解密。约定:程序的可执行文件名为lock.exe, 其用法为:lock +|- <被处理的文件名>,其中“+”为加密,“-”为解密。
/*案例代码文件名:AL9_13.C*/
/*程序功能:带参主函数的应用示例*/
main(int argc, char *argv[])
{char c;
if (argc != 3) printf("参数个数不对!\n");
else
{ c=*argv[1];
/*截取第二个实参字符串的第一个字符*/
switch(c)
{ case '+':
/*执行加密*/
{ /*加密程序段*/
printf("执行加密程序段。\n");
}
break;
case '-':
/*执行解密*/
{ /*解密程序段*/
printf("执行解密程序段。\n");
}
break;
default: printf("第二个参数错误!\n");
}
}
}
1.主函数main()的有参形式
main(int argc, char *argv[])
{ … …}
2.实参的来源
运行带形参的主函数,必须在操作系统状态下,输入主函数所在的可执行文件名,以及所需的实参,然后回车即可。
命令行的一般格式为:
可执行文件名实参[
实参2……]
例如,本案例程序的用法:lock
+|-
<被处理的文件名>←┘
●在TC的集成环境下,也可直接利用Options | Arguments 项,输入主函数所需要的实参:只须输入各参数(相邻参数间用空格分开),可执行文件名可省略。
就本案例而言,输入“+|-
<被处理的文件名>”即可。
3.形参说明
(1)形参argc是命令行中参数的个数(可执行文件名本身也算一个)。
在本案例中,形参argc的值为3(lock、+|-、文件名)。
(2)形参argv是一个字符指针数组,即形参argv首先是一个数组(元素个数为形参argc的值),其元素值都是指向实参字符串的指针。
在本案例中,元素argv[0]指向第1个实参字符串“lock”,元素argv[1] 指向第2个实参字符串“+|-”,元素argv[2]指向第3个实参字符串“被处理的文件名”。
3.形参说明
(1)形参argc是命令行中参数的个数(可执行文件名本身也算一个)。
在本案例中,形参argc的值为3(lock、+|-、文件名)。
(2)形参argv是一个字符指针数组,即形参argv首先是一个数组(元素个数为形参argc的值),其元素值都是指向实参字符串的指针。
在本案例中,元素argv[0]指向第1个实参字符串“lock”,元素argv[1] 指向第2个实参字符串“+|-”,元素argv[2]指向第3个实参字符串“被处理的文件名”。
9.6.3
指向指针的指针变量简介
在[案例9.12]的主函数main()中,数组name是一个字符指针数组,即数组的每一个元素都是一个指向字符串的指针。
既然name是一个数组,则它的每一个元素也同样有相应的地址,因此可以设置一个指针变量pointer,使其指向指针数组的元素(元素的值还是一个指针),称pointer为指向指针的指针变量。显然,指向指针的指针变量是一个两级的指针变量。
1.指向指针的指针变量的定义
数据类型
**指针变量[, **指针变量2……];
2.指向指针的指针变量的赋值
指针变量 = 指针数组名 + i
9.7
函数的指针和指向函数的指针变量简介
1.函数指针的概念
一个函数在编译时,被分配了一个入口地址,这个地址就称为该函数的指针。
可以用一个指针变量指向一个函数,然后通过该指针变量调用此函数。
2.指向函数的指针变量
(1)定义格式
函数类型
(*指针变量)( );
注意:“*指针变量”外的括号不能缺,否则成了返回指针值的函数。
例如,int (*fp)();
/* fp为指向int函数的指针变量*/
(2)赋值
函数名代表该函数的入口地址。因此,可用函数名给指向函数的指针变量赋值。
指向函数的指针变量=[&]函数名;
注意:函数名后不能带括号和参数;函数名前的“&”符号是可选的。
(3)调用格式
(*函数指针变量)([实参表])
3.指向函数的指针变量作函数参数
指向函数的指针变量的常用用途之一,就是将函数指针作参数,传递到其它函数。
函数名作实参时,因为要缺省括号和参数,造成编译器无法判断它是一个变量还是一个函数,所以必须加以说明。函数说明的格式,与第7章中介绍的一样。
注意:对指向函数的指针变量,诸如p+i、p++/p--等运算是没有意义的。 |