How to access fields of an Struct array in a function definition - c

Im new to programming and im having a hard time learning DMA and trying to work with structs and pointer at the same time.
im working on a program that takes in information about books and stores the author and title in an array of structs to be displayed it requires DMA to store the strings in the struct.
my hardest part to understand and trying to fix is when i try to access fields of an Struct array in a function definition
for example:
void getInfo(struct BookInfo *pbook, char author[], char title[])
{
//creating memory for strings
pbook.author = (struct BookInfo*) malloc((strlen(author) +1) * sizeof(char));
pbook.title = (struct BookInfo*) malloc((strlen(title) +1) * sizeof(char));
//copying info into the heap
strcopy(pbook.author, author);
strcopy(pbook.title, title);
}
I would really appreciate your help in any way, thanks in advance
This is my full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define size 5 //size is the total number of elements in the array
//declaration of struct
struct BookInfo {
char* author;
char* title;
};
//prototypes
void getInfo(struct BookInfo *pbook, char author[], char title[]);
void printInfi(struct BookInfo book[]);
int main() {
struct BookInfo myBook[size]; //declare an array.
char author[40] = { '0' }; //temp strings to store input from user
char title[50] = { '0' };
//get input from user
printf("Enter author name: ");
fgets(author, 40, stdin);
printf("Enter book title name: ");
fgets(title, 50, stdin);
// call function to store info (dma) individually in array, loop 5 times do fill struct array
for(int i =0; i < size; i++)
{
void getInfo(struct BookInfo myBook, author, title);
}
// call function to print all info from array, call one time
void printInfi(struct BookInfo myBook);
// Free space from dma
for(int i=0; i < size; i++)
{
free(myBook[i].author);
free(myBook[i].title);
}
return 0;
}
void getInfo(struct BookInfo *pbook, char author[], char title[])
{
//creating memory for strings
pbook.author = (struct BookInfo*) malloc((strlen(author) +1) * sizeof(char));
pbook.title = (struct BookInfo*) malloc((strlen(title) +1) * sizeof(char));
//copying info into the heap
strcopy(pbook.author, author);
strcopy(pbook.title, title);
}
void printInfo(struct BookInfo book[])
{
for(int i = 0; i < size; i++)
{
printf("Title: %s, Author: %s\n", book[i].author, book[i].title);
}
}

If you have a structure pointer example: struct structName *pToAStruct;, to access the value of a field use -> operator like this : var = pToAStruct->field. With var and field having the same type, int for example.
If you have a directly the structure variable, then use . operator. Example: struct structName AStruct; var = AStruct.field;
Beware in these examples I assumed you have allocated memory / initialized the structure when its needs to be.

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;
}

How to initialize an array of struct in a struct?

I have a struct with an array of another struct inside it and I am having trouble initialising the struct.
typedef struct stack * Stack;
typedef struct book * Book;
struct book {
char *title;
int pages;
};
struct stack {
int num_books;
Book array[50]
};
What I am trying to do is to create an empty stack with zero books but I keep getting segmentation faults on everything I tried.
Here is my initialization function:
Stack create_stack(void) {
Stack s = malloc(sizeof(struct stack) * 50);
s->num_books = 0;
// s->array[0]->title = Null;
// s->array[0]->pages = 0;
// the above 2 lines give a seg fault: 11
// I also tried:
// s->array = s->array = malloc(sizeof(struct book) * 50);
// Which gives the error that array type 'Book [50]' is not assignable
return s;
}
How can I create an empty stack with zero books?
You haven't allocated memory for struct book objects. The struct:
struct stack {
int num_books;
Book array[50];
};
defines array member as 50 elements array of pointers to book struct (that is, Book is synonym to struct book *). These are still "wild" pointers, and you need to assign them with allocated struct objects. In other words, by calling:
Stack s = malloc(sizeof(struct stack) * 50);
you have made a room for fifty objects of type struct stack, but inside each of these structs, there is room for struct book pointers, not objects itself.
Like mentioned in comments, typedefing a pointer type is an easy way to obfuscate the code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 2
typedef struct book {
char * title ;
int pages;
} Book;
typedef struct stack {
int num_book;
Book book_arr[SIZE];
} Stack;
//------------------------------------------------
int main (void ){
Stack s1;
printf("Enter Number of Books : " );
scanf("%d",&s1.num_book);
getchar();
//BOOK
for( size_t j = 0 ; j < s1.num_book ; j++ ){
char temp[100];
printf("Enter the Book Title for %zd Book : ", (j+1) );
fgets(temp,100,stdin);
strtok(temp,"\n"); // for removing new line character
s1.book_arr[j].title = malloc ( sizeof(temp) +1 );
strcpy(s1.book_arr[j].title,temp);
// puts(s1.book_arr[j].title );
printf("Enter Pages for %zd Book : ",(j+1) );
scanf("%d",&s1.book_arr[j].pages); getchar();
}
//PRINT
size_t count = 0 ;
for( size_t i = 0 ; i < s1.num_book ; i++ ){
while(count < SIZE ) {
printf("Book Title : %s\nBook pages : %d\n",s1.book_arr[count].title, s1.book_arr[count].pages );
free(s1.book_arr[count].title );
count++;
}
}
return 0;
}
Is that what you were trying to achieve ?

