learn c the hard way ex25 confusing about fgetc stdin - c

I am working on learn c the hard way ex25 by Zed A. Shaw
ex25
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "dbg.h"
#define MAX_DATA 100
int read_string(char **out_string, int max_buffer)
{
*out_string = calloc(1, max_buffer + 1);
check_mem(*out_string);
char *result = fgets(*out_string, max_buffer, stdin);
check(result != NULL, "Input error.");
return 0;
error:
if (*out_string) free(*out_string);
*out_string = NULL;
return -1;
}
int read_int(int *out_int)
{
char *input = NULL;
int rc = read_string(&input, MAX_DATA);
check(rc == 0, "Failed to read number.");
*out_int = atoi(input);
free(input);
return 0;
error:
if (input) free(input);
return -1;
}
int read_scan(const char *fmt, ...)
{
int i = 0;
int rc = 0;
int *out_int = NULL;
char *out_char = NULL;
char **out_string = NULL;
int max_buffer = 0;
va_list argp;
va_start(argp, fmt);
for (i = 0; fmt[i] != '\0'; i++) {
if (fmt[i] == '%') {
i++;
switch (fmt[i]) {
case '\0':
sentinel("Invalid format, you ended with %%.");
break;
case 'd':
out_int = va_arg(argp, int *);
rc = read_int(out_int);
check(rc == 0, "Failed to read int.");
break;
case 'c':
out_char = va_arg(argp, char *);
*out_char = fgetc(stdin);
break;
case 's':
max_buffer = va_arg(argp, int);
out_string = va_arg(argp, char **);
rc = read_string(out_string, max_buffer);
check(rc == 0, "Failed to read string.");
break;
default:
sentinel("Invalid format.");
}
} else {
fgetc(stdin);
}
check(!feof(stdin) && !ferror(stdin), "Input error.");
}
va_end(argp);
return 0;
error:
va_end(argp);
return -1;
}
int main(int argc, char *argv[])
{
char *first_name = NULL;
char initial = ' ';
char *last_name = NULL;
int age = 0;
printf("What's your first name? ");
int rc = read_scan("%s", MAX_DATA, &first_name);
check(rc == 0, "Failed first name.");
printf("What's your initial? ");
rc = read_scan("%c\n", &initial);
check(rc == 0, "Failed initial.");
printf("What's your last name? ");
rc = read_scan("%s", MAX_DATA, &last_name);
check(rc == 0, "Failed last name.");
printf("How old are you? ");
rc = read_scan("%d", &age);
printf("---- RESULTS ----\n");
printf("First Name: %s", first_name);
printf("Initial: '%c'\n", initial);
printf("Last Name: %s", last_name);
printf("Age: %d\n", age);
free(first_name);
free(last_name);
return 0;
error:
return -1;
}
I am confused about the first parameter in read_scan in line 109
The original code works fine. The output:
What's your first name? zed
What's your initial? A
What's your last name? shaw
How old are you? 18
---- RESULTS ----
First Name: zed
Initial: 'A'
Last Name: shaw
Age: 18
However, if I delete the '\n' in line 109 rc = read_scan("%c", &initial);, it will skip the next question and I cannot figure out.
What I think about the influence of '\n' is that the for loop will not go into line 83 fgetc(stdin);
The output will be:
What's your first name? zed
What's your initial? A
What's your last name? How old are you? shaw
---- RESULTS ----
First Name: zed
Initial: 'A'
Last Name:
Age: 0
Thanks for help!

The characters in the "%c\n" to read_scan() mean read and capture one character, and read and discard another character. You could have an X or # instead of the \n and it would work the same. When you delete the newline from the format, the newline after the character is left in the input. Then the next call to read_scan() with "%s" invokes fgets(), which reads up to the next newline, but the next newline is the already in the input stream, so it returns immediately.
Note that if you typed a word instead of an initial, or if you have no middle initial (me!), things go wrong in different ways.

The first parameter is the type and size of the format string which read_scan() needs to process
rc = read_scan("%c\n", &initial);
In this case, it's telling read_scan() to read (a) any character AND (b) a newline character. If you look at the flow in the function, it checks for "%", if found it increments the counter i and then checks the next character in the format string. For line 109, that character is c, so the fgetc() function is called. This reads a single character from the file descriptor stdin and places it into out_char. Then the loop continues, and since we're not reading a string, we go to the else case on line 82 - which reads another character (which is '\n' for newline). The important difference with the fgetc call on line 83 is that we throw away the result.

Related

How to stop reading a file once a certain string appears in c

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
}

strtok() then strcmp() returning false when true

