课堂

C语言中的字符串

1. C 语言本身没有 string 类型

标准 C 中没有:

string s;

string 是 CS50 自己定义的类型,来自:

#include <cs50.h>

在 CS50 中:

string s;

本质上相当于:

char *s;

也就是“指向字符的指针”。

2. 标准 C 中如何定义字符串

方法一:字符数组

char s[100];

表示创建一个最多能容纳 99 个普通字符的字符串,因为最后一个位置要留给:

'\0'

例如:

char s[] = "hello";

内存实际是:

h e l l o \0

也可以写:

char s[6] = "hello";

3. 用指针定义字符串

char *s = "hello";

这里 s 是一个指针,指向字符串常量 "hello"。

通常不要修改它:

s[0] = 'H';   // 不推荐,可能导致未定义行为

如果你需要修改字符串,优先用:

char s[] = "hello";

s[0] = 'H';

这样是安全的。

4. 使用 CS50 输入字符串

如果使用:

#include <cs50.h>

最方便的是:

string s = get_string("Input: ");

例如:

#include <cs50.h>
#include <stdio.h>

int main(void)
{
    string name = get_string("Name: ");

    printf("Hello, %s\n", name);
}

输入:

Tom

输出:

Hello, Tom

get_string() 可以读取带空格的字符串,例如:

Tom Smith

整个都会被读进去。

5. 标准 C 中用 scanf 输入字符串

最基本的方式:

char s[100];

scanf("%s", s);

注意这里不用:

&s

因为数组名 s 本身就可以代表首地址。

例如:

#include <stdio.h>

int main(void)
{
    char name[100];

    scanf("%99s", name);

    printf("%s\n", name);
}

更推荐:

scanf("%99s", name);

而不是:

scanf("%s", name);

因为 %99s 可以避免输入太长导致数组越界。

6. scanf("%s") 的缺点

它遇到空格就停止。

例如输入:

Hello world

代码:

char s[100];

scanf("%99s", s);

最终只得到:

Hello

world 不会进入 s。

所以 scanf("%s") 更适合读取:

apple
ZJU
hello123

这种不含空格的单个字符串。

7. 标准 C 中推荐使用 fgets

如果需要读取整行,包括空格:

char s[100];

fgets(s, sizeof(s), stdin);

例如:

#include <stdio.h>

int main(void)
{
    char s[100];

    printf("Input: ");
    fgets(s, sizeof(s), stdin);

    printf("%s", s);
}

输入:

Hello world

s 中会保存整个:

Hello world\n\0

注意 fgets() 通常会把你按 Enter 产生的:

'\n'

也读进去。

8. 删除 fgets 读入的换行符

常见写法:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char s[100];

    fgets(s, sizeof(s), stdin);

    s[strcspn(s, "\n")] = '\0';

    printf("%s\n", s);
}

其中:

strcspn(s, "\n")

会找到 '\n' 第一次出现的位置,然后:

s[...] = '\0';

把换行符替换成字符串结束符。

9. 三种常用输入方式对比

方法 写法 能否读取空格 是否标准 C
CS50 get_string string s = get_string("Input: "); 可以 ❌
scanf scanf("%99s", s); 不可以 ✅
fgets fgets(s, sizeof(s), stdin); 可以 ✅

对于你现在做 CS50,直接用:

string s = get_string("Input: ");

最方便。

学标准 C 时,建议逐渐习惯:

char s[100];
fgets(s, sizeof(s), stdin);

10. string 和 char[] 的关系

在 CS50 中:

string s = get_string("Input: ");

实际上:

string

就是:

char *

所以:

string s;

基本可以理解成:

char *s;

但是这一点非常重要:

char *s;

只是创建一个指针,不是创建字符串存储空间。

例如:

char *s;

s[0] = 'A';

这是错误的思路,因为 s 没有指向有效的可写内存。

而:

char s[100];

s[0] = 'A';
s[1] = '\0';

是合法的。

你刚才 substitution 代码里的问题:

char *ctxt;

就是这个原因。

应该是:

char ctxt[strlen(ptxt) + 1];

11. 字符串必须以 '\0' 结尾

这是 C 字符串最重要的规则。

例如:

char s[4];

s[0] = 'c';
s[1] = 'a';
s[2] = 't';
s[3] = '\0';

才是合法字符串:

c a t \0

如果没有:

'\0'

那么:

printf("%s", s);

就不知道什么时候停止,可能打印乱码。

12. 字符和字符串不要混淆

单个字符:

'A'

类型是:

char

字符串:

"A"

实际内容是:

'A' '\0'

所以:

char c = 'A';

是对的。

char *s = "A";

也是对的。

但:

char c = "A";

是错的。

你刚才写过:

ctxt[strlen(ptxt)] = "\0";

错误就在这里。

左边是一个 char,所以应该写:

ctxt[strlen(ptxt)] = '\0';

13. 常用字符串操作

需要:

#include <string.h>

常见函数:

函数 作用
strlen(s) 字符串长度
strcmp(a, b) 比较两个字符串
strcpy(a, b) 复制字符串
strcat(a, b) 拼接字符串
strchr(s, c) 找字符
strstr(s, sub) 找子字符串

例如:

string s = "hello";

printf("%zu\n", strlen(s));

输出:

5

注意:

strlen("hello")

返回 5,不会计算最后的:

'\0'

14. 最需要记住的几个写法

CS50:

string s = get_string("Input: ");

标准 C,输入单个词:

char s[100];

scanf("%99s", s);

标准 C,输入整行:

char s[100];

fgets(s, sizeof(s), stdin);

创建可修改字符串:

char s[] = "hello";

创建一块字符串空间:

char s[100];

字符串结尾:

s[i] = '\0';

打印字符串:

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

判断字符串结束:

for (int i = 0; s[i] != '\0'; i++)
{
    // ...
}

你现阶段可以把它浓缩成一句话:

C 里的字符串本质上是以 '\0' 结尾的一串 char;char * 是指针,而 char s[100] 才真正创建了存储字符的空间。