c segmentation fault due to duplicacy in structure in functions - c

The program works fine for the first time , but if we want to add the details of another book, it gives segmentation fault after I enter the first attribute
int main()
{
float count_tot=0,profit_tot=0;
char option='y';
fflush(stdin);
struct book b;
while(option!='n')
{
b=getinput();
display(b);
b.need= calcneed(b);
b.profit=calcprofit(b);
printf("Need To Order:%d\n",b.need);
printf("Total Cost:%f\n",(b.need-b.qtyonhand)*b.price_sin);
printf("do another book(Y/n)");
scanf("%c",&option);
option=getchar();
count_tot+=(b.need-b.qtyonhand)*b.price_sin;
profit_tot+=b.profit;
}
drawline();
printf("TOTAL PROFIT:%f\n",profit_tot);
printf("TOTAL COST:%f\n",count_tot);
return(0);
}
struct book getinput
{
struct book b;
scanf("%d",&b.book_code);
...
...
} //the function contains a number of scanf functions

You are creating a local variable in the function getinput and looking at the usage, you are returning it to the caller. You will be better off passing the structure as a parameter and reading data into that structure. The local copy of the structure is destroyed when the function terminates, unless you use dynamic memory allocation.

Related

Function not saving data to pointer to structure after realloc

This function is supposed to save data to a library.books_count instance of a dynamic array of pointers to structures. Yet it does not. A similar function addexistingBooks() does it flawlessly. What is the problem in realloc()?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char book_name[32];
char book_genre[32];
char author[32];
int page_count;
float price;
}Sbook;
typedef struct
{
char library_name[32];
Sbook * bookp;
int books_count;
}Slib;
void menu(char String[50]);
void addexistingBooks(Slib library, int i);
void addBook(Slib library, int i);
int main()
{
Slib library;
int i=0;
char Choice[30];
printf("Enter amount of books inside the library: ");
scanf("%d", &(library.books_count));
library.bookp = (Sbook *)calloc(library.books_count,sizeof (Sbook));
fflush(stdin);
addexistingBooks(library, i);
menu(Choice);
if(strcmp(Choice,"add")==0)
{
addBook(library, i);
}
free(library.bookp);
return 0;
}
void menu(char String[30])
{
printf("Welcome to the library. If you read about heresy, prepare to be purged \n");
printf("Please choose a command, by writing the appropriate command: \n");
printf("1. Write 'add' to add a book. \n");
printf("2. Write 'remove' to remove a book. \n");
printf("3. Write 'redact' to redact a book. \n");
printf("4. Write 'Sort by criteria' to sort the books, where criteria can stand for: 1.bookname, 2.author, 3.genre, 4.price. \n");
printf("Enter your command: ");
gets(String);
}
void addexistingBooks(Slib library, int i)
{
for(i=0;i<library.books_count;i++)
{
printf("Enter the name of the book: \n");
fgets(library.bookp[i].book_name,32,stdin);
printf("Enter the genre of the book: \n");
fgets(library.bookp[i].book_genre,32,stdin);
printf("Enter the author of the book: \n");
fgets(library.bookp[i].author,32,stdin);
printf("Enter the page count of the book: \n");
scanf("%d", &(library.bookp[i].page_count));
printf("Enter the price of the book: \n");
scanf("%f", &(library.bookp[i].price));
fflush(stdin);
}
}
void addBook(Slib library, int i)
{
(library.books_count)++;
realloc(library.bookp,library.books_count);
fflush(stdin);
if(library.bookp==NULL)
{
exit(1);
}
printf("Enter the name of the book: \n");
fgets(library.bookp[i].book_name,32,stdin);
printf("Enter the genre of the book: \n");
fgets(library.bookp[i].book_genre,32,stdin);
printf("Enter the author of the book: \n");
fgets(library.bookp[i].author,32,stdin);
printf("Enter the page count of the book: \n");
scanf("%d", &(library.bookp[i].page_count));
printf("Enter the price of the book: \n");
scanf("%f", &(library.bookp[i].price));
fflush(stdin);
}
Your code contains several mistakes.
Lets start from the "non-blocking" mistakes. Those that, even if they can be really critical and have to be corrected, are not the real cause of the crash you experience.
The flush of standard input, fflush(stdin); is something that is not defined by the standard, so using it leads to undefined behavior: in some environments it could work, in some other environment it could not work, and there can be environments (the worst ones) in which it seems to work but it is actually harmful. It is recommended to avoid it.
Function gets() is dangerous because it doesn't provide any control on the size of the string inserted by the user, and its use should be avoided.
The issues in the void addBook() function
You try increasing the available space using realloc:
void *realloc(void *ptr, size_t size);
It needs the original pointer and the new size. But you pass library.books_count that is just the number of books. Meaning that, if the library used to contain 4 books, you try to allocate 5 bytes only.
You instead need to allocate library.books_count * sizeof(Sbook) bytes.
Furthermore it returns a new pointer. You need to assign it to the book pointer:
library.bookp = realloc(library.bookp, library.books_count * sizeof(Sbook));
In main() you initialize a i variable, but you never update it, since you store the numbers of books directly in library.books_count.
Then you pass it to addexistingBooks(), and it is redundant because you could use library.books_count itself for the loop, as you actually do. You can use it as the loop variable, but you don't need to have that parameter. Just
void addexistingBooks(Slib library, )
{
int i; /* If your C version is C99 or later, you can declare it in the loop itself */
for(i=0;i<library.books_count;i++)
{
/* Omissis */
}
}
Finally you pass it to addBook(), and not only it is redundant (as you can simply store the new book at index library.books_count-1, but it is actively harmful because you are always updating index 0 (because the value of parameter i is 0).
Although it is possible to pass structures to functions as values, it is not recommended. The first reason is that you will overload the stack of the process (the whole struct will be allocated in the stack area, that is quite big in PC applications but quite limited in embedded systems). The second reason will give you functional problems.
In fact, parameters passed by value are copies of the variables passed to the function. This means that any change made on them will not be reflected to original structures. In your case, the update of library.bookp pointer will not be available outside the function, causing (1) the original structure pointing to an invalid memory address location (becoming a dangling pointer), (2) the leak of the newly allocated memory, that nobody will be able to free().
Pass structures by address, instead, using pointers to structures. addBook() function, considering the i parameter removal, would become as follows
void addBook(Slib *library)
{
int i = library->books_count;
(library->books_count)++;
library->bookp = realloc(library->bookp, library->books_count * sizeof(Sbook));
/* and so on... the value of 'i' is now library->books_count-1 */
}
/* Call from main */
int main()
{
Slib library;
/* Omissis */
menu(Choice);
if(strcmp(Choice,"add")==0)
{
addBook(&library, i);
}
free(library.bookp);
return 0;
}
The definition of realloc function,
void *realloc(void *ptr, size_t size);
So, your realloc function:
realloc(library.bookp,library.books_count);
should change to:
library.bookp = realloc(library.bookp,sizeof(Sbook)*library.books_count);
OT, i see in your menu function, you use gets. It's dangerous, you should use fgets from stdin in stead. See in this link Why is the gets function so dangerous that it should not be used?

Using variables from a struct within an array of structs in C

So the primary objective here is to take input from the user and store it in an array where each element in the array is a struct srecord. I would like to be able to retrieve the strings fname and lname as well as the score. This is crucial because I am going to also design other methods that will calculate the average of all students in the array and tell which students have the highest or lowest score.
For example in fill_in_srecord_array, if I wanted to print out the information in a[i] after running fill_in_srecord, would this be the proper line?
printf("%s %s: Score= %d\n", a[i].fname, a[i].lname, a[i].score);
But this does not compile, so what is wrong here?
Is my fill_in_srecord method working properly and actually filling in the array properly?
For future reference, what is the best way to access variables from a struct being stored in an array?
#include <stdio.h>
#include <string.h>
struct srecord {
char fname[20]; /* first name */
char lname[20]; /* last name */
int score;
};
void fill_in_srecord(struct srecord *r){
struct srecord new_student; //declare a new student record
r = &new_student; //assign a value to the pointer
printf("Enter student first name: "); //request input
scanf("%s", r->fname);
printf("First: %s",r->fname);
printf("\nEnter student last name: ");
scanf("%s", r->lname);
printf("Last: %s",r->lname);
printf("\nEnter student score: ");
scanf("%d", &(r->score));
printf("Score: %d\n", r->score);
}
void fill_in_srecord_array(struct srecord a[], int len){
a[len];
//struct srecord *p; //srecord pointer
for(int i = 0; i<len; i++) {
fill_in_srecord(&a[i]);
}
}
int main(){
struct srecord students[2];
fill_in_srecord_array(students, 2);
exit (0);
}
The problem here is that in the fill_in_srecord function you do
struct srecord new_student;
r = &new_student;
This is problematic for three reasons:
First is that new_student is a local variable, and it will go out of scope and disappear once the function returns. Any pointers to it will be stray pointers and using them will lead to undefined behavior.
The second problem actually makes the first problem moot, because when you pass a value to a function in C the values are copied and the function only gets a copy. Modifying a copy (like e.g. r = &new_student) will of course not modify the original.
The third problem is that when the function is called, you pass a pointer to a valid and existing instance of the srecord structure. There's simply no need for the new_student variable or the reassignment of r inside the function. Modifying r directly will be enough.
So the solution is simply to not have the two problematic lines.
There's another thing as well, the statement a[len]; that you have in the fill_in_srecord_array function it doesn't really do anything. But if it did anything it would lead to undefined behavior because you would index the array a out of bounds.
Right now you were making changes to local variable , which is not accessible out of function block and changes made to it are not done on the variable in calling function itself .
When you pass address of a[i] to function ,and if you make changes to that in function ,a[i] will be modified in the calling function itself . Because the changes will be made directly to content at its address , that is to itself .
What you need to do is write your function like this -
void fill_in_srecord(struct srecord *r){
/* struct srecord new_student; //declare a new student record */
/* r = &new_student; //assign a value to the pointer */
printf("Enter student first name: "); //request input
scanf("%s", r->fname);
printf("First: %s",r->fname);
printf("\nEnter student last name: ");
scanf("%s", r->lname);
printf("Last: %s",r->lname);
printf("\nEnter student score: ");
scanf("%d", &(r->score));
printf("Score: %d\n", r->score);
}

using an array member in a data structure

I have the following data structure:
typedef struct node
{
int key;
int *v;
}node;
... and the global variables:
node *multiway[10];
int contor=0;
I am trying to insert inside this structure all the nodes of a multiway tree, and all the kids of each node. In order to do this, I made this function:
int * getKids(int value,int n) //returneaza vectorul de copii ai unui nod
{
//value-nodul parinte
//n- numarul de noduri ale vectorului
int *result=(int*)malloc(n*sizeof(int));
int counter=0;
int i;
for(i=1;i<=n;i++)
{
if(a[i]==value)
{
counter++;
result[counter]=i;
printf("%d ",result[counter]);
}
}
int copii[counter]; //in vector pun toti copiii valorii date, value
for(i=1;i<=counter;i++)
{
copii[i]=result[i];
}
contor++;
multiway[contor]=(node*)malloc(sizeof(node)); //added this line after a comment
multiway[contor]->key=value; //SEGMENTATION FAULT
multiway[contor]->v=copii;
return result;
}
My code compiles with no warnings, but when I run, it crashes. When I debug, I get a segmentation fault at the line which I commented with "Segmentation fault". Any idea of what I did wrong? Thank you.
Many issue with your code. To point out some of them
Array indexing
for(i=1;i<=n;i++)
{
if(a[i]==value)
arrays in c has 0 based index. So, basically it should be for(i=0;i< n;i++). You also did not show us what a is.
In case of
int copii[counter];
what if if(a[i]==value) never becomes TRUE?
In case of
multiway[contor]->key=value; //SEGMENTATION FAULT
multiway[contor]->v=copii;
what if contor is more than 9? You'll be overrunning the memory.
a function local variable's lifetime is till the function finishes execution. Once the function is finished, that variable won't be present anymore. Trying to access that variable produces undefined behavior.
multiway[contor]->v=copii;
in the above case, copii is local to getKids(), and after getKids() finishes execution, trying to access multiway[contor]->v will lead to UB.
Note: Do not cast the return value of malloc() and family.

Looping problem in C

I am writing a simple program in c so I can understand better the language but I have a strange problem.
As you see from the code below I have only one loop that it exits when I insert 255 as a value. The problem is that when I select the first(insert option) and after I insert a name the program starts something like a looping and gives me all the time the selection screen...
#include<stdio.h>
#include<stdlib.h>
struct student{
char *name;
int id;
};
void insertStudent(void);
struct student * init(void);
int main(){
struct student *p;
int selectionCode=0;
while(selectionCode!=255){
printf("\nInsert students:1");
printf("\nDisplay students:2");
printf("\nExit:255");
printf("\n\nEnter selection:");
scanf("%d",&selectionCode);
p=init();
switch(selectionCode){
case 1:
insertStudent();
//printf("1\n");
break;
case 2:
//printf("2\n");
break;
case 255:
break;
}
}
//p->name="stelios";
//p->id=0;
//printf("Name:%s ID:%d",p->name,p->id);
//free(p);
//p=NULL;
return 0;
}
struct student *init(void)
{
struct student *p;
p=(struct student *)malloc(sizeof(struct student));
return p;
}
void insertStudent(void){
struct student *p;
p=init();
printf("Enter Name:");
scanf("%s",p->name);//return 1;
printf("Enter ID:");
scanf("%d",&p->id);
//printf("test");
}
Part of the problem may be that the code is not allocating memory for the name field in the structure. The init function allocates a new structure but does not initialize the name field. The insertStudent function then uses scanf to read into that uninitialized pointer. That results in writing to "random" memory and can result in any number of problems including an access violation.
Looks like you have a memory leak, I would pass p into insertStudent().
You also have a return 1; in the middle of the insertStudent() call, so it will be returning before finishing its job.
You have "return 1;" after you scan in the name. It looks like logically you should not be returning at that point, since you want to enter in the ID. Also, you declared the function as returning "void" so returning one is an error.
Edit: The real problem is that you never allocated space for the name string.
try to:
struct student *insertStudent(void){
struct student *p;
p=init();
printf("Enter Name:");
scanf("%s",p->name);
printf("Enter ID:");
scanf("%d",&p->id);
//printf("test");
return p;
}
On the main
case 1:
free(p);
p=insertStudent();
//printf("1\n");
On the init you have to allocate space for the name.
What a mess... :-)
You never malloc() a buffer for p->name, but you are filling if with the scanf().
That is corrupting the memory of your program.
Besides... In your functions you are using the variable p and in your main program as well.
This is NOT the same variable, but you seem to assume it is.
Another problem: return 1; after the scanf() aborts the insertStudent() function so "enter ID " is never executed.
It is a void function so it should not return a value, by the way. The compiler has probably issued a warning about that.
There is probably more wrong with it, but this is what I spot after giving it a quick once over.
You need to remove the "return 1;" from insertStudent, otherwise is will no compile.
You should initialize p->name with malloc and change "scanf("%s",p->name);" to "scanf("%s", &p->name);", because you need a pointer to *char.

passing pointer to recursive function in C

I'm just starting on the road the learning C, and ran into some difficulty:
The code listed below is giving me the following error:
Attaching to program: `/workfolder/cocoa/c_stuff/bookshelf/build/Debug/bookshelf', process 1674.
Cannot access memory at address 0xa0df194
Cannot access memory at address 0xa0df194
// code start
#define MAX_NAME_LENGTH 200
#define MAX_AUTHOR_LENGTH 200
#define MAX_DESCRIPTION_LENGTH 1000
#define MAX_PUBLISHER 200
#define MAX_ISBN 50
//structures<
typedef struct {
char title[MAX_NAME_LENGTH];
char author[MAX_AUTHOR_LENGTH];
char ISBN[MAX_ISBN];
char description[MAX_DESCRIPTION_LENGTH];
char publisher[MAX_PUBLISHER];
} Book;
void getUserInput(Book *s[])
{
printf("what is the book's title ?\n");
fgets(s[book_count]->title, MAX_NAME_LENGTH, stdin);
printf("what is the author's name?\n");
fgets(s[book_count]->author, MAX_AUTHOR_LENGTH, stdin);
printf("what is the ISBN?\n");
fgets(s[book_count]->ISBN, MAX_ISBN, stdin);
printf("write a short description\n");
fgets(s[book_count]->description, MAX_DESCRIPTION_LENGTH, stdin);
printf("what is the book's publisher\n");
fgets(s[book_count]->publisher, MAX_PUBLISHER, stdin);
printf("want to add another book ? Y\\N\n");
book_count++;
if(tolower(fgetc(stdin)) == 'y')
{
return getUserInput(s);
}
else
{
return;
}
}
int main (int argc, const char * argv[]) {
// insert code here...
Book *book_shelf[100];
if((book_shelf[0] = (Book *)malloc(sizeof(Book))) == NULL)
{
exit(1);
}
getUserInput(book_shelf);
return 0;
}
The code compiles properly, and the function runs fine the first time (all the questions get asked and the struct receives the data); but when the user types 'y' to add another book, the mem error occurs.
Any ideas where the error is happening?
Thanks in advance!
You've only ever allocated memory for the first book in main - after that it tries to write to the next slot in the array, which doesn't point to an allocated block of memory, giving you a seg-fault. You're going to have to allocate memory for each book you want to read in.
In addition, since C doesn't know how long an array is, you have to pass that information along into function calls. (And I don't see where you're defining book_count.)
You might try something along these lines:
void getUserInput(Book *s[], int *book_count, int max_book_count)
{
if (book_count == max_book_count) return; // If we've filled all the slots, we can't add anymore without causing trouble.
s[book_count] = malloc(sizeof(Book));
..
if(tolower(fgetc(stdin)) == 'y')
{
(*book_count)++;
getUserInput(s, book_count, max_book_count);
}
return;
}
int main (int argc, const char * argv[]) {
// insert code here...
Book *book_shelf[100];
int book_count = 0;
getUserInput(book_shelf, &book_count, 100);
// Make sure to free all the malloc'd data
}
Even better in this situation, would just be using a loop and skipping the whole recursion step.
int main (int argc, const char * argv[]) {
// insert code here...
Book *book_shelf[100];
char response = 'y';
int book_count = 0;
while (book_count < 100 && response == 'y')
{
book_shelf = malloc(sizeof(Book));
response = getUserInput(book_shelf[book_count++]);
}
// make sure to free all the allocated data!
}
char getUserInput(Book *book)
{
// write input straight to book
printf("what is the book's title ?\n");
fgets(book->title, MAX_NAME_LENGTH, stdin);
...
return tolower(fgetc(stdin));
}
Unless I'm reading something wrong, you haven't defined book_count before using it as an array subscript.
Within main, you allocated on the stack an array of 100 pointers to the Book Structure. I believe it was your intent to allocate 100 structures and then pass the address to that block of structures to getUserInput
Change main to:
Book book_shelf[100];
...
getUserInput(book_shelf);
...
EDIT: OOPS Missed the single Book malloc mentioned in the earlier post. That ones Correct for the first book. If you edit as above and eliminate the
if (book_shelf[0]...) check, you'll accomplish your intended results
You allocate just space for the firstbook, not for the others (malloc in main)
I guess there is some code missing, no declaration and initialization of book_count
You should use loops instead of recursion
Use not recursion but loops for this kind of repetition
Recursion is probably overkill for this problem where a simple do { ... } while(user keeps answering yes) would do. However the problem you having is in main with your Book *book_shelf[100]. There are several ways you could solve this problem.
First change it to an array of Book's like samills suggests:
Book book_shelf[100];
and then change your getUserInput to something like this:
getUserInput(Book *book_shelf, int offset, int length) {
if(offset < 0 || offset >= length) {
return;
}
//...
return getUserInput(book_shelf, offset + 1, length)
}
Or you could use your existing code and change you getUserInput function to look something like this and remove the malloc from main:
getUserInput(Book *book_shelf) {
book_shelf[book_count] = (Book*)malloc(sizeof(Book));
// ...
}
props for correct use of the sizeof operator (I see that thing misused so often it makes my eyes bleed).
As in Josh's answer, by adding the following lines to your code should make it work:
book_count++;
if(tolower(fgetc(stdin)) == 'y')
{
if((book_shelf[book_count] = (Book *)malloc(sizeof(Book))) == NULL)
{
printf("Cannot allocate memory for Book");
exit(1);
}
return getUserInput(s);
}
else
{
return;
}
However, I encourage you not to use the recursive function for getting input. Recursive can lead to difficulties in debugging. You may consider using normal loop instead.
Note: I'm assuming the book_count is global variable which has been initialized to 0
thanks a lot for the replies!
I realized that I hadn't malloc-ed enough memory to handle more then one element of the struct array (Exactly what Josh is saying). So essentially:
Book *book_shelf;
if(book_shelf = (Book*)malloc(sizeof(Book)) == NULL)//exit code
so the second time around I would hit a memory issue.
thanks again!
Looks like your still doing it wrong:
Book *book_shelf;
if(book_shelf = (Book*)malloc(sizeof(Book)) == NULL)//exit code
book_shelf is only the size of a pointer. When you do the malloc you only allocate one Book at a time. This is wrong. You need to allocate contiguous memory for an array of Book objects all in one instanciation of an array.
Like
Book book_shelf[100];
not
Book *book_shelf[100];
or using malloc, use your pointer to point to an array instanciated using
100*malloc(sizeof(Book)).
You may get lucky that no other heap memory is allocated in between your malloc(sizeof(Book)) calls and that the memory management system is alocating contiguous memory by default. Also, book_shelf will only point to the last malloced Book structure, not the first one as you indicated you want in your original question.
Josh is also not allocating enough memory at one time. Use a linked list if you want to keep extending elements to the end of your book_shelf one-by-one.
factorial with pointer and recursion
#include<iostream.h>
#include<conio.h>
int show(int *p)
{
int f;
int x=*p;
if(*p==1) //boundry checking for recursion
return 1;
else
f=x*show(&(--*p)); //this code is similar to f=x*show(n-1); with non-pointers
return f;
}
void main()
{
int a=6;
int b=show(&a);
cout<<b;
getch();
}

Resources