第11章 字符串和字符串函数
11.1字符串表示和字符串I/O
11.1.1在程序中定义字符串
字符串定义:
char s[10]="iamaboy!";//这是字符串
char s[]="uareagirl";//这是字符串
char *ps="heishandsome."; //这是字符串
char s[3]={'A','b','x'}; //这是字符数组,而非字符串
char s[3]={'A','b','x',' '}; //增加字符串标志符,表示字符串
一、字符串常量
字符串常量属于静态存储类(static storage),是指如果在一个函数中使用字符串常量,即使多次调用了这个函数,该字符串在程序的整个运行过程只存储一份。定义中引号的内容作为指向该字符串存储位置的指针。
例11.2
#include<stdio.h> int main(void) { printf("%s,%p,%c ","We","are",*"student wang"); return0; } //输出: //We,内存单元地址编号,s
二、字符串数组及其初始化
const char m[40]="limit yuorself to one line's worth.";
//const表明变量m只读,不可写入。
//这小节想说明啥?字符串数组?标题写错了估计,应该是字符数组。
三、数组和指针
对于:
const char *m3[]="I am chinese.";
char m1[]="I am chinese.";
数组形式m1[]在内存中被分配“实际字符串内字符数量+1(字符串结束标志符)”个元素的数组空间(这里因为数组元素为字符),最后一个数组内存放' '。通常,被引用的字符串存储在可执行文件的数据段部分,当程序被加载到内存中时,字符串也被加载到内存中。被引用的字符串位于静态存储区。但是程序运行后才开始为数组分配存储空间,并把静态存储区的字符串数据依次复制到该数组存储区。此后,编译器会把数组名m1看成数组收元素的地址&m1[0]的同义词,即一个地址常量,无法更改。
而指针形式(*m3)也在静态存储区为字符串预留“实际字符串内字符数量+1(字符串结束标志符)”个元素的空间;并且在程序执行后,系统要为变量m3预留一个存储位置,变量m3初始指向静态存储区中字符串的首地址。m3为变量,可改变,如m3+=2;即使得m3指向了字符串第三个字符。另外,对于const char *m3[]...表示的含义是“指针变量m3指向的目标区域内的数值不能被修改”。
//重要的一个知识点
四、数组和指针的差别
对于:
char s1[]="...";
char *s2="...";
s1和s2均可以使用数组符号和指针加法:*(s1+1),s2[3];
区别在于s1如前面所说,为常量,不能改变,如不能进行自增减操作。
而s2是变量,是可以改变的。
五、字符串数组
对于:
const char *p[3]={"ts1","ts22","ts333"}; //定义方法1
//每个数组元素均为指向字符串首地址的指针
//而字符串又类似于一个字符数组,因此字符串数组也类似于二维字符数组,只不过各行的列数未必一致
p[0]="ts1";p[1]="ts22";p[2]="ts333";
*p[0]='t';*p[1]='t';
p[0][0]='t';p[2][2]='3';//这里也可看出与二维数组的类似。
同样也可以用二维数组方式定义字符串数组:
char p[3][6]={"ts1","ts22","ts333"}; //定义方法2
方法1中各行的列数不一致,而这也可以节省存储空间,因为每个字符串内部的字符在内存中连续存放,而各个字符串之间在内存空间中未必是连续的,这不影响逻辑关系上的依序。而方法二用二维数组方式的定义,一方面,各个字符串之间在内存空间里是连续的;另外一方面,为满足列数的一致,对不足的列数要用‘ '进行填充。
引申:为何两种方式在各个字符串的存储连续与否有区别?回顾前面数组和指针的区别,可以注意到:第一种方法是指针,指针引用的是静态存储区的地址;而数组方法是程序运行到数组定义时候分配一段连续空间以存放该数组,再将静态存储区中的字符串复制。
11.2字符串输入
11.2.1创建存储空间
例:
char *name;
scanf("%s",name);
//上述有问题么?
//有。第一句定义了字符指针变量,但未赋值,未赋值的指针是不能使用的;尽管这两句也许可以通过编译。
//正确方法如下:
char name[81]; //之前说到在程序运行到这句时,系统会给分配81个数组元素空间,一旦分配,即name的地址是确定的。
scanf(“%s”,name);
11.2.2gets()函数
gets()函数的返回值是字符串的地址。
11.2.3fgets()函数
gets()函数不检查预留存储区是否有足够容纳字符串的空间。fgets()提供参数可指定最大读入字符数。
gets()函数读取到换行符时则停止读入,并丢弃换行符。而fgets()结束的标志是指定的读取最大数-1个字符或者读取到换行符则停止,但不丢弃换行符。
gets()函数是针对基本输入文件,即键盘的读入;而fgets()是针对文件的操作。因键盘也是属于文件(基本输入文件),fgets()也可对键盘读入,只是要在第三个参数中明确为stdin(键盘文件);
11.2.4scanf()函数
11.3字符串输出
puts()/fputs()/printf()
11.4自定义输入输出
11.5字符串函数
ANSI C用头文件string.h给出字符串操作库函数:
11.5.1 strlen()
返回值为字符串实际长度,不含结束标识符’ '。
11.5.2strcat()
将第二个参数指向的字符串加到第一个结尾,返回值为第一个参数,即新字符串的首地址(其实该地址不变)。
11.5.3strncat()
与strcat()功能相同,只是多个参数指定最多添加的字符数量。
strncat(s1,s2,5);//将s2加到s1结尾处,当加了5个字符后或s2已结束后结束。
11.5.4strcmp()
若两相比的字符串完全相同,则返回0;
若依序比较,直到第一对不相同的字符出现时候,若第一个字符串对应字符在ASCII码表中顺序位于第二个字符串对应字符之后(即大于),则返回正数,反之则负数。
11.5.5strncmp()
例:strcmp(s1,s2,int m) 仅针对两字符串中前m个字符进行比较。
*更多字符串函数详见P307-308