C struct quick sort for strings

I have problem with quick sort. It should sort books with author's names. Here is the code
#include <stdio.h>
#include <stdlib.h>
struct book {
char title[80];
char autor[80];
int pages;
};
int comparator (const void * a, const void *b)
{
struct book * ia=(struct book*)a;
struct book * ib=(struct book*)b;
return (strcmp(ia->autor,ib->autor));
}
int main(int argc, char ** argv)
{
int c = 2;
int i;
//Pointer to array of struct pointers, malloc for 2 structs
struct book **ptr = (struct book*)malloc(c*sizeof(struct book));
for(i=0;i<c;i++) {
//malloc for every struct
//also, if I'm doing it right?
ptr[i] = (struct book*)malloc(sizeof(struct book));
printf("Title: ");
scanf("%s",ptr[i]->title);
printf("Autor: ");
scanf("%s",ptr[i]->autor);
}
for(i=0;i<c;i++) {
printf("Before Quick sort Autor: %s, Title: %s \n",ptr[i]->autor,ptr[i]->title);
}
qsort(ptr,2, sizeof(struct book), comparator);
printf("QSORT DONe...\n\n");
for(i=0;i<c;i++) {
printf("TEST");
printf("After quick sort: Autor: %s, Title: %s \n",ptr[i]->autor,ptr[i]->title);
}
return 0;
}
So program compiles but it reaches only to printf("TEST"); (TEST prints on screen) and then it crashes.
Did I destroy my array with that quick sort? Or what could happen?
Also could you check my code if it's ok? Especially what mallocs (really) do in my code, because I'm not sure if I used them properly.
Thanks!
There were some small issues and confusions:
1) You were missing #include <string.h> for strcmp
2) You were allocating an array of pointers, which is probably not what you meant to do. An array in C is a pointer to the first element of the array, therefore if you allocate using (struct book*) malloc(n * sizeof(struct book)) you're already allocating a full array of n books.
You could also allocate an array of pointers to books, in which case you'd need to assign each pointer to a newly allocated book.
So you could do either of the following (and your code is mixing both):
struct book** ptr = (struct book**) malloc(c * sizeof(struct book*));
struct book* ptr = (struct book*) malloc(c * sizeof(struct book));
In the first case, you need to allocate new books (and therefore the malloc inside the loop would make sense)
In the second case, you just use the array directly, which is what I changed the following code to do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct book {
char title[80];
char autor[80];
int pages;
};
int comparator(const void * a, const void *b)
{
struct book * ia = (struct book*)a;
struct book * ib = (struct book*)b;
return (strcmp(ia->autor, ib->autor));
}
int main(int argc, char ** argv)
{
int c = 3;
int i;
//Pointer to array of struct pointers, malloc for 2 structs
struct book* ptr = (struct book*) malloc(c*sizeof(struct book));
if (ptr == NULL) {
printf("Could not allocate data\n");
return 1;
}
for (i = 0;i<c;i++) {
printf("Title: ");
scanf("%s", ptr[i].title);
printf("Autor: ");
scanf("%s", ptr[i].autor);
}
for (i = 0;i < c;i++) {
printf("Before Quick sort Autor: %s, Title : %s \n", ptr[i].autor, ptr[i].title);
}
qsort(ptr, c, sizeof(struct book), comparator);
printf("QSORT Done...\n\n");
for (i = 0;i<c;i++) {
printf("TEST");
printf("After quick sort: Autor: %s, Title: %s \n", ptr[i].autor, ptr[i].title);
}
free(ptr);
return 0;
}
3) Finally, it is a good practice to test the result of your malloc and to call free when you don't need it anymore.
It is shown for the point to be changed(for Pointer to array of struct pointers(but double pointer is not necessary)) following as
#include <string.h>
struct book * ia=*(struct book**)a;
struct book * ib=*(struct book**)b;
struct book **ptr = malloc(c*sizeof(struct book*));
qsort(ptr,2, sizeof(struct book*), comparator);
Perhaps version what you want
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct book {
char title[80];
char autor[80];
int pages;
};
int comparator (const void * a, const void *b)
{
struct book * ia=(struct book*)a;
struct book * ib=(struct book*)b;
return (strcmp(ia->autor,ib->autor));
}
int main(int argc, char ** argv)
{
int c = 2;
int i;
struct book *ptr = malloc(c*sizeof(struct book));
for(i=0;i<c;i++) {
printf("Title: ");
scanf("%s",ptr[i].title);
printf("Autor: ");
scanf("%s",ptr[i].autor);
}
for(i=0;i<c;i++) {
printf("Before Quick sort Autor: %s, Title: %s \n",ptr[i].autor,ptr[i].title);
}
qsort(ptr,2, sizeof(struct book), comparator);
printf("QSORT DONe...\n\n");
for(i=0;i<c;i++) {
printf("TEST");
printf("After quick sort: Autor: %s, Title: %s \n",ptr[i].autor,ptr[i].title);
}
return 0;
}
I'm going to give you an answer which will keep the definition of ptr, which is a pointer to a pointer to struct book, and also change a minimal amount of your code. This means we will allocate an array of pointers to struct book, and then for each pointer in that array we will allocate an actual object struct book.
The first malloc will allocate an array of cpointers to struct book:
struct book **ptr = (struct book**)malloc(c*sizeof(struct book*));
The for loop which allocates an object struct book using malloc is then correct.
The second correction is at the qsort() call. We are sorting pointers to struct book, not actual objects struct book.
qsort(ptr,4, sizeof(struct book*), comparator);
Then the comparison function needs a fix. Since we are sorting pointers, the comparison function will return a pointer to a pointer to struct book. So we need to dereference that pointer to our pointer to struct book:
int comparator (const void * a, const void *b)
{
struct book* ia=*(struct book**)a;
struct book* ib=*(struct book**)b;
return (strcmp(ia->autor,ib->autor));
}

