How do I insert char* to array of structures? - c

the task is to dynamically allocate memory for the array of structures and then fill them from keyboard. I was able to dynamically allocate and fill amount of pages for each of the struct instance in array, but when I try to add char* to it by doing something like:
strcpy(myArray[i]->author, authorName);
But every time I get segmentation error, so what am I doing wrong?
Is it possible that problem is actually in memory allocation?
Here is the code
#include <stdlib.h>
#include <string.h>
struct Book {
char* author;
char* title;
int pages;
int pubYear;
int copies;
};
void allocList(struct Book **myArray, int booksAmount);
void fillKeyboard(struct Book **myArray, int booksAmount);
int main(void) {
struct Book *booksList = NULL;
int booksAmount = 3;
allocList(&booksList, booksAmount);
fillKeyboard(&booksList, booksAmount);
return 0;
}
void allocList(struct Book **myArray, int booksAmount) {
*myArray = (struct Book*) malloc(sizeof(struct Book) * 100);
printf("memory for %d books was allocated \n", booksAmount);
}
void fillKeyboard(struct Book **myArray, int booksAmount) {
int i = 0;
char* authorName = "author name";
while (booksAmount--) {
printf("book number %d \n", i + 1);
printf("enter amount of pages: ");
scanf("%d", &(*myArray)[i].pages);
printf("\nenter author: ");
strcpy(myArray[i]->author, authorName);
printf("%s is \n", authorName);
i++;
printf("\n");
}
}
Thank you.

myArray[i].author is a string.
so (in C) an array of char.
as an array you need to allocate it with a malloc
myArray[i].author=malloc(sizeof(char) * 100);
your while loop should look like this:
while (booksAmount--) {
myArray[i].author=malloc(sizeof(char) * 100);
printf("book number %d \n", i + 1);
printf("enter amount of pages: ");
scanf("%d", &(myArray)[i].pages);
printf("\nenter author: ");
strcpy(myArray[i].author, authorName);
printf("%s is \n", authorName);
i++;
printf("\n");
}
keep in mind that 100 is a "magic number" so if the author name is longer than 100 character it's not going to work
edit: same thing with the title, you need to allocate the needed memory in your array's element

If you care about memory, you should not create an array of 100 books. A good way to avoid such allocations is linked lists, so your program will always allocate just the needed memory. Your Book struct would look like this:
struct Book {
char* author;
char* title;
int pages;
int pubYear;
int copies;
struct Book *next;
};
The program will certainly be a bit more complex, but the result is a clean memory usage, and a truly dynamic program.

Related

Trying to input a strcture, first time works great, second time crashes

