4

I'm just getting introduced to C, and I was assigned to write a program that would mimic a self check out line at a grocery store. This involves me having to populate an array with the prices of grocery items based on user input, and add them up and copy them to a file.

the easiest way to populate an integer array is with a for loop. But would that be different for an array of type float?

would it look something like this? Or is this incorrect?

int size, i;
float items[size];
printf("How many items are you checking out today?");
scanf("%d", &size);
for(i=0;i<size;i++){
  printf("Enter the price of an item\n");
  scanf("%f", items[i]);
}

I'm new to this site so thanks in advance

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Dillon
  • 41
  • 1
  • 3
    In general, if you have an array of a known size, and you need to loop through the array to perform an operation on each element (like printing the value), a for-loop is a good option whether it's `int`s, `float`s, or your own data type. – Wrokar May 04 '18 at 04:50
  • 2
    Keep the items[size] declaration after the scanf for size – omkaartg May 04 '18 at 04:51
  • 4
    I do not understand how you can (meaningfully) declare `float items[size]` when `size` is not yet initialized. – Uwe Keim May 04 '18 at 05:05
  • 1
    Also be aware (even after moving to the proper location) `float items[size];` will declare a *Variable Length Array* (VLA) which is only supported by C99+ (so if you are using the default VS compiler under Win7 -- you are out of luck...) And.. always compile with **warnings enabled** and do not accept code until it compiles without warning. (minimum `-Wall -Wextra` for gcc/clang, or `/W3` for VS (e.g. `cl.exe`). – David C. Rankin May 04 '18 at 05:15

3 Answers3

3

I would recommend always initializing variables as you declare them to prevent "garbage" values by accident. Also, I don't really recommend pre-declaring your loop counters. You see it in a lot of old code (it used to be required due to compiler limitations), but now I just think it's code noise. It would look like this:

for (int i = 0; i < size; i++) {
    // stuff
}

Also, your code has a big problem. You're using what's known as a variable-size array, and they are not a good idea. You generally want to either declare the array size at compile time, or dynamically allocate the space for the array using malloc.

Going back to the initalization, though, this is how you would set every element in a stack-allocated array on declaration:

#define SIZE 4

int main(void)
{
    float items[SIZE] = { 0 };
}

If you dynamically allocate the array, I recommend using calloc or memset to set the array elements to a default value for the same reason.

To answer your question about populating the array, yes, there is no difference as to how you would actually go about doing it. A for loop works just fine in both cases. Just remember to check the return value of scanf.

2

As has been correctly pointed out, you cannot declare float items[size]; until size has been validly initialized to a positive integer value. Your attempt to declare items before size contains a value invokes Undefined Behavior due to your attempt to access an uninitialized value. (the valid operation of your code is over at that point and it could do anything from appearing to run correctly to StackOverflow, or SegFaulting)

Any time you are taking user-input, you must account for each character that remains in the input buffer (stdin here). This is especially true when taking input with scanf (or family) due to the way scanf handles input or matching failures. When either occurs, no further characters are read, and any offending characters are left unread in the input buffer -- just waiting to bite you again on your next attempted read (generally resulting in an infinite loop if you are taking input within a loop)

(this is one of the primary reason a line-oriented function such as fgets is recommended for taking user input)

scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions

  1. (return == EOF) the user canceling input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10);
  2. (return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, etc..); and
  3. otherwise, you must handle the matching or input failure and you must account for every character that may be left in your input buffer. (generally you will scan forward in the input buffer until a '\n' or EOF is found discarding any extraneous characters that remain)

If you do your job, you can successfully use scanf as needed.

Next, a general caution do not using floating point for currency (people get real mad when you start losing money due to rounding errors) While it is fine for your example program -- just understand, in a real currency handling program, you would handle currency as an unsigned value multiplied by 100 (or whatever is required) to insure all amounts can be represented exactly.

Putting the scanf requirements together, you could do something like the following safely:

#include <stdio.h>

/* function to empty extraneous characters from stdin
 * (like the '\n' generated by pressing [Enter])
 */
void empty_stdin()
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

int main (void) {

    int size = 0, i;
    float total = 0.0;

    for (;;) {  /* loop continually until valid size entered */
        int rtn;
        printf ("How many items are you checking out today?: ");
        rtn = scanf ("%d", &size);
        if (rtn == EOF) {       /* handle EOF */
            fprintf (stderr, "(user canceled input)\n");
            return 1;
        }
        else if (rtn == 1 && size > 0) {    /* valid int received */
            empty_stdin();
            break;
        }   /* otherwise, handle error */
        fprintf (stderr, "error: invalid input.\n\n");
        empty_stdin (); /* remove any chars from stdin up to '\n' */
    }
    float items[size];  /* declare VLA of size floats */

    for (i = 0; i < size; i++) {
        items[i] = 0.0; /* initialize each (or memset VLA) */
        for (;;) {      /* loop continually until valid item price entered */
            int rtn;
            printf ("  price of item[%2d]: ", i + 1); /* prompt for price */
            rtn = scanf ("%f", &items[i]);
            if (rtn == EOF) {       /* handle EOF */
                fprintf (stderr, "(user canceled input)\n");
                return 1;
            }
            else if (rtn == 1 && items[i] > 0) { /* valid price received */
                empty_stdin();
                break;
            }   /* otherwise, handle error */
            fprintf (stderr, "error: invalid input.\n\n");
            empty_stdin (); /* remove any chars from stdin up to '\n' */
        }
        total += items[i];
    }
    printf ("\ntotal (%d items): $%.2f\n", size, total);
}

Example Use/Output

(shown with intentional errors in entry)

$ ./bin/checkout
How many items are you checking out today?: what?
error: invalid input.

How many items are you checking out today?: 4
  price of item[ 1]: free?
error: invalid input.

  price of item[ 1]: 1.25
  price of item[ 2]: 3.50
  price of item[ 3]: discount?
error: invalid input.

  price of item[ 3]: 2.25
  price of item[ 4]: 3

total (4 items): $10.00

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

There is no difference in usage of arrays in terms of usage. But there are few changes required in your code.

#define MAX_SIZE (10)

int size=0, i=0; //It is always better to initialize the variables.
float items[MAX_SIZE] = {0.0f}; //Automatically the entire array will be initialized to zero.
printf("How many items are you checking out today?");
scanf("%d", &size);
if(size > MAX_SIZE)
    size = MAX_SIZE;

for(i=0;i<size;i++){
  printf("Enter the price of an item\n");
  scanf("%f", &items[i]); //You need to pass address of variable to scanf
}

There are other ways to implement your code to handle array size. This is one of the way.

MayurK
  • 1,925
  • 14
  • 27
  • 1
    The parens are not wrong, just not needed in `#define MAX_SIZE (10)` and *always* validate the **return** of `scanf`, otherwise you have no idea whether you are processing valid data. – David C. Rankin May 04 '18 at 05:18
  • @DavidC.Rankin I agree to both the points. As an improvement, check for the return value of scanf() can be added. – MayurK May 04 '18 at 05:25