How to allocate memory with different type in C?

I have the following code in C:
typedef struct
{
int age;
int phoneNumber;
} Student;
typedef struct
{
int id;
int student[1];
} People;
#define NUM_OF_PEOPLE
void *p = malloc(sizeof(People) + sizeof(int) * NUM_OF_PEOPLE + sizeof(Student) * NUM_OF_PEOPLE);
How could I find the pointer to the memory point to the first element of struct Student in the memory?
I try to do it in the following way:
int i = 0;
for(i = 0; i < NUM_OF_PEOPLE; i++)
{
Student * student_p = p.student[NUM_OF_PEOPLE];
}
It does not work, so can we allocate memory in the way?
And how to find the first element of struct Student in the memory?
What you have is an ancient way of having a flexible array member, which was technically also undefined behavior.
You are looking for this.
First, you need to define your struct like this (I don't know what the ints before the Students are, so let's just call it id):
typedef struct
{
int age;
int phoneNumber;
} Student;
typedef struct
{
int id;
Student student;
} StudentAndId;
typedef struct
{
int id;
StudentAndId students[];
} People;
Note the lack of size in the array inside People. Now you do this:
People *p = malloc(sizeof(People) + sizeof(StudentAndId[NUM_OF_PEOPLE]));
Then you can access students inside p as if it was an array of NUM_OF_PEOPLE elements.
Remember to compile with C99 (or C11) support. With gcc that would be -std=c99 or -std=gnu99.
This will allocate memory for storing the date but how you access it depends on how you store date. using C pointers you can store and access data using this structure and allocation but accessing the members will not be direct. it will involve pointer arithmetic. So better to use other structure if possible. If using this way of allocation then you need to do pointer arithmetic to get the next elements.
Try this:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int age;
int phoneNumber;
} Student;
typedef struct
{
int id;
int student[1];
} People;
#define NUM_OF_PEOPLE 10
int main()
{
People *p = malloc(sizeof(People) + sizeof(int) * NUM_OF_PEOPLE + sizeof(Student) * NUM_OF_PEOPLE);
int* id = (int*)(p+1);
Student* s = (Student*)(id+NUM_OF_PEOPLE);
printf("Size of People : %d\n", sizeof(People));
printf("p points to : %p\n", p);
printf("id points to : %p\n", id);
printf("s points to : %p\n", s);
}
Here's a sample output:
Size of People : 8
p points to : 0x80010460
id points to : 0x80010468
s points to : 0x80010490
You may want to add the id field to your Student data structure, e.g.:
typedef struct {
int id;
int age;
int phoneNumber;
} Student;
Then, you can define a structure having a fixed header (in this case, this can be the number of students), followed by a variable-sized array of Students:
#define ARRAY_OF_ANY_SIZE 1
typedef struct {
int count;
Student students[ARRAY_OF_ANY_SIZE];
} People;
This blog post explains this technique of having "arrays of size 1", including a discussion of the alignment problem.
I won't repeat the original blog post code here. Just consider that you can use the portable offsetof() instead of the Windows-specific FIELD_OFFSET() macro.
As a sample code, you may want to consider the following:
#include <stdio.h> /* For printf() */
#include <stddef.h> /* For offsetof() */
#include <stdlib.h> /* For dynamic memory allocation */
typedef struct {
int id;
int age;
int phoneNumber;
} Student;
#define ARRAY_OF_ANY_SIZE 1
typedef struct {
int count;
Student students[ARRAY_OF_ANY_SIZE];
} People;
int main(int argc, char* argv[]) {
People* people;
const int numberOfStudents = 3;
int i;
/* Dynamically allocate memory to store the data structure */
people = malloc(offsetof(People, students[numberOfStudents]));
/* Check memory allocation ... */
/* Fill the data structure */
people->count = numberOfStudents;
for (i = 0; i < numberOfStudents; i++) {
people->students[i].id = i;
people->students[i].age = (i+1)*10;
people->students[i].phoneNumber = 11000 + i;
}
/* Print the data structure content */
for (i = 0; i < people->count; i++) {
printf("id: %d, age=%d, phone=%d\n",
people->students[i].id,
people->students[i].age,
people->students[i].phoneNumber);
}
/* Release the memory allocated by the data structure */
free(people);
return 0;
}
Output:
id: 0, age=10, phone=11000
id: 1, age=20, phone=11001
id: 2, age=30, phone=11002

