TIP如果你从未接触过 C 语言, 那么我建议你先阅读前面的文章:
字符和字符串
#include <stdio.h>
int main() { printf("Hello World");
return 0;}在这个例子中, 程序运行可以打印出Hello World:

不过, 从运行结果中你可能会注意到一个现象: 输出的实际并不是Hello World, 而是Hello World⏎
难道这是 C 语言设计的吗? 通过printf()打印字符串, 打印结果会在末尾加一个特殊符号⏎?
事实上, 末尾的这个特殊符号, 只是终端shell做的一个提醒字符: 上一个输出的结尾没有换行
如果这样调用printf("Hello World\n");, 结果就会不一样:

两者之间的差别就只有一个\n
字符
\n具体是什么, 详情请看下面这张表:
ASCII码表 和 字符类型 **

这张表是 ASCII编码表, 简称ASCII码表, 它在编程的世界里非常重要
NOTE
ASCII是一种用于标准化英文字符在计算机中表示的编码方案, 是英语字符集中最小且最基本的通用标准在几乎所有现代主流的字符编码格式中(如 UTF-8、UTF-16、ISO-8859、GBK 等),
ASCII编码表中的字符都具有统一且一致的含义同样, 在几乎所有主流的编程语言中,
ASCII字符的定义和编码方式也完全一致
在ASCII码表中, 就可以找到'\n'这个符号, 对它的描述是: 换行
是的'\n'只表示一个字符, 而不表示'\'和'n'两个字符
现在你应该可以理解, 为什么执行printf("Hello World\n");终端不会提醒: 上一个输出的结尾没有换行
因为'\n'的意思就是换行
除了'\n'之外, 还可以看到更多'\*'样式的符号, 它们统一被称为转义字符, 通过反斜杠'\'与另一个字符组合, 用以表示某些在代码中不方便直接书写或具有特殊含义的字符:
| 转义字符 | 十进制 | 描述/含义 |
|---|---|---|
'\0' | 0 | 空字符(不是空格), 通常用于字符串的结尾 |
'\a' | 7 | 响铃(会发出 系统指定声音, 可能是 “哔”, 也可能被忽略掉) |
'\b' | 8 | 退格, 即 Backspace 按键的输入 |
'\t' | 9 | 水平制表符, 即 Tab 按键的输入 |
'\n' | 10 | 换行, 即 切换到下一行 |
'\v' | 11 | 垂直制表符 |
'\f' | 12 | 换页 |
'\r' | 13 | 回车, 即 回到当前行的起始位置 |
'\e' | 27 | 同 Esc 按键的输入(非标准 ASCII C 中的转义, GCC 等编译器可能扩展实现) |
'\"' | 34 | 表示字符, 单个双引号", 一般在编程中 用于在 字符串中输入" |
'\'' | 39 | 表示字符, 单个单引号', 一般在编程中 用于在 字符串中输入' |
'\\' | 92 | 表示字符, 单个反斜杠\, 一般在编程中 用于在 字符串中输入\, 因为如果是单个\会与其他字符组合被识别为转义字符 |
在ASCII码表中, 还可以看到特殊符号和大小写英文字母
并且, ASCII码表中所有的字符都有一个对应的十进制数, 这个十进制数表示什么呢?
这个十进制数就表示这个符号的值
#include <stdio.h>
int main() { char a = 'a'; // 初始化字符 'a' char A = 'A'; // 初始化字符 'A' char zero = '0'; // 初始化字符 '0' char add = '+'; // 初始化字符 '+' char space = ' '; // 初始化字符 ' ' char lf = '\n'; // 初始化字符 '\n'
printf("a: %d\n", a); printf("A: %d\n", A); printf("0: %d\n", zero); printf("+: %d\n", add); printf("\' \': %d\n", space); printf("\\n: %d\n", lf);
return 0;}
从结果中可以看到, 打印的几个字符确实是有一个值的
实际上ASCII码表中的所有字符, 都有一个对应的整型值
在 C 语言中, ASCII码表中所有的字符实际上就是整型值, 需要字符形式输出的时候 表现为字符, 需要整型输出的时候 则表现为整型
以'a'为例, 上面的例子中 已经知道了'a'是97
#include <stdio.h>
int main() { char aChar = 'a'; int aNumber = 97;
printf("aChar('a') 的值是 %d\n", aChar); printf("aNumber(97) 表示的字符是 %c\n", aNumber);
return 0;}
可以看到, 字符'a'可以打印出整型值97, 并且整型值97也可以直接打印出字符'a'
这意味着, 在 C 语言中, 字符的真实类型实际是整型, char类型只是占用1byte大小的整型类型
所以, C 语言中, 字符和整型数据是可以直接进行计算的:
#include <stdio.h>
int main() { int a = 'a'; printf("a = %c, a - 32 = %c\n", a, a - 32);
return 0;}这段代码的执行结果是:

