1

I need to build an Coin Dispenser application. I've written the withdrawal and deposit parts but in item inventory creation I couldn't create a solution to that. Item names should be taken from keyboard as strings. Item prices corresponding to the items should be taken from keyboard as unsigned ints.

Arrays should be variable-sized, aka VLAs. I've googled it and found some resources about creating VLAs, I think I should allocate memory to them dynamically using malloc. I couldn't do that, so I basically created limited sized arrays with using BUFSIZ.

How can I use VLAs in this case, and how can I fill them with strings?

Also I should prevent buffer overrun. This will be the second case.

Macros and code blocks:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMBER_OF_COINS 5
#define ITEM_NAME_LEN 9

...

void editorMain(void)
{
    printf("Please enter the number of items:  ");
    scanf("%u", &itemQuantity);
    printf("\n\n");

    char ** itemNames[BUFSIZ] = {0};
    unsigned int itemPrices[BUFSIZ] = {0};

    printf("Please enter the names of items:  \n");

    for (int i = 0; i < itemQuantity; ++i) {
        printf("#%d:  ", i + 1);
        gets(** itemNames[i]);
       puts("");
    }

    printf("Please enter the prices of items:  \n");

    for (int i = 0; i < itemQuantity; ++i) {
        printf("#%d:  ", i + 1);
        scanf("%u", &itemPrices[i]);
        puts("");
    }

    dispenserMain(*itemNames, itemPrices);

    return;
}

Also program should output the item names and prices as a list. Function dispenserMain does this:

void dispenserMain(char * itemNames[], unsigned int itemPrices[]) {

...

    for (counterItemNames = 1; counterItemNames <= itemQuantity; ++counterItemNames) {                //the for loop will print out the item name and prices with their correspondence.
        printf("%10u.%14s%12.2f TL\n", counterItemNames, itemNames[counterItemNames - 1], currencyConverter(itemPrices[counterItemNames - 1]));
    }

...
}

Edit (new code):

void editorMain(void)
{
    printf("Please enter the number of items:  ");
    scanf("%u", &itemQuantity);
    printf("\n\n");

    char itemNames[itemQuantity][ITEM_NAME_LEN+1];

    memset(&itemNames, 0, sizeof(itemNames));

    printf("Please enter the names of items:  \n");

    char line[ITEM_NAME_LEN+1];

    for (int i = 0; i < itemQuantity; ++i) {
        printf("#%d:  ", i + 1);

        if (! fgets(line, sizeof(line), stdin) || !line[0])
            // abort...

            // fgets leaves the trailing newline in the input

            if (line[strlen(line)-1] == '\n') {
                line[strlen(line)-1] = 0;
            }

        snprintf(itemNames[i], ITEM_NAME_LEN+1, "%s", line);
        puts("");
    }

    unsigned int itemPrices[] = {0};

    printf("Please enter the prices of items:  \n");

    for (int i = 0; i < itemQuantity; ++i) {
        printf("#%d:  ", i + 1);
        scanf("%u", &itemPrices[i]);
        puts("");
    }

    dispenserMain(itemNames, itemPrices);

    return;
}   //end globalMenuOut

It outputs:

                   Welcome to CoinedDispenser!

                         Author: Buğra Ekuklu

          Contact: bugraekuklu [at] hotmail.de


  1. Dispenser
  2. Editing variables
  3. Readme

Please make a choice:  2

Please enter the number of items:  3


Please enter the names of items:  
#1:  
#2:  blabla

#3:  glagla

Please enter the prices of items:  
#1:  45

#2:  65

#3:  75

  ................... AVAILABLE ITEMS oo

         #      Item Name          Price
         =      =========         ======

         1.             
        0.45 TL
         2.       blabla
        0.65 TL
         3.       glagla
        0.75 TL

  Enter your item selection:  

Dispenser function:

