Malloc problem with fread() to read file to structure in C

Hello,
I am trying to read a text file into linked list, but always got the first and last records wrong.
1) The problem looks related to the initialization of the node temp with malloc(), but could not figure it out. No error/warning at compiling, though.
2) The output file is empty, indicating line 30 ~ 40 are not working at all, commented along with Line 15 for first debug try.
3) If Line 10 is replaced with Line 11, what is the correct way to do it? Line 23 was used for a try, but did not work with segment fault error.
Trying to understand more on malloc() and FILE stream. Thanks a lot!

#include <stdio.h>
#include <stdlib.h>

struct student
{
int roll_num;
char name[120];        //Line 10
//char *name;         //Line 11
struct student *next;
};

int main ()
{
FILE *fptr;
// FILE *ofptr;         //Line 15

struct student *temp;

fptr = fopen("INFILE.txt","r");
temp = malloc(sizeof(struct student));
// temp->name = malloc(sizeof(char)*120);            //Line 23
while(fread(temp, sizeof(struct student), 1, fptr))
    printf("%d\t%s\n",temp->roll_num, temp->name);
free(temp);
fclose(fptr);

/***************************************************** For writing          //Line 30

ofptr = fopen("OUTFILE.tab","w");
temp = malloc(sizeof(struct student));

while(temp != NULL)
{
    fwrite(temp, sizeof(struct student), 1, ofptr);
    temp = temp->next;
}
fclose(ofptr);
free(temp);
*****************************************************/                 //Line 40
 return 0;
}
/* INFILE.txt:
10001 Angola
10002 Bangalore
10003 Cairo
10004 Dallas
10005 Edmonton
10006 Fargo
10007 Georgia
10008 Halifax
10009 Indianapolis
10010 Jamaica

OUTFILE.tab:
808464433    1 Angola
10002 Bangalore
10003 Cairo
10004 Dallas
10005 Edmonton
10006 Fargo
10007 Georgia
10008 Halifax
10009 Indianapolis
?x.?0010 Ja1

*/

That isn't surprising since your input file is 142 bytes long and you're therefore calling fread() twice.

Let's come back to this later.

Note that the start of the comment on line 30 (marked in orange text in your code below) is not closed until line 40 (also marked in orange below). So, lines 30 to 40 are just a single comment to the C compiler and no code is being executed in this comment.

So the real question is: What are you trying to do?

You are using fread() to fill in an integer, a 120 byte character array and a pointer with a character string read from the file. That clearly isn't what you want, but I'm not sure what you do want. Since the size of your current structure is about 132 bytes long (4 byte int, 120 byte char array, and 8 byte pointer), you are reading the 1st 132 bytes from your input file into the first element of your linked list, but since you free the space for the 1st element of the list before reading the second element (which you don't see because you commented out lines 30-40), you don't really have a linked list.

If you are trying to read each line from your input file, convert the numeric string at the start of the line into an integer and store it into the roll_num element of a struct student, allocate space for and copy the name on the rest of the input line into an area allocated to hold that string, and set the pointer at the end of the structure to the next student's entry, then your code isn't doing any of those things.

If you verify that what I listed above is what you're trying to do, I'll try to come up with an example that shows how to do that in the next couple of days.

2 Likes

Thanks Don!
Two things I was trying to catch:
1) Read the file into linked list, each line for a node which is a structure with two members: roll_num (int) and name (char array or char *); This is similar to my old post when I tried to parse file as 4-line record, i.e. every four line is a record.
2) Save the linked list to a new file.
For point 1), the problem with my code is the first and last record (i.e. nodes) of the linked list were always wrong---printed incorrectly.

For Point 2) my code was just not working at all, so that the corresponding part Line 30 ~ Line 40 were commented out.
For point 1) an extra question in my mind is using char pointer instead of char array, how to accomplish the same job? as pointer is still a big challenge for me, especially when dynamic memory allocation is related.
I am trying to understand the FILE stream and memory allocation with malloc() for file manipulation. (The next step is to do some manipulation of each node, say change each roll_num, modify the name, or add another member for each node, etc. But I am not able to do it at this moment.)

