21 字符串
1 字符串的概念
- 字符串是有序字符的集合
- 字符串是程序中的基本元素之一
- C 语言中没有字符串的概念
- C 语言中通过特殊的字符数组模拟字符串
- C 语言中的字符串是以
'\0'
结尾的字符数组
2 字符数组与字符串
-
在 C 语言中,双引号引用的单个或多个字符是一种特殊的字面量——字符串字面量
- 存储于程序的全局只读存储区
- 本质为字符数组,编译器自动在结尾加上
'\0'
字符
#include <stdio.h> int main() { char ca[] = {'H', 'e', 'l', 'l', 'o'}; // 普通的字符数组 char sa[] = {'W', 'o', 'r', 'l', 'd', '\0'}; // 特殊的字符数组——字符串 char ss[] = "Hello world!"; // 特例:用一个字符串初始化一个字符数组,ss数组包含{'W', 'o', 'r', 'l', 'd', '\0'},'\0'为编译器自动加上的 char* str = "Hello world!"; // 指针指向一个特殊的数组(字符串) printf("%s\n", ca); // 打印错误,普通的字符数组不能作为“字符串处理函数“的参数:Hello烫烫烫烫藹{? printf("%s\n", sa); // World printf("%s\n", ss); // Hello world! printf("%s\n", str); // Hello world! return 0; }
-
字符串字面量
- 本质是一个数组
- 可以看作常量指针
- 字符串字面量中的字符不可改变(存储于全局只读存储区)
- 字符串字面量至少包含一个字符(
'\0'
) "Hello World!"
是一个无名的字符数组
#include <stdio.h> int main() { char b = "abc"[0]; char c = *("123" + 1); char t = *""; printf("%c\n", b); //a:"abc"为一个无名的字符数组,数组第一个元素为'a' printf("%c\n", c); //2:指针运算,指向第二个元素 printf("%d\n", t); //0:空字符串,解引用为'\0',对应的ASCII码为0 printf("%s\n", "Hello"); //Hello printf("%p\n", "World"); //0x8048556 return 0; }
-
字符串的长度
- 字符串的长度就是字符串所包含字符的个数
- 字符串长度指的是第一个
'\0'
字符前出现的字符个数 - 通过
'\0'
结束符来确定字符串的长度 - 函数
strlen
用于返回字符串的长度
#include <stdio.h> #include <string.h> int main() { char s[] = "Hello\0world"; int i = 0; /* 打印数组中的所有元素数据 for(i = 0; i < sizeof(数组名) / sizeof(type); i++) for(i = 0; i < sizeof(数组名) / sizeof(*数组名); i++) for(i = 0; i < sizeof(数组名) / sizeof(*&数组名[0]); i++) */ for (i = 0; i < sizeof(s) / sizeof(char); i++) { printf("%c, ", s[i]); // H, e, l, l, o, , w, o, r, l, d, , => 包含两个'\0' } printf("\n"); printf("%s\n", s); // Hello printf("%d\n", strlen(s)); // 5:字符串处理函数以第一个出现'\0'作为结束符 printf("%d\n", strlen("123")); // 3 return 0; }
3 典型问题分析
3.1 问题1
-
分析以下程序的输出
#include <stdio.h> int main() { char buf[10] = {0}; char src[] = "hello %s"; snprintf(buf, sizeof(buf), src); printf("buf = %s\n", buf); return 0; } //编译 test.c: In function ‘main’: test.c:9: warning: format not a string literal and no format arguments test.c:9: warning: format not a string literal and no format arguments //运行结果 buf = hello ��
-
snprintf
函数本身是可变参数函数,其原型如下:int snprintf(char* buffer,int buf_size,const char* format,...)
当函数只有3个参数时,如果第3个参数没有包含格式化信息,函数调用没有问题;如果第3个参数包含了格式化信息,但缺少后续对应参数,则程序行为不确定(格式化信息必须与变参个数相匹配)
-
修改
#include <stdio.h> int main() { char buf[10] = {0}; char src[] = "hello %s"; snprintf(buf, sizeof(buf), src,"C"); printf("buf = %s\n", buf); return 0; } //运行结果 buf = hello C
-
3.2 问题2
-
分析以下程序的输出
#include <stdio.h> #include <string.h> int main() { #define STR "Hello, \0world\0" char* src = STR; //src指向"Hello, \0world\0",末尾还有个'\0' char buf[255] = {0}; snprintf(buf, sizeof(buf), src); //打印到buf的是字符串,即为"Hello," printf("strlen(STR) = %d\n", strlen(STR)); //7 printf("sizeof(STR) = %d\n", sizeof(STR)); //15 printf("strlen(src) = %d\n", strlen(src)); //7 printf("sizeof(src) = %d\n", sizeof(src)); //4 printf("strlen(buf) = %d\n", strlen(buf)); //7 printf("sizeof(buf) = %d\n", sizeof(buf)); //255 printf("src = %s\n", src); //Hello, printf("buf = %s\n", buf); //Hello, return 0; } //运行结果 strlen(STR) = 7 sizeof(STR) = 15 strlen(src) = 7 sizeof(src) = 4 strlen(buf) = 7 sizeof(buf) = 255 src = Hello, buf = Hello,
- 字符串相关的函数均以第一个出现的
'\0'
作为结束符 - 编译器总是会在字符串字面量的末尾添加
'\0'
- 字符串字面量的本质为数组
- 字符串相关的函数均以第一个出现的
3.3 问题3
-
分析以下程序的输出
#include <stdio.h> #include <string.h> int main() { //这两个宏同样 #define S1 "Hello world" #define S2 "Hello world" //比较的是字符数组地址,而字符数组是需要占用内存空间的,尽管s1,s2在值上相同,但占用的内存起始位置不同 if( S1 == S2 ){ printf("Equal\n"); } else{ printf("Non Equal\n"); } //比较的是字符串 if( strcmp(S1, S2) == 0 ){ printf("Equal\n"); } else{ printf("Non Equal\n"); } return 0; }
- 字符串之间的相等比较需要用
strcmp
完成 - 不可直接用
==
进行字符串直接的比较 - 完全相同的字符串字面量的
==
比较结果为false
- 一些现代编译器能够将相同的字符串字面量映射到同一个无名字符数组,因此
==
比较结果为true
- 字符串之间的相等比较需要用
3.4 问题4
-
字符串循环右移
void right_shift_r(const char* src,char* result,unsigned int n);
函数功能:将输入字符串 src 循环右移 n 位,result 为输出结果
要求:以效率最高的方式实现
示例:"abcde" --2--> "deabc" "abcde" --8(<=>3)-->"cdeab"
-
思路:循环右移 n 位,每个元素的下标 i 变化为:( i + n) % (strlen(src))
#include <stdio.h> #include <string.h> void right_shift_r(const char* src, char* result, unsigned int n) { //取余的基数 const unsigned int LEN = strlen(src); int i = 0; for(i = 0; i < LEN; i++) { result[(n + i) % LEN] = src[i]; } result[LEN] = '\0'; } int main() { char result[255] = {0}; right_shift_r("abcde", result, 2); printf("%s\n", result); right_shift_r("abcde", result, 5); printf("%s\n", result); right_shift_r("abcde", result, 8); printf("%s\n", result); return 0; } //输出结果 deabc abcde cdeab