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]才真正创建了存储字符的空间。