creating, passing, getting back Array of a Struct and loop throuh it in C

I need to execute a function that returns array of a specified struct with variable length. Then I should loop through the returned array.
example struct :
typedef struct student {
int id;
char *name;
int grade;
} Student;
function prototypes 1 :
Student *students;
students = findStudentByGrade(int grade);
function prototypes 2 :
Student *students;
int retval = findStudentByGrade(&students, int grade);
I am bit confused on above methods. How can correctly define a array of struct? call function ? and loop through it untill end? Can some one help me please.
You can do this in this way. This code is working. I tested in CodeLite.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct student {
int id;
char *name;
} Student;
Student *findStudent(int *asize, const int grade);
int main(void)
{
Student *stds;
int asize = 0;
stds = findStudent(&asize, 5);
int i;
for (i = 0; i < asize; i++) {
printf("ID : %i\n", stds[i].id);
}
return 0;
}
Student *findStudent(int *asize, const int grade)
{
struct student *stds = malloc(sizeof(struct student) * 3);
stds[0].id = 10;
stds[1].id = 20;
stds[2].id = 40;
*asize = 3;
return stds;
}
Get the array of struc as returned statement and pass an int variable with argument list to get the size back and simply loop through using a for loop. Or else you will find problem in looping. It is more easy to get the array size from the function which create the array.
I mean this is quite a basic question, but:
Defining array of your structures would look like:
int size = ...;
Student *students = (Student*) malloc(sizeof(Student) * size);
Then just pass that to the function (both size and the array) and then just loop until i < size.
Ofcourse, don't forget to:
free(students);
at the end.

Resources