Hi I'm inputting a structure of a family with children with those two structures:
typedef struct person {
int id;
char* firstName;
int age;
}person;
typedef struct family {
char* lastName;
person father, mother;
person* children;
int numChildren;
}family;
EDIT: this is the edited function and it still crashes :
int initializeHouse(family **pdata)
{
char temp[SIZE];
int size, i, j;
printf("enter the number of families\n");
scanf("%d", &size);
*pdata = (family*)malloc(sizeof( family)*size);
for (i = 0; i<size; i++)
{
printf("Please enter the last name\n");
scanf("%s", temp);
(*pdata)[i].lastName = (char*)malloc(sizeof(char)* (strlen(temp) + 1));
strcpy(pdata[i]->lastName, temp);
printf("Enter the fathers first name\n");
scanf("%s", temp);
initPerson(temp, &pdata[i]->father.firstName);
printf("enter the fathers ID\n");
scanf("%d", &pdata[i]->father.id);
printf("Enter the fathers age\n");
scanf("%d", &pdata[i]->father.age);
printf("Enter the mothers first name\n");
scanf("%s", temp);
initPerson(temp, &pdata[i]->mother.firstName);
printf("enter the mothers ID\n");
scanf("%d", &pdata[i]->mother.id);
printf("Enter the mothers age\n");
scanf("%d", &pdata[i]->mother.age);
printf("enter the number of children");
scanf("%d", &pdata[i]->numChildren);
(*pdata)[i].children= (person*)malloc(sizeof(person)*(pdata[i]->numChildren));
for (j = 0; j<pdata[i]->numChildren; j++)
{
printf("enter the kids name\n");
scanf("%s", temp);
initPerson(temp, &pdata[i]->children[j].firstName);
printf("enter the kids ID\n");
scanf("%d", &pdata[i]->children[j].id);
printf("Enter the kids age\n");
scanf("%d", &pdata[i]->children[j].age);
}
}
return size;
}
void initPerson(char* str, char** fam)
{
*fam = (char*)malloc(sizeof(char)*(strlen(str) + 1));
strcpy(*fam, str);
}
EDIT: I changed the code and it still doesn't work, and it requires me to write some description so here it is ..
int main() {
int size;
family *a = NULL;
size=initializeHouse(&a);
}
declares a pointer to a family structure. When you pass it's address
size = initializeHouse(&a);
the function gets it as a family**
Okay, we're all on the same page to this point. When you allocate the target of that pointer
*pdata = malloc(sizeof(family) * size);
then *pdata points to an allocated array of structs, not pointers to those structs. Each struct is accessed by (*pdata)[i], which means -> dereference the double pointer pdata to get the address of the first element in the array, then access the array element with a subscript.
So your assignment should be
(*pdata)[i].lastName = malloc(sizeof(char)* (strlen(temp) + 1));
You use the dot . operator to access the members, because the result of the subscript access is a struct, not a pointer to a struct.
This little example using (*pdata)[i]. doesn't crash.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 40
typedef struct person {
int id;
char* firstName;
int age;
}person;
typedef struct family {
char* lastName;
person father, mother;
person* children;
int numChildren;
}family;
int initializeHouse(family **pdata)
{
char temp[SIZE];
int size, i, j;
printf("enter the number of families\n");
scanf("%d", &size);
*pdata = malloc(sizeof(family)*size);
for (i = 0; i<size; i++)
{
printf("Please enter the last name\n");
scanf("%39s", temp);
(*pdata)[i].lastName = malloc(sizeof(char)* (strlen(temp) + 1));
strcpy ( (*pdata)[i].lastName, temp);
}
return size;
}
int main ( void) {
int size;
family *a;
size=initializeHouse(&a);
}
It's important that you understand the memory layout.
family *fam;
family **pdata = &fam;
*pdata = (family*)malloc(sizeof(family)*size);
You have essentialy this: fam is a uninitialized pointer of type family.
pdata is a double pointer initialized with the address of fam. The malloc
call allocates space for size family-objects. By doing *pdata = malloc(...)
you are initializing fam.
This is the basic memory layout you have. base is the address returned by
malloc. slf is the size of struct family object, fpl is the size of a
pointer to a struct family object.
base = address returned by malloc
sfl = sizeof(struct family)
fpl = sizeof(struct family*)
base + 0 base + slf base + 2 * slf
+-----------------+------------------+------------------+
|struct family | struct family | struct family |
+-----------------+------------------+------------------+
base + 0 base + fpl base + 2*fpl base + 3*fpl base + 4*fpl
+------------+------------+------------+------------+-----------+
| pdata[0] | pdata[1] | pdata[2] | pdata[3] | pdata[4] |
+------------+------------+------------+------------+-----------+
The first row shows the memory in terms on struct family objects, the second
row shows you the same memory in terms on pointers (to struct family object).
This is very important distinction, because pdata[i] returns you a
pointer, not the object.
Remember pdata is a double pointer, pdata[i] is the equivalent to pdata + i,
that is the ith pointer begining at base.
Because the size of an struct family is defintitely different that the size of
a pointer, you see that the block don't align, that means
base + slf != base + fpl.
In the first iteration you are lucky, because pdata[0] and (*pdata)[0] are
the same. But pdata[1] and *(pdata)[1] are not the same. So doing
pdata[1]->lastname (instead of (*pdata)[1].lastname) you are accessing at a
wrong location in you allocated memory.
The easiest way to fix you code would be to change the pdata[i] in (*pdata)[i] as shown
in user3121023's answer.
edit
I see that user3121023 has retracted his answer. Basically it did:
printf("enter the fathers ID\n");
scanf("%d", &((*pdata)[i].father.id));
in the loop.
Fixing the pointers may have solved your problem. But the program has a lot of duplicate code and rewriting as follows would help you test a small portion and debug the program easily.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 100
typedef struct person {
int id;
char *firstName;
int age;
} person;
typedef struct family {
char *lastName;
person father, mother;
person *children;
int numChildren;
} family;
void input_string(const char *prompt, char **where) {
char temp[SIZE];
printf("%s\n", prompt);
scanf("%s", temp);
*where = malloc(sizeof(char) * (strlen(temp) + 1));
strcpy(*where, temp);
}
void input_int(const char *prompt, int *where) {
printf("%s\n", prompt);
scanf("%d", where);
}
void input_person(const char *name, person *person) {
char prompt[SIZE];
sprintf(prompt, "Enter the %s's first name", name);
input_string(prompt, &person->firstName);
sprintf(prompt, "Enter the %s's ID", name);
input_int(prompt, &person->id);
sprintf(prompt, "Enter the %s's age", name);
input_int(prompt, &person->age);
}
void input_family(family *fam) {
input_string("Please enter the last name", &fam->lastName);
input_person("father", &fam->father);
input_person("mother", &fam->mother);
input_int("Please enter the number of children", &fam->numChildren);
fam->children = malloc(sizeof(person) * (fam->numChildren));
for (int i = 0; i < fam->numChildren; i++) {
input_person("kid", &(fam->children)[i]);
}
}
int initializeHouse(family **families) {
int size;
input_int("Please enter the number of families", &size);
*families = malloc(sizeof(family) * size);
for (int i = 0; i < size; i++) {
input_family(&(*families)[i]);
}
return size;
}
int main() {
int size = 0;
family *a;
size = initializeHouse(&a);
printf("Successfully inputted %d families", size);
return 0;
}

