Dynamically allocating array of structs C

Question

I'm trying to make an array of structs in c, but I can't make it work. When I try to run it, the program crashes.

typedef struct{
    char name[20];
    char manufacturer[20];
    unsigned int price;
} product;

unsigned int stringToNr(char *numbers){
   unsigned int nr = 0; 
   unsigned int i; 
   for (i = 0; i < strlen(numbers); i ++)
   {
       nr  *= 10; nr += numbers[i] - '0'; 
   }
   return nr; 
} 

I have a function that would print the list to a file, sometimes it reaches this function, sometimes it crashes before.

void printList(product *products, unsigned int nr){
    unsigned int i;
    FILE *f;
    f = fopen("output.txt", "w");
    for (i = 0; i < nr; i ++){
        fprintf(f, "%s ", products[i].name);
        fprintf(f, "%s ", products[i].manufacturer);
        fprintf(f, "%d\n", products[i].price);
    }
    fclose(f);
}

I have to use a separate function to read the list from file.

void readList(product **products, unsigned int *nr){
    FILE *f;
    f = fopen("input.txt", "r");
    char *row;
    row = malloc(sizeof(char) * 45);
    unsigned int rowLength;
    fgets(row, 45, f);
    rowLength = strlen(row);
    if (row[rowLength - 1] == '\n'){
        rowLength--;
        row[rowLength ] = '\0';
    }
    *nr = stringToNr(row);
    products = malloc((*nr) * sizeof(product*));
    unsigned int i;
    char *rowElement;
    for (i = 0; i < *nr; i ++){
        fgets(row, 45, f);
        rowElement = strtok(row, " ");
        strcpy((*products)[i].name, rowElement);
        rowElement = strtok(NULL, " ");
        strcpy((*products)[i].manufacturer, rowElement);
        rowElement = strtok(NULL, " ");
        rowLength = strlen(row);
        if (row[rowLength- 1] == '\n'){
            rowLength--;
            row[rowLength] = '\0';
        }
        (*products)[i].price = stringToNr(rowElement);
    }
    free(row);
    fclose(f);
}

Obviously the program has more features, but those work fine.

int main(){
    product *products;
    unsigned int nr;
    readList(&products, &nr);
    printList(products, nr);
    free(products);
    return 0;
}

My input file looks like this:

   3
   AAA FactoryA 300
   BBB FactoryC 550
   ZZZ Factory5 100

Show source
| C   | arrays   | struct   | pointers   | malloc   2017-01-05 22:01 1 Answers

Answers ( 1 )

  1. 2017-01-05 23:01

    Code ignores value of products.

    What ever readList() receives in products is overwritten with the malloc() call.

    void readList(product **products, unsigned int *nr){
        ...
        // bad
        products = malloc((*nr) * sizeof(product*));
    

    Instead, use *products. Also allocate by the size of the referenced variable, not by the size of the type. Easier to code, review and maintain.

        *products = malloc(sizeof *(*products) * (*nr));
        if (*products == NULL) Handle_OOM();
    

    Minor: After fgets(row, ..., ...); , following is not safe from a hacker exploit of reading an initial null character.

        rowLength = strlen(row);
        // What happens when rowLength == 0
        if (row[rowLength- 1] == '\n'){
          ...
    

    Instead code could use below to rid the optional trailing '\n'.

        row[strcspn(row, "\n")] = '\0'; 
    
◀ Go back