realloc: Program received signal SIGTRAP, Trace/breakpoint trap - c

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).

Related

How to add strings and integers to an array then print to a text file

Goodday, when I get to entering the first name of the student, the program immediately crashes. Also I'm not exactly sure how to add the names and the ID into an array to print to the text file. May I have some assistance please?
struct records{
int id;
char fname[15];
char lname[15];
};
struct records student;
int max=1000;
int i;
srand( time(NULL) ); //random numbers generated
ATND= fopen("Student Record.txt","a");
if(ATND== NULL){
printf("ERROR!");
exit(1);
}
for(i=0; i<100; i++){
printf("Enter student\'s first name: ");
scanf("%s", student.fname[i]);
printf("\n\n");
printf("Enter student\'s last name: ");
scanf("%s", student.lname[i]);
/*randomnumber*/student.id[i]=rand() %max + 39048543;
fprintf(ATND,"%s %s %d", student.fname[i], student.lname[i], student.id[i]);
}
fclose(ATND);
Code only provides data space for 1 record whereas it appears to need 1000 records. Number of other issues. Suspect after 10 hours, OP has worked a number of them
//Definition - good
struct records {
int id;
char fname[15]; // IMO 15 is too limiting for first and last
char lname[15];
};
// Only 1 student, need many more
// struct records student;
#define STUDENT_N 1000
struct records student[STUDENT_N];
void read_records(void) {
// avoid magic numbers
// int max = 1000;
int max = STUDENT_N;
int i;
srand(time(NULL)); //random numbers generated
// ATND not declared
FILE *ATND;
ATND = fopen("Student Record.txt", "a");
if (ATND == NULL) {
printf("ERROR!");
exit(1);
}
char buf[100];
// avoid magic numbers
// for (i = 0; i < 100; i++) {
for (i = 0; i < STUDENT_N; i++) {
printf("Enter student\'s first name: ");
// don't use scanf()
// scanf("%s", student.fname[i]);
if (fgets(buf, sizeof buf, stdin) == NULL) break;
if (sscanf(buf, "%14s", student[i].fname) != 1) break;
printf("\n\n");
printf("Enter student\'s last name: ");
// Add flush to insure buffered prompts that do not end in \n are sent
fflush(stdout);
// scanf("%s", student.lname[i]);
if (fgets(buf, sizeof buf, stdin) == NULL) break;
if (sscanf(buf, "%14s", student[i].lname) != 1) break;
// /*randomnumber*/student.id[i] = rand() % max + 39048543;
/*randomnumber*/student[i].id = rand() % max + 39048543;
// Do not index the name, index the structure
// fprintf(ATND, "%s %s %d", student.fname[i], student.lname[i], student.id[i]);
fprintf(ATND, "%s %s %d", student[i].fname, student[i].lname, student[i].id);
}
fclose(ATND);
}
Assuming student.fname is a char array of sufficient size
scanf("%s", student.fname[i]);
should be
scanf("%s", student.fname);
You need to pass a pointer to the beginning of the array, NOT the value of the chars, one by one. scanf will enter the entire name in one call.

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");
}
}

Why is my file output overwritten?

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.

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.

Runtime Error with Printing Double Pointer Char Array

I am working on creating a system for entering student names and scores into arrays and printing the same information to the screen, unfortunately I keep getting the weird output.
I stepped through my program using a debugger and it showed that everything runs smooth until I get to the function that prints the student's information, there the double pointer char arrays have messed up values.
Here's an image of what I see when I run the program. (http://s28.postimg.org/nv29feawt/Error.png)
Note: Although I am aware that there are better and easier ways of doing this I am required to complete this assignment using dynamically allocated memory and arrays.
int main(void)
{
char **firstNames;
char **lastNames;
float *scores;
int recordsLength;
printf("Please indicate the number of records you want to enter: ");
scanf("%d", &recordsLength);
printf("\n\n");
firstNames = (char **)malloc(recordsLength * sizeof(char *));
lastNames = (char **)malloc(recordsLength * sizeof(char *));
scores = (float *)malloc(recordsLength * sizeof(float));
int i = 0;
while(i < recordsLength)
{
createNewEntry(i, firstNames, lastNames, scores);
i++;
}
printEntry(0, firstNames, lastNames, scores);
free(firstNames);
free(lastNames);
free(scores);
return 0;
}
void clearScreen()
{
#ifdef _WIN32
system("cls");
#elif _unix_
system("clear");
#endif
}
void printEntry(int entryID, char *firstNames[], char *lastNames[], float scores[])
{
clearScreen();
printf("|-------------------------------------------------------------------------|\n");
printf("| Student Entry |\n");
printf("|-------------------------------------------------------------------------|\n|\n");
printf("| First Name: %s Last Name: %s Score: %.1f\n|\n|\n|\n", firstNames[entryID], lastNames[entryID], scores[entryID]);
printf("|-------------------------------------------------------------------------|\n");
printf("| |\n");
printf("|-------------------------------------------------------------------------|\n\n");
}
void createNewEntry(int index, char *firstNames[], char *lastNames[], float scores[])
{
printf("Please input the records of the new student.\n\n\n");
char first[20];
char last[20];
float score = 100.0f;
printf("Please enter the student's first name: ");
scanf("%s", &first);
printf("\n\n");
printf("Please enter the student's last name: ");
scanf("%s", &last);
printf("\n\n");
printf("Please enter the student's score: ");
scanf("%f", &score);
printf("\n\n");
firstNames[index] = (char *)malloc((strlen(first)) * sizeof(char));
firstNames[index] = first;
lastNames[index] = (char *)malloc((strlen(last)) * sizeof(char));
lastNames[index] = last;
printf("first name: %s", firstNames[index]);
printf("last name: %s", lastNames[index]);
scores[index] = score;
}
firstNames[index] = (char *)malloc((strlen(first)) * sizeof(char));
firstNames[index] = first; /* You are missing your allocated memory block and assigning local */
Above lines are incorrect. You can not assign c-strings with assignation = operator. You should use strcpy for that.
You are assigning local array to firstnames, which has no life after the function ends. This invokes an undefined behavior. (You are seeing the strange characters, but it can be even worse).
Should be Re-written as (Similar for last name also)
firstNames[index] = malloc((strlen(first) + 1) * sizeof(char)); /* +1 for \0 */
if(firstNames[index] == NULL) {
/* Malloc failed, error handling */
...
}
/* else */
strcpy(firstNames[index], first); /* Use strcpy to copy contents */
Live example here
Before freeing firstNames and lastNames, you should free all the member of firstNames and lastNames in a loop.
I agree with the answer of Mohit Jain, adding to that you can even use sprintf.

Resources