Array of structs in c: giving all the strings same values (with the int it works well). What sould I do?

When I run the program and give values to the id, name, surname it gives them all the value of the last student. For instance if the last students name is Anna then all the other names of the array are Anna. With the grades it works well! I tried and without the 'constructor' function and happenden the same thing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student{ /*struct for student info*/
char *id;
char *name;
char *surname;
int grade;
};
struct Student* Student_new(char* id, char* name, char* surname, int grade);
/*fuction: Student 'constructor'*/
int main(int argc, char *argv[]) {
int no; /*number of students*/
printf("Welcome! \n\nNumber of students: ");
scanf("%d", &no);
struct Student *studentArray[no]; /*arary of struct students*/
int i; /*counter*/
for(i=0; i<no; i++){
char id[10], name[10], surname[10];
int grade;
printf("\n\nStudent(%d)\n", i+1);
printf("id: ");
scanf("%s", id);
printf("name: ");
scanf("%s", name);
printf("surname: ");
scanf("%s", surname);
printf("grade: ");
scanf("%d", &grade);
studentArray[i] = Student_new(id, name, surname, grade); /*using Student
'constructor' to initialize the array*/
}
for(i=0; i<no; i++){
printf("%s %s %s %d \n", studentArray[i]->id, studentArray[i]-
>name, studentArray[i]->surname, studentArray[i]->grade);
}
return 0;
}
struct Student* Student_new(char* id, char* name, char* surname, int grade)
{
struct Student* st = malloc(sizeof(struct Student));
st->id = id;
st->name = name;
st->surname = surname;
st->grade = grade;
return st;
}
PLEASE HELP!!
The issue is that the loop variables go out of scope after each iteration, and you're left with dangling pointers in the Student instances. What you're seeing is the result of undefined behavior.
What's probably happening is that the same char array is being passed into every student instance. Then you modify the same char array, overwriting the previous values.
You'll need to make copies of the strings. Remember to create a function like Student_free where you free the dynamically allocated copies.
struct Student* Student_new(char* id, char* name, char* surname, int grade)
{
struct Student* st = malloc(sizeof(struct Student));
st->id = strndup(id, 10);
st->name = strndup(name, 10);
st->surname = strndup(surname, 10);
st->grade = grade;
return st;
}
You should reserve memory for the string attributes. As a hint, use a struct similar to:
#define MAX_LEN 32
struct Student{ /*struct for student info*/
char id[MAX_LEN];
char name[MAX_LEN];
char surname[MAX_LEN];
int grade;
};
Define MAX_LEN to something that is reasonable for you, and check that the entered values aren't any longer. Also make sure to strcpy the input values to the struct variables.
The student structure only holds pointers to char arrays.
Now the actual space in memory for the strings is allocated in your for cycle an its lifetime is limited by the scope of the for cycle, accessing it afterwards is undefined behaviour. In your case the space where the strings are has not been reused and overridden yet, so coincidentally you are able to get the last value stored (cases like this can also raise segmentation fault).
You should have a look on some basic tutorial about pointers and c memory model. Allocating the arrays in the struct is a good solution (nucleon's answer). Also the scanf function can overflow you should limit the number of retrieved characters to match the size of allocated array.

array of structs in c-user input

I am new to programming in general and to C in particular.
I am trying to write a program that uses an array of structs, but I am experiencing problems if that struct contains strings.
Somehow the compiler crashes after the user has given the last input.
The struct below is just a simplified version containing only one item, because the problem seems to be reading strings into the array.
Any help is much appreciated, thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char* name;
}student;
int main()
{
int size;
printf("enter number of entries\n");
scanf("%d" , &size);
student* all=malloc(size*sizeof(student));
int i;
for(i=0;i<size;i++)
{
printf("enter name\n");
scanf("%s" , all[i].name);
}
return 0;
}
Before taking input scanf("%s" , all[i].name); , you need to allocate memory to all[i].name .
An example-
for(i=0;i<size;i++)
{
all[i].name=malloc(20*sizeof(*(all[i].name)));
if(all[i].name!=NULL){
printf("enter name\n");
scanf("%19s" , all[i].name);
}
}
//use these strings
for(i=0;i<size;i++){
free(all[i].name); //free the allocated memory
}
free(all);
Or in your structure instead of char * ,declare name as a char array (if you don't want to use dynamic allocation)-
typedef struct{
char name[20]; //give any desired size
}student;
/* no need to free in this case */
No memory is allocated for the students names (char* name), so when trying to scanf to that pointer, invalid memory is accessed and the program crashes.
The easiest way is to declare name as an array: char name[28];
The return value of malloc() needs to be checked too, in case there was problem allocating the memory for the students, which would return a NULL pointer. At the end, the allocated memory needs to be freed with free().
For example:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char name[28];
unsigned int age;
} student;
int main()
{
size_t size = 0;
printf("\nEnter number of entries: ");
scanf("%zu", &size);
// add some check for size
student* students = (student*)malloc(size * sizeof(student));
if (students == NULL) {
printf("\nProblem with allocating memory:\n"
" - size: %zu\n"
" - total size needed: %zu\n",
size, size * sizeof(student));
return 0;
}
for (size_t i = 0; i < size; ++i) {
printf("Enter name: ");
scanf("%27s", students[i].name);
printf(" Enter age: ");
scanf("%u", &students[i].age);
}
printf("\nList of students:\n");
for (size_t i = 0; i < size; ++i) {
printf("%s (%u)\n", students[i].name, students[i].age);
}
free(students); // free the allocated memory
return 0;
}

