Why is my file output overwritten? - c

My program takes user input and stores it in an array of Records that I've defined as a structure: struct Record.
The user input is are the fields of the struct. Everything complies error free, but it seems I can't get the formatting correct. My program keeps asking for user input until the user enters 'n' when asked if there are anymore records.
Once there are no more records the program loops through the created records and file prints each of them spaced out by tabs and at the end starting with a newline for the next record. However, instead of starting at a new line and printing another record in the same fashion, it overwrites the previous record printed and tabs the next one even further.
What causes this to happen?
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
char zipcode[51];
char phoneNumber[51];
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = 'y';
int size = 1;
int i = 0;
struct Record *records;
struct Record *records_temp = NULL;
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = calloc((size),sizeof(*records));
records = records_temp;
printf("First Name: \n");
scanf("%s", records[size-1].fname);
printf("Last Name: \n");
scanf("%s", records[size-1].lname);
printf("Address: \n");
scanf(" %[^\n]", records[size-1].address);
printf("City: \n");
scanf("%s", records[size-1].city);
printf("State: \n");
scanf("%s", records[size-1].state);
printf("Zipcode: \n");
scanf("%s", records[size-1].zipcode);
printf("Phone Number: \n");
scanf("%s", records[size-1].phoneNumber);
//stores all record info
printf("Are there anymore records? [y/n] ");
scanf(" %c", &answer);
if(answer == 'y' || answer == 'Y')
{
size++;
printf("\n");
}
}
//open file
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL)
{
for(;i< size; i++)
{
fprintf(fileWriter,"%s\t",records[i].fname);
fprintf(fileWriter,"%s\t",records[i].lname);
fprintf(fileWriter,"%s\t",records[i].address);
fprintf(fileWriter,"%s\t",records[i].city);
fprintf(fileWriter,"%s\t",records[i].state);
fprintf(fileWriter,"%s\t",records[i].zipcode);
fprintf(fileWriter,"%s\n",records[i].phoneNumber);
}
free(records);
fclose(fileWriter);
}
else
{
printf("Error opening file.");
}
}

I changed a little bit your code, but I think you should use linked list as a data structure here, it's more simple and consume less memory.
I made some tries and all went ok. :)
Hope that help you!!
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct Record Record;
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
char zipcode[51];
char phoneNumber[51];
Record *next;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = '\0';
// int size = 1;
// int i = 0;
Record *records = NULL;
Record *records_first = NULL;
Record *records_previous = NULL;
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL) {
for( ; ; ) {
records = (Record*) malloc(sizeof(Record));
if(records_first == NULL)
records_first = records;
if(records_previous != NULL)
records_previous->next = records;
records = records_first;
printf("First Name: \n");
scanf("%s", records->fname);
fprintf(fileWriter,"%s\t",records->fname);
printf("Last Name: \n");
scanf("%s", records->lname);
fprintf(fileWriter,"%s\t",records->lname);
printf("Address: \n");
scanf(" %[^\n]", records->address);
fprintf(fileWriter,"%s\t",records->address);
printf("City: \n");
scanf("%s", records->city);
fprintf(fileWriter,"%s\t",records->city);
printf("State: \n");
scanf("%s", records->state);
fprintf(fileWriter,"%s\t",records->state);
printf("Zipcode: \n");
scanf("%s", records->zipcode);
fprintf(fileWriter,"%s\t",records->zipcode);
printf("Phone Number: \n");
scanf("%s", records->phoneNumber);
fprintf(fileWriter,"%s\t\n\n",records->phoneNumber);
records->next = NULL;
records_previous = records;
printf("Are there anymore records? [y/n] ");
scanf(" %c", &answer);
if(tolower(answer) != 'y') {
free(records);
fclose(fileWriter);
break;
}
}
} else
printf("Error opening file.");
return 0;
}

Open the file with additional a mode as in "append". I think there is no need for further explanation what that would be good for, right?
However, the actual problem seems that you overwrite the pointer to the previous record already in the input loop. How is that supposed to work? Just go through that loop and try to follow the instructions as the program would do.
Problem is you cannot do it like this. Use a linked list for the allocated blocks.

