I am writing a fantasy football draft program for fun.
I encountered a strange problem. I assign a value to a struct field and that happens, but it also assigns that value to another field in the struct. Apologies for the messy debugging printf statements.
I clearly don't understand something about struct field assignment.
code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TRUE 1
int QB_count = 0;
int RB_count = 0;
int WR_count = 0;
int TE_count = 0;
int DEF_count = 0;
struct Player {
char *name;
char *position;
int age;
int bye_week;
};
int get_name (struct Player *drafted) {
char name[20];
fputs("Enter Player Name: ", stdout);
fflush(stdout);
if (fgets(name, sizeof name, stdin) != NULL){
char *newline = strchr(name, '\n');
if (newline != NULL){
*newline = '\0';
}
drafted->name = name;
printf("You've drafted: %s\n", drafted->name);
}
return 0;
}
int get_position(struct Player *drafted){
char position[20];
int depth;
char *nametemp = drafted->name;
printf("nametemp: %s\n", nametemp);
fputs("Enter Player Position in 'QB/RB/WR/TE/DEF' format: ", stdout);
fflush(stdout);
if (fgets(position, sizeof position, stdin) != NULL){
char *newline = strchr(position, '\n');
if (newline != NULL){
*newline = '\0';
}
drafted->position = position;
if (strcmp(position, "QB") == 0){
QB_count++;
depth = QB_count;
} else if (strcmp(position, "RB") == 0){
RB_count++;
depth = RB_count;
} else if (strcmp(position, "WR") == 0){
WR_count++;
depth = WR_count;
} else if (strcmp(position, "TE") == 0){
TE_count++;
depth = TE_count;
} else if (strcmp(position, "DEF") == 0){
DEF_count++;
depth = DEF_count;
} else {
printf("Please re-enter position information using the format 'QB' or 'qb'\n");
get_position(drafted);
return 0;
}
drafted->name = nametemp;
printf("NAME: %s\n", drafted->name);
printf("You've drafted %s at: %s%d\n", drafted->name, drafted->position, depth);
}
return 0;
}
int get_age (struct Player *drafted){
return 0;
}
int get_bye_week (struct Player *drafted){
return 0;
}
int main (){
int stop = 0;
char text[20];
while (TRUE){
struct Player drafted;
printf("Welcome to the 2012 Draft Day Program\n");
get_name (&drafted);
printf("NAME_MAIN: %s\n", drafted.name);
get_position(&drafted);
printf("You've drafted %s at: %s\n", drafted.name, drafted.position);
get_age(&drafted);
get_bye_week(&drafted);
fputs("Would you like to draft another player?\n"
"Enter '1' for no, '0' for yes\n", stdout);
fflush(stdout);
if(fgets(text, sizeof text, stdin)){
int number;
if (sscanf(text, "%d", &number) == 1){
if (number == 1){
printf("Draft Ended!\n");
break;
}
}
}
}
return 0;
}
The resulting output is:
Welcome to the 2012 Draft Day Program
Enter Player Name: Aaron Rodgers
You've drafted: Aaron Rodgers
NAME_MAIN: Aaron Rodgers
nametemp: Aaron Rodgers
Enter Player Position in 'QB/RB/WR/TE/DEF' format: QB
NAME: QB
You've drafted QB at: QB1
You've drafted QB at: QB
Would you like to draft another player?
Enter '1' for no, '0' for yes
1
Draft Ended!
Why does drafted.name become "QB"?
In your get_name function you're assigning to the name field of your struct Player a stack variable name.
In this line:
drafted->name = name;
name is declared in the function and so it's scope is limited to that function. Once get_name returns, the variable goes out of scope, and attempts to use that memory invoke undefined behavior.
Instead of using the simple assignment, you need to allocate space for drafted->name using malloc, and use strncpy to make a copy of the name. If strdup is available, you can use that to allocate the space and do the copy in a single step. Alternately, you could allocate space for drafted->name before reading the name, and use it in place of the name variable.
As a final option, if you assume a maximum length for names - your current code allows names up to a string length of 19 - you can simply declare an array of that size for each struct Player:
struct Player
{
char name[NAME_MAXLEN];
You have an identical problem with your position field in the get_position function.
Related
I'm having trouble figuring out how to get my program to stop reading a file once the character string endOfFileMarker "***" is read given a file called "studentRecords.txt" with sample input as shown:
23456770,Mina,Porter,3,ENEE,114,CMSC,412,ENME,515
23456790,Alex,Simpson,1,CMSC,412
***
I'm reading the file using a while loop indicating that as long the file is not equal to the end of the file and if the first input from which I read is not equivalent to the endOfFileMarker. Right now the output doesn't stop reading at the endOfFileMarker and takes it as a new record in the structure with the given output of a display function (I realize the error with the 2nd record but that appears to be a problem with the display function and not the way I'm storing it):
23456770 Mina Porter 3 ENEE 114 CMSC 412 ENME 515
23456Alex Alex Simpson 1 CMSC 412
*** Alex Simpson 1 CMSC 412
I've tried using fgets earlier and creating an input buffer to read each line. But since there will be variable number of course names and course codes for each student, I found fscanf and using a while loop with control condition of !feof to work better. Kind of at a loss right now of how to stop storing into the structure once I hit the endOfFileMarker. If someone can please help me out with this, that would be very appreciated. My full code is written below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define filename "studentRecords.txt"
typedef struct courseInfo
{//structure defining courseInfo elements
int courseID;
char courseName[30];
}crsInfo;
typedef struct studentInfo
{//structure defining studentInfo elements
char studentID[9];
char firstName[20];
char lastName[25];
int coursesAttended;
crsInfo cInfo[10];
struct studentInfo * next;
}stdInfo;
stdInfo * firstStdNodePointer = NULL;
stdInfo * currentStdNodePointer = NULL;
void addStudentInfo(stdInfo newStd)
{
if (firstStdNodePointer == NULL) //Create the first course node
{
firstStdNodePointer = (stdInfo *) malloc(sizeof(stdInfo));
strcpy(firstStdNodePointer->studentID, newStd.studentID);
strcpy(firstStdNodePointer->firstName, newStd.firstName);
strcpy(firstStdNodePointer->lastName, newStd.lastName);
firstStdNodePointer->coursesAttended = newStd.coursesAttended;
for(int i = 0; i < newStd.coursesAttended; i++)
{
firstStdNodePointer->cInfo[i].courseID = newStd.cInfo[i].courseID;
strcpy(firstStdNodePointer->cInfo[i].courseName, newStd.cInfo[i].courseName);
}
firstStdNodePointer->next = NULL;
currentStdNodePointer = firstStdNodePointer;
}
else // add next course to the end of the course linked list.
{
// Go to the last Course in the list to get the course ID
stdInfo * newStdNodePointer = (stdInfo *) malloc(sizeof(stdInfo));
strcpy(newStdNodePointer->studentID, newStd.studentID);
strcpy(newStdNodePointer->firstName, newStd.firstName);
strcpy(newStdNodePointer->lastName, newStd.lastName);
newStdNodePointer->coursesAttended = newStd.coursesAttended;
for(int j = 0; j < newStd.coursesAttended; j++)
{
newStdNodePointer->cInfo[j].courseID = newStd.cInfo[j].courseID;
strcpy(newStdNodePointer->cInfo[j].courseName, newStd.cInfo[j].courseName);
}
newStdNodePointer->next = NULL;
currentStdNodePointer->next = newStdNodePointer; // Link previous node with newNode
currentStdNodePointer = currentStdNodePointer->next; // Make current node as previous node
}
}
void loadStudentInfo()
{
FILE * fptr = NULL;
fptr = fopen(filename, "r+");
const char endOfFileMarker[] = "***"; //marks the end of the student record list
if(fptr == NULL)
{
printf("File can not be opened\n");
}
stdInfo newStd;//defining a new struct studentInfo variable so I can pass to the addStudent function
//char line[100] = "";
//char * strPtr;
while (!feof(fptr) && strcmp(newStd.studentID, endOfFileMarker) != 0 )
{
fscanf(fptr, "%[^,],", newStd.studentID);
printf("%s\n", newStd.studentID);
fscanf(fptr, "%[^,],", newStd.firstName);
printf("%s\n", newStd.firstName);
fscanf(fptr, "%[^,],", newStd.lastName);
fscanf(fptr, "%i,", &newStd.coursesAttended);
for(int j = 0; j < newStd.coursesAttended; j++)
{//To read each courseName and ID, you need to go according to how many courses they entered
//because the amount of records in cInfo should correspond with how many pairs of courseName
//are entered into the file
fscanf(fptr, "%[^,],", newStd.cInfo[j].courseName);
fscanf(fptr, "%i,", &newStd.cInfo[j].courseID);
}
addStudentInfo(newStd);
}
fclose(fptr);
}
void displayCourseInfo()
{
printf("------------------------------------------------\n");
stdInfo * stdListPointer = firstStdNodePointer;
//start from the beginning
while(stdListPointer != NULL) {
printf("%s %s %s\t%i\t", stdListPointer->studentID, stdListPointer->firstName, stdListPointer->lastName, stdListPointer->coursesAttended);
for(int i = 0; i < stdListPointer->coursesAttended; i++)
{
printf(" %s %i ", stdListPointer->cInfo[i].courseName, stdListPointer->cInfo[i].courseID);
}
printf("\n");
stdListPointer = stdListPointer->next;
}
printf("------------------------------------------------\n");
}
void switchCaseMenu()
{
int selection;
int menuActive = 1;
while(menuActive)
{
printf("60-141 Bonus Assignment - Ben John\n");
printf("------------\n");
printf("1. Add a new student\n");
printf("2. Delete a student\n");
printf("3. Search for a student\n");
printf("4. Display current students\n");
printf("5. Save student information to file\n");
printf("6. Exit\n");
printf("Please enter a selection: ");
scanf("%i", &selection);
switch(selection)
{
case 1:
printf("~Selected - Add a new student~\n");
break;
case 2:
printf("~Selected - Delete a student~\n");
break;
case 3:
printf("~Selected - Search for s student~\n");
break;
case 4:
printf("~Selected - Display current students~\n");
displayCourseInfo();
break;
case 5:
printf("~Selected - Save student information to file~\n");
break;
case 6:
printf("~Selected - Exit~\n");
menuActive = 0;
break;
default:
printf("Invalid Input!\n");
}
}
printf("Goodbye!\n");
}
int main(void)
{
loadStudentInfo();
switchCaseMenu();
return 0;
}
I'll suggest that you read the file line by line using fgets and use sscanf to do the scanning. Then you can use strcmp to break the loop. Something like:
while(fgets(buffer, SIZE_OF_BUFFER, fileptr))
{
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') buffer[len - 1] = '\0'; // Strip \n if present
if (strcmp(buffer, "***") == 0) break; // Stop reading
// use sscanf on buffer to find the individual fields in the line
}
Note that fgets also stores the \n character (aka newline) into the buffer so before doing the string compare, the \n is stripped off (if present).
For your use case you don't really need to test whether the last character in the string is actually a \n. Just make the buffer sufficiently large and always strip off the last character. In this way the code can be simplified to:
while(fgets(buffer, SIZE_OF_BUFFER, fileptr))
{
size_t len = strlen(buffer);
if (len) buffer[len - 1] = '\0'; // Strip last character
if (strcmp(buffer, "***") == 0) break; // Stop reading
// use sscanf on buffer to find the individual fields in the line
}
or an even more compact way (thanks to #melpomene):
while(fgets(buffer, SIZE_OF_BUFFER, fileptr))
{
buffer[strcspn(buffer, "\n")] = '\0'; // Strip \n character if present
if (strcmp(buffer, "***") == 0) break; // Stop reading
// use sscanf on buffer to find the individual fields in the line
}
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;
}
}
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.
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.
I created a program that asks the user to input their name, and then manipulates it in multiple ways. The final way that it manipulates it is by printing the users name backwards. For instance if the user entered John Doe, the program would print Doe John. The only problem I'm having at this point is stopping my program from putting an unnecessary new line between the last and first name.
Example:
I want Doe John on one line but I get
Doe
John
For my assignment I need to get rid of this extra line. How do I do this?
#include <stdio.h>
#include <string.h>
void removeNewLine (char * userName, int charLenght)
{
int i=0;
do {
if (userName [i]=='\n')
{
userName [i]='\0';
}
i++;
} while (i<charLenght);
}
// This is going to tell me exactly how many real character are in my array
int myCounter (char * userName, int size)
{
int counter=0;
do
{
if(userName [counter]=='\0')
{
return counter; //I always thought that you needed to put your return at the end of the function, this is good to know that you don't need too
}
counter++;
}while (counter<size);
return -1;
}
int main ()
{
printf("Enter your first and last name\n");
char name [250]={'\0'};
char * space;
char *first=NULL, *last = NULL, *firstspace;
char *userName;
int numOfChars=0;
//Prevents the potential problem of an overflow = (sizeof(name)-1)
fgets(name,(sizeof(name)-1),stdin);
//This is what is actually doing the dirty work of removing the extra chars
removeNewLine(userName, numOfChars);
//This is going to count the number of characters that were input by the user
numOfChars = strlen(name)-1;
printf("You Entered: %s \n", name);
printf("There are %zu characters in your name including the space. \n", strlen(name));
char end;
int i;
end = strlen(name) -1;
printf("Your name backwards is");
for (i = end; i >= 0; --i)
{
printf("%c", name [i]);
}
printf("\nLooking for the space in your name \n", name);
firstspace=space=strchr(name, ' ');
*firstspace='\0';
while (space!=NULL)
{
printf("The space was found at character %d\n", space-name+1);
last = space+1;
space=strchr(space+1, ' ');
}
printf("%s%s", last, name);
*firstspace=' ';
//This is just to tell the user how many "real" characters were in there name
printf("\n There are %d actual characters in your name including the space", numOfChars);
}
Do little modification and Interchange these below two lines
removeNewLine(userName, numOfChars);
//This is going to count the number of characters that were input by the user
numOfChars = strlen(name)-1;
Like this
numOfChars = strlen(name); // first find the length of input.
removeNewLine(name, numOfChars); // And now remove newline at the end of input
EDIT
Your CODE
#include <stdio.h>
#include <string.h>
void removeNewLine (char * userName, int charLenght)
{
int i=0;
do {
if (userName [i]=='\n')
{
userName [i]='\0';
}
i++;
} while (i<charLenght);
}
int main ()
{
printf("Enter your first and last name\n");
char name [250]={'\0'};
char * space;
char *first=NULL, *last = NULL, *firstspace;
int numOfChars=0;
//Prevents the potential problem of an overflow = (sizeof(name)-1)
fgets(name,(sizeof(name)-1),stdin);
//This is what is actually doing the dirty work of removing the extra chars
numOfChars = strlen(name); // first find the length of input.
removeNewLine(name, numOfChars); // And now remove newline at the end of input
printf("You Entered: %s \n", name);
printf("There are %zu characters in your name including the space. \n", strlen(name));
char end;
int i;
end = strlen(name) -1;
printf("Your name backwards is");
for (i = end; i >= 0; --i)
{
printf("%c", name [i]);
}
printf("\nLooking for the space in your name \n", name);
firstspace=space=strchr(name, ' ');
*firstspace='\0';
while (space!=NULL)
{
printf("The space was found at character %ld\n", space-name+1);
last = space+1;
space=strchr(space+1, ' ');
}
printf("%s %s", last, name);
*firstspace=' ';
//This is just to tell the user how many "real" characters were in there name
printf("\n There are %d actual characters in your name including the space", numOfChars);
}
Output
Enter your first and last name
John Doe
You Entered: John Doe
There are 8 characters in your name including the space.
Your name backwards iseoD nhoJ
Looking for the space in your name
The space was found at character 5
Doe John
There are 9 actual characters in your name including the space
The best way is to use fgets() with a couple of helper functions:
/*Removes remaining characters from keyboard input buffer until next newline*/
/*Returns 0 if OK, a negative value if EOF.*/
int fpurge(FILE *f)
{
int c;
while((c=fgetc(f))!=EOF && c!='\n')
{ }
return (c==EOF ? -1 : 0);
}
/*Find and remove newline from string*/
/* Returns a nonzero value if found, zero if not. */
int truncate_newline(char *str)
{
int bRet=0;
if(str!=NULL)
{
char *pNewline = strchr(str, '\n');
if(pNewLine!=NULL)
{
bRet = 1;
*pNewLine = '\0';
}
}
return bRet;
}
/*Remove newline from string or excess characters from input buffer,
where appropriate.*/
/* Returns 0 if buffer is full, a positive value if line is complete,
a negative value if EOF (implies buffer full). */
int fclean(char *str, FILE *f)
{
int ret = 1;
if(!truncate_newline(str))
ret = fpurge(f);
return ret;
}
It's used this way:
char buf[42];
fgets(buf, sizeof buf, stdin);
fclean(buf);
Now you have a NULL-terminated, newlineless buf, and nothing in the input buffer to corrupt your next fgets call.
Like to offer an "after accepted" solution.
void *removeNewLineAfter_fgets(char *s) {
if (s) {
size_t l = strlen(s);
if ((l > 0) && (s[l-1] == '\n')) {
s[l-1] = '\0';
}
}
return s;
}
// Usage:
if (removeNewLineAfter_fgets(fgets(name,sizeof(name),stdin)) == NULL) { handle EOF }
BTW: OP does not need -1 in fgets(name,(sizeof(name)-1),stdin).