字符串

字符串的概念

  • 字符串是有序字符的集合
  • 字符串是程序中的基本元素之一
  • C 语言中没有字符串的概念
    • C 语言中通过特殊的字符数组模拟字符串
    • C 语言中的字符串是以 ‘\0’ 结尾的字符数组(’\0’ 表示字符串的结束)

字符数组与字符串

在 C 语言中,双引号引用的单个或多个字符是一种特殊的字面量

  • 存储与程序的全局只读存储区
  • 本质为字符数组,编译器会自动在结尾加上 ‘\0’ 字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24



#include <stdio.h>
int main() {

char ca[] = {'H', 'e', 'l', 'l', 'o'};
char sa[] = {'W', 'o', 'r', 'l', 'd', '\0', '1', '2'}; // 字符串
char ss[] = "Hello World!"; // 字符串
char* str = "Hello World!"; // 字符串

printf("%s\n", ca);
printf("%s\n", sa);
printf("%s\n", ss);
printf("%s\n", str);

/*
HelloWorld
World
Hello World!
Hello World!
*/

}
  • 字符串字面量的本质是一个数组
  • 字符串字面量可以看作常量指针
  • 字符串字面量中的字符不可改变
  • 字符串字面量至少包含一个字符(’\0’)

字符串字面量

Hello World! 是一个无名的字符数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main() {

char b = "abc"[0];
char c = *("123" + 1);
char t = *"";

printf("%c\n", b);
printf("%c\n", c);
printf("%c\n", t);

/*
a
2
空的 // 也就是说这里输出的是 '\0'
*/
}

字符串的长度

  • 字符串的长度就是字符串所包含字符的个数
  • 字符串长度指的是第一个 ‘\0’ 字符前出现的字符个数
  • 通过 ‘\0’ 结束符来确定字符串的长度
  • 函数 strlen 用于返回字符串的长度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <string.h>

int main() {

char s[] = "Hello\0World";
int i = 0;

for(i = 0; i < sizeof(s) / sizeof(char); i++) { // 此处的 sizeof(s) 包含了两个 \0 所以为 12
printf("%c\n", s[i]);
}

printf("%ld\n", sizeof(s)); // 12
printf("%ld\n", strlen(s));
printf("%ld\n", strlen("123"));

/*
H
e
l
l
o

W
o
r
l
d

12
5
3
*/
}

当 sizeof() 用于计算字符串长度时会包含其中的 \0;

小结

  • C 语言中通过字符数组模拟字符串
  • C 语言中的字符串使用 ‘\0’ 作为结束符
  • 字符串字面量的本质为字符数组
  • 字符串相关函数都依赖与结束符 ‘\0’

字符串中的一些典型问题

1
2
3
4
5
6
7
8
9
10
11
12
// 问题1
#include <stdio.h>
int main() {

char buf[10] = {0};
char src[] = "Hello %s";

snprintf(buf, sizeof(buf), src); // buf = Hello @x

printf("buf = %s\n", buf);

}

snprintf 是 C 标准库中的一个函数,用于格式化输出字符串,并将结果存储在一个缓冲区中。

int snprintf(char *str, size_t size, const char *format, …);

str: 指向要存储格式化字符串的字符数组(目标缓冲区)。

size: 指定写入 str 的最大字符数,也就是 str 的大小(包括终止的空字符 \0)。

format: 类似于 printf 的格式化字符串,用于指定输出的格式。

: 可变参数,用于指定格式化字符串中的实际值。

注意:

上述输出总为什么会打印 乱码,而不是 %s 呢?

因为当 snprintf 函数只有 3 个参数时,如果第三个参数没有包含格式化信息,函数调用没有问题;相反,如果第三个参数包含了格式化信息,但缺少后续对应参数,则程序行为不确定;

解释一下,他是类似 ptinrf 的,当我们第三个参数包含 %s 时,他会认识是后续有内容需要按照 %s 的格数输出,但我们其实是没有的,所以在打印的时候这里的 %s 就是一个未知的内容了;

可改为:snprintf(buf, sizeof(buf), “%s”, src);,或者将 %s 改为其他的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 问题2
#include <stdio.h>
#include <string.h>

#define STR "Hello, \0Kay.Wang\0"

int main() {

char* src = STR;
char buf[255] = {0};

snprintf(buf, sizeof(buf), "%s", src);

printf("strlrn(STR) = %ld\n", strlen(STR)); // 到 \0 截至
printf("sizeof(STR) = %ld\n", sizeof(STR));

printf("strlrn(src) = %ld\n", strlen(src)); // 到 \0 截至
printf("sizeof(src) = %ld\n", sizeof(src)); // 指针的大小是 8 个字节

printf("strlrn(buf) = %ld\n", strlen(buf)); // 数组中字符串的长度 还是到 \0 截至
printf("sizeof(buf) = %ld\n", sizeof(buf)); // 数组的大小 255

/*
strlrn(STR) = 7
sizeof(STR) = 18
strlrn(src) = 7
sizeof(src) = 8
strlrn(buf) = 7
sizeof(buf) = 255
src = Hello,
buf = Hello,
*/
}
  • 字符串相关的函数均以第一个出现的 ‘\0’ 作为结束符
  • 编译器总是会在字符串字面量的末尾添加\0
  • 字符串字面量的本质为数组

字符串、字符数组、字符指针三者本质上是不同,但是互相之间又有一定的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 问题三
#include <stdio.h>
#include <string.h>

#define S1 "Hello, \0Kay.Wang\0"
#define S2 "Hello, \0Kay.Wang\0"

int main() {
if (S1 == S2){ // 两个字符串进行比较本质上是字符数组首地址的比较
printf("Equal\n"); // Equal
} else {
printf("Non Equal\n");
}

if(strcmp(S1, S2) == 0) { // strcmp比较字符串的内容是否一致
printf("Equal\n"); // // Equal
} else {
printf("Non Equal\n");
}
}

编译器会将相同的字符串优化为一个,也就说 S1、S2引用的是痛同一个字符串,所以二者直接进行比较时,是相同的,因为地址相等;

  • 字符串之间的相等比较需要使用 strcmp 完成
  • 不可直接用 == 进行字符串直接的比较
  • 完全相同的字符串字面量的 == 比较结果为 false

Czifuchuanbijiao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 问题四,字符串循环右移
void right_shift_r(const char* src, char* result, unsigned int n);
/*
函数功能:将输入的字符串 src 循环右移 n 位,result 为输出结果
要求: 以最高效率实现
示例:
"abcde" -->2 "deabc"
"abcde" -->8 "cdeab"
思路讲解:
abcdefg\0 循环右移三位 efgabcde\0 : 公式为,(i + 3) % 7
*/
#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;
}