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

C语言:malloc内存区域重叠与scanf输入溢出问题求助

C语言中malloc内存溢出与scanf安全输入问题

我正在学习C语言,不清楚如何处理malloc返回的内存区域溢出的情况。以下是我的演示程序,当输入超长字符串时,scanf会导致内存溢出,我尝试使用scanf("%s10", str2)也未达到预期效果,程序代码及运行输出如下:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void print_mem(char *from, int64_t amount) {
    int step = 0x8;
    for (int i = 0; i < amount; i = i + step) {
        printf("%x = ", from + i);
        for (int j = 0; j < step; ++j) {
            int c = *(from + i + j);
            if (isprint(c) == 0) {
                c = ' ';
            }
            printf("%02x (%c) ", *(from + i + j), c);
        }
        printf("\n");
    }
}

int main() {
    // len = 48
    // 222222222222222222222222222222222222222222222222
    // 111111111111111111111111111111111111111111111111

    char *str1 = (char *) malloc(0x10 * sizeof(char));
    printf("str1: 0x%x\n", str1);
    char *str2 = (char *) malloc(0x10 * sizeof(char));
    printf("str2: 0x%x\n", str2);

    printf("\n\nInitial memory layout\n");
    print_mem(str1, 0x80);

    printf("\n\nType str2: ");
    scanf("%s", str2);
    printf("Memory after scanf str2\n");
    print_mem(str1, 0x80);

    printf("\n\nType str1: ");
    scanf("%s", str1);
    printf("Memory after scanf str1\n");
    print_mem(str1, 0x80);

    printf("\n\nstr2 = %s\n", str2);
    printf("str1 = %s\n", str1);

    return 0;
}

运行输出:

str1: 0x64c010
str2: 0x64c020


Initial memory layout
64c010 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c018 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c020 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c028 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c030 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c038 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c040 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c048 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c050 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c058 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c060 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c068 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c070 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c078 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c080 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c088 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 


Type str2: 222222222222222222222222222222222222222222222222
Memory after scanf str2
64c010 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c018 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c020 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c028 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c030 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c038 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c040 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c048 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c050 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c058 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c060 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c068 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c070 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c078 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c080 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c088 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 


Type str1: 111111111111111111111111111111111111111111111111
Memory after scanf str1
64c010 = 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 
64c018 = 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 
64c020 = 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 
64c028 = 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 
64c030 = 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 
64c038 = 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 31 (1) 
64c040 = 00 ( ) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c048 = 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 32 (2) 
64c050 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c058 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c060 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c068 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c070 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c078 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c080 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 
64c088 = 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 00 ( ) 


str2 = 11111111111111111111111111111111
str1 = 111111111111111111111111111111111111111111111111

问题分析与解决方案

1. 内存溢出的本质

你用malloc(0x10 * sizeof(char))分配了16字节的内存块,malloc返回的两块内存(str1和str2)本身是不重叠的,但scanf("%s")会持续写入直到遇到空白符,完全不检查内存边界,导致超长输入覆盖了相邻内存区域,这是缓冲区溢出,并非malloc内存重叠。

2. scanf("%s10")的错误

正确的输入长度限制格式是%10s,而非%s10。%10s表示最多读取9个字符(预留1字节存储字符串结束符\0),格式符顺序写反会导致限制失效。

3. 安全输入的实现方案

方案一:带长度限制的scanf

针对16字节的内存,使用%15s(留1字节给\0):

scanf("%15s", str2);
scanf("%15s", str1);

方案二:使用fgets更安全

fgets可以明确指定最大读取字节数,同时处理换行符:

// 读取str2,最多读取15个字符+1个\0
fgets(str2, 16, stdin);
// 去除末尾的换行符
str2[strcspn(str2, "\n")] = '\0';

// str1同理
fgets(str1, 16, stdin);
str1[strcspn(str1, "\n")] = '\0';

方案三:动态分配内存处理任意长度输入

如果需要支持任意长度的输入,可使用getline自动分配足够内存:

char *buf = NULL;
size_t len = 0;
// getline会根据输入长度自动扩容buf
getline(&buf, &len, stdin);
// 去除换行符
buf[strcspn(buf, "\n")] = '\0';
// 使用buf完成操作后释放内存
free(buf);

4. 额外注意事项

  • 每次malloc后必须检查分配是否成功,避免空指针操作:
    if (str1 == NULL || str2 == NULL) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }
    
  • 程序结束前释放malloc分配的内存,避免内存泄漏:
    free(str1);
    free(str2);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:01:28