Since the size of your current structure is about 132 bytes long (4 byte int, 120 byte char array, and 8 byte pointer), you are reading the 1st 132 bytes from your input file into the first element of your linked list,
Yes, that's what I was trying. I had thought of wrong mem allocation, so that I was thinking to use char pointer for name instead of char array for dynamic memory allocation. I know I am too far from this point.

So, lines 30 to 40 are just a single comment to the C compiler and no code is being executed in this comment.
Yes, I did that on purpose because it did not work.

If you verify that what I listed above is what you're trying to do,

Yes, that's what I was trying to do!

I'll try to come up with an example that shows how to do that in the next couple of days.
Great! Thank you very much for your time.

Consider this text:

string1
string2
42
struct mystructure {
        char *string1;
        char *string2;
        int value;
} mystr;

fread(&mystr, sizeof(mystr), 1, fp);

printf("%s\n", mystr.string1);
printf("%s\n", mystr.string2);
printf("%d\n", mystr.value);

What, exactly, does this print?

Segmentation fault

Why...? Because 'char *string1' is a pointer: A four-byte(on my system, 8-byte on yours) integer that describes a location in memory. The four bytes 'stri' aren't likely to refer to any valid memory, and even if we win the lottery and find a valid location, it probably won't be pointing to a location containing "string1\n".

So we do this:

struct mystructure {
        char string1[64];
        char string2[64];
        int value;
} mystr;

fread(&mystr, sizeof(mystr), 1, fp);

printf("string1:  %s\n", mystr.string1);
printf("string2:  %s\n", mystr.string2);
printf("value:  %d\n", mystr.value);

What does it print now?

string1:  string1
string2
42
�ӿom�ӿ��|�
string2:
value:  -1216741388

Oh, it stuffed it all into 'string1' because it didn't stop when the string did.

C library calls will not give your structures the special treatment to put anything but the file's literal contents in that memory? How would it? It doesn't know what your structure is:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

It only knows it was told, 'put this many bytes at this memory', and it did.

C does not know your data structures. Only your own functions do, and then, only if you tell them what it is, and it's still up to you to use them -- it does what you tell it, nothing more. If you want things read in any way other than a raw block, you have to write that.

#include <stdio.h>
#include <string.h>

struct mystruct {
 char *string1;
 char *string2;
 int value;
};

struct mystruct *readmystruct(struct mystruct *s, FILE *fp)
{
 char buf[4096];
 if(fgets(buf, 4096, fp) == NULL) return(NULL);
 s->string1=strdup(buf);
 if(fgets(buf, 4096, fp) == NULL)
 {
   free(s->string1);
   return(NULL);
 }
 s->string2=strdup(buf);
 if(fgets(buf, 4096, fp) == NULL)
 {
  free(s->string1);
  free(s->string2);
  return(NULL);
 }
 s->value=atoi(buf);
 return(s);
}

int main()
{
 struct mystruct s;
 if(readmystruct(&s, stdin) == NULL)
 {
        printf("Couldn't read struct\n");
        return(1);
 }

 printf("string1:  %s\n", s.string1);
 printf("string2:  %s\n", s.string2);
 printf("value:  %d\n", s.value);

 return(0);
}
$ ./a.out < lines
string1:  string1

string2:  string2

value:  42

$

"C library calls do not know your data structures" is a ready answer to a lot of questions. Like, "will free() deallocate pointers inside a structure pointer I give it, or just the structure itself?" It does not, indeed cannot -- it would need to know what's inside it, and it doesn't. All it has is the 4/8 bytes of the pointer you passed into it, and a list(well, heap) of what pointers it gave you before.

1 Like

No! It printed exactly what you stored in those locations. Using fread() to copy ASCII characters from a file into an object of type int in your structure doesn't automatically convert from a string to an integer.
You need to use something like atoi() to convert from a string into an integer. Perhaps the following will show what you were doing (using a union instead of your structure to show what happens when you interpret characters from a string as an integer. Store the following program in a file named string_vs_int.c:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
        union {
                int     i;
                char    c[4];
        } u;
        int     j, k;
        for(j = 1; j < argc; j++) {
                u.i = 0;
                for(k = 0; k < sizeof(u.c) && (u.c[k] = argv[j][k]); k++);
                printf("argv[%d] = %s, u.i = %d, u.c = \"%4.4s\"\n",
                        j, argv[j], u.i, u.c);
        }
}

