在C

时间:2017-03-15 22:00:29

标签: c matrix graph

我编写了一个程序,它从stdin读取整数值,用一个或多个空格或换行符分隔,直到达到EOF。输入将包含不超过80个值。

在标准输出上,我想基于输入值创建一个简单的垂直柱形图,按从左到右的顺序,使用散列#字符。每列中打印的哈希数等于相应的输入值。

完成列上方的区域用空格字符填充。

到目前为止我已经这样了,它对4个数字或更大的输入正常工作,但是当我输入2或3个数字时,我得到一个seg错误。谁能明白为什么?

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

int main(int argc, const char*argv) {

    int arr[80];
    int count=0;
    int i,j;

    while(1) {
        if((scanf("%d", &arr[count++]))==EOF) {
            break;
        }
    }
    int max=arr[0];
    for(i=0; i<count; i++) {
        if(max<arr[i]) {
            max=arr[i];
        }
    }

    char **matrix;
    matrix=(char**)malloc(max*sizeof(char*));

    for(i=0;i<count;i++) {
        matrix[i]=(char*)malloc(sizeof(char)*max);
    }

    for(i=0;i<count-1;i++) {
        for(j=max-1;j>=0;j--) {
            if(max-j<=arr[i]) {
                 matrix[j][i]='#';
            }
            else {
                matrix[j][i]=' ';
            }
        }
    }

    for(i=0;i<max;i++) {
        for(j=0;j<count;j++) {
            printf("%c", matrix[i][j]);
        }
        printf("\n");
    }

    return 0;
}

1 个答案:

答案 0 :(得分:1)

问题在于OP正在分配足够的内存来存储max * max个字符(其中max是输入的最大值),而他们需要的是max * count(其中count如果只允许正数,则为输入的值的数量。

此外,程序会泄漏内存,因为缺少正确的free调用。

处理内存管理的一种更简单的方法(如果OP可以使用符合C99的编译器进行编译)使用可变长度数组:

char matrix[rows][cols];        // where rows and cols aren't known at compile time

如果VLA不是一个选项,仍然可以连续分配内存:

#include <stdlib.h>

char **matrix = malloc(rows * sizeof(*matrix));
if ( !matrix )
    exit(EXIT_FAILURE);
matrix[0] = malloc(rows * cols * sizeof(**matrix));
if ( !matrix[0] ) {
    free(matrix);
    exit(EXIT_FAILURE);
}
for ( int i = 1; i < rows; ++i )
    matrix[i] = matrix[i - 1] + cols;

// do something with 'matrix'...

free(matrix[0]);
free(matrix); 

另一个潜在的问题是负责输入的循环不会将输入的值的数量限制为缓冲区的大小(80),也不会检查这些值是否真的是数字。

以下是一个完整的工作实现(带有一些辅助函数):

#include "stdio.h"
#include "limits.h"

#define MAX_ARR_SIZE 80

int min (int a, int b) {
    return a < b ? a : b;
}

int max (int a, int b) {
    return a > b ? a : b;
}

void draw_bar_chart (FILE *out_stream, char fill_char,
                     int *arr, int size,
                     int bottom, int top);

int read_ints (FILE *in_stream,
               int *arr, int size,
               int *min, int *max);

int main(void) {
    int min_value, max_value;
    int values[MAX_ARR_SIZE];
    int n_values = read_ints(stdin, values, MAX_ARR_SIZE,
                             &min_value, &max_value);
    // Avoid clipping the chart
    int top_view = max(max_value, 0);
    int bottom_view = min(min_value, 0);

    draw_bar_chart(stdout, '#', values, n_values, bottom_view, top_view);
}

int read_ints (FILE *in_stream,
               int *arr, int size,
               int *min, int *max) {
    int count = 0;
    *min = INT_MAX;
    *max = INT_MIN;
    // Reads up to 'size' values to avoid buffer overflow.
    while ( count < size  &&  fscanf(in_stream, "%d", &arr[count]) == 1 )
    { // note that it stops when the read fails (EOF or not an int) ^^^^
        if ( arr[count] > *max )
            *max = arr[count];
        if ( arr[count] < *min )
            *min = arr[count];
        ++count;
    }
    return count;
}

void tidy_up (int a, int b, int *min, int *max) {
    if ( a > b ) {
        *min = b;
        *max = a;
    } else {
        *min = a;
        *max = b;
    }
}

void draw_bar_chart (FILE *out_stream, char fill_char,
                     int *arr, int size,
                     int bottom, int top) {
    int draw_height = top - bottom;
    int i, j, start, end;

    // VLA, requires a C99 compliant compiler
    char canvas[draw_height][size + 1];
    // null-terminates every row to make output easier
    for ( i = 0; i < draw_height; ++i )
        canvas[i][size] = '\0';
    // The "drawing" can be done in many ways...
    for ( j = 0; j < size; ++j ) {
        tidy_up(top, top - arr[j], &start, &end);
        for ( i = 0; i < start; ++i )
            canvas[i][j] = ' ';
        for ( ; i < end; ++i )
            canvas[i][j] = fill_char;
        for ( ; i < draw_height; ++i )
            canvas[i][j] = ' ';
    }

    for ( i = 0; i < draw_height; ++i ) {
        fprintf(out_stream, "%s\n", canvas[i]);
    }    
}

其中,例如那些输入

1 5 6 9 8 7 3 2 0 -3 -8 -5 -4 1 1 2 0 1 q

输出:

   #
   ##
   ###
  ####
 #####
 #####
 ######
 #######       #
########     ### #
         ####
         ####
         ####
          ###
          ##
          #
          #
          #

值得注意的是,对于这项特殊任务,我们根本不需要使用临时2D阵列。负责打印图表的功能可以这样实现:

void draw_bar_chart (FILE *out_stream, char fill_char,
                     int *arr, int size,
                     int bottom, int top) {
    int start, end;
    // "draws" the chart by determining if the current position is inside a bar
    for ( int i = top - 1; i >= bottom; --i ) {
        for ( int j = 0; j < size; ++j ) {
            tidy_up(0, arr[j], &start, &end);
            putc((i >= start  &&  i < end ? fill_char : ' '), out_stream);
        }
        puts("");
    }
}