C语言安全字符串获取函数异常排查及解决方案咨询
问题分析与解决方案
首先,你遇到的连续输出size值的问题,核心原因是输入缓冲区残留了未读取的字符:
当你输入的内容超过read指定的29字节时,read只会取走前29个字符,剩下的字符(包括换行符)会留在标准输入的内核缓冲区里。下一次循环调用read时,会直接读取这些残留的字符,而不是等待用户新的输入——这就导致你看到的连续输出size值的情况,比如第一次读取29个字符(size=29,触发错误),第二次读取缓冲区里剩下的几个字符(比如换行符或剩余内容,size变小),但这并不是用户想要的新输入。
你的代码里还有几个需要修正的细节:
- 未处理
read的错误返回值:read可能返回-1(比如被信号中断或IO错误),直接使用返回值会导致数组越界或逻辑错误。 malloc的大小计算有误:你在调用stripc去掉换行符后,size还是原来包含换行的长度,而且malloc(size)没有给字符串结束符\0留位置,会导致strcpy溢出。- 未清空残留缓冲区:这就是你遇到的核心问题,必须在输入过长时清空缓冲区。
修正后的read版本代码
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef char* string; // 实现一个去掉末尾目标字符的stripc函数 void stripc(char *str, char c) { size_t len = strlen(str); if (len > 0 && str[len-1] == c) { str[len-1] = '\0'; } } string get_string(const string prompt) { char temp[30]; ssize_t count; size_t size; printf("%s", prompt); do { // 处理read的错误返回 count = read(0, temp, 29); if (count == -1) { perror("read failed"); return NULL; } temp[count] = '\0'; size = strlen(temp); if (size > 24) { printf("\x1B[31mError\x1B[0m: too long.\n%s", prompt); // 清空输入缓冲区的残留字符,直到读到换行或EOF char dummy; while (read(0, &dummy, 1) > 0 && dummy != '\n'); } printf("size :%zu\n", size); } while (size > 24); stripc(temp, '\n'); size = strlen(temp); // 重新计算去掉换行后的长度 string word = malloc(size + 1); // 给'\0'留位置 if (word == NULL) { perror("malloc failed"); return NULL; } strcpy(word, temp); return word; }
其他安全获取字符串的方法
1. 用fgets配合缓冲区清空(标准C兼容)
fgets本身是安全的,只要你正确处理输入过长的情况,清空残留缓冲区即可:
typedef char* string; string get_string(const string prompt) { char temp[30]; size_t len; while (1) { printf("%s", prompt); if (fgets(temp, sizeof(temp), stdin) == NULL) { perror("fgets failed"); return NULL; } len = strlen(temp); // 判断是否输入过长:如果没读到换行符,说明缓冲区满了,还有内容留在输入里 if (temp[len-1] != '\n') { printf("\x1B[31mError\x1B[0m: too long.\n"); // 清空残留缓冲区 char c; while ((c = getchar()) != '\n' && c != EOF); } else { // 去掉换行符 temp[len-1] = '\0'; len--; if (len > 24) { printf("\x1B[31mError\x1B[0m: too long.\n"); } else { break; } } } string word = malloc(len + 1); if (word == NULL) { perror("malloc failed"); return NULL; } strcpy(word, temp); return word; }
2. 动态扩容读取(支持任意长度输入,POSIX兼容)
用getline函数可以动态分配缓冲区,读取完整的一行,然后再判断长度是否符合要求:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef char* string; string get_string(const string prompt) { size_t buf_size = 30; char *buf = malloc(buf_size); if (buf == NULL) { perror("malloc failed"); return NULL; } while (1) { printf("%s", prompt); ssize_t read_len = getline(&buf, &buf_size, stdin); if (read_len == -1) { perror("getline failed"); free(buf); return NULL; } // 去掉换行符 if (buf[read_len-1] == '\n') { buf[read_len-1] = '\0'; read_len--; } if (read_len > 24) { printf("\x1B[31mError\x1B[0m: too long.\n"); } else { // 拷贝到新的内存返回 string word = malloc(read_len + 1); if (word == NULL) { perror("malloc failed"); free(buf); return NULL; } strcpy(word, buf); free(buf); return word; } } }
内容的提问来源于stack exchange,提问作者Adonai
相关产品推荐
相关产品推荐