and run the commands:

make string_vs_int
./string_vs_int 10001 1000 1

You will get something like:

argv[1] = 10001, u.i = 808464433, u.c = "1000"
argv[2] = 1000, u.i = 808464433, u.c = "1000"
argv[3] = 1, u.i = 49, u.c = "1"

Then note that the 1st five characters in your input file are "10001". Your use of fread() reads characters into the int at the start of your structure, just like the sample program above does using the union. Note that the number in red from your quote matches the numbers in red from my sample program!

Note that the above was run on a Intel x86 family CPU with int being an integer type occupying 4 bytes. With a different sized int, you will get different results. With a different CPU you might get different results. (The byte order of least significant byte to most significant byte in an int varies from CPU architecture to CPU architecture.)

Yes, it is clear that you still don't understand pointers. Hopefully the sample program at the end of this post will provide an understandable template. And, it appears that you thought fread() reads lines from a file. But, fread() doesn't care about line boundaries; it just copies the specified number of bytes from your input file into the supplied buffer.

If you save, build, and run the following C program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct student {
        int             roll_num;
        char            *name;
        struct student  *next;
}       *head,          // Pointer to first element in the linked list.
        *temp;          // When creating the linked list, a pointer to current
                        // element in the linked list; when deleting the linked
                        // list, a pointer to the next element in the list.

        // Note that both head and temp are initialzed to NULL pointers because
        // they are declared globally; not on the stack in main().

        // Note also that no linked list elements have been allocated yet (we
        // have allocated two pointers to structures, but no structures);
        // space for structures will be allocated as needed as lines are read
        // from the input file.