while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = calloc((size),sizeof(*records));
records = records_temp;
...
}
Did you perhaps mean this?
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = realloc(records, size * sizeof *records);
if (records_temp == NULL)
{
/* Handle allocation error */
}
records = records_temp;
/* ... */
}
Do not confuse calloc with realloc. Read the manuals if you need further clarification.
Don't forget to initialise records to NULL...
If your concern is optimisation, the most significant bottleneck here will be your file input/output. That's unavoidable, and aside from researching setvbuf there isn't much you can do about it. The next bottleneck will be underlying calls to kernel allocation functions. You can reduce that bottleneck by calling allocation functions less. For example, you could grow your array by doubling its size each time rather than by adding 1:
size_t size = 0;
int answer;
do {
size_t index = size++;
if ((index & size) == 0) {
void *temp = realloc(array, (2 * index + 1) * sizeof *array);
if (temp == NULL) {
/* Handle allocation error */
}
array = temp;
}
puts("First Name:");
scanf("%s", array[index].fname);
/* snip */
answer = getchar();
} while (answer != EOF && tolower(answer) == 'y');
Alternatively, you could reinvent the wheel and perform the same work that realloc performs behind the scenes (and possibly lose some benefit of optimisation), by adding calls to memcpy and free to your code like so:
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = calloc((size),sizeof(*records));
if (records_temp == NULL)
{
/* Handle allocation error */
}
if (records != NULL)
{
memcpy(records_temp, records, (size - 1) * sizeof *records);
free(records);
}
records = records_temp;
/* ... */
}
P.S. In case you missed it the first time: Don't forget to initialise records to NULL...

The biggest problem I see is memory allocation logic. The first time through the loop, you allocate memory for 1 record and increment size. The second time through the loop, you allocate memory for 2 more records, because size == 2. The third time through the loop, you allocate memory for 3 more records, for a total of 1+2+3=6.
That answers why it's happening. Olaf's suggested fix, linked list, is a good one.

Related

Need help in C programming linked list

