在C语言中,给出了字符串常量的表示形式(双引号引起来的一串字符),但是并没有字符串类型。我们在数组一项目中学习了使用字符数组保存并使用字符串的方法,本项目将结合指针,对字符串进行更进一步深入研究。
1.字符串常量的存储形式
C语言中对字符串常量的存储也是使用一维字符数组,不同之处在于此字符数组并不是由程序员定义,而是由系统自动生成。如给出字符串“abcdefg”,其存储结构如图7-18所示。
图7-18 字符串常量的存储结构
系统中存储字符串常量时,会自动开辟一维字符数组,其长度等于字符串中字符个数加1,最后一个单元中存放字符串结束标记“\0”。由于此数组不是程序员定义,没有名字,因此会将“abcdefg”本身作为名字来记录该字符数组的首地址。
【例7-19】输出字符串常量中的字符。
输出的是字符串常量中的第3个和第6个字符,因为数组下标从0开始,所以其下标和字符串中字符的实际位置差1。从此例中可以看出,”abcdefg”在输出中可以作为数组名字使用,因为它本身代表的就是一个字符数组的起始地址。
事实上,系统中对字符串常量的界定有两个因素,一个是起始地址,另一个是“\0”,输出字符串时就是从起始地址的字符开始输出到“\0”为止。
【例7-20】输出一个字符串。
由于“abcdefg“+2代表的是字符数组2号单元的地址,因此用%s格式符输出字符串时,就从2号单元中的字符“c”开始输出到“\0”为止,结果为“cdefg”。
所以,字符串常量就是一个从起始地址到“\0”为止的字符序列。
2.用字符数组存储字符串
字符串常量本身就是用字符数组存储,那么程序中定义的字符数组当然也可以存储字符串,这是前边字符数组章节中学习过的。由于一个字符数组中可以存储任何一个不大于数组长度减1(“\0”占一个单元)的字符串,所以也可以将字符数组“认为”是字符串类型的变量。
【例7-21】用字符数组存储字符串。
程序中定义的字符数组的长度为80,因此任何一个长度小于80的字符串常量均可以“赋值”(使用strcpy)给str数组,所以str除了按照数组的特点使用外,还可以作为存储字符串常量的字符串“变量”,这也就是为什么字符数组可以作为一个整体使用而其他类型的数组只能按单元逐个引用的原因。
3.用指向字符类型的指针处理字符串
字符串常量的名字代表的是字符串的起始地址,这个地址指向的是字符数组的0号单元,是一个字符单元,因此该地址就是一个指向字符类型的指针,可以定义一个指针变量来保存这个地址。
【例7-22】用字符指针处理字符串。
注意:程序中定义了字符指针变量string,当执行string=”abcdefg”语句时,赋给string的是该字符串的首地址而不是字符串本身,一个指针变量中也不可能存放一批字符。既然string是该字符串的首地址,也可以将string看成是数组的名字,按照数组的引用方法按单元来处理。
【例7-23】利用指针逐个输出字符串中的字符。
总的来说,用指针处理字符串使用的就是前边学习过的一维数组中的指针关系,只要掌握好指针和一维数组的关系,就可以轻松地处理字符串。
4.字符指针和字符数组处理字符串的区别
字符指针和字符数组虽然都可以处理字符串,但因为字符指针保存的是字符串的首地址,而字符数组中保存的是字符串的“值”,导致二者有一定的区别,主要有以下几点。
(1)字符指针作为一个变量,可以被任何字符串的地址赋值,而数组名作为一个地址常量,不能被赋值。
如定义:
这是一个错误的用法,但如定义为:
则可以将“I love China!”字符串的首地址赋给string。
(2)数组定义时有明确的长度,编译程序会给数组分配相应数量的单元,可以往数组中输入一个字符串,但是指针变量在编译时只是被分配了一个单元,里边只能保存一个字符的地址值,因此不能用指针变量输入字符串。
例如:
这种用法是正确的,因为数组本身被分配了80个单元,输入时是从str地址所指向的单元开始,依次存放我们输入的一串字符,但如将数组改成指针则不可以。
这是错误的用法。一方面这里并没有从string地址开始分配一批单元,而只是给string自己分配了一个单元,可以保存一个字符型数据的地址。另一方面在没有对string初始化的情况下,此时string中究竟是哪一个单元的地址也是未知的,因此我们不能从一个不确定的地址开始去使用其指向的单元,前边强调过,使用不确定的单元地址做间接内存引用是指针使用中的大忌。(www.xing528.com)
(3)对某一个单元执行“写”操作时,注意二者的区别,如有定义:
所谓的“写”操作是指使用赋值、输入等方法来改变一个存储单元的内容。对数组来讲这是正确的使用方法,因为字符数组可以被看成字符串的变量,其每一个单元都是可以改变的。但如将数组改成指针:
则这种用法是错误的,string仅仅是一个首地址,它指向的是字符串常量,常量的每一个单元中的字符都是不能改变的。
一般来说,常常将字符数组和字符指针联合使用来处理字符串。用字符数组来完成字符串的存储,而使用字符指针快速访问每一个单元。
【例7-24】利用字符数组和字符指针完成字符串的复制。
程序中初始时使用ps和pt指针分别指向s数组和t数组的0单元,在循环中使用ps++和pt++操作使二者依次指向两个数组中下一个单元,完成了字符串的复制操作。以上复制过程也可以用函数来实现,程序修改如下:
请同学们自己上机调试此程序并结合指针和一维数组的关系深入理解。
5.多个字符串的存储
在程序中很可能面对要保存一批字符串的情况,例如要保存一批商品的信息,所有商品的名字就是一批字符串。可以使用两种方式来完成多个字符串的存储。
(1)利用二维字符数组存储多个字符串。
由于一个字符串就是一个一维字符数组,因此多个字符串就形成一维字符数组的集合,而二维数组就是保存一维数组的一维数组,因此可以使用二维字符数组保存多个字符串。
例如在日常生活中,经常会遇到一些公司向当天过生日的客户或员工发送祝福信息的短信,这个功能如何完成?生日信息可以从一个客户的身份证号码中获得,只要和当前日期对比是否一致即可轻松完成题目要求。鉴于身份证号码长度为18位,超过了int类型能够表示的最大值,而且从一个整数中取出其中的某些位也是很麻烦的,因此可以用字符串来表示一个客户的身份证号码。对于所有客户的身份证号码,自然可以用二维字符数组来存储,如图7-19所示。
图7-19 用二维字符数组存储多个字符串
由于在比对中只需要每一个人的出生的月份和日期的信息,因此对其他信息统统用x表示,同学们如果上机调试程序时,需要将其具体化,请大家自己编程解决。
针对一批长度基本相同的字符串,用二维数组存储多个字符串可以得到较好的效果,这里每一个身份证号码都是18位,所以用一个19列的二维数组即可。但是也有以下缺点:①在一批字符串长度相差较大时,数组列数应该按照最大字符串长度加1来开辟,那么对于长度较短的字符串,就会形成很大的内存浪费;②对于交换字符串位置的操作比较麻烦,如在对字符串排序时,根据字符串比较的结果,要频繁地互换字符串的存储位置,这比较浪费CPU时间。
(2)使用字符指针数组存储多个字符串。
如前所述,一个字符指针可以保存一个字符串的首地址,因此可以用一批字符指针依次保存每一个字符串的首地址。而对于一批字符指针的管理,当然首选字符指针数组。如用字符指针数组存储“China”“Japan”“Australia”“American”“England”等一批字符串,其存储结构如图7-20所示。
图7-20 用指针数组存储多个字符串
这种存储结构可以解决二维数组存储多个字符串的不足。由于每一个指针指向一个一维字符数组,而每一个一维字符数组可以单独定义,避免了内存的浪费。而且在诸如排序等操作中,只要修改指针的指向顺序,而不用像字符数组中那样移动字符串。下面给出基于两种存储结构的字符串排序的程序。
【例7-25】用二维数组完成字符串的排序。
【例7-26】用指针数组完成字符串的排序。
程序中均使用了冒泡排序的方法来对字符串排序,不同之处在于在例7-25中,交换的是name[j]和name[j+1]单元的字符串,使用strcpy来完成字符串的复制,而在例7-26中,交换的是name[j]和name[j+1]单元的指针,用指针的赋值操作即可完成。请大家体会其中的区别。
6.指向字符的指针数组作main函数的形参
在以前我们写程序时,main函数通常没有形参,一般定义为int main(),实际上main函数也可以定义形参,其定义形式为:
int main(int argc,char*argv[]);
其中argc记录实参的个数,而argv记录实参的值。当main带有形参后,问题是main的实参从哪里来。我们知道main函数是第一个被执行的函数,是由系统直接执行的,因此main函数的直接调用者就是操作系统。我们在调试一个程序时,一般都是在集成环境中直接运行程序的,此时并不能够向main函数传递实参。另一种方式是下dos的命令行方式执行一个C语言程序,当我们在集成环境中完成link操作后,在该程序对应的文件夹中的debug文件夹中,可以找到一个和源程序同名的exe文件,就是此程序对应的可执行文件。在命令行方式下执行程序的命令格式为:
可执行文件名 实参1 实参2… 实参n
如源程序文件名为exp.c,则可单击“开始”菜单,在其中的“运行”命令栏中输入“cmd”打开命令行窗口,输入可执行文件名和实参后敲回车即可运行该程序,如图7-21所示。
图7-21 命令行运行程序
命令行中输入的所有的内容(包含可执行文件名)均被作为字符串看待,其中空格为字符串之间的分隔标记,这些字符串将会被传递给argv数组。argv是一个字符指针数组,它的每一个单元中都是一个字符指针,可以保存一个字符串的起始地址。如本例中argv[0]指向“exp.exe”,argv[1]指向“China”等,依次类推。argc保存了实参(包含可执行文件名)的个数,如本例中argc的值将会是6,说明我们总共输入了6个字符串,用的是argv[0]到argv[5]6个单元分别指向了这6个字符串。程序员可以根据这些信息,对main函数接收到的实参在程序中做出相应的处理。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。