int main(int argc, char *argv[]) {
        char            buffer[120];    // input buffer
        FILE            *fptr;          // input stream pointer
        size_t          len;            // length of name
        char            *p;             // pointer to name in buffer[]

        fptr = fopen("INFILE.txt", "r");
        if(fptr == NULL) {
                fprintf(stderr, "%s: fopen(INFILE.txt) failed.\n", argv[0]);
                exit(1);
        }

        // Read input file into a linked list...
        // Note that fgets() reads no more than one line (up to a given number
        // of bytes)from a file; fread() reads a given number of bytes without
        // regard to line boundaries.
        while(fgets(buffer, sizeof(buffer), fptr)) {
                // XXX Should verify that fgets() returned a complete line.
                printf("Read input line: %s", buffer);

                // Allocate space for this element of the linked list.
                // Note that using calloc() instead of malloc() initializes
                // temp->next (in the newly allocated structure) to a NULL
                // pointer.  (It also fills any space between structure
                // elements (if there is any) to null bytes.  This isn't
                // important for this example, but can be important if
                // structures are to be examined in a core dump or compared.)
                if(head == NULL)
                        // Create 1st element in the linked list.
                        temp = head = calloc(1, sizeof(struct student));
                else    // Add new element to the end of the linked list.  Note
                        // that the temp->next in the next statement is in the
                        // previous element in the linked list and then temp is
                        // set to a pointer to the (new) current element.
                        temp = temp->next = calloc(1, sizeof(struct student));
                if(temp == NULL) {
                        fprintf(stderr,
                                "%s: calloc() for linked list element failed.\n",
                                argv[0]);
                        exit(2);
                }
                printf("%d bytes allocated at %p for linked list element\n",
                        (int)sizeof(struct student), temp);

                // Convert numeric string at start of buffer to int.
                temp->roll_num = atoi(buffer);

                // skip over number and 1st space to find start of name.
                // XXX Should check for string overflow for badly formed input.
                for(p = buffer; *p++ != ' ';);

                // Allocate space and copy name.  Note that strlen() will
                // include space for the trailing newline character but not for
                // the terminating null byte.  But, we'll replace the newline
                // with a null byte before we copy the name from buffer[] to
                // the space we allocated for name in this linked list element.
                len = strlen(p);
                if((temp->name = malloc(len)) == NULL) {
                        fprintf(stderr, "%s: malloc(%d) for name failed.\n",
                                argv[0], (int)len);
                        exit(3);
                }
                // Change trailing newline to string terminator.
                *(p + len - 1) = '\0';
                // Copy name into allocated space.  Note that len includes the
                // null byte that termiantes the string.
                strncpy(temp->name, p, len);
                printf("%d bytes allocated at %p for name %s\n\n", (int)len,
                        temp->name, temp->name);
        }

        // To get to here, we either hit EOF or detected an I/O error.
        if(ferror(fptr)) {
                fprintf(stderr, "%s: I/O error reading input.\n", argv[0]);
                exit(4);
        }
        fclose(fptr);
        printf("End-of-file found on input.\n");

        // We have now completed reading the input into a linked list.  Note
        // that temp->next (the pointer to the next element in the last element
        // in the linked list) is a NULL pointer.

        // Writing a linked list to a file doesn't make any sense.  The pointers
        // in the structures have no meaning in a file and won't be valid if
        // read back into another process.

        // For this example, we'll just print the data from the linked list and
        // free the space reserved for the elements after each element is
        // printed.  Obviously, we could reformat the data and save it in a file
        // but for this demo, showing what we have in the linked list seems more
        // important.
        while(head) {
                printf("\nPrinting list element located at %p:\n", head);
                printf("\troll_num: %d\tname: %s\n", head->roll_num,
                        head->name);
                printf("Freeing name (%p) and list element (%p) space.\n",
                        head->name, head);
                free(head->name);
                // Note that we can't reference head->next after we free head,
                // so we need to save the pointer to the next element before we
                // free the current element.
                temp = head->next;
                free(head->next);
                head = temp;
                // Note that head now points to the 1st remaining element of
                // the linked list again, if there are any elements left.  It is
                // a NULL pointer if no elements remain in the list.
        }

        // We have now freed all of the space we allocated for the linked list
        // elements and the space we allocated for the names associated with
        // each element in the linked list.
        return(0);
}

and you run it in the directory that contains the input file you specified in the 1st post in this thread, you'll get output similar to the following:

Read input line: 10001 Angola
24 bytes allocated at 0x105800890 for linked list element
7 bytes allocated at 0x1058008b0 for name Angola

Read input line: 10002 Bangalore
24 bytes allocated at 0x1058008c0 for linked list element
10 bytes allocated at 0x1058008e0 for name Bangalore

Read input line: 10003 Cairo
24 bytes allocated at 0x1058008f0 for linked list element
6 bytes allocated at 0x105800910 for name Cairo

Read input line: 10004 Dallas
24 bytes allocated at 0x105800920 for linked list element
7 bytes allocated at 0x105800940 for name Dallas

Read input line: 10005 Edmonton
24 bytes allocated at 0x105800950 for linked list element
9 bytes allocated at 0x105800970 for name Edmonton

Read input line: 10006 Fargo
24 bytes allocated at 0x105800980 for linked list element
6 bytes allocated at 0x1058009a0 for name Fargo

Read input line: 10007 Georgia
24 bytes allocated at 0x1058009b0 for linked list element
8 bytes allocated at 0x1058009d0 for name Georgia

Read input line: 10008 Halifax
24 bytes allocated at 0x1058009e0 for linked list element
8 bytes allocated at 0x105800a00 for name Halifax

Read input line: 10009 Indianapolis
24 bytes allocated at 0x105800a10 for linked list element
13 bytes allocated at 0x105800a30 for name Indianapolis

Read input line: 10010 Jamaica
24 bytes allocated at 0x105800a40 for linked list element
8 bytes allocated at 0x105800a60 for name Jamaica

End-of-file found on input.

Printing list element located at 0x105800890:
	roll_num: 10001	name: Angola
Freeing name (0x1058008b0) and list element (0x105800890) space.

Printing list element located at 0x1058008c0:
	roll_num: 10002	name: Bangalore
