You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止字符数组的缓冲区损坏?附相关代码示例

解决字符数组缓冲区损坏的问题

先直接点出你代码里的几个核心问题,这些都是导致缓冲区溢出和打印异常的根源:

  1. 无长度限制的scanf调用:scanf("%[^\n]", &arr1)完全没有限制输入字符的数量,只要用户输入的字符串超过buffSize-1(因为要留一个位置存终止符\0),就会直接溢出缓冲区,破坏相邻内存(比如arr2的空间,甚至是函数栈上的其他数据),这会导致后续打印arr1时出现异常,甚至程序崩溃。另外,&arr1其实是多余的——数组名arr1本身就代表首元素的地址,直接传arr1就行。

  2. fflush(stdin)是未定义行为:C标准里明确规定fflush只能用于输出流,对输入流使用fflush的行为是未定义的,不同编译器可能有不同表现,这会导致你的代码不可移植,甚至出现奇怪的bug。

  3. 依赖strlen判断长度不可靠:当缓冲区已经溢出后,arr1的终止符\0可能已经被覆盖,strlen(arr1)会一直读取内存直到找到一个\0,这时候得到的长度完全是错误的,根本起不到限制作用。


具体的防护和修复方法

方法1:给scanf添加长度限制

修改scanf的格式字符串,明确指定最大读取长度(要比数组大小小1,留位置给\0),同时用安全的方式清理输入缓冲区里的残留换行:

#include <stdio.h>
#include <string.h>
#define buffSize 20

void clrscr() {
    for(int i = 0; i < 25; ++i)
        putchar('\n');
}

int main() {
    char arr1[buffSize];
    char arr2[buffSize];
    int readCount;
    int c;

    do {
        clrscr();
        printf("String 1 :\n");
        // 限制最多读取19个字符(buffSize-1),避免溢出
        readCount = scanf("%19[^\n]", arr1);
        // 处理输入失败的情况(比如直接输入换行)
        if (readCount == 0) {
            arr1[0] = '\0';
        }
        // 清理输入缓冲区里剩下的所有字符(直到换行)
        while ((c = getchar()) != '\n' && c != EOF);
    } while(strlen(arr1) >= buffSize); // 这里用>=,因为strlen返回的是有效字符数,buffSize是总大小

    do {
        clrscr();
        printf("String 1: %s\n\n", arr1);
        printf("String 2 : ");
        readCount = scanf("%19[^\n]", arr2);
        if (readCount == 0) {
            arr2[0] = '\0';
        }
        while ((c = getchar()) != '\n' && c != EOF);
    } while(strlen(arr2) >= buffSize);

    // 测试输出
    clrscr();
    printf("String 1: %s\nString 2: %s\n", arr1, arr2);
    return 0;
}

方法2:优先使用fgets(更推荐)

fgets是专门用于读取整行输入的函数,它可以直接指定最大读取长度,安全性更高,而且不需要处理复杂的格式字符串:

#include <stdio.h>
#include <string.h>
#define buffSize 20

void clrscr() {
    for(int i = 0; i < 25; ++i)
        putchar('\n');
}

// 辅助函数:去掉fgets读取到的换行符
void removeNewline(char *str) {
    char *newline = strchr(str, '\n');
    if (newline != NULL) {
        *newline = '\0';
    }
}

int main() {
    char arr1[buffSize];
    char arr2[buffSize];

    do {
        clrscr();
        printf("String 1 :\n");
        // fgets最多读取buffSize-1个字符,自动添加'\0'
        fgets(arr1, buffSize, stdin);
        removeNewline(arr1);
        // 如果输入的内容超过buffSize-1,fgets会截断,剩下的字符留在缓冲区,需要清理
        if (strlen(arr1) == buffSize-1 && strchr(arr1, '\n') == NULL) {
            int c;
            while ((c = getchar()) != '\n' && c != EOF);
        }
    } while(strlen(arr1) >= buffSize);

    do {
        clrscr();
        printf("String 1: %s\n\n", arr1);
        printf("String 2 : ");
        fgets(arr2, buffSize, stdin);
        removeNewline(arr2);
        if (strlen(arr2) == buffSize-1 && strchr(arr2, '\n') == NULL) {
            int c;
            while ((c = getchar()) != '\n' && c != EOF);
        }
    } while(strlen(arr2) >= buffSize);

    clrscr();
    printf("String 1: %s\nString 2: %s\n", arr1, arr2);
    return 0;
}

核心防护原则总结

  • 永远不要使用无长度限制的输入格式:比如%s、%[^\n]不带长度修饰符,这是缓冲区溢出的头号原因。
  • 避免fflush(stdin):用getchar()循环清理输入缓冲区是标准且可移植的方式。
  • 检查输入函数的返回值:比如scanf的返回值表示成功读取的参数个数,fgets返回NULL表示读取失败(比如到达文件末尾),这些信息能帮你处理异常输入。
  • 优先选择更安全的输入函数:fgets、getline(POSIX标准)比scanf更适合读取字符串,因为它们的行为更明确,更容易控制长度。

内容的提问来源于stack exchange,提问作者Bernard Wijaya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:21:34