Reputation: 826
I have written a program which takes up to 100 words and stores them in an array. It then works out the average length of the words and prints the result to stderr. I have the program working fine on all of my test scripts except one, in the given test script the average is calculated to be 4.04 but I need it to be 4.02. I am not sure why I am not getting this answer precise enough? Cheers
Program -
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *emalloc(size_t s) {
void *result = malloc(s);
if (NULL == result) {
fprintf(stderr, "Memory allocation failed!\n");
exit(EXIT_FAILURE);
}
return result;
}
/* n is the size of the array */
void print_array(char **a, int n, double average) {
if (n == 0) {
/* do nothing */
} else {
if (strlen(a[0]) > average) {
fprintf(stdout, "%s\n", a[0]);
}
print_array(a + 1, n - 1, average);
}
}
int main()
{
#define SIZE 100
char *username[100];
char word[80];
int num_words = 0;
int p;
float average = 0.0;
/* Read words into array */
while(1 == scanf("%s", word)) {
username[num_words] = emalloc((strlen(word) + 1) * sizeof(word[0]));
strcpy(username[num_words], word);
num_words++;
}
/* Print out array */
for (p = 0; p < num_words; p++) {
average += strlen(username[p]);
}
average = average / num_words;
print_array(username, num_words, average);
if (average > 0) {
fprintf(stderr, "%.2f\n", average);
}
return EXIT_SUCCESS;
}
Test file of words -
hello
hi there
The quick brown fox jumps over the lazy dog
Mary had a little lamb
Little lamb little lamb
Mary had a little lamb
Its fleece was white as snow
Everywhere that Mary went
Mary went, Mary went
Everywhere that Mary went
The lamb was sure to go
It followed her to school one day
School one day, school one day
It followed her to school one day
Which was against the rules
It made the children laugh and play
Laugh and play, laugh and play
It made the children laugh and play
To see a lamb in school
Upvotes: 2
Views: 164
Reputation: 84541
Continuing from my comments, you have several areas where you can remove unneeded code, and areas where you need to add checks to insure you do not add more words than you have pointers to hold or read more characters than you have storage for.
For example, you should never attempt to read more than num_words
into your username
array and you should limit the number of character you read each time to SIZE - 1
chars. You can do that with:
/* Read words into array */
while (num_words + 1 < SIZE && 1 == scanf (" %99s", word)) {
To allocate storage for each word, you need nothing more than:
size_t len = strlen (word);
username[num_words] = emalloc (len + 1);
You may also find it more convenient to keep a running sum
to the word lengths and eliminate p
altogether.
num_words++;
sum += len;
Avoid mixing double
and float
types in the same series of computations,
just make average
(and sum
) type double
.
Putting those pieces together, you could rewrite main()
similar to the following:
int main (void)
{
char *username[SIZE],
word[SIZE];
int num_words = 0;
double sum = 0.0,
average = 0.0;
/* Read words into array */
while (num_words + 1 < SIZE && 1 == scanf (" %99s", word)) {
size_t len = strlen (word);
username[num_words] = emalloc (len + 1);
if (!username[num_words]) {
fprintf (stderr, "error: memory exhausted, username[%d]\n",
num_words);
exit (EXIT_FAILURE);
}
strcpy (username[num_words], word);
num_words++;
sum += len;
}
average = sum / num_words;
print_array (username, num_words, average);
if (average > 0) {
fprintf(stderr, "%.2f\n", average);
}
return EXIT_SUCCESS;
}
Example Use/Output
Using your input file:
$ ./bin/words_avg_len <../dat/wcwords.txt
hello
there
quick
brown
jumps
little
Little
little
little
fleece
white
Everywhere
went,
Everywhere
followed
school
School
school
followed
school
Which
against
rules
children
laugh
Laugh
play,
laugh
children
laugh
4.02
(note: 4.02
does include two commas)
As per the discussion, there is also no need to store the words in a array at all to compute the average length of the strings read from input. The only critical pieces of data are the length of each word and the number of words from which lengths have been taken.
You can shorted your code dramatically in that case to something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
int main (void)
{
char word[SIZE];
int num_words = 0;
double sum = 0.0,
average = 0.0;
/* Read words into array */
while (num_words + 1 < SIZE && 1 == scanf (" %99s", word)) {
sum += strlen (word);
num_words++;
}
average = sum / num_words;
if (average > 0) {
fprintf(stderr, "average: %.2f\n", average);
}
return EXIT_SUCCESS;
}
Example Use/Output
$ ./bin/words_avg_len_nosave <../dat/wcwords.txt
average: 4.02
(which leaves a lot less room for error :)
Upvotes: 2
Reputation: 234655
You are including punctuation characters (e.g. ,
on the line starting with "Mary went"), in the word length total.
That's enough to throw off your result.
You can eliminate such characters using the C standard library function isalpha
.
Note also that you have the potential for undefined behaviour here: you could overflow your memory buffer when reading the words in. That could be having an effect here too. Why don't you compute a running average: there's no need to store the words at all?
Upvotes: 4
Reputation: 826
Solution: The script I was running contained 101 words, the array was only supposed to store 100.
Upvotes: -1