void dispenserMain(char (*itemNames)[ITEM_NAME_LEN+1], unsigned int itemPrices[])
{

printf("%s\n\n", "  ................... AVAILABLE ITEMS oo");
printf("%10s%15s %14s\n", "#", "Item Name", "Price");
printf("%10s%15s %14s\n", "=", "=========", "======");
puts("");

unsigned int counterItemNames = 0;   //initializing counter

for (counterItemNames = 1; counterItemNames <= itemQuantity; ++counterItemNames) {    //the for loop will be print out the item name and prices with their correspondence.
    printf("%10u.%12s%12.2f TL\n", counterItemNames, itemNames[counterItemNames - 1], currencyConverter(itemPrices[counterItemNames - 1]));
}
puts("");
...
}
4
  • 1
    Unrelated to your problem, but the usual way to handle loops over arrays is to start at zero, and use e.g. counterItemNames < itemQuantity for condition. Then you don't have to subtract one from the index inside the loop. You do it in some places but not in others, consistency is good when programming. It will make the code easier to read and understand, especially for yourself in a few months time. Commented Apr 21, 2014 at 14:09
  • 1
    You don't want char ** itemNames[BUFSIZ]; because it is one too many levels of pointer. You might use char *itemNames[BUFSIZ];. To get VLAs, you could use: char itemNames[itemQuantity][ITEM_NAME_LEN+1]; and unsigned itemPrices[itemQuantity];. By definition, you are not allowed to provide initializers for VLAs. Remember to use "%9s" for inputting the strings to prevent overflows. Commented Apr 21, 2014 at 14:17
  • Why is itemNames an array of pointers to pointers to characters? Why not simply an array of pointers to characters. Even so, who allocates those character buffers? Apparently nobody... Commented Apr 21, 2014 at 14:19
  • And never, never, never, never, NEVER use gets(). No program that uses gets() can defend itself against attack, or protect itself from overflow. It is no longer part of Standard C. You should assume that it crashes your program with a core dump. Commented Apr 21, 2014 at 14:19

1 Answer 1

1

Where you write char ** itemNames[BUFSIZ] = {0}; should be:

char itemNames[itemQuantity][ITEM_NAME_LEN+1];

This is a VLA because it has an array that has a dimension known at runtime. Space allocated via malloc is not a VLA.

Unfortunately VLAs cannot have initializers, so you would also have to write:

memset(&itemNames, 0, sizeof itemNames);

If you want to dynamically allocate this array it would be:

char (*itemNames)[ITEM_NAME_LEN+1];
itemNames = calloc( itemQuantity, sizeof *itemNames );

and in both cases the usage will be identical. itemNames[i] designates an array which you have allocated.

Your code gets(** itemNames[i]); has two problems; it dereferences a null pointer, and it uses the gets function which allows the user to cause a buffer overflow. You should be reading into the array itemNames[i], which after using my suggestion above, designates storage you've allocated.

Unfortunately it's awkward in C to just input a string of fixed length. You have to worry about what happens if the user types more than your line length. If that doesn't concern you too much then:

char line[BUFSIZ];
if ( ! fgets(line, sizeof line, stdin) || !line[0] )
    // abort...

// fgets leaves the trailing newline in the input    
if ( line[strlen(line)-1] == '\n' )
    line[strlen(line)-1] = 0;

snprintf(itemNames[i], ITEM_NAME_LEN+1, "%s", line);

To call your function do:

 dispenserMain(itemNames, itemPrices);

and the function has to be

void dispenserMain(char (*itemNames)[ITEM_NAME_LEN+1], unsigned int itemPrices[]);

If you want to use your original the syntax for dispenserMain then you have to allocate an array of pointers, as opposed to using a 2-D array as I have described. In that case you would use char **itemNames = malloc(itemQuantity * sizeof *itemNames); and the loop through and malloc each pointer to point to some more storage of length ITEM_NAME_LEN+1.

Sign up to request clarification or add additional context in comments.

2 Comments

Now I've done what you said, however I have the same problem I had. It passes the name of the first item. Let me clarify, when I run the program, I can successfully enter the number of items, item prices and item names except first one. Also it doesn't output in tabular format, prices go output in new line. One more thing, the program passes the first item name, it does not take any string from keyboard, and it does not ever ask it. #1 comes and it directly goes to ask #2 item name. At output the name of first item is blank. What do you think about this?
I don't see you mentioning that problem earlier; can you describe what input you give, what happens, and what you expect to happen instead? Also would be a good idea to post an update to your post with the new code (do this at the end, don't change the original code)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.