Freeing name (0x1058008e0) and list element (0x1058008c0) space.

Printing list element located at 0x1058008f0:
	roll_num: 10003	name: Cairo
Freeing name (0x105800910) and list element (0x1058008f0) space.

Printing list element located at 0x105800920:
	roll_num: 10004	name: Dallas
Freeing name (0x105800940) and list element (0x105800920) space.

Printing list element located at 0x105800950:
	roll_num: 10005	name: Edmonton
Freeing name (0x105800970) and list element (0x105800950) space.

Printing list element located at 0x105800980:
	roll_num: 10006	name: Fargo
Freeing name (0x1058009a0) and list element (0x105800980) space.

Printing list element located at 0x1058009b0:
	roll_num: 10007	name: Georgia
Freeing name (0x1058009d0) and list element (0x1058009b0) space.

Printing list element located at 0x1058009e0:
	roll_num: 10008	name: Halifax
Freeing name (0x105800a00) and list element (0x1058009e0) space.

Printing list element located at 0x105800a10:
	roll_num: 10009	name: Indianapolis
Freeing name (0x105800a30) and list element (0x105800a10) space.

Printing list element located at 0x105800a40:
	roll_num: 10010	name: Jamaica
Freeing name (0x105800a60) and list element (0x105800a40) space.

Obviously, the pointers malloc() and calloc() return to you will vary from OS to OS (and are even highly likely to change even if you run the same program with the same data on the same machine twice).

Hopefully, there are enough comments in the code to demonstrate how to build a linked list, load data into the structures in the linked list, walk through the linked list to get data out of the list, and to deallocate the data after it is no longer needed.

Note that I use fgets() to read lines (rather than fread() to read buffers) from the input file. Note that since your input file has data that has to be converted from strings to integers and has variable length names, each line is read into an input buffer and then a linked list element structure is allocated for each line read and space to hold the name found on that input line is allocated for that list element's name field. Then data is copied into those allocated spaces. That allocated space CANNOT be freed until it is no longer going to be used.

Always determine how much space you need, allocate that much space (or more), and then copy your data into that space -- in that order. If you copy data into an uninitialized pointer or copy more data into an allocated space than was allocated, hard-to-track-down strange things may happen when copying the data, in the statement after you copied the data, or hundreds of statements after you copied the data. Check, double-check, and triple-check that you have allocated the space you need, have copied the correct data into your allocated space, and don't use any allocated space after it has been freed. Know when you have arrays of characters that don't (always) have a null byte terminator and either make sure you add a null terminator or never treat that array as a string. (Did you notice that u.c in the 1st program in this posting is printed using the format string %.4s instead of %s ? That was done because that array will not have a null byte string terminator for any command line argument that contains for or more characters!)

Note that a couple of comments in this code start with XXX . They are reminders for code that should be added, but is not included in this sample. Filling in those missing data verification checks is left as an exercise for the reader.

1 Like

Hi Corona & Don, Thanks you both so much!
This is very helpful as I could not find these explanations in any of the C books. All the books I have read so far did not combine these two parts (Corona's explanation and Don's Linked_list). I will try to digest more.
By the way, Don's code was compiled without any warning, but the roll_num value of the structure was wrong from strange 9065056 instead of 10002 with increment of 64 there after, except the first node 10001, which is correct. I realized why you gave an introduction program string_vs_int at the beginning. I'll try to catch why only 10001 was correctly printed. Thank you very much!

 *** ***
Printing list element located at 0x8a5250:
roll_num: 10001    name: Angola
Freeing name (0x8a5270) and list element (0x8a5250) space.
roll_num: 9065056    name: Bangalore
Freeing name (0x8a52b0) and list element (0x8a5290) space.
roll_num: 9065120    name: Cairo
Freeing name (0x8a52f0) and list element (0x8a52d0) space.
roll_num: 9065184    name: Dallas
Freeing name (0x8a5330) and list element (0x8a5310) space.
roll_num: 9065248    name: Edmonton
Freeing name (0x8a5370) and list element (0x8a5350) space.
roll_num: 9065312    name: Fargo
Freeing name (0x8a53b0) and list element (0x8a5390) space.
roll_num: 9065376    name: Georgia
Freeing name (0x8a53f0) and list element (0x8a53d0) space.
roll_num: 9065440    name: Halifax
Freeing name (0x8a5430) and list element (0x8a5410) space.
roll_num: 9065504    name: Indianapolis
Freeing name (0x8a5470) and list element (0x8a5450) space.
roll_num: 9065568    name: Jamaica
Freeing name (0x8a54b0) and list element (0x8a5490) space.

