1

Edit: If it matters, I'm using Dev C++ with -std=c99 as an option.

My professor was able to run this code in class and successfully open a file that eventually reads the data into a linked list. When running the exact same code, my program abruptly exits despite the file being successfully opened.

All I'm trying to do is get this code to run. I've solved what he wants us to solve in my own example, but I can't figure out why his code doesn't run on my machine.

I did add in a puts("Success") line to verify the file has been opened, and I've gone through the methods it calls to see if I can find an error, but I cannot.

Here is the method with the issue (I'm assuming)

int ReadFileStoreInList(void)
{ 
   FILE *cfPtr; 

   if ((cfPtr = fopen("Hertz-Homework-9-List.txt", "r")) !=NULL) 
   { 
      char  make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
      char rented = 'A';
      float daily_rate=0.0;
      char dAilyRate[TWELVE]; 
      fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);
      while (!feof(cfPtr)) 
      { 
         fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);
         rented = 'A';  
         add_at_end(make, model, size, color, power, daily_rate, rented);
      } 
      printScreenTitleAndHeaderForCars();   
      traverse_in_order();
   }
   else 
   {
      puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
   }
   fclose(cfPtr); 
}

This calls traverse_in_order(); and printScreenTitleAndHeaderForCars();, which I will list below.

void traverse_in_order() 
{    
    node *ptr;  

    if(start==NULL) 
    {   
        printf("list is empty\n");        
        return;    
    }
    printScreenTitleAndHeaderForCars(); 

    for(ptr=start; ptr!=NULL; ptr=(*ptr).next)
         printf("%-12s%-12s%-12s%-12s%-12s%9.2f%12c\n", ptr->make, ptr->model, ptr->size, ptr->color, ptr->power, ptr->daily_rate, ptr->rented);
}  

void printScreenTitleAndHeaderForCars()
{
    system("Cls");
      printf("%35s\n\n","Hertz Rental Cars");
      printf("%79s\n","Avail");
      printf("%-12s%-12s%-12s%-12s%-12s%-12s%8s\n", "Make", "Model", "Size", "Color", "Power", "Daily_Rate", "Rented");

      for (int x=0; x< 7; x++)
         printf("----------- ");
      printf("\n");
}

In case the structure/header file was also needed:

#define TWELVE 12

typedef struct node_type 
{   
    char make[TWELVE];
    char model[TWELVE];
    char size[TWELVE];
    char color[TWELVE];
    char power[TWELVE];
    float daily_rate;
    char rented;         
    struct node_type *next;
} node;

node *start=NULL;

int ReadFileStoreInList(void);

void add_at_beginning();
void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented);
void add_after_element();
void add_before_element();
void traverse_in_order();
void traverse_in_reverse_order(node *);
void delete_at_beginning();
void delete_at_end();
void delete_after_element();
void delete_before_element();
void sort();
void doSomething();      // menu to ask operator what to do 
void RentaCar();
void FindCarAndUpdateAsRented(char *modelSelected);
void ReturnCar();
void toTitleCase(char *aString);
void printScreenTitleAndHeaderForCars();

Requested add_at_end function:

void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented)
{    
    node *ptr, *loc;          
    ptr = (node *) malloc(sizeof(node));  
    if(ptr==NULL)            
    {  
    printf("no space\n");  
    return;    
    }

    strcpy((*ptr).make,make);    
    strcpy((*ptr).model,model);
    strcpy((*ptr).size,size);    
    strcpy((*ptr).color,color);    
    strcpy((*ptr).power,power);    
    (*ptr).daily_rate = daily_rate;
    (*ptr).rented = rented;
    if(start==NULL)            
    {  
        start=ptr;             
        (*start).next=NULL;    
    }
    else  
    {
        loc = start;                
        while((*loc).next != NULL)  
           loc=(*loc).next;
        (*loc).next=ptr;            
        (*ptr).next=NULL;          
    }  
}  