I'm creating a program using linked list which will later store in a text file. Basically this program will create an inventory, later update it as well. So I've successfully created the inventory (a function) and I've problem with updating it.
The inventory function is asking user to key in inputs, save into a text file and display it. The updating function has to select a particular item first and after that choosing whether to add or subtract the amount and the text file has to be updated at the same time.
So my problem is, I'm not able to compare the string input from the inventory function with this update function string input. Also, I need to receive an amount of item from user and then add the amount to the existing amount which I had entered in the inventory function earlier.
Visual Studio shows me that String 'code' might not be zero-terminated and String 'update' might not be zero-terminated for the update function.
I'm a beginner so I really need help from all of you, thank you!
This is the linked list codes:
struct donations
{
char supplyName[100], supplyCode[20], donator[150];
int no_ofShipment;
float quantityReceived;
struct donations* ptr;
} *start, *curr, *temp;
This is the inventory function (not a main function and will be changed later its a function for another main menu as the update function)
void main()
{
void details();
int i;
FILE *fp;
start = curr = NULL;
curr = start;
if (start == NULL)
{
start = curr = (struct donations *)malloc(sizeof(struct donations));
for (i = 0; i < 5; i++)
{
curr = (struct donations *)malloc(sizeof(struct donations));
details();
if (i == 0)
start = temp = curr;
else
{
temp->ptr = curr;
temp = curr;
}
}
temp->ptr = NULL;
temp = start;
printf("\n\t Inventory Created Successfully!");
printf("\n\t Recorded to the end of the list!");
}
//create file & print output into the file
if (fopen_s(&fp, "Donation.txt", "w") != 0)
{
printf("\nError");
return;
}
if (!fp)
{
printf("\n Error in opening file!");
//_getch();
return;
}
curr = start;
while (curr)
{
fprintf(fp, "%-30s", curr->supplyName);
fprintf(fp, "%-30s", curr->supplyCode);
fprintf(fp, "%-30s", curr->donator);
fprintf(fp, "%10d", curr->no_ofShipment);
fprintf(fp, "\t%.1f\n", curr->quantityReceived);
curr = curr->ptr;
}
printf("\n\n\t File (records) has been created!");
fclose(fp);
// display output
printf("\n\n\n\tDisplay Inventory");
while (temp)
{
printf("\n\n\n\t Name of Supply: %-30s", temp->supplyName);
printf("Supply Code: %-30s", temp->supplyCode);
printf("Donator: %-30s", temp->donator);
printf("No. of Shipment: %10d", temp->no_ofShipment);
printf("\tQuantity Received: %.1f", temp->quantityReceived);
temp = temp->ptr;
}
}
void details()
{
int i = 0;
system("cls");
printf("\tInventory Creation\t");
printf("\n\n\tEnter Name of Supply: ",(i+1));
gets_s(curr->supplyName);
while (getchar() != '\n');
printf("\n\tEnter Supply Code: ", (i + 1));
gets_s(curr->supplyCode);
while (getchar() != '\n');
printf("\n\tEnter Donator: ", (i + 1));
gets_s(curr->donator);
while (getchar() != '\n');
printf("\n\tEnter No. of Shipment: ", (i + 1));
scanf_s("%d", &curr->no_ofShipment);
while (getchar() != '\n');
printf("\n\tEnter Quantity Received: ", (i + 1));
scanf_s("%f", &curr->quantityReceived);
while (getchar() != '\n');
}
this is my update function, but not finished yet.
void update_DonationQuantity()
{
char code[10];
char update[5];
float quantity, Quantity;
FILE *fp;
printf("\n\tUpdate Donation Quantity\t\n");
printf("\n\n\tSelect donation (CT/HS/FM/SM/OM): ");
gets_s(code);
if (strcmp(code, curr->supplyCode) == 0)
{
printf("\n\tReceived/Distibuted item (+/-): ");
gets_s(update);
while (strcmp(update, "+") && (update, "-") != 0)
{
printf("\n\tWrong input, Try Again");
printf("\n\tReceived/Distibuted item (+/-): ");
gets_s(update);
}
if (strcmp(update, "+") == 0)
{
printf("\n\t Enter received amount: ");
scanf_s("%f", &quantity);
if (fopen_s(&fp, "Donation.txt", "a") != 0)
{
printf("\nError");
return;
}
if (!fp)
{
printf("\n Error in opening file!");
//_getch();
return;
}
curr->quantityReceived = quantity + curr->quantityReceived;
fprintf(fp, "\t%.1f\n", curr->quantityReceived);
}
}
}
There are multiple problems in your code:
struct donations *ptr; is confusing: linked lists usually have a next member to chain the subsequent list element.
void main() is incorrect: the return type of main is int.
void details(); defining a function in a local scope, while allowed is very bad style. You should move this declaration before the start of the main function.
if (start == NULL) is always true.
start = curr = (struct donations *)malloc(sizeof(struct donations)); This line is useless and causes a memory leak: both curr and start are overwritten in the for loop just below.
if (!fp) is redundant: if fopen_s failed, it would have returned 0, otherwise fp must have been set to a valid pointer. Using fopen instead seems simpler and more portable.
printf("\n\n\tEnter Name of Supply: ",(i+1)); the (i+1) argument is useless. The same problem appears in the next printf statements.
gets_s(curr->supplyName); should have a size argument. You might be compiling as C++ for the compiler to accept this. Microsoft has a non-standard template with the same name as the C so call safe function gets_s they forcefully introduced into the C Standard. This is utmostly confusing. You should use fgets(), scanf() or a custom made function to read a string into the destination array, consuming the excess characters and the newline if present. gets_s does not do that, it behavior if the line entered is too long is obscure... The return value should be checked for errors.
while (getchar() != '\n'); is risky: if the stream reaches the end of file before reading a newline, this loop will get stuck in an infinite loop. Furthermore gets_s() should have read the newline, so this loop is only useful for the %d and %f conversions. For this case you could use a flush_stdin() function written this way:
int flush_stdin(void) {
int c;
while ((c = getchar()) != EOF && c != '\n')
continue;
return c;
}

realloc: Program received signal SIGTRAP, Trace/breakpoint trap

