Search code examples
cstructprintfscanfconsistency

C struct data failing to be consistent outside iterative loop


I have a program where I am expected to read in car data into a struct array within a for loop using fscanf (I know, I should be using fgets or something else, but it is required for this assinment) and sorting the data by average MPG and outputting the sorted data to a new file using fprintf. In my for loop in my main function when I am reading the data, as a debug feature, I print all the data that is assigned to the struct[i] before continuing to the next line in the text file containing the data for the subsequent struct object. However, after all the data has been inputted and everything looks good so far, I re-print all the information in the struct array, and the first two data regions "make" and "model" all take on the string of the last struct assigned. I have been bugfixing this for hours and have found no solution. Can anyone tell me what I am doing wrong with my code? Thank you all in advance!

// Written by 4ur0r4
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Car {
    char *make;
    char *model;
    int year;
    int city_mpg;
    int highway_mpg;
};

int averagempg(struct Car car) {
    return (car.city_mpg + car.highway_mpg) / 2;
}

void selection_sort(struct Car *cars, int n) {
    struct Car temp;
    int i;
    for (i = 0; i < n - 1; i++) {
        if (averagempg(cars[i]) > averagempg(cars[i + 1])) {
            temp = cars[i];
            cars[i] = cars[i + 1];
            cars[i + 1] = temp;
        }
    }
}

void write_cars(struct Car *cars, int n) {
    FILE *output = fopen("sorted_cars.txt", "w");
    int i;
    for (i = 0; i < n; i++) {
        fprintf(output, "%s ", cars[i].make);
        fprintf(output, "%s ", cars[i].model);
        fprintf(output, "%d ", cars[i].year);
        fprintf(output, "%d ", cars[i].city_mpg);
        fprintf(output, "%d\n", cars[i].highway_mpg);
    }
}

void print_cars(struct Car *cars, int n) {
    int i;
    for (i = 0; i < n; i++) {
        printf("%s ", cars[i].make);
        printf("%s ", cars[i].model);
        printf("%d ", cars[i].year);
        printf("%d ", cars[i].city_mpg);
        printf("%d\n", cars[i].highway_mpg);
    }
}

int main() {
    FILE *input = fopen("cars.txt", "r");
    struct Car cars[9];
    int i;
    char make[20], model[20];
    int year, city_mpg, highway_mpg;
    if (input == NULL) printf("Unable to open file.\n");
    else {
        for (i = 0; i < 9; i++) {
            fscanf(input, "%s %s %d %d %d",
                make,
                model,
                &year,
                &city_mpg,
                &highway_mpg);

            cars[i].make = make;
            cars[i].model = model;
            cars[i].year = year;
            cars[i].city_mpg = city_mpg;
            cars[i].highway_mpg = highway_mpg;

            printf("%s ", cars[i].make);
            printf("%s ", cars[i].model);
            printf("%d ", cars[i].year);
            printf("%d ", cars[i].city_mpg);
            printf("%d\n", cars[i].highway_mpg);
        }
        printf("\n");
        print_cars(cars, 9);

        selection_sort(cars, 9);

        write_cars(cars, 9);

    }
    return 0;
}

This is the result I get:

Mercury Sable 2009 18 28
Jeep Wrangler 2016 17 21
Honda Civic 2015 31 41
Toyota Corolla 2015 30 42
Toyota Prius 2010 51 48
Ford Escape 2013 23 33
Ford Fusion 2013 25 37
Acura MDX 2014 20 28
Lexus RX 2013 32 28

Lexus RX 2009 18 28
Lexus RX 2016 17 21
Lexus RX 2015 31 41
Lexus RX 2015 30 42
Lexus RX 2010 51 48
Lexus RX 2013 23 33
Lexus RX 2013 25 37
Lexus RX 2014 20 28
Lexus RX 2013 32 28

This is the text file I'm reading from:

Mercury Sable 2009 18 28
Jeep Wrangler 2016 17 21
Honda Civic 2015 31 41
Toyota Corolla 2015 30 42
Toyta Prius 2010 51 48
Ford Escape 2013 23 33
Ford Fusion 2013 25 37
Acura MDX 2014 20 28
Lexus RX 2013 32 28

Solution

  • You set every cars[i].make to make and every cars[i].model to model. So they all point to the very same place. How is that supposed to work if the cars are different makes and models?

    You need to make some copy of the data in make and model and make cars[i].make and cars[i].model point to that copy. Otherwise, when you read in the next make and model, you will overwrite the only place you ever stored the previous car's make and model.

    If your platform has strdup, you can change:

            cars[i].make = make;
            cars[i].model = model;
    

    to

            cars[i].make = strdup(make);
            cars[i].model = strdup(model);
    

    The strdup function allocates a new chunk of memory and copies a string into it, returning a pointer to the new chunk of memory.