Queue using Arrays - c

Below is my implementation of a simple queue using arrays.
#include<stdio.h>
#include<stdlib.h>
#define QSIZE 5 //Limit size of queue to just 5 enteries
/*Beginning of prototype for queue functions: Insert, retrieve and display*/
void qdisp(); //Display to queue array contents
void qinsert(); //Insert a element into rear of queue
int qdelete(); //Remove an element from front of queue
/*End of prototyping*/
// Variables
int fe=0,re=0,q[QSIZE],item; //fe(front entry), re(rear entry), q[](queue array), item(element to i/p or delete)
void main()
{
int choice;
while(1)
{
printf("1.Insert element\n2.Remove element\n3.Display element(s)\n4.Exit program\nEnter number for appropriate choice: ");
scanf("%d",&choice);
switch(choice)
{
case 1: printf("Enter value of element: ");
scanf("%d",&item);
qinsert();
break;
case 2: item=qdelete();
printf("Removed \'%d\' from the queue\n",item);
break;
case 3: qdisp();
break;
case 4: exit(0);
/*case default : printf("Wrong choice i/p!");
break;*/
}
}
}
//End of main, beginning for function definitons
void qinsert()
{
if(re==QSIZE-1)
{
printf("Queue Overflow\n");
exit(0);
}
q[re]=item;
re++;
}
int qdelete()
{
if(fe>re)
{
printf("Queue is empty!\n");
exit(0);
}
item=q[fe];
fe++;
return item;
}
void qdisp()
{
int i; //i is loop counter variable
if(fe>re)
{
printf("Queue is empty!\n");
exit(0);
}
printf("Queue items are: \n");
for(i=fe;i<=re;i++)
printf("%d\n",q[i]);
}
I have used initial front and rear entry as 0 since initially in a queue any entry that goes first becomes the last entry as well. However my teacher says I should keep the rear entry as '-1' and while inserting an element into queue, first increment the rear entry index and then add opposing my code of first adding then incrementing. I looked into it and online and till now I don't find how I'm wrong.
Provide me information if I'm wrong or my teacher is?

Both pre-incrementing and post-incrementing can be used in a queue. What changes however is the full and empty conditions. With pre-increment the full condition is QSIZE-1, with post-incrementing the full condition is QSIZE. With pre-increment the empty condition is fe==re, with post-increment fe>re.
Using pre-increment can save a temporary variable in delete. Notice how you must save the current element into item, then increment the index, then return item.
item=q[fe];
fe++;
return item;
You can do away with the item variable by incrementing the index, then returning the element.
fe++;
return q[fe];
Just remember, if you pre-increment to insert, you need to pre-increment to delete.
Ok, consider this. What happens fe and re are both QSIZE-1? The queue is empty but your code will interpret it as full (because re==QSIZE-1).

Any teachers around? Okay, good.
Teachers aren't always right, but there's probably a good chance you simply misunderstood him. As long as you understand that when fe == re the queue is empty, both being 0 initially is just fine.
However, some of your functions need to change:
Delete
void qdisp(void)
{
int i;
if(fe == re) // 1
{
printf("Queue is empty!\n");
return; // 2
}
printf("Queue items are:\n");
for(i = fe; i < re; i++) // 3
printf("%d\n", q[i]);
}
== instead of >
return instead of exit(0)
< instead of <=
Insert
if(re == QSIZE) // 1
{
printf("Queue Overflow\n");
return; // 2
}
QSIZE instead of QSIZE-1
return instead of exit(0)
Delete
There's no reason to call exit(0). Nothing fatal with trying to delete from an empty queue.

in insert function when first to check queue is empty or not condition is:
if(r==0)
because when queue is empty rear is always at 0..
then check wheather queue has only 1 element or more than 1..
if(f==r-1)
{f=0;r=0;}
else
{f++;}
in insert function when u insert last element your rear will incremented and set to 5
then when u delete element front is incremented and for last element front is 4 which is less than rear by 1...so condition to check whether only 1 element left or not is f==r-1
and if it is satisfied then array becomes completely empty so we set front and rear to 0..