I am working on a program that stores an arbitrarily long list of contacts (names and phone numbers). The entries are stored in an array that is resized each time a new element is added with the realloc function. The first contact can be added and displayed normally. However, when I try to add the second contact, the program crashes with the following message:
Program received signal SIGTRAP, Trace/breakpoint trap.
In ntdll!RtlZeroHeap () (C:\WINDOWS\SYSTEM32\ntdll.dll)
No breakpoints have been set in my editor. The debugger says the problem is on the line containing the realloc statement. How can I fix this issue? I have included the relevant sections of code below.
#define STRING_LENGTH 32
typedef struct entry_t {
char name[STRING_LENGTH];
char number[STRING_LENGTH];
} Entry;
void *add_entry(Entry*, int);
int main()
{
int entry_count = 0;
Entry *entries = calloc(1, sizeof(Entry));
int choice = 0;
while (1) {
printf("Options:\n1) Add Entry\n2) Modify Entry\n3) Print Entries\n4) Exit\n\nSelect an option: ");
scanf(" %d", &choice);
switch (choice) {
case 1:
entries = add_entry(entries, entry_count++);
break;
// ...
}
}
}
void *add_entry(Entry *entries, int current_count)
{
Entry entry;
printf("Enter name: ");
scanf(" %[^\n]s", entry.name);
printf("Enter number: ");
scanf(" %[^\n]s", entry.number);
printf("\n");
entries[current_count] = entry;
return realloc(entries, sizeof(Entry) * (current_count + 1));
}
There is a logic problem here. You initially allocate space for one object with
calloc then call add_entry with the count equals to 0.
Then you add the new entry at index current_count which is 0 at this point.
Then you resize the memory with current_count + 1, which is also 1. So you are
not resizing the memory at all.
In the next iteration, entry_count is 1 and you add a new element at
entries[1]. And that's the problem, you are accessing the memory out of
bounds, because you still have space for only one object at this time.
Instead of reallocating by current_count + 1, you should reallocate by
current_count + 2, so that the next iteration has space to put the new
elements at the end of the memory.
void *add_entry(Entry *entries, int current_count)
{
Entry entry;
printf("Enter name: ");
scanf(" %[^\n]s", entry.name);
printf("Enter number: ");
scanf(" %[^\n]s", entry.number);
printf("\n");
entries[current_count] = entry;
return realloc(entries, sizeof(Entry) * (current_count + 2)); // <-- +2
}
Note that your current_count variable is always one step behind the real size of the allocation, that's why you need the +2
edit
Note also that the more natural way would be to resize first, and then insert
the new object. So I would initialize the memory with NULL and do it like
this:
int main()
{
size_t entry_count = 0;
Entry *entries = NULL, *tmp;
int choice = 0;
while (1) {
printf("Options:\n1) Add Entry\n2) Modify Entry\n3) Print Entries\n4) Exit\n\nSelect an option: ");
scanf(" %d", &choice);
switch (choice) {
case 1:
tmp = add_entry(entries, &entry_count);
if(tmp == NULL)
{
// error handling
// entries still point to the old memory
// could be useful in error handling
free(entries);
return 1;
}
entries = tmp;
break;
// ...
}
}
}
void *add_entry(Entry *entries, size_t *current_count)
{
if(current_count == NULL)
return NULL;
Entry entry;
printf("Enter name: ");
scanf(" %[^\n]s", entry.name);
printf("Enter number: ");
scanf(" %[^\n]s", entry.number);
printf("\n");
if(entries == NULL)
*current_count = 0;
Entry *tmp = realloc(entries, (*current_count + 1) * sizeof *entries);
if(tmp == NULL)
return NULL;
entries = tmp;
entries[(*current_count)++] = entry;
return entries;
}
Note here that the realloc and the increment of the counting variable
happens in the same function. Only when everything goes OK, you should increase
the counter. Also note that entries is initialized with NULL, because
realloc(NULL, size) is equivalent to malloc(size).

C string input overflows other string input