注意到'a' - 32的结果, 以字符的形式打印是'A'
这也就对应了: 'a'的ASCII码值为97, 'A'的ASCII码值是65
这一切现象, 都说明了: 字符在 C 语言中的实际类型是整型, char仅表示一种特定大小的整型类型
字符串 **
字符串的结尾
C 语言中, 被""双引号包裹起来的一串字符, 被称为字符串, 也叫字符串字面量
字符串结束的标志为'\0', 即 ASCII码表中值为0的符号
C 语言标准中, 处理字符串相关的函数基本都是以'\0'作为字符串的结尾判断的
并且, 每个字符串字面量的结尾都存在一个隐藏的'\0', 即:
"Hello World"实际组成是'H' 'e' 'l' 'l' 'o' ' ' 'W' 'o' 'r' 'l' 'd' '\0'也可以使用代码验证:
#include <stdio.h>#include <string.h>
int main() { printf("\"Hello World\" size: %lu\n", sizeof("Hello World")); printf("\"Hello World\" length: %lu\n", strlen("Hello World"));
return 0;}
可以看到输出结果为: "Hello World"的实际占用空间大小是12, 而字符串的有效长度为11
这就说明"Hello World"在实际的存储中, 并不是只有字面上的11个字符, 在结尾还有一个看不到的'\0'
如果你手动把这个'\0'给拿掉, 再使用这个字符串就可能出现问题:
#include <stdio.h>#include <string.h>
int main() { char str1[] = { 'H', 'e', 'l', 'l', 'o' }; char str2[] = { 'H', 'e', 'l', 'l', 'o', '\0' }; printf("str1: %s, size: %lu, length: %lu\n", str1, sizeof(str1), strlen(str1)); printf("str2: %s, size: %lu, length: %lu\n", str2, sizeof(str2), strlen(str2));
return 0;}
不去细究, 从打印结果上 就可以看出端倪
str1填入了5个字符, 计算出的实际大小也是5, 但 字符串长度却计算出了6
这明显是有问题的
虽然打印字符串确实是Hello, 但这可能是编译器做出了优化
事实上, 如果像str1这样没有'\0'结尾的字符数组, 如果直接当作字符串使用, 是有问题的, 不能当作合法字符串使用
NOTE
strlen()C 语言标准中, 用于 取字符串有效长度的函数, 以
'\0'判断字符串结尾使用前, 需要包含头文件
string.h
字符串的格式化
在上面的例子中, 已经使用过printf()打印字符串了
一个完整的字符串打印是这样调用的: printf("Hello World\n");
但是你不能使用printf()直接打印整型或者其他值数据:
#include <stdio.h>
int main() { int age = 22; printf(22); printf(age); printf("age");
return 0;}这段代码编译会有警告, 运行会出错误:

编译的警告大致为: printf()的第一个参数预期应该传入const char*类型, 但是实际传入的确实int类型
不过, 也编译出了一个可执行程序
但是, 运行可执行程序的时候, 会发生错误, 运行被终止
难道printf()不能打印其他数据类型的值吗?
当然不是的, 可以通过printf()将其他类型值格式化为字符串, 然后再打印出来
在之前的例子中也使用过, 但是并没有介绍过, 比如:
#include <stdio.h>#include <string.h>
int main() { int age1 = 18; int age2 = 20; printf("age1是: %d, age2是 %d\n", age1, age2);
printf("\"Hello World\" size: %lu\n", sizeof("Hello World")); printf("\"Hello World\" length: %lu\n", strlen("Hello World"));
return 0;}这段代码的执行结果是:

