Sending "cls" to dos causes exception - c

Due to a problem posting, my last question (duplicate of this) was closed.
Background is provided at the end so you can get straight to the problem.
I have a text-based program to help learn vocabulary or anything else (basically simulates flash cards, but flashes up the ones you don't know more often). It seemed to work fine while I was testing it, until I got fed up of the constant backlog of text on the screen, so I implemented a (somewhat unportable) clear screen routine.
Then it started throwing up exceptions, and I put in all sorts of debugging code to try and track it down.
Well... I managed to narrow it down to the following command on line 445:
system("cls");
How can this command cause an exception? Does anyone know a workaround?
I've run this in command prompts on both Windows Vista and Windows 7 with the same result.
Complete source in case anyone wants to compile it themselves or take a look through:
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
#include <ctype.h>
#include <string.h>
#define DINPUTFILENAME "vtdb.~sv"
#define DOUTPUTFILENAME "vtdb.~sv"
#define MAXINTVALUE 2147483647
#define MAXTEXTLENGTH 256
#define N2LTONORM 5
#define NORMTON2L 3
#define NORMTOKNOWN 5
#define KNOWNTONORM 2
#define KNOWNTOOLD 2
#define OLDTONORM 1
struct vocab
{
int index; //identifies the entry in the list, allowing it to be selected by use of a random number
char * question;//pointer to question text
char * answer;//pointer to the answer text, which is required for the response to be considered correct
char * info;//pointer to optional extra text giving advice such as to how to format the response
char * hint;//pointer to optional text giving a clue to the answer
int right;//indicates whether counter is counting correct or incorrect responses
int counter;//counts how many times in a row the answer has been correct/incorrect
int known;//indicates to what level the vocab is known, and thus to which list it belongs (when loading/saving)
struct vocab * next;//pointer to next in list
};
struct listinfo//struct holds head, tail and the number of entries for the n2l, norm, known and old lists
{
struct vocab * head;
int entries;
struct vocab * tail;
};
struct listinfo n2l, norm, known, old;
int n2l_flag; //Prevents 'need to learn's coming up twice in a row
int maxtextlength = MAXTEXTLENGTH; //allows use of this #define within text strings
FILE * inputfile;
FILE * outputfile;
void getrecordsfromfile(char * inputfilename,char separator);//load
char * readtextfromfile(int maxchars,char separator);//get text field from file
int readnumberfromfile(int maxvalue,char separator);//get integer field from file
struct vocab * addtolist(struct vocab * newentry, struct listinfo * list);//add given (already filled in) vocab record to given list
int removefromlist(struct vocab * entry, struct listinfo * list,int freeup);//remove given entry from given list. Also destroy record if freeup is true
void reindex (struct listinfo * list);//necessary to stop gaps in the numbering system, which could cause random vocab selection to fail
int writeliststofile();//save
void testme();//main code for learning vocab, including options menu
char * gettextfromkeyboard(char * target,int maxchars);//set given string (char pointer) from keyboard, allocating memory if necessary
int getyesorno();//asks for yes or no, returns true (1) if yes
void testrandom();//code keeps causing exceptions, and as it's so random, I'm guessing it's to do with the random numbers
void getrecordsfromfile(char * inputfilename,char separator)
{
int counter = 0;
struct vocab * newvocab;
struct listinfo * newvocablist;
if (!(inputfile = fopen(inputfilename, "r")))
{
printf("Unable to read input file. File does not exist or is in use.\n");
}
else
{
printf("Opened input file %s, reading contents...\n",inputfilename);
while (!feof(inputfile))
{
newvocab = (struct vocab *)malloc(sizeof(struct vocab));
if (!newvocab)
{
printf("Memory allocation failed!\n");
return;
}
else
{
newvocab->question=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->answer=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->info=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->hint=readtextfromfile(MAXTEXTLENGTH,separator);
newvocab->right=readnumberfromfile(1,separator);
newvocab->counter=readnumberfromfile(0,separator);
newvocab->known=readnumberfromfile(3,separator);
switch (newvocab->known)
{
case 0: newvocablist = &n2l;break;
case 1: newvocablist = &norm;break;
case 2: newvocablist = &known;break;
case 3: newvocablist = &old;break;
}
addtolist(newvocab,newvocablist);
if (newvocab->question==NULL||newvocab->answer==NULL)
{
printf("Removing empty vocab record created from faulty input file...\n");
removefromlist(newvocab,newvocablist,1);
}
else counter++;
}
}
fclose(inputfile);
printf("...finished.\n%i entries read from %s.\n\n",counter,inputfilename);
}
return;
}
char * readtextfromfile(int maxchars,char separator)
{
int i=0;
char ch;
char * target = (char *)malloc(maxchars+1); //allocate memory for new string
if (!target) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
ch=getc(inputfile);
if (ch==separator){free(target);return NULL;}//if field is blank (zero-length), return null pointer
while (isspace(ch))
{
ch = getc(inputfile);//cycle forward until you reach text
if (ch == separator||ch=='\n'||ch==EOF) {free(target);return NULL;}//if no text found(reached ~ before anything else), return null pointer
}
if (ch=='"') //Entry is in quotes (generated by excel when exporting to .csv and field contains a comma)
{
ch=getc(inputfile);//move to next character after the quotes
while (i<maxchars && ch!='"' && ch!='\n')//stop when you reach the end quotes, end of line, or when text too long
{
target[i++]=ch;
ch = getc(inputfile); //copy name from file to target, one char at a time
}
}
else //entry is not in quotes, so char is currently first letter of string
{
while (i<maxchars && ch!=separator && ch!='\n')//stop when you reach separator, end of line, or when text too long
{
target[i++]=ch;
ch = getc(inputfile); //copy name from file to target, one char at a time
}
}
target[i] = '\0';//terminate string
return target;
}
int readnumberfromfile (int maxvalue,char separator)
{
int number, i=0;
char ch;
char * buff = (char *)malloc(11);
if (!buff) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
if (!maxvalue) maxvalue=MAXINTVALUE;
ch=getc(inputfile);
while (!isdigit(ch))
{
ch = getc(inputfile);//cycle forward until you reach a digit
if (ch == separator||ch=='\n'||ch==EOF) {printf("Format error in file\n");return 0;}//if no number found(reached ~ before digit), print error and return 0
}
while (i<11 && ch!=separator && ch!='\n')//stop when you reach '~', end of line, or when number too long
{
buff[i++]=ch;
ch = getc(inputfile); //copy number from file to buff, one char at a time
}
buff[i] = '\0';//terminate string
number = atoi(buff)<=maxvalue ? atoi(buff) : maxvalue;//convert string to number and make sure it's in range
free(buff);
return number;
}
struct vocab * addtolist(struct vocab * newentry, struct listinfo * list)
{
if (!list->head)//if head is null, there is no list, so create one
{
list->head = list->tail = newentry;//this is the new head and tail
list->entries = newentry->index = 1;
newentry->next = NULL;// FISH! not sure if this is necessary, but just be sure...
}
else//just appending to the list
{
list->tail->next = newentry;//adjust current tail to point to new entry
list->tail = newentry;//make the new entry the new tail
newentry->index=++list->entries;
newentry->next = NULL;
}
return newentry;
}
int removefromlist(struct vocab * entry, struct listinfo * list,int freeup)
{
struct vocab * prev;
if (list->head == entry) //if entry being deleted is first in the list
{
if (list->tail == entry) //if entry is only item in the list
{
list->head = list->tail = NULL;
}
else //if first in list, but not last
{
list->head = entry->next;
}
}
else //entry is not first in list, so set prev to point to previous entry
{
prev = list->head;
while (prev->next!=entry)
{
prev=prev->next;
if (!prev)
{
printf("Trying to delete an entry from a list it's not in!!\n");
return 0;
}
}
if (list->tail == entry)//if entry is at the end of the list
{
list->tail = prev;
list->tail->next = NULL;
}
else //if entry is somewhere in middle of list
{
prev->next=entry->next;
}
}//this entry is now not pointed to in any list
list->entries--;
/*following line removed because it could theoretically break a list if the entry was removed from a list after it had been added to another
entry->next = NULL;//and doesn't point to anything either*/
reindex(list);
if (freeup) //if freeup is set, this also wipes the record and frees up the memory associated with it
{
free(entry->question);
free(entry->answer);
free(entry->info);
free(entry->hint);
free(entry);
}
return 1;
}
void reindex (struct listinfo * list)
{
int counter = 1;
struct vocab * workingentry = list->head;
while (workingentry)
{
workingentry->index = counter++;
workingentry=workingentry->next;
}
if (list->entries!=counter-1) printf("Reindexing Error!\n");
}
int writeliststofile()
{
int i,counter=0;
struct listinfo * list;
struct vocab * entry;
if (!(outputfile = fopen(DOUTPUTFILENAME, "w")))
{
printf ("Error accessing output file!\n");
return 0;
}
else
{
printf("Saving...\n");
for (i=0;i<=3;i++)
{
switch (i)
{
case 0: list = &n2l;break;
case 1: list = &norm;break;
case 2: list = &known;break;
case 3: list = &old;break;
default: printf("Loop Error!\n");break;
}
entry=list->head;
while (entry!=NULL)
{
if (counter) fprintf(outputfile,"\n");
fprintf(outputfile,"%s~%s~%s~%s~%i~%i~%i",entry->question,entry->answer,entry->info,entry->hint,entry->right,entry->counter,i);
entry=entry->next;
counter++;
}
}
fclose(outputfile);
printf("...finished. %i entries saved.\n",counter);
return 1;
}
}
void testme()
{
int list_selector, entry_selector, bringupmenu = 0, testagain=1;
char testmenuchoice = '\n';
char * youranswer = (char *)malloc(MAXTEXTLENGTH+1);
struct listinfo * currentlist;
struct vocab * currententry;
if (!youranswer) {printf("Memory allocation error!\n");return;}
while (testagain)
{
fprintf(stderr,"Start of 'testagain' loop\nClearing screen...\n");
system("cls");
//select a list at random, using the percentage probabilities in the if statements. FISH! Can this be done with a switch and ranges?
fprintf(stderr,"Assigning list selector to random value...");
list_selector = (((float)rand() / 32768) * 100)+1;
fprintf(stderr,"assigned list selector value %i\nAssigning list pointer...",list_selector);
if (list_selector<33) currentlist = &n2l;
if (list_selector>32&&list_selector<95) {n2l_flag=0;currentlist=&norm;} //use norm list and cancel n2l flag (not cancelled with other lists)
if (list_selector>94&&list_selector<100) currentlist = &known;
if (list_selector==100) currentlist = &old;
fprintf(stderr,"assigned list pointer %x\nModifying pointer...",currentlist);
//do a little control over random selection
if (currentlist==&n2l && n2l_flag) {currentlist=&norm; n2l_flag=0;} //if n2l list was used last time as well (flag is set), use entry from the norm list instead
if (currentlist==&n2l) n2l_flag = 1; //is using n2l this time, set flag so it won't be used next time as well
if (currentlist->entries==0) currentlist = &norm;//if current list is empty, default to normal list
if (currentlist->entries==0 && !n2l_flag) currentlist = &n2l;//if normal list is empty, try n2l list if it wasn't used last time
if (currentlist->entries==0 && list_selector%10==5) currentlist = &old;//if list is still empty, in 10% of cases try old list
if (currentlist->entries==0) currentlist = &known;//in the other 90% of cases, or if old is empty, use the known list
if (currentlist->entries==0) currentlist = &old;//if known list is empty, try the old list
if (currentlist->entries==0) {currentlist = &n2l;n2l_flag=1;}//if old list is empty, use n2l list EVEN if it was used last time
if (currentlist->entries==0) {printf("No entries in list!");return;} //if list is STILL empty, abort
fprintf(stderr,"modified list pointer\nAssigning entry selector...");
//we now have the desired list of words with at least one entry, let's select an entry at random from this list
entry_selector = (((float)rand() / 32767) * currentlist->entries)+1;
fprintf(stderr,"assigned entry selector value %i\nAssignig pointer...",entry_selector);
currententry = currentlist->head;
fprintf(stderr,"set entry pointer to head, going to loop to it...\n");
while (currententry->index!=entry_selector)
{
currententry = currententry->next;//move through list until index matches the random number
if (currententry==NULL) {printf("Indexing error!\nCurrent list selector: %i, entries: %i, entry selector: %i\n",list_selector,currentlist->entries,entry_selector);return;}//in case not found in list
}
fprintf(stderr,"Looped, testing.\n");
printf("Translate the following:\n\n\t%s\n\n",currententry->question);
if (!currententry->info) printf("There is no additional information for this entry.\n");
else printf("Useful Info: %s\n\n",currententry->info);
printf("Your Translation:\n\n\t");
gettextfromkeyboard(youranswer,MAXTEXTLENGTH);
if (!strcmp(youranswer,currententry->answer))//if you're right
{
printf("Yay!\n");
if(currententry->right) currententry->counter++;
else currententry->right = currententry->counter = 1;
if (currententry->counter>2) printf("You answered correctly the last %i times in a row!\n",currententry->counter);
//make comments based on how well it's known, and move to a higher list if appropriate
if (currentlist==&n2l && currententry->counter>=N2LTONORM)
{
removefromlist(currententry,currentlist,0);
printf("Looks like you know this one a little better now!\nIt will be brought up less frequently.\n");
currententry->counter = 0;
addtolist(currententry,&norm);
}
if (currentlist==&norm && currententry->counter>=NORMTOKNOWN)
{
removefromlist(currententry,currentlist,0);
printf("Looks like you know this one now!\nIt will be brought up much less frequently.\n");
currententry->counter = 0;
addtolist(currententry,&known);
}
if (currentlist==&known && currententry->counter>=KNOWNTOOLD)
{
removefromlist(currententry,currentlist,0);
printf("OK! So this one's well-learnt.\nIt probably won't be brought up much any more.\n");
currententry->counter = 0;
addtolist(currententry,&old);
}
}
else //if you're wrong
{
printf("\nSorry, the correct answer is:\n\n\t%s\n\n",currententry->answer);
if(!currententry->right) currententry->counter++;
else {currententry->right = 0; currententry->counter = 1;}
if (currententry->counter>1) printf("You've got this one wrong the last %i times.\n",currententry->counter);
if (currentlist==&norm && currententry->counter>=NORMTON2L)
{
removefromlist(currententry,currentlist,0);
printf("This one could do with some learning...\n");
currententry->counter = 0;
addtolist(currententry,&n2l);
}
if (currentlist==&known && currententry->counter>=KNOWNTONORM)
{
removefromlist(currententry,currentlist,0);
printf("OK, perhaps you don't know this one as well as you once did...\n");
currententry->counter = 0;
addtolist(currententry,&norm);
}
if (currentlist==&old && currententry->counter>=OLDTONORM)
{
removefromlist(currententry,currentlist,0);
printf("This old one caught you out, huh? It will be brought up a few more times to help you remember it.\n");
currententry->counter = 0;
addtolist(currententry,&norm);
}
}
fprintf(stderr,"Tested, options menu?\n");
printf("Type 'o' for options or strike enter for another question\n");
testmenuchoice = getchar();
fprintf(stderr,"Got choice\n");
if (tolower(testmenuchoice)=='o') bringupmenu = 1;
fprintf(stderr,"set menuflag\n");
if (testmenuchoice!='\n') while (getchar()!='\n')getchar();
fprintf(stderr,"cleared getchar\n");
while (bringupmenu)
{
system("cls");
printf("Current Entry:\n\nQuestion: %s\nAnswer: '%s'\n",currententry->question,currententry->answer);
if (currententry->info) printf("Info: %s\n",currententry->info); else printf("No info.\n");
if (currententry->hint) printf("Hint: %s\n\n",currententry->hint); else printf("No hint.\n\n");
printf("Options Menu:\n\nType q to modify the question phrase displayed for translation.\nType a to change the answer phrase you must provide.\nType i to add/modify additional info for this entry.\nType h to add/modify the hint for this entry.\nType p to mark this entry as high priority to learn.\nType d to delete this entry from the database.\nType x to end testing and return to the main menu.\n\n");
testmenuchoice=getchar();
while (getchar()!='\n') getchar();
switch (testmenuchoice)
{
case 'q': printf("Enter new question text for this entry (max %i chars):\n",maxtextlength);
currententry->question=gettextfromkeyboard(currententry->question,MAXTEXTLENGTH);
break;
case 'a': printf("Enter new answer text for this entry (max %i chars):\n",maxtextlength);
currententry->answer=gettextfromkeyboard(currententry->answer,MAXTEXTLENGTH);
break;
case 'i': printf("Enter new info for this entry (max %i chars):\n",maxtextlength);
currententry->info=gettextfromkeyboard(currententry->info,MAXTEXTLENGTH);
break;
case 'h': printf("Enter new hint for this entry (max %i chars):\n",maxtextlength);
currententry->hint=gettextfromkeyboard(currententry->hint,MAXTEXTLENGTH);
break;
case 'p': if(currentlist=&n2l)printf("Already marked as priority!\n");
else
{
removefromlist(currententry,currentlist,0);
currententry->counter = 0;
currentlist=&n2l;
addtolist(currententry,currentlist);
printf("Entry will be brought up more often\n");
}
break;
case 'd': printf("Are you sure you want to delete this entry?\nOnce you save, this will be permanent!(y/n)");
if (getyesorno()) {removefromlist(currententry,currentlist,1);printf("Entry deleted!\n");bringupmenu=0;}
else printf("Entry was NOT deleted.\n");
break;
case 'x': bringupmenu = testagain = 0;
break;
default: printf("Invalid choice.\n");
}
if (bringupmenu)
{
printf("Select again from the options menu? (y/n)");
bringupmenu = getyesorno();
}
if (!bringupmenu&&testagain)
{
printf("Continue testing? (y/n)");
testagain = getyesorno();
}
}
fprintf(stderr,"End of 'testagain' loop.\n Clearing Screen...");
system("cls");
}
free(youranswer);
// getchar();
return;
}
char * gettextfromkeyboard(char * target,int maxchars)
{
int i =0;
char ch;
if (!target)//if no memory already allocated (pointer is NULL), do it now
{
target=(char *)malloc(maxchars+1);
if (!target) {printf("Memory allocation failed!");return NULL;} //return null if failed
}
ch = getchar();
if (ch=='\n') {free(target);return NULL;}//if zero length, free mem and return null pointer
while (!isalnum(ch))//cycle forward past white space
{
ch=getchar();
if (ch=='\n') {free(target);return NULL;}//if all white space, free mem and return null pointer
}
while (ch!='\n' && i<maxchars)
{
target[i++]=ch;
ch=getchar();
}
target[i]='\0';
return target;
}
int getyesorno()
{
char yesorno = '\n';
while (toupper(yesorno)!='Y'&&toupper(yesorno)!='N')
{
yesorno=getchar();
if (toupper(yesorno)!='Y'&&toupper(yesorno)!='N') printf("Invalid choice. You must enter Y or N:\n");
}
while (getchar()!='\n') getchar();
if (toupper(yesorno)=='Y') return 1;
else return 0;
}
void testrandom()
{
return;
}
int main(int argc, char* argv[])
{
char * inputfilename = DINPUTFILENAME;
char * outputfilename = DOUTPUTFILENAME;
char separator = '~';
char menuchoice = '\0';
n2l.entries = norm.entries = known.entries = old.entries = 0;
srand((unsigned)time(NULL));
fprintf(stderr,"Start...\n");
printf("Loading...\nLoad default database? (y/n)");
if (!getyesorno())
{
printf("Default file type is .~sv. Import .csv file instead? (y/n)");
if (getyesorno())
{
separator = ',';
printf("Enter name of .csv file to import:\n");
}
else
{
printf("Enter name of .~sv file to load:\n");
}
inputfilename = gettextfromkeyboard(inputfilename,256);
}
getrecordsfromfile(inputfilename,separator);
while (menuchoice!='x')
{
printf("Welcome to the Vocab Test, version C!\n\nMain menu:\n\n\tt: Test Me!\n\ts: Save\n\tx: Exit\n\n");
menuchoice = getchar();
while (getchar()!='\n') getchar();
switch (tolower(menuchoice))
{
case 'x': break;
case 't': testme(); break;
case 's': writeliststofile();break;
case 'w': testrandom(); break;
default: printf("Invalid choice. Please try again.\n"); break;
}
system("cls");
}
system("cls");
printf("Bye for now!\n\nPress enter to exit.");
getchar();
fprintf(stderr,"Successfully closed\n");
return 0;
}
I tried adding the output of stderr on a typical run, but it makes the body too long. Also tried adding it as an answer, but:
Users with less than 100 reputation can't answer their own question for 8 hours after asking. You may self-answer in 7 hours. Until then please use comments, or edit your question instead.
Background: I made my first foray into programming earlier this year, and decided I wanted to start with C before moving on to C++, Java, and perhaps Python and C#.
To get me started in C, after the obligatory hello world, I wrote a small game (text based, also including the "cls" command), and then moved onto this little vocab tester, which was to help me learn Indonesian while I was away in Austria speaking German :-D. I eventually got exasperated at the cls crash and haven't programmed since. I really want to pick it back up, so I'm starting here with this question.

Have you tried printing '\f'? That's the "formfeed" character.
EDIT: I've had a closer look at your code, and there's some stuff going on that I don't like. :-)
For example, in gettextfromkeyboard, if you enter only whitespace, it'll free target, even if that was non-NULL on entry.
In this line:
inputfilename = gettextfromkeyboard(inputfilename,256);
it passes inputfilename, which points to a constant string, into gettextfromkeyboard. Trying to free that is a bad idea.
I also have my doubts about while (getchar()!='\n') getchar();.
Suppose the input is "ABC\n".
The condition will consume and return 'A', the body of the loop will consume 'B', the condition will consume and return 'C', and the body of the loop will consume '\n'.

Try one of the many curses, ncurses, etc. packages around. There should be one somewhere on the web for your version of C, if it is not too uncommon. It should handle all kinds of text screen functionality, including clearing the screen and it is pretty portable.

Possibly system('cls') has been deprecated? Here is another way of doing it.

The code runs fine for me and the CLS command works. Non-reproduceable crashes often indicate memory corruption. I'd say a good place to look first is your readtextfromfile function since it will overwrite the input buffer if a file contains 256 chars.

Related

String in structure gets deleted

I'm working on the last exercise of the "Think like a computer scientist, C version" book and I have some trouble with one particular point.
The exercise consists of making a small game, where the computer picks a random value between 0 and 20 and then asks me to guess the number.
After that, the computer counts the number of tries I made and, if I get a better score than the previous party, I need to store my name and the number of tries in a structure.
My problem is the following: When I restart the game, the string value, player_name, in the structure gets somehow deleted but player_score is still there.
First, I made a "call by value" function to create the structure and then a tried with a "call by reference" but getting the same results.
I think I tried everything I could with my actual knowledge for now; so, if someone could check my code and give me some tips about what's wrong I would much appreciate it!
//HEADERS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FALSE 0
#define TRUE 1
//TYPEDEF STRUCTS
typedef struct
{
int player_score;
char *player_name;
} HS_Player;
//FUNCTION PROTOTYPES
int Random_Value(void);
int Get_User_Choice(void);
int Check_Result(int computer, int my_choice);
int Try_Again(int game_result, int computer);
void Player_Infos(HS_Player *player_p, int score);
int Game_Restart(void);
//MAIN
int main(void)
{
int end_game;
int high_score_value = 100;
HS_Player player;
while (end_game != TRUE)
{
int computer_number = Random_Value();
printf("Guess the number between 0 et 20 chosen by the computer.\n");
int your_number = Get_User_Choice();
int result_game = Check_Result(computer_number, your_number);
int tries_to_win = Try_Again(result_game, computer_number);
printf("Number of tries: %i\n", tries_to_win);
if (tries_to_win < high_score_value)
{
Player_Infos(&player, tries_to_win );
high_score_value = player.player_score;
}
printf("Highest score: %i By: %s\n", player.player_score, player.player_name);
printf("\n");
end_game = Game_Restart();
}
return EXIT_SUCCESS;
}
//Random_Value FUNCTION
int Random_Value(void)
{
srand(time(NULL));
int x = rand();
int y = x % 20;
return y;
}
//Get_User_Choice FUNCTION
int Get_User_Choice(void)
{
int success, x;
char ch;
printf("Your Guess:\t");
success = scanf("%i", &x);
while (success != 1)
{
printf("Your input is not a number. Please try again:\t");
while ((ch = getchar()) != '\n' && ch != EOF);
success = scanf("%i", &x);
}
if (x < 0 || x > 20)
{
printf("Your input must be between 0 and 20. Please try again.\n");
Get_User_Choice();
}
return x;
}
//Check_Result FUNCTION
int Check_Result(int computer, int my_choice)
{
int check_result;
if (my_choice < computer)
{
printf("Computer number is larger!\n");
check_result = FALSE;
}
else if (my_choice > computer)
{
printf("Computer number is smaller!\n");
check_result = FALSE;
}
else if (my_choice == computer)
{
printf("It's a Match! You chose the same number than the computer.\n");
printf("\n");
check_result = TRUE;
}
return check_result;
}
//Try_Again FUNCTION
int Try_Again(int game_result, int computer)
{
int tries_befor_success = 1;
while (game_result != TRUE)
{
int your_number = Get_User_Choice();
game_result = Check_Result(computer, your_number);
tries_befor_success++;
}
return tries_befor_success;
}
//Player_Infos FUNCTION
void Player_Infos(HS_Player *player_p, int score)
{
char new_name[80];
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
scanf("%s", new_name);
printf("\n");
player_p->player_score = score;
player_p->player_name = new_name;
}
//Game_Restart FUNCTION
int Game_Restart(void)
{
int quit_value;
printf("Quit Game ?\n");
printf("Press 'y' to quit or any other keys to continue.\n");
fflush(stdin);
char quit_game = getchar();
printf("\n");
if (quit_game == 'y')
{
quit_value = TRUE;
}
else
{
quit_value = FALSE;
}
return quit_value;
}
The problem is that, in your Player_Infos function, you are assigning the address of a local array to the char* player_name pointer member of the passed structure. When that function ends, the local array it used will be deleted and the pointer in the structure will be invalid. (In the case of the player_score, you don't have that problem, because the given value is copied to the structure member.)
There are several ways around this; one would be to use the strdup() function to make a copy of the local char new_name[80]; array – but that is really overkill, and you would need to manage (i.e. free()) that allocated string whenever you make a modification.
A simpler way is to make the player_name member an actual array of char and then use strcpy() to copy the local array into that member.
Better, still, with the player_name member defined as char [80], you can read directly into that (in the function), and avoid the local array completely:
typedef struct
{
int player_score;
char player_name[80];
} HS_Player;
//...
void Player_Infos(HS_Player *player_p, int score)
{
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
// Read directly. Limit input to 79 chars (allowing room for null terminator).
scanf("%79s", player_p->player_name);
printf("\n");
player_p->player_score = score;
}
Also, just as a "style" tip, you may want to change the member names to just score and name, as the "player" part is implied by the structure type-name itself.
This issue you are having is that you are associating the player name pointer to a variable that goes out of scope when you leave the "player_Infos" function. What you probably would want to do is define the name as a character array in your structure and then use the "strcpy" call in your function instead. Following is a couple of code snippets illustrating that point.
//TYPEDEF STRUCTS
typedef struct
{
int player_score;
char player_name[80];
} HS_Player;
Then, in your function, use the "strcpy" call.
//Player_Infos FUNCTION
void Player_Infos(HS_Player *player_p, int score)
{
char new_name[80];
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
scanf("%s", new_name);
printf("\n");
player_p->player_score = score;
strcpy(player_p->player_name, new_name);
//player_p->player_name = new_name;
}
When I tested that out, I got a name to appear in the terminal output.
Computer number is smaller!
Your Guess: 4
It's a Match! You chose the same number than the computer.
Number of tries: 8
Highest score: 4 By: Craig
FYI, you will need to include the "string.h" file.
Give that a try.
Name Update
The reason your player.player_name is not getting updated is because you can't assign a string this way in C. When doing player_p->player_name = new_name; you're actually saving in player_p->player_name the memory address of new_name.
Instead, what you want to achieve, is to copy each character of new_name to player_p->player_name and in order to achieve this, you have to change the type of prlayer_name field from char* player_name to char player_name[80], then assign it using, for example, strcpy():
#include <string.h>
// [...]
//TYPEDEF STRUCTS
typedef struct
{
unsigned int player_score;
char player_name[80];
} HS_Player;
// [...]
//Player_Infos FUNCTION
void Player_Infos(HS_Player *player_p, int score)
{
char new_name[80];
printf("Congrats! Your made a new high score.\n");
printf("What's your name ?\t");
scanf("%s", new_name);
printf("\n");
player_p->player_score = score;
strcpy(player_p->player_name, new_name);
}
Data Persistence
To make data (players info) persistent over multiple runs, you have to save the content of the struct to a file.
Example
int Save_Score(char* filename, HS_Player* player)
{
FILE* file = fopen(filename, "w");
if (file == NULL)
{
fprintf(stderr, "\nAn error occurred while opening the file\n");
return -1;
}
if (fprintf(file, "%d %s", player->player_score, player->player_name) < 0)
return -1;
fclose(file);
return 0;
}
int Load_Score(char* filename, HS_Player* player)
{
FILE* file = fopen(filename, "r");
if (file == NULL)
{
fprintf(stderr, "\nAn error occurred while opening the file\n");
return -1;
}
if (fscanf(file, "%d %79s", &player->player_score, player->player_name) < 0)
return -1;
fclose(file);
return 0;
}

Searching for a string in a struct

The code below is a code that will track my product costs and remaining quantity.The problem I'm facing with is I can't search the code by
if(g[n].name == search[10])
The out put keep showing
"Item not found"
Im a beginner of c language and was hope to learn more. Please correct my code and send it here so that I can know why my code is wrong.
#include <stdio.h>
struct product
{
char name[10];
int quantity;
float costs;
};
void fn_search (struct product g[]);
int main ()
{
int n;
struct product g[4];
strcpy(g[0].name,"aa1");
g[0].quantity = 10;
g[0].costs = 1;
strcpy(g[1].name,"bb2");
g[1].quantity = 10;
g[1].costs = 2;
strcpy(g[2].name,"bb3");
g[2].quantity = 10;
g[2].costs = 3;
fn_search (g);
}
void fn_search (struct product g[10])
{
int n;
char search[10];
printf("Search>> ");
scanf("%s",&search[10]);
for (n=0;n<4;n++)
{
if(g[n].name == search[10])
{
printf ("\ncosts = NTD%.2f",g[n].costs);
printf ("\nquantity = %d\n",g[n].quantity);
}
else
{
printf("\nItem not found.");
break;
}
}
}
Two bugs:
Incorrect use of scanf :
scanf("%s",&search[10]); --> scanf("%9s", search);
Note: scanf("%9s", &search[0]); is also fine but the above is the common way.
Incorrect string compare :
if(g[n].name == search[10]) --> if(strcmp(g[n].name, search) == 0)
Also notice that you never initialized g[3] but fn_search checks it.
Then this part:
else
{
printf("\nItem not found.");
break;
}
means that you break the for loop as soon as an item doesn't match. In other words: Currently you only compare against g[0]
You don't want that! Check all items before printing "Item not found".
So the for loop should be more like:
for (n=0;n<4;n++)
{
if(strcmp(g[n].name, search) == 0)
{
printf ("\ncosts = NTD%.2f",g[n].costs);
printf ("\nquantity = %d\n",g[n].quantity);
return; // Exit function when match is found
}
}
// When execution arrives here, there was no matching element
printf("\nItem not found.");
Finally:
void fn_search (struct product g[10])
^^
why ??
Either do
void fn_search (struct product g[])
or
void fn_search (struct product *g)

Printing an array of structs in C

I'm trying to print an array of structs that contain two strings. However my print function does not print more than two indices of the array. I am not sure why because it seems to me that the logic is correct.
This is the main function
const int MAX_LENGTH = 1024;
typedef struct song
{
char songName[MAX_LENGTH];
char artist[MAX_LENGTH];
} Song;
void getStringFromUserInput(char s[], int maxStrLength);
void printMusicLibrary(Song library[], int librarySize);
void printMusicLibraryTitle(void);
void printMusicLibrary (Song library[], int librarySize);
void printMusicLibraryEmpty(void);
int main(void) {
// Announce the start of the program
printf("%s", "Personal Music Library.\n\n");
printf("%s", "Commands are I (insert), S (sort by artist),\n"
"P (print), Q (quit).\n");
char response;
char input[MAX_LENGTH + 1];
int index = 0;
do {
printf("\nCommand?: ");
getStringFromUserInput(input, MAX_LENGTH);
// Response is the first character entered by user.
// Convert to uppercase to simplify later comparisons.
response = toupper(input[0]);
const int MAX_LIBRARY_SIZE = 100;
Song Library[MAX_LIBRARY_SIZE];
if (response == 'I') {
printf("Song name: ");
getStringFromUserInput(Library[index].songName, MAX_LENGTH);
printf("Artist: ");
getStringFromUserInput(Library[index].artist, MAX_LENGTH);
index++;
}
else if (response == 'P') {
// Print the music library.
int firstIndex = 0;
if (Library[firstIndex].songName[firstIndex] == '\0') {
printMusicLibraryEmpty();
} else {
printMusicLibraryTitle();
printMusicLibrary(Library, MAX_LIBRARY_SIZE);
}
This is my printing the library function
// This function will print the music library
void printMusicLibrary (Song library[], int librarySize) {
printf("\n");
bool empty = true;
for (int i = 0; (i < librarySize) && (!empty); i ++) {
empty = false;
if (library[i].songName[i] != '\0') {
printf("%s\n", library[i].songName);
printf("%s\n", library[i].artist);
printf("\n");
} else {
empty = true;
}
}
}
I think the problem is caused due to setting : empty = true outside the for loop and then checking (!empty) which will evaluate to false. What I am surprised by is how is it printing even two indices. You should set empty = false as you are already checking for the first index before the function call.
The logic has two ways to terminate the listing: 1) if the number of entries is reached, or 2) if any entry is empty.
I expect the second condition is stopping the listing before you expect. Probably the array wasn't built as expected (I didn't look at that part), or something is overwriting an early or middle entry.
you gave the definition as:
typedef struct song
{
char songName[MAX_LENGTH];
char artist[MAX_LENGTH];
}Song;
the later, you write if (library[i].songName[i] != '\0') which really seems strange: why would you index the songname string with the same index that the lib?
so I would naturally expect your print function to be:
// This function will print the music library
void printMusicLibrary (Song library[], int librarySize) {
for (int i = 0; i < librarySize; i ++) {
printf("%s\n%s\n\n", library[i].songName,
library[i].artist);
}
}
note that you may skip empty song names by testing library[i].songName[0] != '\0' (pay attention to the 0), but I think it would be better not to add them in the list (does an empty song name make sens?)
(If you decide to fix that, note that you have an other fishy place: if (Library[firstIndex].songName[firstIndex] == '\0') with the same pattern)

Linked List won't return to main function when head is deleted and moved forward

I'm currently stuck at this part in coding a dictionary projects. I can pretty much breeze through everything else, but here.
My problem is that whenever I return the dictionary(a linked list head) to the main function after it passes through this particular if statement, an exception pops out.
item *deleteEntry(item *entry, item *dictionary)
{
item *cont,*temp;
int check;
check = 0;
for (cont = dictionary; cont != NULL; cont = cont->next)
{
if (!strcmp(cont->word, entry->word) && cont == dictionary)
{
dictionary = cont->next;
free(cont);
check = 1;
break;
}
//other non-problematic entry-deletion statements
}
if (!check)
return 0;
return dictionary;
}
Even though it does store the data correctly inside the function(I've tested by printing), it just doesn't work when returned to the main function.
Here's the part where I call the function
printf("\nEnter word to delete entry: ");
getchar();
fgets(entry->word, WORDLIMIT, stdin);
if (dictionary[toupper(entry->word[0]) - 65] == NULL)
printf("\nWord Does not exist");
else
{
cont = deleteEntry(entry, dictionary[toupper(entry->word[0]) - 65]);
if (cont)
dictionary[toupper(entry->word[0] - 65)] = cont;
else
printf("\nError: Word doesn't exist in archive %d!!\n", toupper(entry->word[0]));
And the exception is :
Access violation reading location 0xDDDDDDDD. (I don't know what this is, even after google searches. please enlighten me)
And this is my first post here, so I'll take my chance to say Thank you for this is the first place I go to whenever I get stuck :)
Problem Solved!
As I commented, I think the key point is dictionary[toupper(entry->word[0] - 65)]
This code causes to access your array of pointers out of bounds.
E.g.
entry->word[0] = 'a'
toupper(entry->word[0] - 65) = 32
But I think that your array has 26 elements.
It must be
dictionary[toupper(entry->word[0]) - 65]
/* this is part of your code. check comments added by me
*/
for (cont = dictionary; cont != NULL; cont = cont->next /*->next can be null*/)
{
if (!strcmp(cont->word, entry->word) && cont == dictionary)
{
dictionary = cont->next; /*if ->next above is null than boom (null->null)*/
free(cont);
check = 1;
break;
}
//other non-problematic entry-deletion statements
}
/* better use this */
cont = dictionary->next;
while(cont){
if(...){
dictionary=cont->next;
free(cont);
break; /* or return something*/
}
cont=cont->Next;
}
/*return other thing*/

Doubly Linked List C, insertion at specific position

I could really use some help with an address book program I've been struggling on for days now. I'm working with a doubly linked list in C. I'm trying to add nodes into the list at user-entered positions, starting with position 0. The positions will not be entered out of range. (no inserts at position 1 before something at position 0 etc.) The positions can be repeated though: inserting the new node in the position before the previous position occupant. (for example: if position 1 has x, and new node is inserted at position 1 with y, position 1 now has y and position 2 has x)
I need to take the user entered position number and retrieve the current person in that position, but I can't quite get it right. Also, I have included my insert function if you wanted to take a look at that as well because it isn't working properly either. Thanks for any help!
EDIT: The main problem right now is that my code for finding pPersonCur is failing when position == 1. Also, the insert function is not entering things in the proper order (the newest insertion in a position does not displace the older insertion correctly). The broken pPersonCur code makes it hard to diagnose why exactly this is, however.
addressbook.h excerpt:
typedef struct person Person;
struct person {
char lastName[255];
char firstName[255];
char email[255];
char phoneNumber[255];
Person *pNext;
Person *pPrev;
};
addressbook.c excerpt:
#include "addressbook.h"
Person * InsertPerson(Person * pPersonCur) {
Person * pPersonNew;
/* data gathered for CreatePerson() function here */
pPersonNew = CreatePerson(pLastName, pFirstName, pEmail, pPhoneNumber);
if (pPersonCur)
{
pPersonNew->pNext = pPersonCur;
pPersonNew->pPrev = pPersonCur->pPrev;
pPersonCur->pPrev = pPersonNew;
if (pPersonNew->pPrev)
pPersonNew->pPrev->pNext = pPersonNew;
} else
{
pPersonNew->pPrev = pFirst;
pPersonNew->pNext = NULL;
if (pFirst)
pFirst->pNext = pPersonNew;
}
return (pPersonNew);
}
main.c excerpt:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "addressbook.h"
Person *pFirst; /* First name in list */
int main(void) {
Person *pPersonCur = NULL; /* current Person */
int bDone = 0, position = 0, counter = 0;
pFirst = NULL;
printf("Ready\n");
while (!bDone) {
char input = getchar();
switch (input) {
case 'a':
counter = 0;
scanf("%d", &position); /* Where desired position is entered */
if (position == 0) {
if (pFirst) {
if (pFirst->pNext) {
pPersonCur = pFirst->pNext;
}
} else {
pPersonCur = pFirst;
}
} else {
pPersonCur = pFirst->pNext;
while (counter < position) {
pPersonCur = pPersonCur->pNext;
counter++;
}
}
InsertPerson(pPersonCur); /* Takes in person at desired position, return value is new inserted person */
break;
/* Some other cases here */
case 'q':
bDone = 1;
break;
}
}
/* Rest of code */
It seems so that you never assign a value to pFirst.
When position is not 0 the line pPersonCur = pFirst->pNext; is executed and pFirst in this place is still NULL.
Add a condition to your insert function to check whether list's head is assigned.
Person * InsertPerson(Person * pPersonCur) {
. . .
else
{
pPersonNew->pPrev = pFirst;
pPersonNew->pNext = NULL;
if (pFirst)
pFirst->pNext = pPersonNew;
else
pFirst = pPersonNew; // If pFirst is not assigned, assign it to newly created person
}
return (pPersonNew);
}
Despite that, if you happen to call InsertPerson with NULL argument, your code would put new Person after the first one and cut the rest of the list off.
To put new Person to the end of the list when called with NULL you could use something like this in your InsertPerson function:
if(pFirst) {
Person *last = pFirst;
while(last->pNext != NULL) {
last = last->pNext;
}
last->pNext = pPersonNew;
pPersonNew->pPrev = last;
}
else
pFirst = pPersonNew;
Insertion according to position index might fail as well if you give a position index that is higher than there are nodes in the list. Some sort of safety check should be added as well.
pPersonCur = pFirst->pNext;
while (counter < position && pPersonCur->pNext != NULL) { // If last node reached, stop the loop
pPersonCur = pPersonCur->pNext;
counter++;
}
This implementation would add new Person to the end of the list if position index is too high.

Resources