首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

11.6  设计程序

在本章的最后,通过以下的案例实践本章学到的知识。

11.6.1  问题

数值数据用图表表示通常更容易理解。现在要处理的问题是编写一个程序,从一组数值中生成柱状图。选择柱状图的理由有如下三个:

(1) 实践结构的用法。

(2) 了解如何在有效的空间中放置并显示柱状图。

(3) 柱状图在实际应用中很常见。

11.6.2  分析

无须对纸张大小、列数甚至图的比例作任何假设。只需编写一个函数,它将纸张大小作为参数,使柱状图能放在该纸张上。这可以使函数适用于任何的情况。我们将数值存放在链表的一系列结构里。这样,就只需将第一个结构传递给函数,函数就能够从链表中得到所有的结构。这个结构非常简单,但以后可以用自己设计的信息去修饰它。

假定柱状图中的竖条显示顺序和数据输入的顺序相同,因此无须排序数据。这个程序有两个函数:生成柱状图的函数和main()函数,用来练习柱状图生成过程。

以下是所需的步骤:

(1) 编写柱状图函数。

(2) 编写main()函数,测试柱状图函数。

11.6.3  解决方案

本节列出了解决问题的步骤。

1. 步骤1

很明显,这个程序将使用结构,因为这是本章讨论的主题。第一步是设计程序要使用的结构。这里将使用typedef,以避免重复使用关键字struct。

/* Program 11.9 Generating a bar chart */

#include <stdio.h>

typedef struct barTAG

{

double value;

struct barTAG *pnextbar;

}bar;

int main(void)

{

/* Code for main */

}

/* Definition of the bar-chart function */

barTAG结构用它的值定义一个竖条。注意将该结构中的指针定义为指向下一个结构。这样就可以把竖条存储为链表,其优点是在分配内存时不会浪费内存。链表适合于本例,因为我们只想按顺序遍历所有的竖条,从第一个到最后一个。接着按输入值的顺序创建竖条,将新建的竖条追加到前一个竖条之后。然后遍历链表中的结构,生成柱状图的可视化表示。

这里必须使用struct barTAG定义结构,而不能使用类型名称bar,因为此时编译器还没有完成typedef的处理,所以bar还未定义。换句话说,编译器先分析barTAG结构,再利用typedef定义bar的意义。

现在为柱状图函数指定函数原型,给这个函数定义添加框架。它需要的参数有指向链表中第一个竖条的指针、页高、页宽及图表的标题。

/* Program 11.9 Generating a bar chart */

#include <stdio.h>

#define PAGE_HEIGHT 20

#define PAGE_WIDTH 40

typedef struct barTAG

{

double value;

struct barTAG *pnextbar;

}bar;

typedef unsigned int uint; /* Type definition*/

/* Function prototype */

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height, char *title);

int main(void)

{

/* Code for main */

}

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height,char *title)

{

/* Code for function…*/

return 0;

}

添加一条typedef语句,将uint定义为unsigned int的同义词。这样可以缩短用unsigned int声明变量的语句长度。

接下来为柱状图需要的基本数据添加一些声明和代码。需要竖条的最大和最小值、图表的垂直高度(即最大值和最小值之差)。另外,需要根据纸张的宽度及竖条的数量计算竖条的宽,并调整高度,以包含水平轴和标题。

/* Program 11.9 Generating a bar chart */

#include <stdio.h>

#define PAGE_HEIGHT 20

#define PAGE_WIDTH   40

typedef struct barTAG

{

double value;

struct barTAG *pnextbar;

}bar;

typedef unsigned int uint; /* Type definition */

/* Function prototype */

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height, char *title);

int main(void)

{

/* Code for main */

}

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height,char *title)