Interesting enough, if I rename this file to something that isn't correct, the proper printScreenTitleAndHeaderForCars() text executes and shows on screen. That's why I believe it has something to do with ReadFileStoreInList();

I've tried to debug this for a few hours now, but with the knowledge I've learned in class I just can not figure out why this doesn't run.

I expect the output to have the header information given from printScreenTitleAndHeaderForCars(), and then the data from the file I'm reading to appear on screen.

When the file is named improperly in my code, it runs this:

                  Hertz Rental Cars

                                                                          Avail
Make        Model       Size        Color       Power       Daily_Rate    Rented
----------- ----------- ----------- ----------- ----------- ----------- -----------
list is empty

enter choice
 1. Select Model and Rent
 2. Select Model and Return
 5. traverse in order
 11. sort
 12. exit

Where "list is empty" is situated, it should be populating the data from the text file and putting it there.

Instead, I just get:


--------------------------------
Process exited after 1.433 seconds with return value 3221225477
Press any key to continue . . .

I have a feeling this has to do with the way the pointers are written, but I'm struggling to understand how he could run the code and I couldn't.

Any knowledge as to why this might happen would be appreciated!

Edit: Text file contents:

make, model, size, color, power,daily rate.
Mazda,3,4-door,Black,4-Cyl,$99.73 
Jeep,Cherokee,4-door,Blue,8-Cyl,$131.92 
Buick,Regal,4-door,Purple,6-Cyl,$125.19 
Fullsize,SUV,5-door,Brown,8-Cyl,$163.94 
Chrysler,Pacifica,4-door,Green,6-Cyl,$127.49 
Ford,Focus,2-door,Red,4-Cyl,$99.73 
VW,Jetta,2-door,Orange,4-Cyl,$94.91 
Chevrolet,Suburban,4-door,Yellow,8-Cyl,$204.92 
Nissan,Pathfinder,4-door,White,6-Cyl,$145.11 
Chevrolet,Spark,2-door,Teal,4-Cyl,$99.55 
Jet
  • 13
  • 5
  • 1
    Thx for the struct edit. I dont see any obvious Mistakes... I can reimplement the Codebase and try to debug it. But than i also need the add_at_end function. Did you get any warning at compiling with -Wall -Wextra? Did you try to use valgrind? – Buh13246 Apr 29 '19 at 17:24
  • @Buh13246 Thank you for the reply! I just added the add_at_end underneath the struct. I don't see any blatantly obvious issues either which is why I'm so incredibly confused. I'm not a C expert by any means, so I'm hoping to get some guidance here. – Jet Apr 29 '19 at 17:27
  • What is TWELVE ? – Buh13246 Apr 29 '19 at 17:29
  • @Buh13246 No warnings with -Wall -Wextra and Valgrind showed no issue. TWELVE is defined in the .h file as #define TWELVE 12, I will also edit with the .h – Jet Apr 29 '19 at 17:32
  • I got one warning test.c:49:1: warning: control reaches end of non-void function [-Wreturn-type] in int ReadFileStoreInList(void) – Buh13246 Apr 29 '19 at 17:35
  • Sry .. but it does work for me... `Make Model Size Color Power Daily_Rate Rented ----------- ----------- ----------- ----------- ----------- ----------- ----------- test test test test test 0.00 A` there is just one Error `sh: Cls: command not found` because its clear on my terminal. Can you give me your file for testing purpose? – Buh13246 Apr 29 '19 at 17:39
  • 3
    Please, please, please rename `TWELVE` to something like `NUMBER_OF_ITEMS`. – pmg Apr 29 '19 at 18:07
  • 1
    Given that the same code ran fine under other circumstances, the most likely issues revolve around externalities, such as the presence, location, and access-control mode of the data file. In that regard, I observe that in the event that you fail to open the file, you nevertheless try to close it. Passing a null pointer to `fclose()` could plausibly crash your program (abrupt termination), and even if that's not your issue, it would be better form to move the `fclose()` into the `if` block so that it is executed only if the file was successfully opened. – John Bollinger Apr 29 '19 at 18:27
  • @Buh13246 I added the test file at the end of the post, thank you! – Jet Apr 29 '19 at 18:29
  • @pmg I would if it was my code, but it's my professors. Unfortunately I'm just trying to get it to run. – Jet Apr 29 '19 at 18:31
  • @JohnBollinger I didn't even realize this was an issue. Thanks for that! – Jet Apr 29 '19 at 18:31
  • @Buh13246 Upon closer inspection, it seems like this issue comes from the test file - it doesn't use proper spacing and the commas are strangely placed. I'm guessing my professor actually messed up with the parsing of the file and manually changed it. I sincerely appreciate the help. – Jet Apr 29 '19 at 18:34
  • 1
    `while (!feof(cfPtr))`? Read [**Why is “while (!feof(file))” always wrong?**](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong) – Andrew Henle Apr 29 '19 at 19:52