What OS are you using?
What is the size of the following types in your programming environment:

  1. int
  2. char *
  3. struct student *
  4. struct student

What were the sizes of the buffers allocated by the calls to malloc() and calloc() that you deleted from the output my code produced?

The output you showed in message #6 in this thread is missing newline and tab characters that my code writes and has changed a tab character to a constant string of four spaces? Did you modify my code? Did you modify the output it produced (other than removing the first two sections of the output)? If these alterations were done by the way you copy and paste code for display here, please redirect the output to a file (such as output.txt) and upload that text.

Please also upload the source you compiled so I can compare it with what I compiled.

I am using

$ uname -a
$ Linux 3.5.0-44-generic #67-Ubuntu SMP Tue Nov 12 19:36:14 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct student {
        int             roll_num;
        char            *name;
        struct student  *next;
}       *head,          // Pointer to first element in the linked list.
        *temp;          // When creating the linked list, a pointer to current
                        // element in the linked list; when deleting the linked
                        // list, a pointer to the next element in the list.

        // Note that both head and temp are initialzed to NULL pointers because
        // they are declared globally; not on the stack in main().

        // Note also that no linked list elements have been allocated yet (we
        // have allocated two pointers to structures, but no structures);
        // space for structures will be allocated as needed as lines are read
        // from the input file.

int main(int argc, char *argv[]) {
        char            buffer[120];    // input buffer
        FILE            *fptr;          // input stream pointer
        size_t          len;            // length of name
        char            *p;             // pointer to name in buffer[]

        fptr = fopen("INFILE.txt", "r");
        if(fptr == NULL) {
                fprintf(stderr, "%s: fopen(INFILE.txt) failed.\n", argv[0]);
                exit(1);
        }

        // Read input file into a linked list...
        // Note that fgets() reads no more than one line (up to a given number
        // of bytes)from a file; fread() reads a given number of bytes without
        // regard to line boundaries.
        while(fgets(buffer, sizeof(buffer), fptr)) {
                // XXX Should verify that fgets() returned a complete line.
//yifangt                printf("Read input line: %s", buffer);

                // Allocate space for this element of the linked list.
                // Note that using calloc() instead of malloc() initializes
                // temp->next (in the newly allocated structure) to a NULL
                // pointer.  (It also fills any space between structure
                // elements (if there is any) to null bytes.  This isn't
                // important for this example, but can be important if
                // structures are to be examined in a core dump or compared.)
                if(head == NULL)
                        // Create 1st element in the linked list.
                        temp = head = calloc(1, sizeof(struct student));
                else    // Add new element to the end of the linked list.  Note
                        // that the temp->next in the next statement is in the
                        // previous element in the linked list and then temp is
                        // set to a pointer to the (new) current element.
                        temp = temp->next = calloc(1, sizeof(struct student));
                if(temp == NULL) {
                        fprintf(stderr,
                                "%s: calloc() for linked list element failed.\n",
                                argv[0]);
                        exit(2);
                }
                printf("%d bytes allocated at %p for linked list element\n",
                        (int)sizeof(struct student), temp);

                // Convert numeric string at start of buffer to int.
                temp->roll_num = atoi(buffer);

                // skip over number and 1st space to find start of name.
                // XXX Should check for string overflow for badly formed input.
                for(p = buffer; *p++ != ' ';);

                // Allocate space and copy name.  Note that strlen() will
                // include space for the trailing newline character but not for
                // the terminating null byte.  But, we'll replace the newline
                // with a null byte before we copy the name from buffer[] to
                // the space we allocated for name in this linked list element.
                len = strlen(p);
                if((temp->name = malloc(len)) == NULL) {
                        fprintf(stderr, "%s: malloc(%d) for name failed.\n", argv[0], (int)len);
                        exit(3);
                }
                // Change trailing newline to string terminator.
                *(p + len - 1) = '\0';
                // Copy name into allocated space.  Note that len includes the
                // null byte that termiantes the string.
                strncpy(temp->name, p, len);
        printf("%d bytes allocated at %p for name %s\n\n", (int)len,
                        temp->name, temp->name);
        }

        // To get to here, we either hit EOF or detected an I/O error.
        if(ferror(fptr)) {
                fprintf(stderr, "%s: I/O error reading input.\n", argv[0]);
                exit(4);
        }
        fclose(fptr);
       printf("End-of-file found on input.\n");

        // We have now completed reading the input into a linked list.  Note
        // that temp->next (the pointer to the next element in the last element
        // in the linked list) is a NULL pointer.

        // Writing a linked list to a file doesn't make any sense.  The pointers
        // in the structures have no meaning in a file and won't be valid if
        // read back into another process.

        // For this example, we'll just print the data from the linked list and
        // free the space reserved for the elements after each element is
        // printed.  Obviously, we could reformat the data and save it in a file
        // but for this demo, showing what we have in the linked list seems more
        // important.

        printf("\nPrinting list element located at %p:\n", head);
        while(head) {
                printf("roll_num: %d\tname: %s\n", head->roll_num, head->name);
//yifangt        printf("Freeing name (%p) and list element (%p) space.\n",
        //                head->name, head);
                free(head->name);
                // Note that we can't reference head->next after we free head,
                // so we need to save the pointer to the next element before we
                // free the current element.
                temp = head->next;
                free(head->next);
                head = temp;
                // Note that head now points to the 1st remaining element of
                // the linked list again, if there are any elements left.  It is
                // a NULL pointer if no elements remain in the list.
        }

        // We have now freed all of the space we allocated for the linked list
        // elements and the space we allocated for the names associated with
        // each element in the linked list.
        return(0);
}