I'm doing a simple console type command system, and inputting a command will scanf an integer and then will scanf a string, but the contents of the second string overflows the original string
while (exit == 0) {
scanf("%s", input);
if (strcmp(input, "parent") == 0) {
free(input);
ptemp = malloc(sizeof(node_p));
printf("Id: ");
scanf("%d", &ptemp->itemid);
printf("\nElement:");
scanf("%s", ptemp->element);
add_parent_node(parent, ptemp->itemid, ptemp->element);
free(ptemp);
}
}
ptemp is a pointer to a struct containing:
int itemid;
char *element;
I've tried using arrays with predefined size, but nothing seems to work...
Someone that made a comment about nothing overflowing is correct. What you were missing were (in laymans terms) a reservation for characters. Declaring something as char* instead of char[xx] means that you're prepared to reference another part of memory that you're allowed to manipulate with your characters. To keep things simple, I rewritten your code so your program works. Keep in mind that this code relies on users entering strings that are less than 100 to 200 characters long. Feel free to increase the number in square brackets if you need more characters.
I also made an add_parent_node function to verify that the data processing works.
If you want to get a little paranoid and you feel your systems implementation of scanf is screwy, then you can place the following under the while statement:
memset(ptemp,0,sizeof(ptemp));
What that does is floods the entire struct with null characters. This means the value of itemid would be zero since zero is null, and element would be 200 null characters.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
int itemid;
char element[200]; //fixed array of chars to actually store a string
}mycollection;
void add_parent_node(char* parentnodename,int itemid,char* element){
printf("Added node as follows\n");
printf("Parent: %s\n",parentnodename);
printf("Item ID: %d\n",itemid);
printf("Element: %s\n\n",element);
}
int main(){
char input[100]; //limit command to 99 characters
mycollection ptemp[1];
while(1){ //while(1) = endless loop
printf("\nEnter command: ");
scanf("%s", input);
if (strcmp(input, "parent") == 0) {
printf("\nId: ");
scanf("%d", &ptemp->itemid);
printf("\nElement:");
scanf("%s", ptemp->element);
add_parent_node("im_the_parent", ptemp->itemid, ptemp->element);
}
if (strcmp(input, "exit") == 0) {
return 0; //return 0 = exit
}
}
}
If you don't want to change anything outside your while loop I think this is what you can do.
while (exit == 0) {
scanf("%s", input);
if (strcmp(input, "parent") == 0) {
if(0 == ptemp ){
/* Allocate memory only once and reuse it.*/
ptemp = malloc(sizeof(node_p));
/* Allocate memory for element. */
ptemp->element = malloc(sizeof(char) * 1024 /* Max string len + 1 */)
}
printf("Id: ");
scanf("%d", &ptemp->itemid);
printf("\nElement:");
scanf("%s", ptemp->element);
add_parent_node(parent, ptemp->itemid, ptemp->element);
}
else if (strcmp(input, "exit") == 0) {
if(0 != ptemp){
/* If memory is allocated for ptemp, free it. */
if(0 != ptemp->element){
free(ptemp->element);
ptemp->element = 0;
}
free(ptemp);
ptemp = 0;
}
free(input);
input = 0;
exit = 1;
break;
}
}

How do I add a contact to a phonebook program in C?