2 Answers2

2

You concluded in comments that you thought the data file contents were to blame for your issue. You now having posted those, I can confirm that they are indeed a contributing factor. Imprudent details of your scanf format also contribute.

In the first place, the call for reading the header is dangerous:

      fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate);

Since the data are just going to be discarded, there is no point in storing them. Moreover, it's not necessarily safe to assume that the header data will have characteristics matched to those of the associated data. It would be better to read and not assign the whole line:

      fscanf(cfPtr, "%*[^\n]");

The * in the format says that the field directive is not to be assigned, only read. Overall, that reads (and ignores) everything up to but not including the first newline. That also allows you to get rid of the abominably-named dAilyRate variable.

Then there is the format for reading the actual data:

         fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate);

It simply does not match the data. Specifically, the %s field descriptor skips leading whitespace and matches a whitespace-delimited string. Your data are comma-delimited, not whitespace-delimited except for line terminators. As a result, that scanf call will try to write a whole line's worth of data into each of the first five strings, thus overrunning each of their bounds. That's a plausible reason for a segfault.

What's more, the line read into daily_rate will fail, since the next data available at that point will be non-numeric. Yet even if the commas were changed to spaces, the rate data would still not be read correctly, because $ is not a valid part of a number. And that, in turn will throw off the reads for the second and subsequent lines.

The field overruns could have been avoided by specifying maximum field widths in the format. It would, moreover, be prudent to check the return value of scanf() to verify that all fields were read, as @Achal demonstrated in his answer, before relying on that data.

Here's a data file in a format compatible with the formats you're actually using:

make model size color power rate
Mazda 3 4-door Black 4-Cyl 99.73 
Jeep Cherokee 4-door Blue 8-Cyl 131.92 
Buick Regal 4-door Purple 6-Cyl 125.19 
Fullsize SUV 5-door Brown 8-Cyl 163.94 
Chrysler Pacifica 4-door Green 6-Cyl 127.49 
Ford Focus 2-door Red 4-Cyl 99.73 
VW Jetta 2-door Orange 4-Cyl 94.91 
Chevrolet Suburban 4-door Yellow 8-Cyl 204.92 
Nissan Pathfinder 4-door White 6-Cyl 145.11 
Chevrolet Spark 2-door Teal 4-Cyl 99.55

And here's a safer way to read it:

#define STR_SIZE 12

char  make[STR_SIZE]={""}, model[STR_SIZE]={""}, size[STR_SIZE]={""}, color[STR_SIZE]={""},
        power[STR_SIZE]={""};
float daily_rate;

fscanf(cfPtr, "%*[^\n]");
while (fscanf(cfPtr, "%11s%11s%11s%11s%11s%f", make, model, size, color, power, &daily_rate) == 6) { 
    add_at_end(make, model, size, color, power, daily_rate, 'A');
}

The %11s fields will read up to 11 characters into your 12-character arrays, leaving room for the string terminator that fscanf() will append to each. This will still run into trouble if there is overlong data in the file, but it should not segfault.