When I run the code you showed us in message #8 in this thread, the output I get is:

24 bytes allocated at 0x10a000890 for linked list element
7 bytes allocated at 0x10a0008b0 for name Angola

24 bytes allocated at 0x10a0008c0 for linked list element
10 bytes allocated at 0x10a0008e0 for name Bangalore

24 bytes allocated at 0x10a0008f0 for linked list element
6 bytes allocated at 0x10a000910 for name Cairo

24 bytes allocated at 0x10a000920 for linked list element
7 bytes allocated at 0x10a000940 for name Dallas

24 bytes allocated at 0x10a000950 for linked list element
9 bytes allocated at 0x10a000970 for name Edmonton

24 bytes allocated at 0x10a000980 for linked list element
6 bytes allocated at 0x10a0009a0 for name Fargo

24 bytes allocated at 0x10a0009b0 for linked list element
8 bytes allocated at 0x10a0009d0 for name Georgia

24 bytes allocated at 0x10a0009e0 for linked list element
8 bytes allocated at 0x10a000a00 for name Halifax

24 bytes allocated at 0x10a000a10 for linked list element
13 bytes allocated at 0x10a000a30 for name Indianapolis

24 bytes allocated at 0x10a000a40 for linked list element
8 bytes allocated at 0x10a000a60 for name Jamaica

End-of-file found on input.

Printing list element located at 0x10a000890:
roll_num: 10001	name: Angola
roll_num: 10002	name: Bangalore
roll_num: 10003	name: Cairo
roll_num: 10004	name: Dallas
roll_num: 10005	name: Edmonton
roll_num: 10006	name: Fargo
roll_num: 10007	name: Georgia
roll_num: 10008	name: Halifax
roll_num: 10009	name: Indianapolis
roll_num: 10010	name: Jamaica