{

bar *plastbar = pfirstbar; /* Pointer to previous bar */

double max = 0.0;           /* Maximum bar value */

double min = 0.0;           /* Minimum bar value */

double vert_scale = 0.0;    /* Unit step in vertical direction */

uint bar_count = 1;         /* Number of bars - at least 1 */

uint barwidth = 0;          /* Width of a bar */

uint space = 2;             /* spaces between bars */

/* Find maximum and minimum of all bar values */

/* Set max and min to first bar value */

max = min = plastbar->value;

while((plastbar = plastbar->pnextbar) != NULL)

{

bar_count++; /* Increment bar count */

max = (max < plastbar->value)? plastbar->value : max;

min = (min > plastbar->value)? plastbar->value : min;

}

vert_scale = (max - min)/page_height; /* Calculate step length */

/* Check bar width */

if((barwidth = page_width/bar_count - space) < 1)

{

printf("\nPage width too narrow.\n");

return -1;

}

/* Code for rest of the function…*/

return 0;

}

space变量存放两个竖条间的空格数,初值设为2。柱状图在输出时是一次显示一行,因此需要一个字符串来对应一行一行地绘制该竖条时所使用的区段,还需要另一个相同长度的字符串,包含页面上特定位置没有竖条时所使用的空格数。下面添加如下代码:

/* Program 11.9 Generating a bar chart */

#include <stdio.h>

#include <stdlib.h>

#define PAGE_HEIGHT 20

#define PAGE_WIDTH   40

typedef struct barTAG

{

double value;

struct barTAG *pnextbar;

}bar;

typedef unsigned int uint; /* Type definition */

/* Function prototype */

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height, char *title);

int main(void)

{

/* Code for main */

}

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height,  char *title)

{

bar *plastbar = pfirstbar; /* Pointer to previous bar */

double max = 0.0;           /* Maximum bar value */

double min = 0.0;           /* Minimum bar value */

double vert_scale = 0.0;    /* Unit step in vertical direction */

uint bar_count = 1;         /* Number of bars - at least 1 */

uint barwidth = 0;          /* Width of a bar */

uint space = 2;             /* spaces between bars */

uint i = 0;                 /* Loop counter */

char *column = NULL;        /* Pointer to bar column section */

char *blank = NULL;         /* Blank string for bar+space */

/* Find maximum and minimum of all bar values */

/* Set max and min to first bar value */

max = min = plastbar->value;

while((plastbar = plastbar->pnextbar) != NULL)

{

bar_count++; /* Increment bar count */

max = (max < plastbar->value)? plastbar->value : max;

min = (min > plastbar->value)? plastbar->value : min;

}

vert_scale = (max - min)/page_height; /* Calculate step length */

/* Check bar width */

if((barwidth = page_width/bar_count - space) < 1)

{

printf("\nPage width too narrow.\n");

return -1;

}

/* Set up a string that will be used to build the columns */

/* Get the memory */

if((column = malloc(barwidth + space + 1)) == NULL)

{

printf("\nFailed to allocate memory in barchart()"

" - terminating program.\n");

exit(1);

}

for(i = 0 ; i<space ; i++)

*(column+i)=' '; /* Blank the space between bars */

for( ; i<space+barwidth ; i++)

*(column+i)='#'; /* Enter the bar characters */

*(column+i) = '\0'; /* Add string terminator */

/* Set up a string that will be used as a blank column */

/* Get the memory */

if((blank = malloc(barwidth + space + 1)) == NULL)

{

printf("\nFailed to allocate memory in barchart()"

" - terminating program.\n");

exit(1);

}

for(i = 0 ; i<space+barwidth ; i++)

*(blank+i) = ' '; /* Blank total width of bar+space */

*(blank+i) = '\0'; /* Add string terminator */

/* Code for rest of the function…*/

free(blank);        /* Free memory for blank string */

free(column);       /* Free memory for column string */

return 0;

}

这里用字符'#'绘制竖条。画竖条时,要先编写一个字符串,使之含有space个空格和barwidth个'#'字符。之后使用库函数malloc()为它动态分配内存,因此必须给头文件<stdlib.h>添加#include指令。用来画竖条的字符串是column,blank是包含空格的且长度与column相同的字符串。画完柱状图后,在退出之前要释放column和blank所占的内存。

接下来,添加画竖条图的最后一段代码:

/* Program 11.9 Generating a bar chart */

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

#define PAGE_HEIGHT 20

#define PAGE_WIDTH   40

typedef struct barTAG

{

double value;

struct barTAG *pnextbar;

}bar;

typedef unsigned int uint; /* Type definition */

/* Function prototype */

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height, char *title);