For my intro to programming class, we have to code a phonebook in C that lets users add contacts, as well as delete and display them. It also has to allocate and free memory as necessary (I tried to do this, but I honestly don't really know what I'm doing).
Anyway, I cannot figure out how to add a contact to the phonebook. I've pasted the relevant part of the program so far. It compiles, but it crashes every time I try to add a contact. Once I get this figured out, I think I can get the rest of the functions without too much trouble. If anyone could help me out, I'd really appreciate it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct entry {
char fname[20];
char lname[20];
char pnumber[20];
} entry;
// function prototypes
void addentry(int, entry*, char addfname[20], char addlname[20], char addpnumber[20]);
main() {
int selection = 0;
int inputtest = 1;
int pnum = 0; // keeps track of number of contacts
char addfname[20] = { '\0' };
char addlname[20] = { '\0' };
char addpnumber[20] = { '\0' };
entry *pcontacts;
pcontacts = (entry*)calloc(1, (sizeof(entry)));
if (pcontacts == NULL) {
printf("No memory is available.");
free(pcontacts);
return 0;
}
while (1) {
do {
printf("\nPhonebook Menu\n\n");
printf("1:\tAdd contact\n");
printf("2:\tDelete contact\n");
printf("3:\tDisplay contacts\n");
printf("4:\tExit\n");
printf("\nChoose an action (1-4): ");
scanf("%d", &selection);
if (selection < 1 || selection > 4) {
printf("Invalid input. Please enter an integer between 1 and 4.\n");
inputtest = 0;
}
if (selection == 4) {
free(pcontacts);
printf("\nThank you for using this phonebook.");
return 0;
}
switch (selection) {
case 1:
pnum++;
printf("\nEnter first name: ");
scanf("%s", addfname);
printf("Enter last name: ");
scanf("%s", addlname);
printf("Enter phone number (no spaces): ");
scanf("%s", addpnumber);
addentry(pnum, pcontacts, addfname[20], addlname[20], addpnumber[20]);
break;
}
} while (inputtest == 1);
}
}
void addentry(int pnum, entry *pcontacts, char addfname[20], char addlname[20], char pnumber[20]) {
pcontacts = (entry*)malloc(pnum * (sizeof(entry)));
if (pcontacts != NULL) {
strcpy(*pcontacts[pnum - 1].fname, addfname);
printf("\nContact has been added.");
} else {
printf ("No memory is available.\n");
}
}
You get strings from standard input with scanf, but you should tell scanf the maximum number of bytes to store to the destination arrays to avoid buffer overruns:
scanf("%19s", addfname);
...
scanf("%19s", addlname);
...
scanf("%19s", addpnumber);
The way you call addentry is incorrect:
addentry(pnum, pcontacts, addfname[20], addlname[20], addpnumber[20]);
You actually try to read the byte just after the end of addfname, addlname and addpnumber. You should instead pass the arrays themselves, that will be passed to the function addentry as pointers to their first bytes:
addentry(pnum, pcontacts, addfname, addlname, addpnumber);
addentry should reallocate the array with realloc. It should be passed a pointer to the array pointer to it can update the pointer in main.
addentry does not copy the strings correctly: it only copies one, but with a syntax error.
Here is a corrected version:
void addentry(int, entry**, char addfname[20], char addlname[20], char addpnumber[20]);
int main(void) {
int selection = 0;
int inputtest = 1;
int pnum = 0; // keeps track of number of contacts
char addfname[20];
char addlname[20];
char addpnumber[20];
entry *pcontacts = NULL;
for (;;) {
do {
printf("\nPhonebook Menu\n\n");
printf("1:\tAdd contact\n");
printf("2:\tDelete contact\n");
printf("3:\tDisplay contacts\n");
printf("4:\tExit\n");
printf("\nChoose an action (1-4): ");
scanf("%d", &selection);
if (selection < 1 || selection > 4) {
printf("Invalid input. Please enter an integer between 1 and 4.\n");
inputtest = 0;
}
if (selection == 4) {
free(pcontacts); /* OK for NULL */
printf("\nThank you for using this phonebook.");
return 0;
}
switch (selection) {
case 1:
printf("\nEnter first name: ");
scanf("%19s", addfname);
printf("Enter last name: ");
scanf("%19s", addlname);
printf("Enter phone number (no spaces): ");
scanf("%19s", addpnumber);
addentry(pnum, &pcontacts, addfname, addlname, addpnumber);
pnum++;
break;
}
} while (inputtest == 1);
}
}
/* add an entry at position pnum */
void addentry(int pnum, entry **pp, char addfname[20], char addlname[20], char pnumber[20]) {
entry *pcontact = *pp;
pcontacts = realloc(pcontacts, (pnum + 1) * sizeof(entry));
if (pcontacts != NULL) {
*pp = pcontacts; /* update pointer in main */
strcpy(pcontacts[pnum].fname, addfname);
strcpy(pcontacts[pnum].lname, addlname);
strcpy(pcontacts[pnum].pnumber, addpnumber);
printf("\nContact has been added.");
} else {
printf ("No memory is available.\n");
}
}

Segmentation fault when writing data to a dynamic array