Related

Why the array not continue where the last number insert?

My idea is to make this program to first queue the number (start from 1001)until 10 loop.But at the same time every twice loop. i want it to delete the first number insert.Then it continue insert number after the last number insert. For example. (0) insert 1001,(1) insert 1002,(2) delete 1001,(3) insert 1003,(4) insert 1004,(5)delete 1002. This is what i imagine and the desire output. But now. When it delete it reset to the initial number.
#include <stdio.h>
#define MAX 10 /* The maximum size of the queue */
#include <stdlib.h>
void insert(int queue[], int *rear, int value)
{
if(*rear < MAX-1)
{
*rear= *rear +1;
queue[*rear] = value;
printf("\n%d queue at counter 1",value);
}
else
{
printf("\nThe queue is full can not insert a value\n");
exit(0);
}
}
void delete(int queue[], int *front, int rear, int * value)
{
if(*front == rear)
{
printf("\nThe queue is empty can not delete a value\n");
exit(0);
}
*front = *front + 1;
*value = queue[*front];
printf("\n%d left counter 1",*value);
}
int main()
{
int queue[MAX];
int iCounter,front,rear,loop=0,a,b,c;
front=rear=-1;
a=1001;
do{
printf("\n------------------------------");
printf("\n\tWelcome!!\n");
printf("\n------------------------------");
printf("\nPress which counter you prefer");
printf("\n1-Pay bill");
printf("\n2-Check up");
printf("\n3-QnA");
printf("\n------------------------------\n");
scanf(" %d",&iCounter);
loop++;
switch(iCounter)
{
case 1:
insert(queue,&rear,a);
a++;
break;
default:
printf("\nError input!");
break;
}
while(loop==2)
{
delete(queue,&front,rear,&a);
loop=0;
}
}while(rear<MAX-1);
return 0;
}
Your delete function take the address of a and writes the deleted value into it. So it will get set to the value you deleted.
You could just remove
*value = queue[*front];
and change
printf("\n%d left counter 1",*value);
to
printf("\n%d left counter 1",queue[*front]);
and also remove the whole parameter and then I think it does what you want it to do.
your delete() function pass a as pass by reference that's why you did not get what you want
delete(queue,&front,rear,&a);
For your design you should pass a as pass by value
delete(queue,&front,rear,a);
void delete(int queue[], int *front, int rear, int value)

Delete Single Student from Linked List

I cannot get this function to work properly. It crashes when I try to delete a specific record. What do I need to change for it to work as expected?
//Delete Students Function
void delete_single_Student(Student *pfirst, int id)
{
int search_id;
bool found = false;
Student *pcurrent = pfirst;
Student *temp = NULL;
printf("Please enter the student ID of the student that will be deleted.\n");
scanf("%d", &search_id);
while (search_id < 999 || search_id > 9999)
{
printf("\nPlease enter a valid id.\n");
scanf("%d", &search_id);
}
do
{
temp = pcurrent;
pcurrent = pcurrent->next;
if (pfirst->id == search_id)
{
found == true;
printf("**********************\n\n");
printf(" Student %d Deleted \n\n", search_id);
printf("*********************\n\n");
pfirst = pfirst->next;
free(temp);
break;
}
} while (found != true);
}
Notice that you are querying pfirst, but you are not generally changing pfirst in your loop. The conditional will only be true if the first element is your result. Additionally, found != true is always true, so you are bound to end up with a pcurrent null pointer after traveling the list and upon referencing pcurrrent->next, you bomb.
I would suggest that you incrementally attack with easier problems.
1) Just write a procedure to print each element, terminating when the pointer is null. You do not need a found variable.
2) Repeat number #1 but print that element side by side with its predecessor, requiring you to track the previous element.
3) After mastering #2, you will see that the first element is a special case and that you cannot really solve the problem without having access to the information that the calling function had.

Two different definitions of a priority queue?