int main(void)

{

/* Code for main */

}

int bar_chart(bar *pfirstbar, uint page_width, ,

uint page_height char *title)

{

bar *plastbar = pfirstbar;   /* Pointer to previous bar */

double max = 0.0;            /* Maximum bar value */

double min = 0.0;            /* Minimum bar value */

double vert_scale = 0.0;     /* Unit step in vertical direction */

double position = 0.0;       /* Current vertical position on chart */

uint bar_count = 1;          /* Number of bars - at least 1 */

uint barwidth = 0;           /* Width of a bar */

uint space = 2;              /* spaces between bars */

uint i = 0;                  /* Loop counter */

uint bars = 0;               /* Loop counter through bars */

char *column = NULL;         /* Pointer to bar column section */

char *blank = NULL;          /* Blank string for bar+space */

bool axis = false;           /* Indicates axis drawn */

/* Find maximum and minimum of all bar values */

/* Set max and min to first bar value */

max = min = plastbar->value;

while((plastbar = plastbar->pnextbar) != NULL)

{

bar_count++; /* Increment bar count */

max = (max < plastbar->value)? plastbar->value : max;

min = (min > plastbar->value)? plastbar->value : min;

}

vert_scale = (max - min)/page_height; /* Calculate step length */

/* Check bar width */

if((barwidth = page_width/bar_count - space) < 1)

{

printf("\nPage width too narrow.\n");

return -1;

}

/* Set up a string that will be used to build the columns */

/* Get the memory */

if((column = malloc(barwidth + space + 1)) == NULL)

{

printf("\nFailed to allocate memory in barchart()"

" - terminating program.\n");

exit(1);

}

for(i = 0 ; i<space ; i++)

*(column+i)=' '; /* Blank the space between bars */

for( ; i < space+barwidth ; i++)

*(column+i)='#'; /* Enter the bar characters */

*(column+i) = '\0'; /* Add string terminator */

/* Set up a string that will be used as a blank column */

/* Get the memory */

if((blank = malloc(barwidth + space + 1)) == NULL)

{

printf("\nFailed to allocate memory in barchart()"

" - terminating program.\n");

exit(1);

}

for(i = 0 ; i<space+barwidth ; i++)

*(blank+i) = ' ';      /* Blank total width of bar+space */

*(blank+i) = '\0';       /* Add string terminator */

printf("^ %s\n", title); /* Output the chart title */

/* Draw the bar chart */

position = max;

for(i = 0 ; i <= page_height ; i++)

{

/* Check if we need to output the horizontal axis */

if(position <= 0.0 && !axis)

{

printf("+");          /* Start of horizontal axis */

for(bars = 0; bars < bar_count*(barwidth+space); bars++)

printf("-");        /* Output horizontal axis */

printf(">\n");

axis = true;          /* Axis was drawn */

position -= vert_scale;/* Decrement position */

continue;

}

printf("|");           /* Output vertical axis */

plastbar = pfirstbar; /* start with the first bar */

/* For each bar…*/

for(bars = 1; bars <= bar_count; bars++)

{

/* If position is between axis and value, output column */

/* otherwise output blank */

printf("%s", position <= plastbar->value &&

plastbar->value >= 0.0 && position > 0.0 ||

position >= plastbar->value &&

plastbar->value <= 0.0 &&

position <= 0.0 ? column: blank);

plastbar = plastbar->pnextbar;

}

printf("\n");             /* End the line of output */

position -= vert_scale; /* Decrement position */

}

if(!axis) /* Have we output the horizontal axis? */

{                           /* No, so do it now */

printf("+");

for(bars = 0; bars < bar_count*(barwidth+space); bars++)

printf("-");

printf(">\n");

}

free(blank);     /* Free memory for blank string */

free(column);    /* Free memory for column string */

return 0;

}

for循环会输出page_height行字符。每行表示垂直轴上一段由vert_scale代表的距离。这个值是page_height除以最大值和最小值之差得到的。因此,第一行输出对应的position是max,在每次迭代中,position都会递减vert_scale,直到到达min为止。

在输出每一行之前,都要先确定是否要输出水平轴。当position小于或等于0,且尚未显示水平轴时,就需要输出水平轴。

