本文部分内容参考了C Primer Plus(Fifth Edition)
C语言字符串表示
字符串是C语言中最常用也是最重要的数据类型,但是C语言没有专门提供这种类型。因为字符串由字符组成,所以声明字符串,我们用字符数组。字符数组是字符串的变量表示方法。纯字符数组和字符串的区别和联系就是:字符串是一个以' '结尾的字符数组。因此,我们声明一个字符数组char ch[32]实际上它最多只能存储31个可显示字符,最后一个字符是' ',它是字符串结尾的标志。
字符串还有一种表示方法,那就是字符串常量(字符串字面量)。例如printf("%s","King and Queen");这个表达式语句中"King and Queen"就是字符串常量。实际上,它也是一个元素为字符常量的数组,这个数组内容为(char []){'K','i','n','g',' ','a','n','d',' ','Q','u','e','e','n',' '};千万注意别忘了' '。
因为字符串常量如"Anytime"可以表示这个字符串(准确说是元素为字符常量的数组)的首地址。因此,我们可以用指针来操作字符串。我们可以这样声名:
char * chptr = "Anytime";
但不能这样:
char * chptr; *chptr = "Anytime";
因为*chptr表示chptr所指向的地址上面的内容,如果这个指针未初始化,那么这就是个很危险的操作,指针有可能乱指向内存空间,如果指向的是系统文件,它就会修改系统文件。即使指针已经初始化,我们也不应该这样做,原因有两个:
1.这样做可能导致溢出(超出了安全的内存空间)
2.会更改不应该更改的内容(详见这篇文章,它解释得很好)
因此,指针操作字符串常量不安全,一般只用来传输字符串变量的地址(内容为字符变量的数组)。
字符串基本输入
scanf()虽然有专门的%s来输入字符串,但它的终止条件是遇到如空格,换行符等空白字符。不如说它是用来处理单词输入的。而处理长字符串输入,最早是用gets(),当它读到换行符时丢弃' '并结束输入,它很好用(对于很早以前来说),但我不希望大家掌握它,尽管很多随便的程序都在用它。
char cha[5]; gets(cha);
这个函数真的非常好用,直接把数组首地址代进去就行了。你可否注意到一个严重的缺陷?这个函数不知道这个数组的大小,也就是不知道它最多只能接受多少字符的输入,这就会导致溢出!有一些UNIX系统的代码大量使用了gets(),使得黑客有机会通过这个漏洞编写程序将垃圾数据写入系统,导致系统瘫痪,这就是流行于这些UNIX计算机之间的蠕虫病毒。
我推荐大家使用fgets()函数,这个函数使用起来比gets()安全,但更加麻烦。
char * cha[16]; fgets(cha, 16, stdin); //fgets(名称,大小,读取文件) //如果要用这个函数从键盘读取,请在读取文件的地方用上stdin
传入的大小为n,它就最多读取n - 1个字符或遇到换行符时终止。例如,上面的代码运行后我输入"1234567890123456"后,cha = "123456789012345"。这个函数看起来很完美,但十全十美的东西是不存在的。fgets()的缺陷在于它读到换行符时保存了换行符!下面是C Primer Plus第五版的有关程序示例:
此时,我们很迫切想编写一个函数,让它丢弃fgets()保存的换行符及后面的无效字符。如果不丢弃无效字符,就会导致后面的语句误读了缓冲区,就像初学字符输入时输入一个字符回车后再要输入一个字符,可还没有输入就已经执行到后面去了。我们把我们自己编写的函数取名为s_gets(),我们让它的返回值和fgets()的返回值一样。我们编写的函数代码如下:
1 char * s_gets(char * sptr, int size){ 2 int i = 0; //i表示读取项数 3 char * re; //re返回和fgets()一样的数值 4 re = fgets(sptr,size,stdin); 5 if(re){ //如果re != NULL 6 while( (sptr[i] != ' ') && (sptr[i] != '