I don't understand if both of the following definitions of a priority queue are correct:
1.
-ascending priority queue - elements are inserted arbitrarily, but after deletion, the smallest element is removed (assuming that data is an integer).
-descending priority queue - elements are inserted arbitrarily, but after deletion, the largest element is removed (assuming that data is an integer).
Examples for each:
5 15 10 -> after dequeue() -> 15 10
15 5 10 -> after dequeue() -> 5 10
2.
Every element of priority queue has a priority by which deletion is done.
There can be two cases. First, element with the highest priority is removed. Second, element with the lowest priority is removed.
Clearly, this is different from the first definition. If we assign priorities 6,3,12 to numbers 15, 10, 5, then after dequeue() operation there are two cases. If element with the lowest priority is removed, then the queue is 15,5 (10 is removed). If element with the highest priority is removed, then the queue is 15,10 (5 is removed).
Also, if element of a queue aren't numbers (strings, for example), then the first definition is useless.
Is that correct?
Question: Are both definitions correct? It seems to me that the first is only usable for numbers but even then it violates the priority from the second definition. Could someone explain this?
Here are two implementations for both definitions in C:
//1. DEFINITION//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX 6
int intArray[MAX];
int itemCount = 0;
int peek(){
return intArray[itemCount - 1];
}
bool isEmpty(){
return itemCount == 0;
}
bool isFull(){
return itemCount == MAX;
}
int size(){
return itemCount;
}
void insert(int data){
int i = 0;
if(!isFull()){
// if queue is empty, insert the data
if(itemCount == 0){
intArray[itemCount++] = data;
}else{
// start from the right end of the queue
for(i = itemCount - 1; i >= 0; i-- ){
// if data is larger, shift existing item to right end
if(data > intArray[i]){
intArray[i+1] = intArray[i];
}else{
break;
}
}
// insert the data
intArray[i+1] = data;
itemCount++;
}
}
}
int removeData(){
return intArray[--itemCount];
}
int main() {
insert(3);
insert(5);
insert(9);
insert(1);
insert(12);
int num = removeData();
printf("Element removed: %d\n",num);
return 0;
}
//2. DEFINITION//
#include<stdio.h>
#include<stdlib.h>
#define SIZE 5 /* Size of Queue */
int f=0,r=-1; /* Global declarations */
typedef struct PRQ
{
int ele;
int pr;
}PriorityQ;
PriorityQ PQ[SIZE];
PQinsert(int elem, int pre)
{
int i; /* Function for Insert operation */
if( Qfull()) printf("\n\n Overflow!!!!\n\n");
else
{
i=r;
++r;
while(PQ[i].pr >= pre && i >= 0) /* Find location for new elem */
{
PQ[i+1]=PQ[i];
i--;
}
PQ[i+1].ele=elem;
PQ[i+1].pr=pre;
}
}
PriorityQ PQdelete()
{ /* Function for Delete operation */
PriorityQ p;
if(Qempty()){ printf("\n\nUnderflow!!!!\n\n");
p.ele=-1;p.pr=-1;
return(p); }
else
{
p=PQ[f];
f=f+1;
return(p);
}
}
int Qfull()
{ /* Function to Check Queue Full */
if(r==SIZE-1) return 1;
return 0;
}
int Qempty()
{ /* Function to Check Queue Empty */
if(f > r) return 1;
return 0;
}
display()
{ /* Function to display status of Queue */
int i;
if(Qempty()) printf(" \n Empty Queue\n");
else
{
printf("Front->");
for(i=f;i<=r;i++)
printf("[%d,%d] ",PQ[i].ele,PQ[i].pr);
printf("<-Rear");
}
}
main()
{ /* Main Program */
int opn;
PriorityQ p;
do
{
printf("\n ### Priority Queue Operations(DSC order) ### \n\n");
printf("\n Press 1-Insert, 2-Delete,3-Display,4-Exit\n");
printf("\n Your option ? ");
scanf("%d",&opn);
switch(opn)
{
case 1: printf("\n\nRead the element and its Priority?");
scanf("%d%d",&p.ele,&p.pr);
PQinsert(p.ele,p.pr); break;
case 2: p=PQdelete();
if( p.ele != -1)
printf("\n\nDeleted Element is %d \n",p.ele);
break;
case 3: printf("\n\nStatus of Queue\n\n");
display(); break;
case 4: printf("\n\n Terminating \n\n"); break;
default: printf("\n\nInvalid Option !!! Try Again !! \n\n");
break;
}
printf("\n\n\n\n Press a Key to Continue . . . ");
getch();
}while(opn != 4);
}
A priority queue is a data structure holding elements (like any data structure) as well as their priority. This is your second definition.
However, in some cases, the elements actually represent their own priority. This is your first definition : sometimes, you just need to store a bunch of unordered numbers and retrieve them in order. Note that in this case, elements are not necessarily numbers. Other data types might have a property that can be used as priority.

