So i need to write and read to and from a binary file, but cant seem to do it, and when using readFromFile, the program crashes. I need help write to binary file, and then reading from it and resuming my work later after turning off the program.I have no idea what i am doing wrong, and i have googled for a long time now, but with no results. Here is the code of my program:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
char *subjName;
char *lectName;
char *lectSurname;
int credits;
int num_students;
}Subject;
typedef struct{
Subject **subjs;
int num_subjs;
}Subjects;
int numOfSubjs=0;
void listInput();
void listEdit();
void listDelete();
void listPrint();
int userChoice(int select);
int enterNumber(char *name);
void saveToFile(Subjects *subjects);
void readFromFile(Subjects *subjects);
int main() {
Subjects *subjects = malloc(sizeof(Subjects));
subjects->num_subjs = 0;
subjects->subjs = NULL;
readFromFile(subjects);
int r=1;
while(r!=0){
int select=userChoice(select);
switch(select){
case 1:
listPrint(subjects);
break;
case 2:
listInput(&subjects);
break;
case 3:
listDelete(subjects);
break;
case 4:
listEdit(subjects);
break;
case 0:
r=0;
break;
}
}
saveToFile(subjects);
return 0;
}
int userChoice(int select){ // menu options
int choice,input=0;
printf("(1). View all the data\n");
printf("(2). Enter new data\n");
printf("(3). Delete data\n");
printf("(4). Edit data\n");
printf("(0). Exit\n");
printf("-----------------------------\n");
while(input!=1){
choice = enterNumber("menu");
if(choice>4 || choice<0){
printf("Invalid input \n");
}
else
input = 1;
}
return choice;
}
void listPrint(Subjects *subjects){ // print data
int i;
for(i=0; i< numOfSubjs; i++){
printf("%d, %s, %s, %s, %d, %d\n",i+1, subjects->subjs[i]->subjName, subjects->subjs[i]->lectName, subjects->subjs[i]->lectSurname, subjects->subjs[i]->credits, subjects->subjs[i]->num_students);
}
printf("Number of entries: %d \n", numOfSubjs);
}
char *getln() //dynamically allocate input string
{
char *line = NULL, *tmp = NULL;
size_t size = 0, index = 0;
int ch = 1;
while (ch) {
ch = getc(stdin);
if (ch == '\n')
ch = 0;
if (size <= index) {
size += 1;
tmp = realloc(line, size);
if (!tmp) {
free(line);
line = NULL;
break;
}
line = tmp;
}
line[index++] = ch;
}
return line;
}
void saveToFile(Subjects *subjects){
FILE *data;
data = fopen("data.bin","wb");
printf("%s", subjects->subjs[0]);
for(int i=0; i<numOfSubjs; i++){
fwrite(&subjects->subjs[i],sizeof(Subject*),1,data);
}
fclose(data);
}
void readFromFile(Subjects *subjects){
FILE *data;
int i=0;
data = fopen("data.bin","rb");
while(!feof(data))
{
fread(&subjects->subjs[i],sizeof(Subject*),1,data);
i++;
}
fclose(data);
}
int isText(char *str,char *name){ // check if is text
for(int i = 0; i < strlen(str);i++){
if((str[i]<'A' || str[i]>'z') && str[i]!=' '){
printf("Error, %s must be a text \n",name);
return 0;
}
}
return 1;
}
int enterNumber(char *name){ // enter number and check if is number
int input=0, crash=0, num=0;
while(input!=1)
{
crash=0;
printf("Enter the number of %s\n", name);
scanf("%d", &num);
while(getchar()!='\n')
{
crash++;
}
if(crash>0 || num<0)
printf("Error, enter a not negative number of %s\n", name);
else if(crash==0)
input=1;
}
return num;
}
void listInput(Subjects **p_subjects){ // input new data
Subject *new_subj = malloc(sizeof(Subject));
new_subj->subjName = NULL;
new_subj->lectName = NULL;
new_subj->lectSurname = NULL;
new_subj->credits = 0;
new_subj->num_students = 0;
do{
printf("Enter the name of the subject \n");
new_subj->subjName = getln();
}while(!isText(new_subj->subjName,"Subject name"));
do{
printf("Enter the name of the lecturer \n");
new_subj->lectName = getln();
new_subj->lectName[0] &= '_';
}while(!isText(new_subj->lectName,"Lecturer's name"));
do{
printf("Enter the surname of the lecturer\n");
new_subj->lectSurname = getln();
new_subj->lectSurname[0] &= '_'; //Convert to uppercase if lowercase
}while(!isText(new_subj->lectSurname,"Lecturer's name"));
new_subj->credits = enterNumber("credits");
new_subj->num_students = enterNumber("students");
(*p_subjects)->subjs = realloc((*p_subjects)->subjs,sizeof(Subject*)*(++(*p_subjects)->num_subjs));
(*p_subjects)->subjs[(*p_subjects)->num_subjs-1] = new_subj;
numOfSubjs++;
printf("Added a new entry.\n\n");
}
void listDelete(Subjects *subjects){ // delete entries
int del;
if(numOfSubjs==0)
printf("Number of entries is 0, can't delete anything\n");
else{
printf("Enter 0 to exit. Number of subjects : %d \n", numOfSubjs);
while(1){
del = enterNumber("entry which you would like to delete");
if(del<=numOfSubjs && del>0){
for(int i = del-1; i<numOfSubjs-1; i++){
subjects->subjs[i]=subjects->subjs[i+1];
subjects->subjs = realloc(subjects->subjs,sizeof(Subject*)*(--subjects->num_subjs));
}
numOfSubjs--;
break;
}
if(del>numOfSubjs)
printf("Error, input a number between 1 and %d (or enter 0 to exit)\n", numOfSubjs);
else
break;
}
}
}
void listEdit(Subjects *subjects){ // edit entries
int choice=0, editEntry=0, editSubj=0;
if(numOfSubjs == 0)
printf("Number of entries is 0, can't edit anthing\n");
else{
while(1){
printf("Number of entry must be between 1 and %d \n", numOfSubjs);
choice = enterNumber("entry you would like to edit.");
if(choice>0 && choice<=numOfSubjs){
while(1){
editEntry = enterNumber("what would you like to edit\n 1 - Subject name\n 2 - Lecturer's name\n 3 - Lecturer's surname\n 4 - Number of credits\n 5 - Number of students");
if(editEntry>0 && editEntry <=5){
switch(editEntry){
case 1:
do{
printf("Enter the name of the subject \n");
subjects->subjs[choice-1]->subjName = getln();
}while(!isText(subjects->subjs[choice-1]->subjName,"Subject name"));
break;
case 2:
do{
printf("Enter Lecturer's name \n");
subjects->subjs[choice-1]->lectName = getln();
}while(!isText(subjects->subjs[choice-1]->lectName,"Lecturer's name"));
break;
case 3:
do{
printf("Enter Lecturer's surname \n");
subjects->subjs[choice-1]->lectSurname = getln();
}while(!isText(subjects->subjs[choice-1]->lectSurname,"Lecturer's surname"));
break;
case 4:
subjects->subjs[choice-1]->credits = enterNumber("credits");
break;
case 5:
subjects->subjs[choice-1]->num_students = enterNumber("students");
break;
}
}
break;
}
}
break;
}
}
}
&subjects->subjs[i], This is Undefined behavior. Accessing garbage value. You need to properly initialize it proper memory address. You ddin't do it anywhere.
(*subjects).subjs or subjects->subjs -> This is not pointing anywhere. It is NULL.
Also here you don't need the double pointer. A single pointer would do the thing you want to do.
typedef struct{
Subject *subjs;
int num_subjs;
}Subjects;
For single pointer this would be like
Subjects *subjects = malloc(sizeof(Subjects));
subjects->num_subjs = 10;
subjects->subjs = malloc(subjects->num_subjs * sizeof Subject);
subjects->subjs[0].subjName = malloc(40);
Each of the malloc should be checked with it's return Value. If it's NULL then it would be error to proceed further or access it.
And free() it accordingly when you are done working with it.
Some basic things:-
typedef struct{
Subject *subjs;
int num_subjs;
}Subjects;
Now let's look a bit in the code.
Op asked why OP should initialize and isn;t subjects->num_subjs = 0;
subjects->subjs = NULL; not enough?
A pointer is a variable that is supposed to hold address. here Subject* will hold the address of the variables of type Subject.
Now here initially you initialized it.
You have allocated a memory and assigned it's address to the Subject* variable subjects.
Now let's see what else you do.
subjects->num_subjs = 0;
subjects->subjs = NULL;
You initialized it. And then you try to access it(subjects->subjs[i]). Can you tell me where it points to? (subject->subjs)?
Answer is nope. It is pointing to nowhere. It contains NULL value now. Don't you think you should tell it how many subject you want to hold and allocate accordingly? Yes you should and that's what I did precisely in the example shown.
Whenever you have a pointer variable ask yourself what it contains - and if the value is something you know about, not some random garbage value.
Related
I want to insert the data in ascending order based on the partNumber.
When the function is called in main, then the node is successfully added at the first position. But on calling the function second time, there is some problem in insertion and I am unable to figure it out. When I enter the values(in second call), I get the error
Process exited after 8.277 seconds with return value 3221225477
typedef struct part {
int partNumber;
char partName[200];
int partQuantity;
struct part *nextPart;
} Part;
Part *inventory = NULL;
void insertPart();
int
main(int argc, char *argv[])
{
insertPart();
insertPart();
insertPart();
insertPart();
return 0;
}
void
insertPart()
{
Part *tempPart,
*traversePart,
*swapPart;
int counter = 0;
traversePart = inventory;
tempPart = (Part *) malloc(sizeof(Part *));
printf("Enter the Part Number\n");
scanf("%d", &(tempPart->partNumber));
getchar();
printf("Enter the Part Name\n");
fgets(tempPart->partName, 200, stdin);
printf("Enter the Part Quantity\n");
scanf("%d", &(tempPart->partQuantity));
getchar();
if (inventory == NULL) {
inventory = tempPart;
printf("Part added at the first position.\n");
}
else {
while (traversePart->nextPart->partNumber < tempPart->partNumber) {
counter++;
traversePart = traversePart->nextPart;
if (traversePart->nextPart == NULL) {
break;
}
}
if (counter == 0) {
swapPart = inventory;
inventory = tempPart;
tempPart->nextPart = swapPart;
}
else if (traversePart->nextPart == NULL) {
traversePart->nextPart = tempPart;
}
else {
swapPart = traversePart->nextPart;
traversePart->nextPart = tempPart;
tempPart->nextPart = swapPart;
}
}
printf("Element added at position : %d", counter);
}
The problem is traversePart->nextPart->partNumber traversePart->nextPart is not referring to anything or it is not holding any of the address. When you insert first value if condition is true
if (inventory == NULL) {
inventory = tempPart;
printf("Part added at the first position.\n");
}
inventory now holding the address of tempPart but while assigning values of tempPart you never assign an address to its nextvalue and it's not there because you only inserted the first value. For the second position
else{
while(traversePart->nextPart!=NULL)
{
traversePart=traversePart->nextPart;
}
if(traversePart->partNumber < tempPart->partNumber){
//here you can verify conditions
traversePart->nextPart = tempPart
}
}
You're intermixing fgets and scanf [and getchar]. Better to use just fgets and then apply strtol for numbers [or sscanf].
You're linked list code is a bit convoluted. It can be simplified.
Here's the refactored code. I've pulled some helper functions that I had lying around to do the prompting.
And, I added list printing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct part {
int partNumber;
char partName[200];
int partQuantity;
struct part *nextPart;
} Part;
Part *inventory = NULL;
void insertPart();
int getstr(char *buf,int buflen,const char *prompt);
long getnum_strtol(const char *prompt);
int
main(int argc, char **argv)
{
insertPart();
insertPart();
insertPart();
insertPart();
for (Part *cur = inventory; cur != NULL; cur = cur->nextPart)
printf("partNumber=%d partQuantity=%d partName='%s'\n",
cur->partNumber,cur->partQuantity,cur->partName);
return 0;
}
void
insertPart(void)
{
Part *tempPart;
Part *cur;
Part *prev = NULL;
int counter = 0;
#if 0
tempPart = (Part *) malloc(sizeof(Part *));
#else
tempPart = malloc(sizeof(*tempPart));
#endif
tempPart->partNumber = getnum_strtol("Enter the Part Number");
getstr(tempPart->partName,sizeof(tempPart->partName),"Enter the Part Name");
tempPart->partQuantity = getnum_strtol("Enter the Part Quantity");
tempPart->nextPart = NULL;
// find the tail/end of the list
for (cur = inventory; cur != NULL; cur = cur->nextPart) {
++counter;
// insert in sorted part order
if (cur->partNumber > tempPart->partNumber)
break;
prev = cur;
}
do {
tempPart->nextPart = cur;
// insert in the middle or end of list
if (prev != NULL) {
prev->nextPart = tempPart;
break;
}
// insert in new list or before first element of existing list
tempPart->nextPart = inventory;
inventory = tempPart;
} while (0);
printf("\nElement added at position : %d\n", counter);
}
// getstr -- get a string with prompt
// RETURNS: length or (<0 -> error)
int
getstr(char *buf,int buflen,const char *prompt)
{
char *cp;
int ret = 0;
// NOTE: usage of the error codes in errno.h is arbitrary
while (ret <= 0) {
// ensure buffer has enough space
if (buflen < 2) {
ret = -ENOMEM;
break;
}
// output prompt
if (prompt != NULL) {
printf("%s: ",prompt);
fflush(stdout);
}
// get a line
cp = fgets(buf,buflen,stdin);
// EOF
if (cp == NULL) {
ret = -ENODATA;
break;
}
// get buffer length
ret = strlen(buf);
// empty string
if (ret <= 0)
continue;
// point to last char
cp = &buf[ret - 1];
// ensure we got a newline -- if not, fgets had to chop the line (i.e.)
// the line is too long to fit in the buffer
if (*cp != '\n') {
ret = -ENOSPC;
break;
}
// strip the newline -- we are done
*cp = 0;
--ret;
}
return ret;
}
// getnum_strtol -- get number using strtol
long
getnum_strtol(const char *prompt)
{
int len;
int readflg = 1;
char *cp;
char buf[100];
long num = 0;
while (readflg) {
len = getstr(buf,sizeof(buf),prompt);
if (len < 0)
exit(1);
num = strtol(buf,&cp,10);
// ensure we got a least one digit
if (cp <= buf)
continue;
switch (*cp) {
case ' ':
case '\t':
case 0:
readflg = 0;
break;
default:
printf("getnum_strtol: not a valid number -- buffer '%s', invalid '%s'\n",
buf,cp);
break;
}
}
return num;
}
Here's the input file I used to test:
37
Hex Bolt
12
28
Machine Screw
6
23
Brad Nail
1000
27
Lock Nut
300
Here's the program output:
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 0
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 1
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 1
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 2
partNumber=23 partQuantity=1000 partName='Brad Nail'
partNumber=27 partQuantity=300 partName='Lock Nut'
partNumber=28 partQuantity=6 partName='Machine Screw'
partNumber=37 partQuantity=12 partName='Hex Bolt'
I am a beginner to C and am trying to learn dynamic memory allocation in the simplest way - I have a program that manages student records and I have a few problems with it that I cannot for the life of me figure out even after hours and hours of googleing/stackoverflowing -- Here is what this program is to accomplish:
Take in student records
Add and Delete Records ( THESE ARE THE COMMANDS THAT DO NOT WORK)
Sort student records
Find a few extra values from these records
Here is what happens to create this error as found in GDB debugger (the problem comes with the addRecord function and the freeMemory function):
*** Error in `/home/a.out': realloc(): invalid next size:
0x0000000000c42060 ***
Aborted
And when compiled with GCC and ran outside of the GDB debugger it will just seg fault.
This only happens when I do these set of options in the program:
1) ADD A RECORD
2) DELETE A RECORD
3) ADD A RECORD ( THEN CRASHES )
or
1) ADD A RECORD
2) ADD A RECORD ( THEN CRASHES )
I'm not sure whats causing this and I have no idea how to fix it, can anyone help direct me in the right direction? I've tried adding a free after the realloc success in the freeMemory function but to no avail this then causes the program to crash upon deletion of an added record.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
// Declare functions:
void printMenu();
void printRecords(char** fn, char** ln, float* s);
void addRecord(char** fn, char** ln, float* s);
void deleteRecord(char** fn, char** ln, float* s);
int freeMemory(char** fn, char** ln, float* s, char* n, int matches);
int findName(char** ln,char* n);
static int records; // global records variable
int main(){
int i, j;
int choice;
int option = -1;
printf("WELCOME TO THE STUDENT RECORD MANAGER 100 V 1.0! \n");
printf("Please indicate the number of student records you want to enter (min 5, max 15): \n");
scanf("%d",&records);
if(records < 5 || records > 15){
printf("You must enter more than five and less than 15... terminating. \n");
return 0;
}
// Declare arrays
char** firstNames;
char** lastNames;
float* scores;
// Declare variables
char* search = malloc(64);
firstNames = malloc(records*sizeof(*firstNames));
lastNames = malloc(records*sizeof(*lastNames));
scores = malloc(records*sizeof(float));
printf("Please enter the records now (ex. firstName lastName score ENTER):\n");
// Gather Records
for(i = 0; i < records; i ++){
printf("%d ",i+1);
firstNames[i] = malloc(254 * sizeof(char));
lastNames[i] = malloc(254 * sizeof(char));
//scores[i] = malloc(sizeof(float));
scanf("%s %s %f",firstNames[i], lastNames[i], &scores[i]);
}
// Generate menu and do actions
do{
printMenu();
scanf("%d",&choice);
switch(choice){
case 1:
printRecords(firstNames, lastNames, scores);
break;
case 2:
addRecord(firstNames, lastNames, scores);
break;
case 3:
deleteRecord(firstNames, lastNames, scores);
break;
case 0:
return 0;
}
}
while(1);
return 0;
}
// Print user menu
void printMenu(){
printf("\tMain Menu\t\n"
"============================\n"
" > Print records (press 1) \n"
" > Add a new record (press 2) \n"
" > Delete a record (press 3) \n"
" > Exit the program (press 0)\n"
"============================\n"
"Please select an option: ");
}
// Print all user records
void printRecords(char** fn, char** ln, float* s){
int i;
printf("THERE ARE %d RECORDS \n",records);
for(i=0;i<records;i++)
printf("(%d) First name: %s | Last name: %s | Score: %0.2f \n",i,fn[i],ln[i],s[i]); // will start at the first item and go i amount in the index
}
// Add user record
void addRecord(char** fn, char**ln, float* s){
char** tempPtr;
float* tempFPtr;
printf("Please input the values that you'd like to add (ex. firstName lastName score ENTER): \n");
if(records+1 < 15){ // if the array is not larger than maximum value when we make it larger
// lets reallocate the arrays
tempPtr = realloc(fn, (records+1)*sizeof(*fn));
if(tempPtr){
printf("ALLOCATION successfully");
fn = tempPtr;
}
else{
printf("FAILED");
// Realloc Failed
}
tempPtr = realloc(ln, (records+1)*sizeof(*ln));
if(tempPtr){
printf("ALLOCATION successfully");
ln = tempPtr;
}
else{
printf("FAILED");
// Realloc Failed
}
/*tempFPtr = realloc(s, records+1*sizeof(float));
if(tempFPtr){
s = tempFPtr;
}*/
printf("There are now %d items.. \n", records);
fn[records] = malloc(64); // in the LAST value of all arrays
ln[records] = malloc(64);
scanf("%s %s %f",fn[records], ln[records], &s[records]);
printf("(%d) > ADDED -> First name: %s | Last name: %s | Score: %0.2f \n",records,fn[records],ln[records],s[records]);
int i;
records++; // increment by one
printf("There are now %d items.. \n", records);
printf(" > Record successfully added.\n");
}
else{
printf(" > You have hit the max amount of records allowed for this program (15).\n");
}
}
// Delete all instances of user record by (last Name)
void deleteRecord(char** fn, char**ln, float* s){
char* lastName = malloc(32 * sizeof(char*));
printf("What is the last name of the student you would like to delete? \n");
scanf("%s", lastName);
// searching for that record
int matches = findName(ln, lastName);
if(matches && records-matches > 4){ // this means it doesn't go below minimum 5
if(freeMemory(fn, ln, s, lastName, matches)){
printf(" > Record successfully deleted.\n");
free(lastName);
}
}
else{
printf(" > Either no matches were found or deleting this value would put the number of records at less than 5. \n");
}
}
// Extension of deleteRecord that does duplication to push 'delete' lastName to the end, then reallocates array to cut it off the end
int freeMemory(char** fn, char** ln, float* s, char* n, int matches){
int i,j;
/* LOGIC:
- REALLOCATE ARRAY BY X LESS SIZE AFTER PUSHING THE VALUES TO BE REMOVED TO THE END
- NEED TO DO THIS FOR THREE ARRAYS
- TO REMOVE:
- RUN THROUGH X AMOUNT OF TIMES PUSHING EACH SPECIFIC REMOVAL VALUE (IE) 1st found match
- TO THE END
SHIFT
- FOUND 1st MATCH
- MATCH = MATCH + 1
- MATCH + ! = MATCH + 2
- ETC UNTIL YOU HAVE A DUPLICATE AT THE END, THEN REALLOCATE BY 1 LESS
*/
char** tempPtr = NULL;
float* tempFPtr = NULL;
for(i=0;i<matches;i++){ // gonna go through as many times as matches
int match = 0;
// let's make sure the name isn't on the end of the array before we do all this junk
if(strcmp(n,*(ln+records-1)) == 0){
printf("FN");
tempPtr = realloc(fn, (records-1)*sizeof(*fn));
if(tempPtr){
fn = tempPtr;
printf("FN PASSED");
//free(fn[records]);
}
else{
// Realloc Failed
printf("FN FAILED");
}
printf("LN");
tempPtr = realloc(ln, (records-1)*sizeof(*ln));
if(tempPtr){
ln = tempPtr;
printf("LN PASSED");
//free(ln[records]);
}
else{
printf("LN FAILED");
// Realloc Failed
}
records--;
}
else{
// lets find the first match by going through each last name
for(j=0;j<records-1;j++){
// Lets find first match
if(strcmp(n,ln[j]) == 0){ // accessing index
// lets start copying
*(ln+j) = *(ln+j+1);
*(fn+j) = *(fn+j+1);
*(s+j) = *(s+j+1);
match = 1;
}
else if(match){
// already copied first val so lets start copying the rest until we get to the end.
*(ln+j) = *(ln+j+1);
*(fn+j) = *(fn+j+1);
*(s+j) = *(s+j+1);
}
}
printf("FN");
tempPtr = realloc(fn, (records-1)*sizeof(*fn));
if(tempPtr){
fn = tempPtr;
printf("FN PASSED");
//free(fn[records]);
}
else{
// Realloc Failed
printf("FN FAILED");
}
printf("LN");
tempPtr = realloc(ln, (records-1)*sizeof(*ln));
if(tempPtr){
ln = tempPtr;
printf("LN PASSED");
//free(ln[records]);
}
else{
printf("LN FAILED");
// Realloc Failed
}
/*tempFPtr = realloc(s, records-1*sizeof(float));
if(tempFPtr){
s = tempFPtr;
printf("S PASSED");
}
else{
printf("s FAILED ");
}*/
records--;
match = 0;
}
}
return 1;
}
// Finds how many instances of a last name are present
int findName(char** ln,char* n){
int i, counter=0;
for(i=0;i<records-1;i++){
if(strcmp(n,*(ln+i)) == 0){
counter++;
}
}
return counter;
}
The error appears to be in your function addRecord. You have the expression
tempPtr = safe_trim(ln, records+1*sizeof(*ln));
Which is giving a size of records + (sizeof (*ln)) because of C operator precedence. Multiplication comes before addition. The line should be changed to:
tempPtr = safe_trim(ln, (records + 1) * sizeof(*ln));
Also because safe_trim always return a valid pointer, either the original or the resized, this check
tempPtr = safe_trim(fn, records+1*sizeof(*fn));
if(tempPtr){
printf("ALLOCATION successfully");
fn = tempPtr;
}
else{
printf("FAILED");
// Realloc Failed
}
will never follow the else branch and won't handle allocation failures. Replacing calls to safe_trim with realloc shouldn't have any affect on the program except to allow detecting allocation failures.
I have solved the problem. It was because I was not using triple pointers to it was not addressing the original variable.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void printMenu();
void printRecords(char** fn, char** ln, double* s);
void addRecord(char ***fn, char ***ln, double **s);
void addString(char ***strArray, char* str);
void addDouble(double** dubArray, double dub);
void deleteRecord(char*** fn, char*** ln, double** s);
void removeString(char ***strArray);
void removeDouble(double** dubArray);
int findName(char** ln,char* n);
int records;
int counter;
int main(){
records = 0;
counter = 1;
int initRecords = 0;
int choice,i;
char **fn; // first Names
char **ln; // last Names
double *s; // score
printf("= =\n"
"Please enter how many value"
"s you would like to initall"
"y enter [min 5, max 15]: ");
scanf("%d", &initRecords);
if(initRecords < 5 || initRecords > 15)
return 0;
// allocate memory
fn = malloc(0); // will reallocate later
ln = malloc(0);
s = malloc(0);
printf("Please enter the value (firstName lastName score ENTER): \n");
for(i = 0;i<(initRecords);i++){
addRecord(&fn, &ln, &s);
counter++;
}
printf("Records added... \n");
do{
printMenu();
scanf("%d",&choice);
switch(choice){
case 1:
printRecords(fn, ln, s);
break;
case 2:
addRecord(&fn,&ln,&s);
printf("> Record added. \n");
break;
case 3:
deleteRecord(&fn, &ln, &s);
printf("> Record deleted. \n");
break;
case 0:
return 0;
default:
return 0;
}
}
while(1);
}
// Print user menu
void printMenu(){
printf("\tMain Menu\t\n"
"============================\n"
" > Print records (press 1) \n"
" > Add a new record (press 2) \n"
" > Delete a record (press 3) \n"
"============================\n"
"Please select an option: ");
}
// Print all user records
void printRecords(char** fn, char** ln, double* s){
int i;
for(i=0;i<records;i++)
printf("(%d) First name: %s | Last name: %s | Score: %0.2lf \n",i+1,fn[i],ln[i],s[i]); // will start at the first item and go i amount in the index
}
void addRecord(char*** fn, char*** ln, double** s){
// gather value
char* fname = malloc(64);
char* lname = malloc(64);
double score;
records++;
printf("%d) ", (counter));
scanf("%s %s %lf",fname, lname, &score);
addString(fn, fname);
addString(ln, fname);
addDouble(s, score);
}
void addString(char*** strArray, char* str){
// realloc array one larger
*strArray = realloc(*strArray, (records) * sizeof(char*));
// set pointer equal
(*strArray)[records-1] = str;
}
void addDouble(double** dubArray, double dub){
// realloc array one larger
*dubArray = realloc(*dubArray, (records) * sizeof(double));
// set pointer equal
(*dubArray)[records-1] = dub;
}
void deleteRecord(char*** fn, char*** ln, double** s){
//This part gets the name
int i, j;
char *name = malloc(64);
printf("Please enter the last name of the record(s) you'd like to delete: ");
scanf("%s",name);
while(findName(*ln, name) != 0){
for(i = 0;i<records && strcmp(name,(*ln)[i]) != 0;i++);
records--;
//free((*ln)[i]);
//free((*fn)[i]);
for(i=i; i < records-1; i++){
(*ln)[i] = (*ln)[i+1];
(*fn)[i] = (*fn)[i+1];
(*s)[i] = (*s)[i+1];
}
removeString(fn);
removeString(ln);
removeDouble(s);
}
}
void removeString(char ***strArray){
*strArray = realloc(*strArray, (records) * sizeof(char*));
}
void removeDouble(double** dubArray){
*dubArray = realloc(*dubArray, (records) * sizeof(double));
}
// Finds how many instances of a last name are present
int findName(char** ln,char* n){
int i, counter=0;
for(i=0;i<records-1;i++){
if(strcmp(n,*(ln+i)) == 0){
counter++;
}
}
return counter;
}
I'm having a bit of problem. I've set the string 'Absent' to all student in a file. However, I want to replace 'Absent' with 'present' when the correct ID assigned to a student is entered. In other words, 'Absent' will only change to 'Present' for a specific person at a time. I'm not sure how to implement this and I'm asking kindly if someone would help. Thanks in advance.
UPDATE::====
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <errno.h>
typedef struct record {
char *fname;
char *lname;
int code;
char *stat;
} information;
int main (void) {
int i;
char ffname[28], flname[28], ans, status[15];
int fID, j, id_, x;
FILE *kfile, *ufile;
x = 0;
j = 0;
i = 0;
char buf[150];
time_t curtime;
struct tm* loc_time;
information array[100];
printf(" **********Attendance Recording System**********\n");
printf(" MENU \n");
//Getting current time of system
curtime = time (NULL);
// Converting current time to local time
loc_time = localtime (&curtime);
strftime (buf,150, "%I:%M %p.\n", loc_time);
//prints error message if file cannot be found within the system
if ((kfile = fopen("information.txt", "r")) == NULL) //If the file path is incorrect, an error message is displayed
{
fprintf(stderr, "Error while opening file (%d: %s)\n",errno, strerror(errno)); //Error message that will be displayed if file path is incorrect
return;
}
//while the file is opened and not at the end, the strings are stored into variables which forms an array of strings
for (x = 0; x < 200; x++) {
if (fscanf(kfile, "%s %s %d", ffname, flname, &fID) != 3) //Reads the contents of the file
break;
array[x].fname = strdup(ffname);
array[x].lname = strdup(flname);
array[x].code = fID;
}
fclose(kfile);
ufile= fopen("update.txt","w");
strcpy(status, "Absent");
fprintf(ufile,"First Name Last Name ID Status Time Arrived\n");
for (i = 0; i < x; i++) {
fprintf(ufile,"%-15s%-14s%2d%12s ",(array[i].fname), (array[i].lname), (array[i].code), status);
fprintf(ufile,"%16s",(buf));
}
fclose(ufile);
while(j < x){
printf("Enter you ID: ");
scanf("%d", &id_);
strcpy(status, "Absent");
bool isPresentInFile = false;
for(i=0; i<x; i++)
{
if(array[x].code == id_)
{
printf(" %s %s?", array[x].fname, array[x].lname);
isPresentInFile = true;
break;
}
}
if(isPresentInFile)
{
strcpy(status, "present");
}
j++;
}
fprintf(ufile,"First Name Last Name ID Status Time Arrived\n");
for (i = 0; i < x; i++) {
fprintf(ufile,"%-15s%-14s%2d%12s ",(array[i].fname), (array[i].lname), (array[i].code), status);
fprintf(ufile,"%16s",(buf));
}
fclose(ufile);
getch();
return 0;
}
UPDATE #3
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <errno.h>
typedef struct record {
char *fname;
char *lname;
int code;
} information;
int main (void) {
int i;
char ffname[28], flname[28], ans, ans1, status[15];
int fID, j, id_, x;
FILE *kfile, *ufile;
x = 0;
j = 0;
i = 0;
char buf[150];
time_t curtime;
struct tm* loc_time;
information array[100];
printf(" **********Attendance Recording System**********\n");
printf(" MENU \n");
//Getting current time of system
curtime = time (NULL);
// Converting current time to local time
loc_time = localtime (&curtime);
strftime (buf,150, "%I:%M %p.\n", loc_time);
//prints error message if file cannot be found within the system
if ((kfile = fopen("information.txt", "r")) == NULL) //If the file path is incorrect, an error message is displayed
{
fprintf(stderr, "Error while opening file (%d: %s)\n",errno, strerror(errno)); //Error message that will be displayed if file path is incorrect
return;
}
//while the file is opened and not at the end, the strings are stored into variables which forms an array of strings
for (x = 0; x < 200; x++) {
if (fscanf(kfile, "%s %s %d", ffname, flname, &fID) != 3) //Reads the contents of the file
break;
array[x].fname = strdup(ffname);
array[x].lname = strdup(flname);
array[x].code = fID;
}
fclose(kfile);
while(j < x){
Next:
printf("Enter you ID: ");
scanf("%d", &id_);
strcpy(status, "Absent");
bool isPresentInFile = false;
for(i=0; i<x; i++)
{
if(array[i].code == id_)
{
printf("Are you %s %s?", array[i].fname, array[i].lname);
printf("\n");
printf("[y/n only]: ");
scanf(" %c", &ans);
isPresentInFile = true;
break;
}//end of if statement
}//end of for loop
if(isPresentInFile)
{
strcpy(status, "present");
}//end of if statement
switch (ans){
case 'y':
ufile= fopen("update.txt","w");
fprintf(ufile,"First Name Last Name ID Status Time Arrived\n");
for (i = 0; i < x; i++) {
fprintf(ufile,"%-15s%-14s%2d%12s ",(array[i].fname), (array[i].lname), (array[i].code), status);
fprintf(ufile,"%16s",(buf));
}//end of for loop
fclose(ufile);
printf("Continue?\n");
printf("[y/n]: ");
scanf(" %c", &ans1);
if(ans1 == 'y'){
break;
}//end of if statements
else if (ans1 == 'n'){
exit(EXIT_SUCCESS);
}//end of else statement to check if ans1 is equal to 'n'
case 'n':
goto Next;
break;
default:
printf("invalid entry. Try again");
}//end of switch case statememt
j++;
}//end of while
getch();
return 0;
}
From your question, I didn't get when exactly do you want to change status from "Absent" to "present".
But you can use the same strcpy() method to overwrite your status variable.
...
strcpy(status, "Absent");
if(//condition)
{
strcpy(status, "present");
}
UPDATE 1:
After reading your comment, I understood that you will prompt the user to enter an ID. And if it matches with the ID present in the file, you need to update the status to "present" and then write to the new file.
I can think of something like this:
...
// read from file
.....
int ID;
printf("Enter you ID: ");
scanf("%d", &ID);
strcpy(status, "Absent");
bool isPresentInFile = false;
for(int i=0; i<x; ++i)
{
if(array[x].code == ID)
{
isPresentInFile = true;
break;
}
}
if(isPresentInFile)
{
strcpy(status, "present");
}
...
// write to file
.....
UPDATE 2:
After reading the updated question, I notice that you are writing to the file at the end! But overwriting the status variable each time. This way you end up writing either "Absent" or "Present" for all the entries in the file (Note that it pertains to the last candidate who enters his ID!)
To avoid this, do either of the following:
Write to the file as soon as you know the status of a candidate.
while(j < x)
{
printf("Enter you ID: ");
scanf("%d", &id_);
strcpy(status, "Absent");
bool isPresentInFile = false;
for(i=0; i<x; i++)
{
if(array[x].code == id_)
{
printf(" %s %s?", array[x].fname, array[x].lname);
isPresentInFile = true;
break;
}
}
if(isPresentInFile)
{
strcpy(status, "present");
}
// write to file here
j++;
}
Also have a field called status in the structure information.
By default, keep it "Absent" for all the candidates.
Then make it "Present" only for those who enter their IDs. Finally, write it to the file.
Can someone help me making a queue program. I want to set the array[0] to be array[1] just in display but in real I am adding value at array[0]. I got how to run the add function to it, but I can't do the view and delete command that will view from ex. array[0] to array[4], when displayed array[1] to array[5] with the value inserted.
#include <stdio.h>
#include <stdlib.h>
#define p printf
#define s scanf
int rear = 0;
int front = 0;
int *q_array = NULL;
int size = 0;
main()
{
int num, opt;
char cont[] = { 'y' };
clrscr();
p("Queue Program\n\n");
p("Queue size: ");
s("%d", &size);
p("\n");
if(size > 0)
{
q_array = malloc(size * sizeof(int));
if(q_array == NULL)
{
p("ERROR: malloc() failed\n");
exit(2);
}
}
else
{
p("ERROR: size should be positive integer\n");
exit(1);
}
while((cont[0] == 'y') || (cont[0] == 'Y'))
{
clrscr();
p("Queue Program");
p("\n\nQueue size: %d\n\n", size);
p("MAIN MENU\n1. Add\n2. Delete\n3. View");
p("\n\nYour choice: ");
s("%d", &opt);
p("\n");
switch(opt) {
case 1:
if(rear==size)
{
p("You can't add more data");
}
else
{
p("Enter data for Queue[%d]: ", rear+1);
s("%d", &num);
add(num);
}
break;
case 2:
delt();
break;
case 3:
view();
break;
}
p("\n\nDo you want to continue? (Y\/N)");
s("%s", &cont[0]);
}
}
add(int a)
{
q_array[rear]=a;
rear++;
}
delt()
{
if(front==rear)
{
p("Queue Empty");
}
else
{
p("Queue[%d] = %d removed.", front, q_array[front]);
front++;
}
}
view()
{
int i;
for(i=front;i<=rear;i++)
p("\nQueue[%d] = %d", i, q_array[i]);
}
One serious problem here is
char cont[] = { 'y' };
...
s("%s", &cont[0]);
You've only reserved one byte but scanf will write at least 2 bytes, meaning that you are going to have a buffer overflow and then the overall behaviour is unpredictable. If you want to read a single character then use"%c" as a pattern but the problem here is that characters will be in the buffer for the next read, so you are going to have to clear the buffer.
It's much easier to do:
char line[1024];
fgets(line, sizeof line, stdin);
if(line[strlen(line)-1] == '\n')
line[strlen(line)-1] = 0;
if(strcmp(line, "Y") == 0 || strcmp(line, "y")==0)
It's a little more code but it's safer this way.
There are many queues, there are fifo, lifo, and depending on it you choose how to build it
When dealing with queues, it's better to use function names like push, pop and top because they are widely used among other programmers and queue libraries. Use
these names instead.
In your case, instead if remembering with front and rear you should use
memmove instead and use a variable len to count the current number of
elements in the node. Once you've popped one element, you gained new space for
more elements.
Also, try to use fewer global variables and more encapsulation: (in my example I am
not going to care about malloc returning NULL, I want to keep it short)
#include <string.h> /* for size_t */
typefed struct {
size_z len;
size_z max_size;
int *data;
} queue;
void queue_init(queue *q, size_t max_size)
{
q->len = 0;
q->max_size = max_size;
q->data = malloc(max_size * sizeof *(q->data));
/* this is a good trick!
* If you need to change the datatype of 'data',
* you only need to change the definition of it.
* This code is valid for any type */
}
int push(queue *q, int data)
{
if(q->len == q->max_size)
return 0; /* not enough space */
q->data[q->len++] = data;
return 1;
}
int top(queue *q, int *data)
{
if(q->len == 0)
return 0; /* no elements in the queue */
*data = q->data[0];
return 1;
}
int pop(queue *q, int *data)
{
if(top(q, data) == 0)
return 0;
memmove(q->data, q->data + sizeof *(q->data), q->len--);
return 1;
}
BTW:
#define p printf
#define s scanf
Just like Daniel Fischer said, this is ugly; don't do that.
I am making a linked list of objects. For some reason when i go to print the value of a value within the object (a char array) i'm getting junk printing before the value. Why is this happening and how do i get rid of it? Here is a part of my code:
int num = 0;
int input = 1;
int retval = 0;
struct PDB2 *llist;
char avalue[100] = "";
llist = (struct PDB2 *)malloc(sizeof(struct PDB2));
llist->data1[100] = NULL;
llist->next = NULL;
while(input != 0) {
printf("\n-- Menu Selection --\n");
printf("0) Quit\n");
printf("1) Insert\n");
printf("2) Delete\n");
printf("3) Search\n");
printf("4) Display\n");
scanf("%d", &input);
switch(input) {
case 0:
default:
printf("Goodbye ...\n");
input = 0;
break;
case 1:
printf("Your choice: `Insertion'\n");
printf("Enter the value which should be inserted: ");
scanf("%s", &avalue);
append_node(llist, avalue);
break;
case 2:
printf("Your choice: `Deletion'\n");
printf("Enter the value which should be deleted: ");
scanf("%s", &avalue);
delete_node(llist, avalue);
break;
case 3:
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%s", &avalue);
if((retval = search_value(llist, avalue)) == -1)
printf("Value `%s' not found\n", avalue);
else
printf("Value `%s' located at position `%d'\n", avalue, retval);
break;
case 4:
printf("You choice: `Display'\n");
display_list(llist);
break;
} /* switch */
} /* while */
free(llist);
return(0);
}
void append_node(struct PDB2 *llist, char message[])
{
int x = 0;
while(llist->next != NULL)
{
llist = llist->next;
}
llist->next = (struct PDB2 *)malloc(sizeof(struct PDB2));
for(x = 0; x < 100; x++)
{
llist->next->data1[x] = message[x];
}
llist->next->next = NULL;
}
void display_list(struct PDB2 *llist)
{
while(llist->data1 == NULL)
{
llist = llist->next;
}
while(llist->next != NULL)
{
printf("%s ", llist->data1);
llist = llist->next;
}
printf("%s", llist->data1);
}
The char array in your initial node is uninitialized. Change
llist->data1[100] = NULL;
to
llist->data1[0] = '\0';
to stop it from being printed.
These two lines, together, are wrong:
char avalue[100] = "";
[...]
scanf("%s", &avalue);
The proper way to scanf a string is:
scanf("%s", avalue); // No Address-Of operator.
The important part of your code (based on your description of the problem) can be boiled down to this:
struct PDB2 {
char data[100];
...other fields...
};
struct PDB2 * llist = (struct PDB2 *)(malloc(sizeof(struct PDB2)));
llist->data[100] = NULL; /* This is wrong */
printf("%s", llist->data); /* This prints rubbish */
The marked line is wrong because:
NULL is meant to be used as a pointer value, not as a character. You probably mean to use '\0'.
The point seems to be to clear the string, so the correct method is
.
llist->data[0] = '\0';
Also, the call to scanf seems that it should be
char avalue[100];
scanf("%s", avalue);
since avalue is already of type char *. With the extra & you may be writing to an invalid location. However,
int input;
scanf("%d", &input);
is correct for integer input.