As you can see, this is considerably different from the output you said you got from this code in message #6 in this thread. But, most importantly the roll_num values shown while walking the linked list match the values found in your input file. I'm not doing anything with atoi() , calloc() , exit() , fclose() , ferror() , fgets() , fopen() , fprintf() , free() , malloc() , printf() , strlen() , or strncpy() that should behave differently on OS X than it should on any Linux distribution. I can't see how you would get the values for roll_num that you showed us in message #6 in this thread from the code you posted in message #8 in this thread.

1 Like

Just in case I forgot some lines that may have been modified, here I copy and paste the same code of my post #8 and compile in another PC with same Ubuntu:

yifangt@box $ gcc -o readf -Wall readfile.c  
yifangt@box $ ./readf 

24 bytes allocated at 0x1797250 for linked list element
7 bytes allocated at 0x1797270 for name Angola

24 bytes allocated at 0x1797290 for linked list element
10 bytes allocated at 0x17972b0 for name Bangalore

24 bytes allocated at 0x17972d0 for linked list element
6 bytes allocated at 0x17972f0 for name Cairo

24 bytes allocated at 0x1797310 for linked list element
7 bytes allocated at 0x1797330 for name Dallas

24 bytes allocated at 0x1797350 for linked list element
9 bytes allocated at 0x1797370 for name Edmonton

24 bytes allocated at 0x1797390 for linked list element
6 bytes allocated at 0x17973b0 for name Fargo

24 bytes allocated at 0x17973d0 for linked list element
8 bytes allocated at 0x17973f0 for name Georgia

24 bytes allocated at 0x1797410 for linked list element
8 bytes allocated at 0x1797430 for name Halifax

24 bytes allocated at 0x1797450 for linked list element
13 bytes allocated at 0x1797470 for name Indianapolis

24 bytes allocated at 0x1797490 for linked list element
8 bytes allocated at 0x17974b0 for name Jamaica

End-of-file found on input.

Printing list element located at 0x1797250:
roll_num: 10001    name: Angola
roll_num: 24736352    name: Bangalore
roll_num: 24736416    name: Cairo
roll_num: 24736480    name: Dallas
roll_num: 24736544    name: Edmonton
roll_num: 24736608    name: Fargo
roll_num: 24736672    name: Georgia
roll_num: 24736736    name: Halifax
roll_num: 24736800    name: Indianapolis
roll_num: 24736864    name: Jamaica

This is almost the same of your output of post #9, that roll_num are wrong except the first one, very similar to post #6 where the first half was not shown. I will try more test. Anyway, gdb is the another one that I am trying to get familiar with at the meantime.
And thank you pointing out that I'm not doing anything with atoi() , calloc() , exit() , fclose() , ferror() , fgets() , fopen() , fprintf() , free() , malloc() , printf() , strlen() , or strncpy() that should behave differently on OS X than it should on any Linux distribution. which I have no experience how they may cause difference.

Now that you have finally shown us the output from the first part of my code, it is obvious that something is overwriting the roll_num member of struct student (after the first element in the linked list) with a pointer. If you convert the bad values in roll_num from decimal to hexadecimal you'll note that the value is between the values of the pointers assigned to temp->name and temp->next .

For example 24736352(decimal) == 1797260(hex) and the pointer assigned to head is 17297250(hex) and the pointer assigned to head->name is 1797270(hex).

This should make absolutely no difference, but please try changing:

                if(head == NULL)
                        // Create 1st element in the linked list.
                        temp = head = calloc(1, sizeof(struct student));
                else    // Add new element to the end of the linked list.  Note
                        // that the temp->next in the next statement is in the
                        // previous element in the linked list and then temp is
                        // set to a pointer to the (new) current element.
                        temp = temp->next = calloc(1, sizeof(struct student));

to:

                if(head == NULL) {
                        // Create 1st element in the linked list.
                        head = calloc(1, sizeof(struct student));
                        temp = head;
                } else {// Add new element to the end of the linked list.  Note
                        // that the temp->next in the next statement is in the
                        // previous element in the linked list and then temp is
                        // set to a pointer to the (new) current element.
                        temp->next = calloc(1, sizeof(struct student));
                        temp = temp->next;
                }

Assuming that doesn't make any difference, while you're exploring gdb watch what gets assigned to temp->roll_num .