Elements are not being inserted in different positions in the queue and value is not showing correctly

Here is my en_queue function for insertion of new elements in different conditions.
void en_queue(int *queue,int max,int front,int rear)
{
int ch1;
printf("\n Enter element to add->");
scanf("%d",&ch1);
if(front==0 && rear==(max-1))
{
printf("\n Caution!! Queue Overflow!!");
}
else if(rear==(max-1) && front>0)
{
rear=0;
queue[rear]=ch1;
printf("\n %d added to the queue where front fo the queue has room but rear does not" ,ch1);
}
else if(front==-1 && rear==-1)
{
front=rear=0;
queue[rear]=ch1;
printf("\n %d added to the queue where this is the first element to the queue" ,ch1);
}
else
{
rear++;
queue[rear]=ch1;
printf("\n %d added to the queue where the element added in the rear" ,ch1);
}
}
and here is my show_queue function.
void show_queue(int *newqueue,int front,int rear)
{
int i=0;
for(i=front;i<=rear;i++)
{
printf("%d",newqueue[i]);
}
}
By the print statements, I examine that always the elements are being inserted into the first position. So, my best guess is the rear and front elements are not getting updated successfully but I am unable to find why. Also, I should have seen the correctly inserted first value at least by the show_queue function; instead, I am seeing garbage values but the values are constant viz. 4235968 everytime.
UPDATE- Here is the main function as requested.
#include<stdio.h>
#include<stdlib.h>
#define MAX 10
void en_queue(int *,int, int, int);
void show_queue(int *,int,int);
int main()
{
int queue[MAX],front=-1,rear=-1,ch;
do{
printf("\n <<Queue MENU>>");
printf("\n 1. Add Element");
printf("\n 2. Delete Element");
printf("\n 3. Show Queue");
printf("\n 4. Exit menu");
printf("\n Enter your choice->");
scanf("%d", &ch);
switch(ch)
{
case 1: en_queue(queue,MAX,front,rear);
break;
/*
case 2: del_queue(queue,MAX,front,rear);
break;
*/
case 3: printf("\n The queue is->");
show_queue(queue,front,rear);
break;
case 4:exit(0);
default: printf("\n Invalid Choice!!!");
return 0;
}
} while(ch!=4);
return 0;
}
Your front and rear are passed by value, thus their changes are not maintained outside this function.
Basically what is happening is that your front and rear variables are copies of those that exist in the calling function, say main. Thus changes made to it here are not reflected in the calling function.
You will have this issue with dequeue as well.
You might want to do one of the below, in reverse order of recommendation
Make them globals. - Not recommended, bad style, bug prone, only allows 1 queue.
Make them pointers to variables from the parent function
OOP style, make the queue into a struct which contains the array, max, front and rear, and pass that as a pointer.
else
{
rear++;
queue[rear]=ch1;
printf("\n %d added to the queue where the element added in the rear" ,ch1);
}
change rear++ to *rear++ . It should solve your problem.
EDIT:
I tried you modified code and the problem seems to be *rear++, change it to
*rear++ to (*rear)++

Sending "cls" to dos causes exception

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.

Resources