I should say also that scanf is difficult to use safely and properly, and that there are other, better alternatives for parsing and consuming the data -- either in the original or in the modified format.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    yeah since its using scanf with %s I though that the test file is in that format. Thx for writing an answer. I was really confused why it doesn't work with his file... So maybe the Prof should go to a C Course and learn Something about writing good code ;) . – Buh13246 Apr 29 '19 at 21:13
0

Here are few observation.

Firstly provide more readbility to your code by giving some meaningful macro name instead of TWELVE. For e.g #define NUMBER_OF_ITEMS 12

Secondly, In function definition the formal argument make[TWELVE] doesn't look good as you are passing char array & it decays to char*, so only char *make is enough. This

void add_at_end(char make[TWELVE], char model[TWELVE], char size[TWELVE], char color[TWELVE], char power[TWELVE], float daily_rate, char rented) { }

can be replaced as

void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented) { }

And most importantly, this

fscanf(cfPtr, "%s%s%s%s%s%s", make, model, size, color, power, dAilyRate); /* Just Remove it */

just before

while (!feof(cfPtr)) 

creates an issue i.e that information you are not using, its getting overwritten by second fscanf() statement inside loop. Also do read Why is “while (!feof(file))” always wrong?

Sample code

void add_at_end(char *make, char *model, char *size, char *color, char *power, float daily_rate, char rented)
{
   /* same code */
}

And

int ReadFileStoreInList(void)
{
    FILE *cfPtr;

    if ((cfPtr = fopen("input", "r")) !=NULL)
    {
        char  make[TWELVE]={""}, model[TWELVE]={""}, size[TWELVE]={""}, color[TWELVE]={""}, power[TWELVE]={""};
        char rented = 'A';
        float daily_rate=0.0;
        char dAilyRate[TWELVE];
        while (fscanf(cfPtr, "%s%s%s%s%s%f", make, model, size, color, power, &daily_rate) == 6)
        {
            rented = 'A';
            add_at_end(make, model, size, color, power, daily_rate, rented);
        }
        printScreenTitleAndHeaderForCars();
        traverse_in_order();
    }
    else
    {
        puts("Input data file could not be opened, I have no new inventory of cars from headquarters\n\n\n");
        return 0; /* in case of fopen failed */
    }
    fclose(cfPtr);
}
Achal
  • 11,821
  • 2
  • 15
  • 37
  • Although I substantially agree with all those criticisms (but do note the header in the data file just posted by the OP), they do not seem either individually or collectively to address the OP's question of why the program is failing for them. – John Bollinger Apr 29 '19 at 18:35
  • Thank you for the feedback! These general rules I do make sure to follow in my code, but this is specifically my professors and I didn't want to change anything. I was mainly trying to figure out why it wouldn't run. It seems to be an issue with the text file parameters from what I've found – Jet Apr 29 '19 at 18:36
  • @JohnBollinger data read by first `fscanf()` didn't used or utilized anywhere. `fscanf()` just before `add_at_end()` call were updated in the linked list. This is surely an issue I guess if file contains more than one line of info – Achal Apr 29 '19 at 18:38
  • @Achal, that the data (not) read by the final `scanf` are used to update the linked list is indeed a flaw in the program, but it's not one of the ones you called out. In any case, although that might lead to surprising and unwanted behavior, I see no reason to expect that it would lead to a segfault. – John Bollinger Apr 29 '19 at 18:54
  • True @JohnBollinger agree. – Achal Apr 29 '19 at 18:56
  • @JohnBollinger I believe I found the problem. For whatever reason it ran for him, I'm unsure - because my issue seemed to be the data file. The syntax was off, it was missing commas and spaces where they needed to be - yet the exact same file ran perfectly fine for him. I updated my data file to be correctly formatted and the issue seems to resolve itself. I've also went through your suggestions along with Achal and the program is more efficient. I sincerely appreciate your help fellas! – Jet Apr 29 '19 at 19:14