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

C语言直方图程序:最后一次循环字符串缓冲区被跳过问题

C直方图程序输出异常的原因分析

我编写了一个打印简单直方图的C程序,核心代码如下(完整代码超100行,需补充可说明):

#define LINE_CHAR '#'

void printHistogram(FILE * const file, size_t const fields_size, char const * const * const names, unsigned const * const data) {
    // 复用缓冲区避免不必要的堆分配
    size_t const name_buffer_size = max_string_length(fields_size, names) + 1;
    char name_buffer[name_buffer_size];
    name_buffer[name_buffer_size - 1] = '\0';

    // 数据条的缓冲区同理
    size_t line_buffer_size = max_unsigned(fields_size, data) + 1;
    char line_buffer[line_buffer_size];
    memset(line_buffer, LINE_CHAR, line_buffer_size);

    for (size_t i = 0; i < fields_size; i++) {
        size_t current_name_length = strlen(names[i]);
        // 填充剩余空间为空格
        memset(name_buffer + current_name_length, ' ', name_buffer_size - 1 - current_name_length);
        // 复制名称到缓冲区(不带终止符)
        memcpy(name_buffer, names[i], current_name_length);
        
        // 仅打印所需长度的条形
        line_buffer[data[i] + 1] = '\0';
        fprintf(file, "%s|%s\n", name_buffer, line_buffer);
        // 恢复缓冲区内容
        line_buffer[data[i] + 1] = LINE_CHAR;
    }
}

int main() {
    char const * const names[] = {"a", "b", "abc"};
    unsigned const data[] = {5, 10, 15};
    printHistogram(stdout, 3, names, data);
}

预期输出:

a  |#####
b  |##########
abc|###############

实际输出:

a  |######
b  |###########
|################

最后一次循环中名称缓冲区的内容被跳过,完全不显示,同时所有条形的#数量都比预期多1个,原因如下:

问题根源

  1. 数组越界写入破坏栈内存
    处理最后一组数据(data[i]=15)时,line_buffer_size的值为max_unsigned(3, data)+1 = 15+1=16,因此line_buffer是长度为16的数组,有效索引范围是0~15。但代码中执行了line_buffer[data[i]+1] = '\0';,也就是line_buffer[16] = '\0';——这属于越界写入,会直接破坏栈上相邻内存区域的数据。

    栈变量的存储位置连续,越界写入的'\0'恰好覆盖了name_buffer的起始字节,导致name_buffer变成空字符串,因此fprintf打印%s时完全没有输出名称。

  2. 条形长度计算错误
    代码中用line_buffer[data[i]+1] = '\0';截断条形,会导致打印出data[i]+1个#,而非预期的data[i]个。比如data[i]=5时,实际打印6个#,与预期不符。

修复方案

  • 修正条形截断的索引:将line_buffer[data[i]+1]改为line_buffer[data[i]],确保打印的#数量与data[i]一致。
  • 避免数组越界:修改后,当data[i]等于max_unsigned时,line_buffer[data[i]]是数组的最后一个有效索引(line_buffer_size-1),不会越界。

修复后的核心循环代码:

for (size_t i = 0; i < fields_size; i++) {
    size_t current_name_length = strlen(names[i]);
    memset(name_buffer + current_name_length, ' ', name_buffer_size - 1 - current_name_length);
    memcpy(name_buffer, names[i], current_name_length);
    
    // 修正截断索引,避免越界
    line_buffer[data[i]] = '\0';
    fprintf(file, "%s|%s\n", name_buffer, line_buffer);
    line_buffer[data[i]] = LINE_CHAR;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 10:05:55