基础知识
字符串常量
定义
字符串常量是指用双引号括起来的内容(不包含双引号本身)。
双引号是用来告知编译器它括起来的是字符串。——《C Primer Plus》
性质
- 字符串常量是储存在静态空间里的静态数组,拥有静态变量所拥有的性质:只被储存一次、存在于整个生命周期。
在定义相同内容的字符串常量时,可能只为其分配一次存储空间,也可能分配多次 - 字符串常量(包括双引号及其括起来的内容)代表该字符串存储空间的首地址——类似数组名。
- 定义时在内存中储存的内容是双引号括起来的内容+结尾的空字符。
字符串数组
- 指针存储
int main()
{
char string1[3] = "Hi";
char string2[6] = "Hello";
char string3[7] = "hahaha";
char* StringPtr[3]; //建立一个含三个类型为指向字符的指针的元素的数组
StringPtr[0] = string1;
StringPtr[1] = string2;
StringPtr[2] = string3;
/*再依次让三个指针指向不同的字符串首元素的地址(即字符串的首地址)*/
}
- 数组存储
int main()
{
char stringss[3][7] = {"Hi","Hello","hahaha"};//建立一个二维数组
}
- 两者的差别
- 二维数组的储存方式更直观,但占用了更多不必要的空间(当字符串长度差别很大时,这种劣势会更明显),指针的储存方式则相反。
- 在做排序时,直接排序指针比对数组的直接排序(利用strcpy函数)内容简单,而且保留了数组中字符串的原始顺序。见代码:
#include <string.h>
int main()
{
char stringss[3][7] = {"Hi","Hello","hahaha"};
for( int i = 0; i < 3; i++ )
{
StringPtr[i] = stringss[i];
}
/*按字典顺序排序指针*/
for( int i = 0; i < 3; i++ )
{
for( int k = i+1; k < 3; i++ )
{
if( strcmp( StringPtr[i], StringPtr[k]) > 0 )
{
char* TempPtr = StringPtr[i];
StringPtr[i] = StringPtr[k];
StringPtr[k] = TempPtr;
}
}
}
/*此时stringss中的元素均没有发生改变*/
/*直接按字典顺序排序数组内容*/
for( int i = 0; i < 3; i++ )
{
for( int k = i+1; k < 3; k++ )
{
if( strcmp( stringss[i], stringss[k]) > 0 )
{
char TempPtr[7];
strcpy(TempPtr,stringss[i]);
strcpy(stringss[i] ,stringss[k]);
strcpy(stringss[k] ,TempPtr);
}
}
}
/*此时stringss中原来的顺序无法找到*/
}
空字符vs空指针
- 相同点:空字符和空指针都可用数值0来表示
- 不同点:空字符代表的0是指ASCII码表上0对应的空字符’\0’,空指针(NULL)代表的0是指的是一个地址,该地址不会与任何数据的有效地址对应。
定义和初始化
用数组储存字符串(3种方式)
第一种:
char str[] = "Hello"; //用字符串常量初始化
第二种:
char str[6] = {'H''i''\0'};//省略括号中的无语法问题,但数组大小只有3
char str[] = {'H''i''\0'};
第三种:
#include <stdio.h>
char str[10];
scanf("%s", str);
- 在初始化数组时,若指定了数组大小,所有未被使用的元素都被自动初始化为’\0’。
因此如上方法2的第一种,初始化的结果是’H’ ‘i’ ‘\0’ ‘\0’ ‘\0’ ‘\0’ - 字符串常量之间没有空格或用空白字符分隔,等价于其串连起来的字符串。
char str[] = "Hello " "wor""ld";
char str1[] = "Hello world";
这里str和str1的内容是一样的。
用指针访问字符串
char *str = "Hello"; //名为str的指针指向字符串常量所在的首地址
不同指针指向相同内容的字符串常量,编译器可能只给该字符串常量分配1次,(也可能分配多次)储存空间。
二者的对比
- 数组有顶层const,因此数组不是可被赋值的左值。数组名代表的地址不可改变。而指针指向的地址可以改变(但其指向的字符串地址无法改变)。
- 定义时,字符串被储存在静态存储区中。
用数组存储字符串时,是将静态存储区中的字符串拷贝到数组中;
而指针未为字符串的存储分配具体空间,用指针定义字符串时,实则是让指针访问该静态变量所储存的地址。这也就导致此时不能用scanf对字符串进行输入,用指针修改字符串中元素的值时,可能导致其它指向相同内容字符串的指针指向的值发生改变(而相同内容的数组之间是独立的)。
建议在把指针初始化为字符串字面量时使用const限定符。
如果不修改静态存储的字符串本身,不要使用指针指向字符串字面量。
——《C Primer Plus》
字符串函数
输入与输出(头文件stdio.h)
字符串的输入
调用格式 | 读取时的停止标识符 | 读取方式 | 是否导致越界 | 优势 | 特点 | |
---|---|---|---|---|---|---|
gets | gets(StringName); | 换行符 | 整行读入,丢弃末尾换行符 | 可能 | 优势,可能c99建议不用,c11废除此函数 | 方便,但容易产生不可预判的结果 |
fgets | fgets(StringName, StringLength, stdin); | return键 | 整行读入(会保留末尾的换行符)或读入了StringLength-1个字符 | 否 | 避免越界,还可以读取文件中的字符 | 所有输入的文字留在缓冲区,若不清理则可能被下一个字符数组读取 |
gets_s | gets_s(StringName, StringLength); | 换行符 | 整行读入(丢弃末尾的换行符)或读入了StringLength-1个字符 | 否 | 避免越界 | 越界时会丢弃该输入行的其他内容,程序将中止或退出,若想程序继续运行,需要另外写“处理函数” |
sprintf | 与printf基本相同,除左括号后的第一个量是被填入的字符串名 | 后引号 | 可能 | 将本应打印于电脑屏幕的字体储存在字符串中,变量的内容应预先输入好 | ||
scanf | scanf("%s", StringName); | 空白字符:空格、tab、enter | 按单词读入,丢弃末尾空白符 | 可能 | 能读取不同种类的数据 | 详情见数据类型、转换符及标准化输入输出 |
字符串的输出
调用格式 | 是否额外添加换行符 | 备注 | |
---|---|---|---|
puts | puts(StringName); | 是 | 一直打印直至遇到’\0’,已经被废弃 |
fputs | fputs(StringName, FileName); | 否 | 将字符串的内容写入文件,若要打印在屏幕上,FileName位置写stdout |
printf | printf("%s", StringName); | 否 | 可以输出不同类型的变量,详情见数据类型、转换符及标准化输入输出 |
常用函数(头文件string.h)
strlen | strcat(strncat) | strcmp(strncmp) | strcpy(strncpy) | |
---|---|---|---|---|
作用 | 计算字符串(不算结尾空字符)长度 | 将字符串2拼接到字符串1后面 | 比较两字符串的的机器排序序列(一般是ASCII码表),str2位于str1前返回正数,位于其后返回负数,二者相等返回0 | 用第二个字符串的内容等位地覆盖第一个字符串 |
调用格式 | strlen(StringName); | strcat(str1,str2); | strcmp(str1,str2); | strcpy(str1,str2); |
特点/局限性 | 暂无 | 不能检查组合后的字符串是否越界 | 比较的是字符串而非字符 | str1未必指向字符串的开始。若字符串2过短,会保留字符串1的部分内容。 |
是否改变字符串 | 否 | str1改变,str2不变 | 否 | str1改变,str2不变 |
改变结果 | str1变为原str1除去空字符后接上整个str2 | 从str1指向的地址开始,被str2的内容覆盖 | ||
加n与原函数的区别 | 可控制连接后字符串(不包括空字符)的总长度不超过n | 只比较前n个字母的机器排序序列 | 只覆盖从str1到str1+n指向的地址中间的区域 | |
带n函数的调用格式 | strncat(str1,str2,n); | strncmp(str1,str2,n); | strncpy(str1,str2,n); |
函数原型
size_t strlen(const char*);
char* strcmp(const char*, const char*);
char* strncmp(const char*, const char*, size_t);
char* strcat(char*, const char*);
char* strcpy(char*, const char*);
char* strncat(char*, const char*, size_t);
char* strncpy(char*, const char*, size_t);
较常用函数
/*调用函数strrchr和strchr时,第二个实参可以是字符。*/
/*虽然函数原型中第二个参数的类型是int,但字符在计算机中的储存形式是其ASCII码,因此可直接用int类型进行读取。*/
char* strrchr(const char* s, int c); //查找字符串s中是否有字符c,若有则返回该字符最后一次出现的地址,若无则返回空指针
char* strchr(const char* s, int c); //查找字符串s中是否有字符c,若有则返回该字符首次出现的地址,若无则返回空指针
char* strstr(const char* s1, const char* s2);//查找字符串s1中是否有字符串s2,若有则返回该字符串首次出现的首地址,若无则返回空指针
char* strpbrk(const char* s1, const char* s2);//查找字符串s1中是否有字符串s2中的部分内容,若有则返回该部分字符串在s1中首次出现的首地址,若无则返回空指针