跳到主要内容
🔍

代码示例:1.2.7 输入输出深入 scanf/printf

全部示例均标注编译命令、适用环境与实测状态;完整验证记录见《03.5-代码编译运行验证记录》。

  • examples/ex1-printf-format.c · printf 宽度/精度/左对齐/补零(%5d / %.2f / %#x)

    /* ex1-printf-format.c —— printf 宽度/精度/对齐/补零(1.2.7)
     * 适用环境:Linux / Windows(MinGW) 标准:C11
     * 编译:gcc ex1-printf-format.c -o ex1 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./ex1(Windows: ex1.exe)
     */
    #include <stdio.h>
     
    int main(void)
    {
        printf("[%5d]  [%-5d]  [%05d]\n", 42, 42, 42);
        printf("[%8.2f]  [%-8.3f]  [%.1f]\n", 3.14159, 3.14159, 3.14159);
        printf("%#x  %#o\n", (unsigned int)255, (unsigned int)255);
        printf("%c  %s  %%\n", 'A', "literal");
        return 0;
    }
     
  • examples/ex2-scanf-checks.c · scanf 返回值检查、输入残留与 %s 限宽(输入固定来自 ex2-input.txt)

    /* ex2-scanf-checks.c —— scanf 返回值检查、输入残留与 %s 限宽(1.2.7)
     * 适用环境:Linux / Windows(MinGW) 标准:C11
     * 编译:gcc ex2-scanf-checks.c -o ex2 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./ex2(Windows: ex2.exe)——输入固定来自同目录 ex2-input.txt(freopen 重定向,保证可复现)
     * 输入文件内容:25 / Alice / hello(各占一行)
     */
    #include <stdio.h>
     
    int main(void)
    {
        int age;
        char name[32];
        char first;
        char leftover[32];
     
        if (freopen("ex2-input.txt", "r", stdin) == NULL) {
            printf("打不开 ex2-input.txt(需与程序同目录)\n");
            return 1;
        }
     
        /* 1) 返回值检查:scanf 返回成功匹配的项数 */
        int matched = scanf("%d", &age);
        printf("scanf(\"%%d\") 返回 %d,age = %d\n", matched, age);
     
        /* 2) 输入残留:上一行行尾的 \\n 还留在缓冲区,%%c 会直接把它读走 */
        if (scanf("%c", &first) == 1) {
            printf("残留字符被 %%c 读走,ASCII = %d(10 = 换行符 \\n)\n", (int)first);
        }
     
        /* 3) %s 必须限宽:%31s 保证最多写 31 个字符 + \\0 */
        if (scanf("%31s", name) == 1) {
            printf("名字(限宽 %%31s)= %s\n", name);
        }
     
        /* 4) 故意喂错输入:期望整数却给单词 "hello",scanf 返回 0 且不消费输入 */
        int x;
        int m2 = scanf("%d", &x);
        printf("喂错输入后 scanf 返回 %d(0 = 一个都没匹配上;本例不读 x 的值)\n", m2);
     
        /* 5) 错误输入仍残留在缓冲区:再用 %s 把它读走,证明"没消费" */
        if (scanf("%31s", leftover) == 1) {
            printf("残留在缓冲区的错误输入被读走 = \"%s\"\n", leftover);
        }
        return 0;
    }
     
  • examples/ex3-snprintf-safe.c · sprintf 溢出风险(conceptual 说明)vs snprintf 截断与返回值(实跑 snprintf 版)

    /* ex3-snprintf-safe.c —— sprintf 溢出风险 vs snprintf 截断判断(1.2.7)
     * 适用环境:Linux / Windows(MinGW) 标准:C11
     * 编译:gcc ex3-snprintf-safe.c -o ex3 -std=c11 -Wall -Wextra -Wpedantic
     * 运行:./ex3(Windows: ex3.exe)——标题从同目录 ex3-input.txt 读取(运行时长度,编译期不可证明)
     * 说明:sprintf 的无边界写入风险仅在正文/注释静态讲解(conceptual),本程序实际运行 snprintf 版本。
     */
    #include <stdio.h>
    #include <string.h>
     
    int main(void)
    {
        char small[16];
        char big[64];
        char title[32];
     
        if (freopen("ex3-input.txt", "r", stdin) == NULL) {
            printf("打不开 ex3-input.txt(需与程序同目录)\n");
            return 1;
        }
        if (fgets(title, sizeof(title), stdin) == NULL) {
            printf("输入为空\n");
            return 1;
        }
        title[strcspn(title, "\r\n")] = '\0';   /* strcspn 技巧:去掉行尾换行 */
     
        /* snprintf 返回"本应写入"的字符数(不含结尾 \0);
         * 返回值 >= 目标容量 → 说明发生了截断 */
        int need = snprintf(small, sizeof(small), "标题:%s", title);
        printf("small 容量 = %zu,本应写入 = %d 字符\n", sizeof(small), need);
        printf("截断后 small = \"%s\"\n", small);
        printf("截断判断:%d >= %zu%s\n", need, sizeof(small),
               need >= (int)sizeof(small) ? "发生了截断" : "未截断");
     
        snprintf(big, sizeof(big), "标题:%s", title);
        printf("big 中完整内容 = \"%s\"\n", big);
        return 0;
    }