I'm writing a program that takes records(first and last name,id and mark), reads them from a file and inserts the information in StudentRecord structures. For some reason,when I go to list the information, the first and last name strings in each structures are all the same. The first and last names are the last ones loaded from the file. I tried printing the members right after they're assigned and it's the right name but when I call the list function the names aren't right but the ID and mark data is correct. For the list function all I do at the moment is print each member in all the structures. For example I could have the following in a file:
FName1 LName1 1 99.9
FName2 LName2 2 54.9
FName3 LName3 3 69.9
FName4 LName4 4 57.9

The output for my list function ends up being:
FName4 LName4 1 99.9
FName4 LName4 2 54.9
FName4 LName4 3 69.9
FName4 LName4 4 57.9

Here is my code, the openfile function was written by me, all the rest was provided to me and cannot be changed whatsoever. Thank you in advanced for any help you can give me.

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

typedef struct StudentRecord{
	char* firstName;
	char* lastName;
	int id;
	float mark;
}StudentRecord;

StudentRecord** g_ppRecords;	/* array of pointers to StudentRecords */
int g_numRecords = 0;			/* how many records */
char* g_FileName;				/* file to open */
FILE* g_pf;						/* file pointer */
typedef enum{FALSE,TRUE}BOOL;

void OpenFile(void);
void ListRecords(void);
void AddRecord(void);
void DeleteRecord(void);
void SaveRecord(void);

int main(){
	BOOL running = TRUE;
	char response;
	OpenFile();	// Open a file and copy the records into memory

	while(running){
		// The menu
		printf(
				"1. List records in memory\n"
				"2. Add a new record\n"
				"3. Delete first record\n"
				"4. Quit and save records to file\n"
				);
		fflush(stdin);
		scanf("%c",&response);

		switch(response){
		case '1':
			ListRecords();	/* Lists the records in memory */
			break;
		case '2':
			AddRecord();	/* Add a new record */
			break;
		case '3':
			DeleteRecord();		/* Delete a record */
			break;
		case '4':
			running = FALSE;
			break;
		default: printf("Invalid choice\n");
		}
	}
	return 0;
}	

void OpenFile(void){
	char* input = (char*) malloc(254);
	BOOL fileExists = FALSE;
	printf("OPEN FILE\n");
	g_FileName = (char*) malloc(26);

	while(fileExists == FALSE){
		printf("Please enter the name of the file to open: ");
		scanf("%s",g_FileName);
		printf("\nFileName: %s\n",g_FileName);

		if((g_pf = fopen(g_FileName, "r")) == NULL)
			printf("File does not exists\n");
		else
			fileExists = TRUE;
		
	}

	if(fileExists == TRUE){
		int i = 0;
		char delimiter[] = " ";
		char *token = NULL;
		g_ppRecords = (StudentRecord**) malloc(3*sizeof(StudentRecord));

		while(fgets(input,150,g_pf)!=NULL){
			printf("HERE %d:%s\n",g_numRecords,input);
			i = 0;
			g_ppRecords[g_numRecords] = (StudentRecord*) malloc(sizeof(StudentRecord));
			token = NULL;
			token = strtok(input,delimiter);
			
			while(token != NULL){
				switch(i){
				case 0:
					g_ppRecords[g_numRecords]->firstName =(char*) malloc(sizeof(token));
					g_ppRecords[g_numRecords]->firstName = token;	break;
				case 1:
					g_ppRecords[g_numRecords]->lastName = (char*) malloc(sizeof(token)); 
					g_ppRecords[g_numRecords]->lastName = token;	break;
				case 2:
					g_ppRecords[g_numRecords]->id = atoi(token);	break;
				case 3:
					g_ppRecords[g_numRecords]->mark = atof(token);	break;                                                                                  default: printf("Error loading data\n");
				}			
				i++;
				token = strtok(NULL,delimiter);
			}
			g_numRecords++;
		}
	}
	else
		printf("File not loaded");
}

but when I call the list function the names aren't right but the ID and mark data is correct. For the list function all I do at the moment is print each member in all the structures.

Then could you post the list function

this is my output.

OPEN FILE
Please enter the name of the file to open: 001.txt

FileName: 001.txt
1. List records in memory
2. Add a new record
3. Delete first record
4. Quit and save records to file
1
Record #1
First Name = Adam
Last Name = Smith
ID = 1
Mark = 88.500000

Record #2
First Name = Adam
Last Name = Smith
ID = 2
Mark = 73.699997 

Record #3
First Name = Adam
Last Name = Smith
ID = 3
Mark = 12.500000

1. List records in memory
2. Add a new record
3. Delete first record
4. Quit and save records to file

I removed printf("HERE %d:%s\n",g_numRecords,input);, it was just for testing purposes. My input file has the following:
Jeff Watts 1 88.5
Toby Jefferson 2 73.7
Adam Smith 3 12.5

Oh and here is my ListRecords function:

void ListRecords(void){
	int i;
	for(i=0;i<g_numRecords;i++){
		printf("Record #%d\nFirst Name = %s\nLast Name = %s\nID = %d\nMark = %f\n\n",
				i+1,
				g_ppRecords[i]->firstName,
				g_ppRecords[i]->lastName,
				g_ppRecords[i]->id,
				g_ppRecords[i]->mark);
	}
}

What we need to find out here is whether the records are being added to the structure properly in the first place or is there a problem with the printing (list function as zeroliken mentioned). Also when you wrote that printf for testing purposes , was it printing all records properly?

that printf was to make sure that I was getting one line at a time from the file and that it was the correct information before splitting it up. When I assign a new first and last name from the file, it assigns it to the previous strings as well. I thought that maybe char* firstname and char* lastname are pointing to the same memory for each struct but I call malloc() each time.

The 'scanf' buffer gets full after you enter the name. So either flush it or use 'gets' function. All the best.

Comments
Use gets() on;y if you want to write a program that is broken before you even compile it. Search to see why...

I'm using fgets not scanf. I get a line from the file and then use strtok to split the line. Then I use a switch statement to assign the values to the members of my struct.

Comments
Good. fgets() is the right way to do it...

Does anyone have any ideas?

Of course. Assigning a pointer does not copy the string contents:

g_ppRecords[g_numRecords]->firstName =(char*) malloc(sizeof(token));
	g_ppRecords[g_numRecords]->firstName = token;

Now firstName points to token, not the memory you've allocated. That is, firstName in every record points somewhere into the input array, and most likely to the exact same place.

This article has been dead for over six months. Start a new discussion instead.