我正在阅读K&R第二版,但在理解练习1-13时遇到困难。答案是这样的代码
#include <stdio.h>
#define MAXHIST 15
#define MAXWORD 11
#define IN 1
#define OUT 0
main()
{
int c, i, nc, state;
int len;
int maxvalue;
int ovflow;
int wl[MAXWORD];
state = OUT;
nc = 0;
ovflow = 0;
for (i = 0; i < MAXWORD; i++)
wl[i] = 0;
while ((c = getchar()) != EOF)
{
if(c == ' ' || c == '\n' || c == '\t')
{
state = OUT;
if (nc > 0)
{
if (nc < MAXWORD)
++wl[nc];
else
++ovflow;
}
nc = 0;
}
else if (state == OUT)
{
state = IN;
nc = 1;
}
else
++nc;
}
maxvalue = 0;
for (i = 1; i < MAXWORD; ++i)
{
if(wl[i] > maxvalue)
maxvalue = wl[i];
}
for(i = 1; i < MAXWORD; ++i)
{
printf("%5d - %5d : ", i, wl[i]);
if(wl[i] > 0)
{
if((len = wl[i] * MAXHIST / maxvalue) <= 0)
len = 1;
}
else
len = 0;
while(len > 0)
{
putchar('*');
--len;
}
putchar('\n');
}
if (ovflow > 0)
printf("There are %d words >= %d\n", ovflow, MAXWORD);
return 0;
}
在顶部,wl
被声明和初始化。我不明白的是,如果它只计算单词的长度,为什么要遍历它并将所有内容都设置为零?它不会跟踪有多少个单词,而只会跟踪单词的长度,因此为什么将所有内容都设置为0?
我知道目前还不清楚,在过去的20分钟里一直在向我施加压力,我不知道为什么。
答案 0 :(得分:3)
数组i
的第wl[]
个元素是在输入文件中找到的长度为i
的单词数。 wl[]
数组需要首先进行零初始化,以便++wl[nc];
不会通过尝试使用未初始化的变量而导致未定义的行为,并且表示不存在的字长的数组元素反映出否找到了这样的字长。
请注意,遇到长度为++wl[nc]
的单词时,wl[nc]
将使值nc
递增。如果未初始化数组,则代码第一次尝试递增数组元素时,它将试图递增 indeterminate 值。这种尝试将导致不确定的行为。
此外,表示在输入中找不到的字长计数的数组索引应保持零值,但如果不进行零初始化,则这些值将不确定。即使尝试打印这些不确定的值也会导致未定义的行为。
道德:将变量初始化为明智的值,或在尝试使用它们之前将其存储在其中。
使用数组初始值设定项对wl[]
数组进行零初始化似乎更加简单明了:
int wl[MAXWORD] = { 0 };
此后,无需执行将另一个文件的数组值设置为零(除非再次使用该数组)的循环。但是,发布的代码来自Tondo和Gimpel的 The C Answer Book 。本书以K&R的形式提供了第二版K&R中的练习的解决方案,并且仅使用每次练习之前书中介绍的思想。此练习1.13在“第1章-教程简介”中进行。这是该语言的简短介绍,缺少许多细节,稍后将在书中找到。至此,已经引入了赋值和数组,但是还没有引入数组初始化器(必须等到第4章),并且到目前为止,使用数组的K&R代码已经使用循环对数组进行了初始化。请不要从已有30多年历史的书籍的介绍性章节中过多地了解代码风格。
自K&R发布以来,C语言发生了许多变化,例如main()
不再是main()
函数的有效函数签名。请注意,函数签名必须是int main(void)
或int main(int argc, char *argv[])
(或int main(int argc, char **argv)
)之一,并警告main()
的实现定义的签名。
答案 1 :(得分:1)
所有设置为0,因为如果不初始化数组,则将使用随机数初始化数组。随机数将导致程序错误。除了可以循环遍历数组的每个位置之外,您还可以在int wl[MAXWORD] = {0};
处执行int wl[MAXWORD];
,这将在数组的每个位置放置0,因此您不必进行循环。
答案 2 :(得分:-1)
我编辑了您的代码并在处理过程中添加了一些注释,以解释发生了什么。我还更改了您的某些直方图计算方式,因为它们对我而言似乎没有意义。
底线:它使用原始的“状态机”来计数不是空格的每组字符中的字母。它将其存储在wl[]
中,以便wl[i]
包含一个整数,该整数告诉您有多少组字符(有时称为“令牌”)的字长为i
。因为这是通过递增w[]
的适当元素来完成的,所以必须将每个元素初始化为零。不这样做将导致不确定的行为,但可能会导致w[]
的每个元素中的计数变得荒谬而荒谬。
此外,任何长度无法反映在w[]
中的令牌都将被记入ovflow
变量中,因此最后 将会 是每个令牌的记帐。
#include <stdio.h>
#define MAXHIST 15
#define MAXWORD 11
#define IN 1
#define OUT 0
int main(void) {
int c, i, nc, state;
int len;
int maxvalue;
int ovflow;
int wl[MAXWORD];
// Initializations
state = OUT; //Start off not assuming we're IN a word
nc = 0; //Start off with a character count of 0 for current word
ovflow = 0; //Start off not assuming any words > MAXWORD length
// Start off with our counters of words at each length at zero
for (i = 0; i < MAXWORD; i++) {
wl[i] = 0;
}
// Main loop to count characters in each 'word'
// state keeps track of whether we are IN a word or OUTside of one
// For each character in the input stream...
// - If it's whitespace, set our state to being OUTside of a word
// and, if we have a character count in nc (meaning we've just left
// a word), increment the counter in the wl (word length) array.
// For example, if we've just counted five characters, increment
// wl[5], to reflect that we now know there is one more word with
// a length of five. If we've exceeded the maximum word length,
// then increment our overflow counter. Either way, since we're
// currently looking at a whitespace character, reset the character
// counter so that we can start counting characters with our next
// word.
// - If we encounter something other than whitespace, and we were
// until now OUTside of a word, change our state to being IN a word
// and start the character counter off at 1.
// - If we encounter something other than whitespace, and we are
// still in a word (not OUTside of a word), then just increment
// the character counter.
while ((c = getchar()) != EOF) {
if (c == ' ' || c == '\n' || c == '\t') {
state = OUT;
if (nc > 0) {
if (nc < MAXWORD) ++wl[nc];
else ++ovflow;
}
nc = 0;
} else if (state == OUT) {
state = IN;
nc = 1;
} else {
++nc;
}
}
// Find out which length has the most number of words in it by looping
// through the word length array.
maxvalue = 0;
for (i = 1; i < MAXWORD; ++i) {
if(wl[i] > maxvalue) maxvalue = wl[i];
}
// Print out our histogram
for (i = 1; i < MAXWORD; ++i) {
// Print the word length - then the number of words with that length
printf("%5d - %5d : ", i, wl[i]);
if (wl[i] > 0) {
len = wl[i] * MAXHIST / maxvalue;
if (len <= 0) len = 1;
} else {
len = 0;
}
// This is confusing and unnecessary. It's integer division, with no
// negative numbers. What we want to have happen is that the length
// of the bar will be 0 if wl[i] is zero; that the bar will have length
// 1 if the bar is otherwise too small to represent; and that it will be
// expressed as some fraction of MAXHIST otherwise.
//if(wl[i] > 0)
// {
// if((len = wl[i] * MAXHIST / maxvalue) <= 0)
// len = 1;
// }
// else
// len = 0;
// Multiply MAXHIST (our histogram maximum length) times the relative
// fraction, i.e., we're using a histogram bar length of MAXHIST for
// our statistical mode, and interpolating everything else.
len = ((double)wl[i] / maxvalue) * MAXHIST;
// Our one special case might be if maxvalue is huge, a word length
// with just one occurrence might be rounded down to zero. We can fix
// that manually instead of using a weird logic structure.
if ((len == 0) && (wl[i] > 0)) len = 1;
while (len > 0) {
putchar('*');
--len;
}
putchar('\n');
}
// If any words exceeded the maximum word length, say how many there were.
if (ovflow > 0) printf("There are %d words >= %d\n", ovflow, MAXWORD);
return 0;
}