-1

Program to calculate the average of n numbers given by the user.

Okay so I have this program whose purpose is what you have read above. Its output is not quite right. I figured out what the problem is but couldn't find the solution as I am not a leet at programming (newbie actually). Here is the code:

#include <stdio.h>

int main(void) {
    char user_data[100];
    long int sum = 0;
    double average;
    unsigned int numbers_count = 0;

    for (int i = 0; i <= 99; ++i)
        user_data[i] = 0;

    unsigned int numbers[100];

    for (int i = 0; i <= 99; ++i)
        numbers[i] = 0;

    printf("Please enter the numbers:");

    fgets(user_data, sizeof(user_data), stdin);

    int i = 0;
    while (user_data[i] != 0) {
        sscanf(user_data, "%u", &numbers[i]);
        ++i;
    }

    i = 0;
    while (numbers[i] != 0) {
        sum += numbers[i];
        ++i;
    }

    i = 0;
    while (numbers[i] != 0) {
        ++numbers_count;
        ++i;
    }

    average = (float)sum / (float)numbers_count;

    printf("\n\nAverage of the entered numbers is: %f",average);

    return 0;
}

Now here comes the problem.

When I enter an integer say 23, it gets stored into the user_data in two separate bytes. I added a loop to print the values of user_data[i] to figure out what was wrong.

    i = 0;
    while (i <= 99) {
        printf("%c\n",user_data[i]);
        ++i;
    }`

and the result was this

user_data insight

This was the first problem, here comes the second one. I added another loop same like the above one to print the numbers stored in numbers[100] and figure out what was wrong and here is the output. Here's a sample

numbers stored in numbers[]

Now my main question is

How to extract the full number from user_data?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Muneeb
  • 31
  • 1
  • 6
  • The enter key (newline, `\n`) is actually `10, 13` (on Windows. on Mac it's just `10` and on Unix it's just `13`). C strings have to be null-terminated. So your actual string will end when you either read a 100 random bytes off the stack, or sooner if you encounter a zero. – Shark Jun 20 '17 at 18:46
  • `sscanf(user_data,"%u",&numbers[i]);` that only scans the first integer of your `user_data` string. – Jean-François Fabre Jun 20 '17 at 18:49
  • 2
    @Shark I didn't catch that – Muneeb Jun 20 '17 at 18:50
  • @Jean-FrancoisFabre Yes, I tried using user_data[i] but it gave me a runtime error. Any solution? – Muneeb Jun 20 '17 at 18:50
  • 1
    I would do a `strtok` on the input to separate the tokens and scan them. – Jean-François Fabre Jun 20 '17 at 18:52
  • @Shark: That#s not correct. It is newline (`10`) on Linux/Unix as well! How do you think it is CR?? That's just the return-key, which is the same on all systems! Actually MacOS before X used `CR`. – too honest for this site Jun 20 '17 at 18:58
  • 1
    Possible duplicate of [Converting string to integer C](https://stackoverflow.com/questions/7021725/converting-string-to-integer-c) – John Coleman Jun 20 '17 at 18:59
  • using strtoul() may be helpful – Pushan Gupta Jun 20 '17 at 19:09
  • @Olaf fair enough, I just know there's a difference between the three in CRLF (windows), CR(mac) and LF (unix). I somewhat remember those two being `10, 13` but I could be wrong. – Shark Jun 20 '17 at 20:31
  • @Shark: Quite simple: all good hosted environments are POSIX-based and Unix. They don't need to differentiate between "text" and "binary" file mode for open. All POSIX systems use `'\n'` as line seperator (and `'/'`) for directories, btw). Only one hosted environment went riot for stupid reasons (back in the old times the extra byte was indeed quite expensive to store): DOS. And the offspring of DOS is WinDOS ;-) (and now they are already searching for pointy stones …) – too honest for this site Jun 20 '17 at 20:35

3 Answers3

2

I believe it could be helpful to layout user_data after the fgets() of "23" (assuming Linux or Mac new line):

 +-----+-----+----+----+
 | '2' | '3' | \n | \0 | .....
 +-----+-----+----+----+
    0     1     2    3 

Note that user_data[0] does not contain 2 (the number 2)! It contains '2' (the character '2') whose code is (again, assuming Linux) 0x32 (in hex or 50 in decimal).

This is why your attempt to print the values of user_data[] have not been fruitful: you were trying to print the representation of the number, not the number itself.

To convert that string to the integer it represents, you can do something like:

 num = atoi(user_data)

The function atoi() does the work for you. A more flexible function is strtol() which does the same but for long int (and also can handle string that represents numbers in a base that is not 10).

I hope this answers to your question: How to extract the full number from user_data?

There are some other points where you should clean up and simplify your code, but you can open another question in case you need help.

Remo.D
  • 16,122
  • 6
  • 43
  • 74
  • On Windows the code should work as well. `stdin` is open in text mode as a default and in text mode Window's 2-character line endings are automatically replaced by `\n`. Even if this weren't the case, `atoi` would ignore the trailing white space. – John Coleman Jun 20 '17 at 19:29
0

Try this:

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


int main(void)
{
   int i;

   char user_data[100];

   long int sum = 0;

   double average;

   unsigned int numbers_count = 0;

   for( i=0; i<= 99; ++i)
   user_data[i] = 0;

   unsigned int numbers[100];

   for( i=0; i<= 99; ++i)
        numbers[i] = 0;

   printf("Please enter the numbers:");

   fgets(user_data,sizeof(user_data),stdin);
    //int p=0;//use with strtol(see further code)
    i = 0;
    int j;//this will store each number in numbers array.. so this is also the count of numbers stored - 1


    for(j=0;;){

        for(i=0;i<strlen(user_data);i++)
        {
            if(user_data[i]=='\n'){
                    break;
            }
            if(user_data[i]==' '){
                    j++;
                    i++;
                    //p=i;//to be used with strtol
            }
            numbers[j]=(numbers[j]*10)+((user_data[i]-48));//alternatively use => numbers[j]=strtol(user_data+p,NULL,10);

        }

        break;
    }

    i = 0;

    while( i<=j)
    {
        sum += numbers[i];
        ++i;
    }

    average = (float)sum/(j+1);


    printf("\n\nAverage of the entered numbers is: %f",average);


    return 0;
}

Sample input

10 11 12

Sample output

11.00000000


I have shown two approaches to solve this:

  • One is straight-forward, subtract 48 from each char and add it to numbers array(ASCII manipulation) .
  • Other is to use strtol. Now strtol converts the number pointed by the char pointer(char array in this case) until the next char is not a number. So use pointer arithmetic to point to further numbers(like here I have added p(yeah I know p is not a good variable name, so does i and j!)).
  • There are more ways to solve like using atoi library functions.
Pushan Gupta
  • 3,697
  • 5
  • 23
  • 39
0

regarding the posted code:

what happens if one of the numbers is zero?

What happens if the sum of the numbers exceeds the capacity of 'sum'

#include <stdio.h>    // sscanf(), fgets(), printf()
#include <stdlib.h>   // strtol()
#include <string.h>   // strtok()

// eliminate the 'magic' number by giving it a meaningful name
#define MAX_INPUTS 100
int main(void) 
{
    // the array can be initialized upon declaration
    // which eliminates the 'for()' loop to initialize it
    // char user_data[100];
    // and
    // initialization not actually needed as 
    // the call to 'fgets()' will overlay the array
    // and 'fgets()' always appends a NUL byte '\0'
    char user_data[ MAX_INPUTS ];
    long int sum = 0;
    double average;

    // following variable not needed
    // unsigned int numbers_count = 0;

    // following code block not needed when
    // 'user_data[]' initialized at declaration
    // for (int i = 0; i <= 99; ++i)
    //    user_data[i] = 0;

    // not needed, see other comments
    //unsigned int numbers[100];

    // not needed, as 'numbers' is eliminated
    // for (int i = 0; i <= 99; ++i)
    //    numbers[i] = 0;

    printf("Please enter the numbers:");

    // should be checking the returned value
    // to assure it is not NULL 
    // And
    // this call to 'fgets()' is expecting 
    // all the numbers to be on a single input line
    // so that could be a problem
    fgets(user_data, sizeof(user_data), stdin);

    // the following two code blocks will not extract the numbers
    // for a number of reasons including that 'sscanf()'
    // does not advance through the 'user_data[]' array
    // int i = 0;
    // while (user_data[i] != 0) {
    //     sscanf(user_data, "%u", &numbers[i]);
    //     ++i;
    // }

    // i = 0;
    // while (numbers[i] != 0) {
    //     sum += numbers[i];
    //     ++i;
    // }

    // suggest the following, 
    // which also eliminates the need for 'numbers[]'
    // note: the literal " \n" has both a space and a newline
    //     because the user is expected to enter numbers, 
    //     separated by a space and 
    //     'fgets()' also inputs the newline
    int i = 0;
    char *token = strtok( user_data, " \n");
    while( token )
    {
        // not everyone likes 'atoi()'
        // mostly because there is no indication of any error event
        // suggest using: 'strtol()'
        //sum += atoi( token );  
        sum += strtol( token, NULL, 10 ) // could add error checking           
        i++;
        token = strtok( NULL, " \n" );
    }

    // the value assigned to 'numbers_count' 
    // is already available in 'i'
    // suggest eliminate the following code block 
    // and 
    // eliminate the 'numbers_count' variable
    // i = 0;
    // while (numbers[i] != 0) {
    //    ++numbers_count;
    //    ++i;
    // }


    // 'average' is declared as a 'double', 
    // so the casting should be to 'double'
    // and 
    // if incorporating the prior comment about 'numbers_count'
    // average = (float)sum / (float)numbers_count;
    average = (double)sum / (double)i'

    // to have the text immediately displayed on the terminal
    // place a '\n' at the end of the format string.
    // without adding the '\n' the text only displays 
    // as the program exits
    // printf("\n\nAverage of the entered numbers is: %f",average);
    printf("\n\nAverage of the entered numbers is: %f\n",average);

    return 0;
} // end function: main
user3629249
  • 16,402
  • 1
  • 16
  • 17