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
相关产品推荐
相关产品推荐

