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

C语言安全字符串获取函数异常排查及解决方案咨询

问题分析与解决方案

首先,你遇到的连续输出size值的问题,核心原因是输入缓冲区残留了未读取的字符:

当你输入的内容超过read指定的29字节时,read只会取走前29个字符,剩下的字符(包括换行符)会留在标准输入的内核缓冲区里。下一次循环调用read时,会直接读取这些残留的字符,而不是等待用户新的输入——这就导致你看到的连续输出size值的情况,比如第一次读取29个字符(size=29,触发错误),第二次读取缓冲区里剩下的几个字符(比如换行符或剩余内容,size变小),但这并不是用户想要的新输入。

你的代码里还有几个需要修正的细节:

  1. 未处理read的错误返回值:read可能返回-1(比如被信号中断或IO错误),直接使用返回值会导致数组越界或逻辑错误。
  2. malloc的大小计算有误:你在调用stripc去掉换行符后,size还是原来包含换行的长度,而且malloc(size)没有给字符串结束符\0留位置,会导致strcpy溢出。
  3. 未清空残留缓冲区:这就是你遇到的核心问题,必须在输入过长时清空缓冲区。

修正后的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:26:52