除了水平轴之外,还必须确定每个竖条位置显示什么内容。这是为每个竖条执行的内层for循环内决定的。printf()函数内的条件运算符会选择输出column或blank。如果position的值介于竖条最大值和0之间,则输出column,否则输出blank。在输出完整的一行后,输出'\n'以结束这行,并递增position的值。

所有的竖条可能都是正的,此时需要确保在循环结束后输出水平轴,因为它在循环内没有输出。

2. 步骤2

现在需要实现main()函数,调用bar_chart()函数:

/* Program 11.9 Generating a bar chart */

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <stdbool.h>

#define PAGE_HEIGHT 20

#define PAGE_WIDTH 40

typedef struct barTAG       /* Bar structure */

{

double value;             /* Value of bar */

struct barTAG *pnextbar; /* Pointer to next bar */

}bar;                       /* Type for a bar */

typedef unsigned int uint; /* Type definition */

/* Function prototype */

int bar_chart(bar *pfirstbar, uint page_width,

uint page_height, char *title);

int main()

{

bar firstbar;          /* First bar structure */

bar *plastbar = NULL; /* Pointer to last bar */

char value[80];        /* Input buffer */

char title[80];        /* Chart title */

printf("\nEnter the chart title: ");

gets(title);           /* Read chart title */

for( ;; )              /* Loop for bar input */

{

printf("Enter the value of the bar, or use quit to end: ");

gets(value);

if(strcmp(value, "quit") == 0) /* quit entered? */

break;                          /* then input finished */

/* Store in next bar */

if(!plastbar)                     /* First time? */

{

firstbar.pnextbar = NULL; /* Initialize next pointer */

plastbar = &firstbar;      /* Use the first */

}

else

{

/* Get memory */

if(!(plastbar-> = malloc(sizeof(bar))))

{

printf("Oops! Couldn't allocate memory\n");

return -1;

}

plastbar = plastbar->pnextbar; /* Old next is new bar */

plastbar->pnextbar = NULL;      /* New bar next is NULL */

}

plastbar->value = atof(value); /* Store the value */

}

/* Create bar-chart */

bar_chart(&firstbar, PAGE_WIDTH, PAGE_HEIGHT, title);

/* We are done, so release all the memory we allocated */

while(firstbar.pnextbar)

{

plastbar = firstbar.pnextbar;            /* Save pointer to next */

firstbar.pnextbar = plastbar->pnextbar; /* Get one after next */

free(plastbar); /* Free next memory */

}

return 0;

}

int bar_chart(bar *pfirstbar, uint page_width, uint page_height, char *title)

{

/* Implementation of function as before…*/

}

使用gets()函数读取柱状图的标题后,就在for循环内读入连续的数值。除了第一个值外,其他值都要用于给新的竖条结构分配内存,之后存储该值。当然,可以跟踪第一个结构,因为它链接了其他结构,跟踪上一个新增结构中的指针,在添加下一个结构时更新它的指针pnextbar。输入了所有的值后,就调用bar_chart()函数,生成柱状图。最后,释放竖条结构的内存。注意,不能删除firstbar,因为它的内存不是动态分配的。给头文件<string.h>包含#include指令,因为使用了gets()函数。

之后在main()函数中添加一行代码,根据输入的值生成柱状图。程序的输出如下:

Enter the chart title: Trial Bar Chart

Enter the value of the bar, or use quit to end: 6

Enter the value of the bar, or use quit to end: 3

Enter the value of the bar, or use quit to end: -5

Enter the value of the bar, or use quit to end: -7

Enter the value of the bar, or use quit to end: 9

Enter the value of the bar, or use quit to end: 4

Enter the value of the bar, or use quit to end: quit

^ Trial Bar Chart

|                          ####

|                          ####

|                          ####

|                          ####

| ####                    ####

| ####                    ####

| ####                    ####

| ####                    #### ####

| #### ####              #### ####

| #### ####              #### ####

| #### ####              #### ####

| #### ####              #### ####

+------------------------------------>

|             #### ####

|             #### ####

|             #### ####

|             #### ####

|             #### ####

|                   ####

|                   ####

|                   ####

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论