Array of structs - How to create them correctly

I'm learning about structs and memory allocation.
One of the things i found out by studying was that there is not a way to see if a block of memory was allocated correctly.
I think it's working properly, but i was getting ready to read about linked lists and got all confused again.
I ask this, because on linked lists, i have seen:
typedef LIST* Lint;
int main(){
Lint *lint;
}
and by defining the type of class like this on my code:
typedef CLASS* t;
int main(){
t class; // WITHOUT THE * BEFORE class
}
it worked! I thought it would be ok, to give the definition like this. To me makes sense, the class is now a pointer to a type (type ie. CLASS);
So, am i on the right track or this working was pure luck?
The code structure sucks, but i'm still learning :)
typedef struct student {
char name[50];
int grade;
} TURMA;
typedef CLASS* t;
void showS(struct student i){
printf("Name of Student: %s \n Grade of Student: %d\n", i.name, i.grade);
}
void insert(struct student *i,int k){
char n[100]; int q=0;
printf("Define name of student %d\n", k+1); scanf("%s", n);
printf("Define the grade of %s\n", n); scanf("%d", &q);
strcpy(i->name,n);
i->grade = q;
}
int main()
{
t class;
int total,plus,i;
printf("Define number of total students: \n"); scanf("%d", &total);
class = (struct student*) malloc(total*(sizeof(CLASS)));
for(i=0; i<total; i++){
insert(&class[i], i);
}
printf("\n\nThis is the complete class: \n");
for(i=0; i<total; i++){
showS(class[i]);
}
printf("(Total size after malloc: %lu) Add more students (Int)\n",sizeof(*turma)); scanf("%d", &plus);
class = realloc(class, plus*sizeof(CLASS));
free(class);
printf("(Total size after malloc: %lu)", sizeof(*class));
return 0;
}
It is really hard to tell exactly what you are trying to accomplish. I looks like you are trying to create a singly-linked-list out of struct student with a typedef of struct student to TURMA. That's about where the useful information ends. Attempting to declare typedef CLASS* t; makes no sense. The compiler doesn't know what CLASS is. Guessing what you were attempting, the following is what flows from your code.
note: I added one function flush_stdin to empty stdin so you wouldn't have pesky newline characters left in the buffer. I also changed the scanf format string to prevent leaving newlines following your reading of character strings. Take a look below, and I'll look at your latest additions.
Also note in your code, you cannot free (class); then expect to take sizeof(*class)) -- that is undefined behavior.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 100
typedef struct student {
char name[50];
int grade;
} TURMA;
typedef TURMA* t;
void flush_stdin ()
{
int c = 0;
while ((c = getchar()) != '\n' && c != EOF);
}
void showS (TURMA i)
{
printf("Name of Student: %s \n Grade of Student: %d\n", i.name, i.grade);
}
void insert (t i, int k)
{
char n[MAXN] = {0}; int q=0;
printf("Define name of student %d\n", k+1); scanf ("%[^\n]%*c", n);
printf("Define the grade of %s\n", n); scanf("%d", &q); flush_stdin();
strcpy(i->name,n);
i->grade = q;
}
int main()
{
t class;
int total,plus,i;
printf("Define number of total students: \n"); scanf("%d", &total); flush_stdin();
class = malloc (total * sizeof *class);
if (!class) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
for (i=0; i<total; i++){
insert (&class[i], i);
}
printf("\n\nThis is the complete class: \n");
for (i=0; i<total; i++){
showS (class[i]);
}
printf("(Total size after malloc: %lu) Add more students (Int)\n", sizeof *class * total);
scanf("%d", &plus); flush_stdin();
TURMA *turma = realloc (class, plus * sizeof *turma);
if (!turma) {
fprintf (stderr, "error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
free (class);
printf("(Total size after realloc: %lu)\n", sizeof *turma * (total + plus));
free (turma);
return 0;
}
Output
$ ./bin/llclass
Define number of total students:
2
Define name of student 1
John James
Define the grade of John James
8
Define name of student 2
Jill James
Define the grade of Jill James
9
This is the complete class:
Name of Student: John James
Grade of Student: 8
Name of Student: Jill James
Grade of Student: 9
(Total size after malloc: 112) Add more students (Int)
2
(Total size after realloc: 224)

Program with array of structs crashes

I have an array with multiple structs. When i ask the user to enter data the first time everything works but when i ask again for the next position in the array the program crashes. If this method doesn't work souldn't the program crash in the beginning? Is something wrong with malloc?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student {
char name[50];
int semester;
};
struct prof {
char name[50];
char course[50];
};
struct student_or_prof {
int flag;
int size;
int head;
union {
struct student student;
struct prof prof;
}
}exp1;
struct student_or_prof *stack;
void init(int n)
{
stack = malloc(n);
}
int push(struct student_or_prof **pinx,int *head,int n)
{
char name[50];
printf("\nn= %d\n",n);
printf("\nhead= %d\n",*head);
if(*head==n)
{
printf("Stack is full.\n");
return 1;
}
char x;
printf("Student or Professor? [s/p] ");
getchar() != '\n';
scanf("%c",&x);
if(x=='s')
{
getchar() != '\n';
pinx[*head]->flag = 0;
printf("\n\nGive student's name: ");
fgets(pinx[*head]->student.name,sizeof(pinx[*head]->student.name),stdin);
printf("\nGive student's semester: ");
scanf("%d",&(pinx[*head]->student.semester));
printf("\nName = %s\tSemester = %d",pinx[*head]->student.name,pinx[*head]->student.semester);
}
else if(x=='p')
{
getchar() != '\n';
pinx[*head]->flag = 1;
printf("\n\nGive professor's name: ");
fgets(pinx[*head]->prof.name,sizeof(pinx[*head]->prof.name),stdin);
printf("\nGive course: ");
fgets(pinx[*head]->prof.course,sizeof(pinx[*head]->prof.course),stdin);
printf("\nName = %s\tCourse = %s\n",pinx[*head]->prof.name,pinx[*head]->prof.course);
}
(*head)++;
printf("\nhead= %d\n",*head);
}
int main()
{
int n,i;
printf("Give size: ");
scanf("%d",&n);
init(n);
for(i=0;i<n;i++)
push(&stack,&exp1.head,n);
return 0;
}
You need to malloc the structure not n
malloc(sizeof(struct student_or_prof)*n)
EDIT:
And your code crashes again because pinx is a double pointer, so this operation is not valid:
pinx[*head]->flag = 0;
this is equivalent to:
*(pinx + *head)->flag = 0;
Since you are not changing what stack points to, you are better off using a single pointer instead of a double pointer.
So instead you should change your push API:
int push(struct student_or_prof *pinx,int *head,int n)
and call it like:
push(stack,&exp1.head,n);
malloc allocates the given number of bytes.
You have to multiply n with the size of your struct, to allocate enough memory.
pinx does not point to an array, so pinx[*head] is going to access invalid memory unless *head is zero.
I think you meant (*pinx)[*head] , which accesses the N-th element of the array you allocated via malloc. For example (*pinx)[*head].prof.name etc.
BTW, your head number doesn't seem to be used at all, except for exp1.head, maybe it'd be better to remove head from the struct, and just have a single variable head?

Resources