I'm guessing this is a type issue, so maybe you can show me how it is correctly done.
I'm reading command inputs from stdin and I want to quit when the user enters q.
I'm reading a user input from stdin using fgets() into a pointer to a character array. Then I splice off the first word using strtok() and assign it to another pointer to a character array. Then I compare it to a q in order to see if the user wants to quit the program by using the strcmp() function.
Here is some code:
char *input = (char*)malloc(32 * sizeof(char));
char *command = (char*)malloc(8 * sizeof(char));
while (strcmp(command, "q") != 0)
{
memset(input, 0, sizeof(input));
printf("Enter command: ");
fgets(input, 64, stdin);
command = strtok(input, " ");
//if I enter q --> printf("%d", strcmp(command, "q")) == 10
//if I enter w --> printf("%d", strcmp(command, "q")) == 6
}
What I want is, if command == q then printf("%d", strcmp(command, "q")) should equal 0 else it should print any other integer.
I should also note that I have verified command is being correctly assigned. In other words, when I enter q, command == "q".
Maybe you can try this code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *input = (char*)malloc(32 * sizeof(char));
char *command = (char*)malloc(8 * sizeof(char));
while (strcmp(command, "q") != 0)
{
memset(input, 0, sizeof(input));
printf("Enter command: ");
fgets(input, 64, stdin);
command = strtok(input, " \n"); // Line corrected.
//if I enter q --> printf("%d", strcmp(command, "q")) == 10
//if I enter w --> printf("%d", strcmp(command, "q")) == 6
}
return 0;
}
Several issues here.
The memory allocated here
char *command = (char*)malloc(8 * sizeof(char));
leaks the moment this line
command = strtok(input, " ");
gets execute as the one and only reference to the memory allocated gets overwritten and therefore lost.
A possible buffer overflow can occur here
fgets(input, 64, stdin);
as allowing to read more bytes (64) ito input as it points to be the allocation done here
char *input = (char*)malloc(32 * sizeof(char));
Assuming the data input by the user does not contain a sequence like '[blanks]q ...thencommandget assignedNULL` by this call
command = strtok(input, " ");
which leads to passing NULL to strcmp() here on testing for next iteration here
while (strcmp(command, "q") != 0)
Doing so invoke undefined behaviour.
The code misses to check the outcome of relevant function calls, like malloc()`` andfgets()`.
Casting the result of malloc() & friends isn't needed in C nor is it recommended in way. So just do not do it. It might very well hide errors.
sizeof (char) is defined to be 1. No need to use it.
Do not spoil you code with "Magic Numbers"/"Tokens" like 32, 8, 64, "q" ...
Using while-loop conceptionally is the wrong approach if you want to perform a certain action at least once. In such cases use a do-while loop.
Fixing all this might lead to the following code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define INPUT_BUFFER_SIZE (64)
#define QUIT_STRING "q"
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
char *input = malloc(INPUT_BUFFER_SIZE * sizeof *input);
if (NULL == input)
{
perror("malloc () failed");
result = EXIT_FAILURE;
}
else
{
char *command;
do
{
printf("Enter command:\n");
if (NULL == fgets(input, INPUT_BUFFER_SIZE, stdin))
{
if (!feof(stdin))
{
result = EXIT_FAILURE;
fprintf(stderr, "fgets() failed.\n");
}
break;
}
command = strtok(input, " \n");
} while ((command == NULL) || strcmp(command, QUIT_STRING) != 0);
if (NULL != command)
{
printf("User quit.\n");
}
free(input);
}
return result;
}

User-Defined function for reading input not working

I've made a user-defined function for reading input and replacing newline character '\n' with '\0' so when I use printf statement for printing the string it won't add newline at the end.
char xgets(char *line, int size, FILE *stdn)
{
//READS THE LINE
fgets(line, size, stdn);
//REMOVES NEWLINE CHARACTER '\n' AND ADDS '\0'
line[strcspn(line, "\n")] = '\0';
return line;
}
When I call xgets inside main() function it works properly, but when it is called in other user-defined function it does not wait for user-input.
I'm using Visual Studio 2015 for debugging my code.
Here's my code:
#include<stdio.h>
#include<stdlib.h>
#include<process.h>
//USER-DEFINED FUNCTION
char xgets(char *line, int size, FILE *stdn);
void sortm_hgrade();
void sortm_rcharge();
void header(void);
void header(void)
{
printf("*-*-*-*-*HOTEL_INFO*-*-*-*-*");
printf("\n\n");
}
char xgets(char *line, int size, FILE *stdn)
{
//READS THE LINE
fgets(line, size, stdn);
//REMOVES NEWLINE CHARACTER '\n' AND ADDS '\0' END LINE CHARACTER
line[strcspn(line, "\n")] = '\0';
return line;
}
#define MAX 1000
//PROGRAMS STARTS HERE
int main(void)
{
//VARIABLE-DECLARATION
int i = 0, j = 0, n = 0;
char line[MAX] = { 0 };
char o = { 0 };
char h[10] = { 0 };
//FUCNTION CALL-OUT
header();
printf("Type anything : ");
xgets(h, sizeof(h), stdin);
printf("Enter one option from the following : \n\n");
printf("(a) To Print out Hotels of a given Grade in order of charges. \n");
printf("(b) To Print out Hotels with Room Charges less than a given Value. \n");
printf("Please type a proper option. \n");
while (n == 0){
scanf_s(" %c", &o);
switch (o){
case 'a':
sortm_hgrade();
n = 1;
break;
case 'b':
sortm_rcharge();
n = 1;
break;
default:
printf("Option INVALID \n");
printf("Please type a proper option \n");
n = 0;
break;
}
}
//TERMINAL-PAUSE
system("pause");
}
void sortm_hgrade()
{
//FOR SORTING BY GRADE
char g[10] = { 0 };
printf("Enter the Grade : ");
xgets(g, sizeof(g), stdin);
printf("\n");
}
void sortm_rcharge()
{
printf("----");
}
You should change
scanf(" %c", &o);
to
scanf("%c ", &o);
This force scanf to consume trailing chars, like '\n'
In your code '\n' of user input for scanf %c is not consumed and it is consumed by fgets in your xgets function that exit immediately with an empty buffer.
BTW that solution can wok only if a single char is input by user.
Best code would be
char c;
while (n == 0)
{
o = getchar();
while ((c = getchar()) != EOF && c != '\n') ;
EDIT
With the second solution code is waiting, and discarding, chars until a '\n' is triggered or end of file. In your specific case (using stdin as console) EOF is not mandatory. It will be mandatory in case of input is being read from a "real file".
You need to skip the \n character after you take in a character. you can command scanf for that. fgets reads that newline character up first and then hence it terminates. use this
scanf(" %c *[^\n]", &o);
This should do the trick

How do I get rid of this new line?

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

C struct field assignment overwrites another field

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.

Resources