加载中...
从零接触C语言-III: 字符 和 字符串

从零接触C语言-III: 字符 和 字符串

周三 7月 09 2025
2943 字 · 14 分钟

C语言的字符和字符串

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码表, 它在编程的世界里非常重要

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'27Esc 按键的输入(非标准ASCII C中的转义, GCC等编译器可能扩展实现)
'\"'34表示字符, 单个双引号", 一般在编程中 用于在 字符串中输入"
'\''39表示字符, 单个单引号', 一般在编程中 用于在 字符串中输入'
'\\'92表示字符, 单个反斜杠\, 一般在编程中 用于在 字符串中输入\, 因为如果是单个\会与其他字符组合被识别为转义字符

ASCII码表中, 还可以看到特殊符号和大小写英文字母

并且, ASCII码表中所有的字符都有一个对应的十进制数, 这个十进制数表示什么呢?

这个十进制数就表示这个符号的值

C
#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

C
#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语言中, 字符和整型数据是可以直接进行计算的:

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语言中的实际类型是整型

字符串 **

字符串的结尾

C语言中, 被""双引号包裹起来的一串字符, 被称为字符串, 也叫字符串字面量

字符串结束的标志为'\0', 即 ASCII码表中值为0的符号

C语言标准中, 处理字符串相关的函数基本都是以'\0'作为字符串的结尾判断的

并且, 每个字符串字面量的结尾都存在一个隐藏的'\0', 即:

C
"Hello World"
实际组成是
'H' 'e' 'l' 'l' 'o' ' ' 'W' 'o' 'r' 'l' 'd' '\0'

也可以使用代码验证:

C
#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'给拿掉, 再使用这个字符串就可能出现问题:

C
#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'结尾的字符数组, 如果直接当作字符串使用, 是有问题的

字符串的格式化

在上面的例子中, 已经使用过printf()打印字符串了

一个完整的字符串打印是这样调用的: printf("Hello World\n");

但是你不能使用printf()直接打印整型或者其他值数据:

C
##include <stdio.h>

int main() {
    int age = 22;
    printf(22);
    printf(age);
    printf("age");

    return 0;
}

这段代码编译会有警告, 运行会出错误:

编译的警告大致为: printf()的第一个参数预期应该传入const char*类型, 但是实际传入的确实int类型

不过, 也编译出了一个可执行程序

但是, 运行可执行程序的时候, 会发生错误, 运行被终止

难道printf()不能打印其他数据类型的值吗?

当然不是的, 可以通过printf()将其他类型值格式化为字符串, 然后再打印出来

在之前的例子中也使用过, 但是并没有介绍过, 比如:

C
#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这样的数字字符

但是, 程序确实将 变量age1age2 以及 sizeof()的结果 与 strlen()的返回值, 以字符串的形式打印出来了

可以对比一下printf()的调用和实际的打印结果:

C
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()的用法就是这样的:

  1. 第一个参数是一个字符串: ""
  2. 后面可以加参数, 参数类型要与 字符串中的格式说明符一一对应

这里提到的 格式说明符 是什么?

C语言为格式化字符串 提供了一些 格式说明符, 可以在字符串中使用 用于将对应非字符串类型的数据 格式化为字符串

格式说明符, 就是%开头 后接特定字符组合的字符串

C语言中的每种数据类型都对应一种格式说明符, 具体可以看下表:

格式说明符对应类型举例含义
%dintprintf("%d", 10);打印有符号十进制整数
%uunsigned intprintf("%u", 10u);打印无符号十进制整数
%x / %Xunsigned intprintf("%x", 255);十六进制输出(小/大写)
%ounsigned intprintf("%o", 8);八进制输出
%ldlongprintf("%ld", 123456L);长整型输出
%luunsigned longprintf("%lu", 123456UL);无符号长整型
%lldlong longprintf("%lld", 123456789012LL);长长整型
%ffloat / doubleprintf("%f", 3.14);浮点数输出
%ccharprintf("%c", 'A');字符输出
%schar*printf("%s", "abc");字符串输出
%p指针printf("%p", ptr);输出地址
%%——printf("100%%");输出字面上的 % 字符
因为单个%已经被当作格式说明符的开始

这张表是需要铭记在心的, 它为你格式化字符串提供了基础

所以, 就可以使用printf()实现格式化的字符串打印, print是打印的意思, f就是格式化的意思

sprintf()snprintf()

printf()是将字符串格式化打印到标准输出中, 一般就是终端中

C语言还提供有其他相关函数, 可以将字符串格式化打印到另一个字符串中

sprintf()snprintf():

  1. sprintf()

    用于将一个字符串格式化输出、存储到另一个 可存储字符串的空间中

    使用方法基本和printf()一致

    但, 需要指定输出到某个可以存储字符串的空间中

    示例代码如下:

    C
    #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这个空间中

  2. snprintf()

    用于将一个字符串的前n个字符格式化输出、存储到另一个 可存储字符串的空间中

    使用方法基本与sprintf()一致, 但除了需要指定目标空间以外, 还需要指定一个整型n, 表示只将第n个字节之前的字符写入到另一个字符串中

    这里的n表示的是字节数

    实例代码如下:

    C
    #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个字节的字符写入到目标空间中


Thanks for reading!

从零接触C语言-III: 字符 和 字符串

周三 7月 09 2025
2943 字 · 14 分钟