My assignment is to write a file that displays an unknown number of records entered by the user. Each record has the following fields: First Name, Last Name, Address, City, State, Zip Code, and Phone Number.
I assumed the best way to do this would be to define a struct Record with the fields above, then declare an array of Records that would contain as many records as the user entered. To accomplish this I would use a loop to get the inputs for each field per record, then if the user wanted to continue dynamically allocate an extra space in the Record array and continue until the user enters no. I encountered an access violation writing location error at line:
scanf("%s", records[i]->fname);
What's wrong with my code?
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Record;
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
int zipcode;
int phoneNumber;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = 'y';
int size = 1;
int i = 0;
struct Record **records;
records = malloc(sizeof(*records)*(size));
while(answer == 'y' || answer == 'Y')
{
printf("First Name: \n");
scanf("%s", records[i]->fname);
printf("Last Name: \n");
scanf("%s", records[i]->lname);
printf("Address: \n");
scanf("%s", records[i]->address);
printf("City: \n");
scanf("%s", records[i]->city);
printf("State: \n");
scanf("%s", records[i]->state);
printf("Zipcode: \n");
scanf("%d", records[i]->zipcode);
printf("Phone Number: \n");
scanf("%d", records[i]->phoneNumber);
//stores all record info
printf("Are there anymore records? [y/n] ");
answer = getchar();
if(answer == 'y' || answer == 'Y')
{
size++;
records[i++];
printf("\n");
}
records = realloc(records,sizeof(*records)*(size));
}
//open file
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL)
{
if(fwrite(records,sizeof(*records),size,fileWriter) != 1)
{
fprintf(stderr, "Failed to write to %s\n", filename);
exit(1);
}
fclose(fileWriter);
}
else
{
printf("Error opening file.");
}
}
EDITED VERSION
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
struct Record
{
char fname[51];
char lname[51];
char address[51];
char city[51];
char state[51];
int zipcode;
int phoneNumber;
};
int main()
{
FILE *fileWriter;
const char filename[] = "data.txt";
char answer = 'y';
int size = 1;
int i = 0;
struct Record *records = NULL;
struct Record *records_temp;
while(answer == 'y' || answer == 'Y')
{
struct Record *records_temp = realloc(records,(size)*sizeof(*records));
if(records_temp == NULL)
{
free(records);
}
records = records_temp;
printf("First Name: \n");
scanf("%s", records[i].fname);
printf("Last Name: \n");
scanf("%s", records[i].lname);
printf("Address: \n");
scanf(" %[^\n]", records[i].address);
printf("City: \n");
scanf("%s", records[i].city);
printf("State: \n");
scanf("%s", records[i].state);
printf("Zipcode: \n");
scanf("%d", &records[i].zipcode);
printf("Phone Number: \n");
scanf("%d", &records[i].phoneNumber);
//stores all record info
printf("Are there anymore records? [y/n] ");
answer = getchar();
if(answer == 'y' || answer == 'Y')
{
size++;
records[i++];
printf("\n");
}
//open file
fileWriter = fopen(filename,"wb");
if(fileWriter != NULL)
{
if(fwrite(records,sizeof(*records),size,fileWriter) != 1)
{
fprintf(stderr, "Failed to write to %s\n", filename);
exit(1);
}
fclose(fileWriter);
}
else
{
printf("Error opening file.");
}
}
}
Well, you get a segfault because you haven't allocated memory for the first entity in your records.
So to resolve that you need to
records[size-1] = malloc(sizeof(Records));
To put it in this way:
You have records that is a pointer to a pointer to Records.
When you did
records = malloc(sizeof(*records)*(size));
You actually asked for size pointers to Records.
But that is not enough, you need to allocate another memory to store the actual Records so that is why we have to
records[size - 1] = malloc(sizeof(Records));
Note: if size > 1 then you should do:
int i = 0;
for(;i < size; i++) {
records[i] = malloc(sizeof(Records));
}
In addition to that, why did you go with Records **, as Arjun has already explained, you should use Records * and fix the part of realloc-ing new memory, because if realloc fails, it returns NULL and you end up with memory leak or another segfault in the worst scenario, either way -- it is not good for your program.
Please see Arjun's post
When you want to dynamically allocate space for a list of Records, you should be doing:
struct Record *records;
records = malloc(size * sizeof(*records));
This allocates space for size number of Records.
To increment the allocated size, you should:
struct Record *records_temp = realloc(records, newsize * sizeof(*records));
if (records_temp == NULL) {
free(records);
/* die with error -ENOMEM */
}
records = records_temp;
Do not realloc to the same pointer. It can cause you to leak memory on failure.
Or, you can avoid malloc() and use just realloc() in a loop by providing it with a NULL pointer initially.
C 89 standards says:
4.10.3.4 The realloc function
If ptr is a null pointer, the realloc function behaves like the malloc
function for the specified size.
struct Record *records = NULL;
struct Record *records_temp;
size = INITIAL_SIZE;
while (/* your condition */) {
records_temp = realloc(records, size * sizeof(*records));
if (records_temp == NULL) {
free(records);
/* die with error -ENOMEM */
}
records = records_temp;
/* do stuff */
size += SIZE_INCREMENT;
}
As Jonathan Leffler commented, but declined to make an answer out of his comments:
Note that the line records[i++]; increments i and does nothing else useful.
And also:
Also note that the struct Record; line really isn't necessary. The only time it might make a difference is if you are defining mutually recursive structures in a function scope rather than at file scope (and this use is at file scope). As it is, the line says "there is a type struct Record", and the next block of code says "there is a type struct Record and this is how it is defined".
When asked by Cool Guy to illustrate what was meant by that, Jonathan said:
struct A { … };
struct B { … };
void f(void)
{
struct A;
struct B
{
…;
struct A *a_ref;
…
};
struct A
{
…;
struct B *b_ref;
…
};
…
}
Without the struct A; line, the a_ref element would point at a structure of the externally defined type struct A, not the mutually recursive pair of structure types. The error messages could be quite confusing too! However, reusing type names like this is a bad idea.

Resources