可以看到, 虽然printf()需要打印的字符串中, 我们并没有填入18 20 12 11这样的数字字符
但是, 程序确实将 变量age1和age2 以及 sizeof()的结果 与 strlen()的返回值, 以字符串的形式打印出来了
可以对比一下printf()的调用和实际的打印结果:
printf("age1是: %d, age2是 %d\n", age1, age2);实际输出的可见字符为age1是: 18, age2是: 20
printf("\"Hello World\" size: %lu\n", sizeof("Hello World"));实际输出的可见字符为"Hello World" size: 12
printf("\"Hello World\" length: %lu\n", strlen("Hello World"));实际输出的可见字符为"Hello World" length: 11通过对比可以发现: printf()调用中的%d和%lu被替换成了变量值的字符串 或 计算结果的字符串
printf()的用法就是这样的:
- 第一个参数, 必须是一个字符串:
"", 格式字符串 - 后面可以加参数, 参数类型要依次对应格式字符串中的格式说明符
这里提到的 格式说明符 是什么?
C 语言为格式化字符串 提供了一些 格式说明符, 可以在字符串中使用 用于将对应非字符串类型的数据 格式化为字符串
格式说明符, 就是以%开头 后接特定字符组合的字符串
C 语言中的每种数据类型都对应一种格式说明符, 具体可以看下表:
| 格式说明符 | 对应类型 | 举例 | 含义 |
|---|---|---|---|
%d | int | printf("%d", 10); | 打印有符号十进制整数 |
%u | unsigned int | printf("%u", 10u); | 打印无符号十进制整数 |
%x / %X | unsigned int | printf("%x", 255); | 十六进制输出(小/大写) |
%o | unsigned int | printf("%o", 8); | 八进制输出 |
%ld | long | printf("%ld", 123456L); | 长整型输出 |
%lu | unsigned long | printf("%lu", 123456UL); | 无符号长整型 |
%lld | long long | printf("%lld", 123456789012LL); | 长长整型 |
%f | float / double | printf("%f", 3.14); | 浮点数输出 |
%c | char | printf("%c", 'A'); | 字符输出 |
%s | char* | printf("%s", "abc"); | 字符串输出 |
%p | 指针 | printf("%p", ptr); | 输出地址 |
%% | —— | printf("100%%"); | 输出字面上的 % 字符因为单个 %已经被当作格式说明符的开始 |
这张表是需要铭记在心的, 它为你格式化字符串提供了基础
所以, 就可以使用printf()实现格式化的字符串打印, print是打印的意思, f就是格式化的意思
sprintf() 和 snprintf()
printf()是将字符串格式化打印到标准输出中, 一般就是终端中
C 语言还提供有其他相关函数, 可以将字符串格式化打印到另一个字符串中
sprintf()和snprintf():
-
sprintf()用于将一个字符串格式化输出、存储到另一个 可存储字符串的空间中
使用方法基本和
printf()一致但, 需要指定输出到某个可以存储字符串的空间中
示例代码如下:
#include <stdio.h>int main() {char destStr[256] = {0};printf("destStr: %s\n", destStr);sprintf(destStr, "1234 + 4321 = %d\n", 1234 + 4321);printf("destStr: %s\n", destStr);return 0;}执行结果为:

可以看到
sprintf()将"1234 + 4321 = 5555"写到了destStr这个空间中 -
snprintf()用于将一个字符串的前
n个字符格式化输出、存储到另一个 可存储字符串的空间中使用方法基本与
sprintf()一致, 但除了需要指定目标空间以外, 还需要指定一个整型n, 表示只将第n个字节之前的字符写入到另一个字符串中这里的
n表示的是字节数实例代码如下:
#include <stdio.h>int main() {char destStr[256] = {0};printf("destStr: %s\n", destStr);snprintf(destStr, 6, "1234567890\n");printf("destStr: %s\n", destStr);return 0;}执行结果为:

n传入为6, 则只把前5个字节的字符写入到目标空间中