I know you can pass specific elements of a structure to a function, but is it possible to pass an entire structure, similar to how you could pass an entire 1 dimensional array? I'm trying to pass movie num[1,2,3] etc. to a function. I'm pretty sure you could pass it using a for loop, but is it possible to pass the structure in its entirety without a loop? My code is below.
#include <stdlib.h>
#include <stdio.h>
void InputData(struct movie *num[50]);
int main()
{
struct movie
{
char name[50];
int curatornum;
int votes;
};
struct movie *num[50];
for (int i = 0; i <= 50; i++)
{
num[i] = (struct movie *)malloc(1 * sizeof(struct movie *));
}
InputData(num);
for (int i = 50; i > 0; i--)
{
free(num[i]);
}
}
void InputData(struct movie *num[50])
{
//Compiler gives an error: conflicting types for 'InputData'void InputData(struct movie *num[50]
}
Yes, you can return the entire structure, or just the address of one. You can even return an array of nested things and so on.
You can use of a loop and pass each movie, but that would imply and calling the function for each element and I believe this is not what you want to do.
The are many problems in the code, and this is probably not the best way to write this in C
.
I will add below 2 examples: one with some changes to the original code and other using a common way to write this in
C
for(int i=0; i<=50; i++)
{
num[i]=(struct movie *)malloc(1*sizeof(struct movie *));
}
InputData(num);
for(int i=50; i>0; i--)
{
free(num[i]);
}
The compiler warned you about this: from 0
to 50
there are 51
values. These loops are wrong.
And there is no reason to invert the order to free an array of pointers. You do this when you have pointers to dynamic data inside a dynamic structure: then you must free the areas allocated inside and then going up free'ing the struct
itself.
InputData()
void InputData(struct movie *num[50]);
No, do not return void
: it is a waste, many times an error. Here you could simply return the adress of an array of struct movie
.
Here InputData
gets and array of 50 pointers to struct movie
. num
is struct movie* [50]
as your IDE can probably tell you.
Not entering in religious discussion here, but in C
you declare a name for a variable of a type. If the type is a pointer, so be it. If num
is a pointer then it is clear that *num
is a value of the thing num
points to. It is called dereferencing and it is basic C
. But it not what you declare.
If num
is int*
then *num
is an int
. But you declare num
.
So it is easier to reason about this
struct movie* num[50];
than to read
struct movie *num[50];
because you are declaring num
as struct movie* [50]
.
InputData()
work in the original codeAnyway, you have in main
struct movie* num[50];
And this is the static
data you want InputData
to act upon: an array of 50 pointers to a struct
. So you need to pass its address to InputData
and it is what your compiler is trying to tell you
with something like
warning C4133: 'function': incompatible types - from 'movie *[50]' to 'movie **'
Changing the argument in InputData()
to
void InputData(Movie* (*num)[50]);
would tell the compiler that InputData
will receive a pointer to the array of pointers.
The common way of write this is to allocate the array inside InputData
and return its address. See the second example
This code is from the example I will left below:
Movie* num[50] = {0};
InputData(&num);
printf(
"\
First movie is \"%s\", num = %6d, votes = %2d\n\
Last movie is \"%s\", num = %6d, votes = %2d\n\
\n",
num[0]->name, num[0]->curatornum, num[0]->votes,
(*num[49]).name, (*num[49]).curatornum, (*num[49]).votes);
So you see that the array num
is declared in main
and its address is passed to InputData()
. The function uses this area to write the data in. And you see here the 2 common notations in the printf
to display the first and last movie of the array.
When writing these kind of code never write interactive code. It makes testing a PITA. See this function:
Movie* factory()
{
static int num = 1;
Movie* movie = malloc(sizeof(Movie));
if (movie == NULL) return NULL; // malloc failed
int value = rand();
movie->curatornum = num * 1000 + value % 1000;
movie->votes = value % 10;
sprintf(movie->name, "Title %07d", num);
num += 1;
return movie;
}
Anytime you call it you get a Movie
, repeatable, reliable. From the example:
void InputData(Movie* (*num)[50])
{
for (size_t i = 0; i < 50; i += 1)
(*num)[i] = factory();
}
this version of InputData()
gets the data right away. Always. 50 or 50,000 movies.
Here are the 1st and last movies in a run of the example:
First movie is "Title 0000001", num = 1650, votes = 1
Last movie is "Title 0000050", num = 50161, votes = 2
And the math in factory()
helps to see if the values are ok.
typedef
You may find it easier to use
typedef struct
{
char name[50];
int curatornum;
int votes;
} Movie;
And reference Movie
in code instead of struct movie
every time. This is also a religious thing, anyway.
// https://stackoverflow.com/questions/77809883/passing-structure-through-function
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
char name[50];
int curatornum;
int votes;
} Movie;
Movie* factory();
void InputData(Movie* (*num)[50]);
int main(void)
{
srand(241012);
Movie* num[50] = {0};
InputData(&num);
printf(
"\
First movie is \"%s\", num = %6d, votes = %2d\n\
Last movie is \"%s\", num = %6d, votes = %2d\n\
\n",
num[0]->name, num[0]->curatornum, num[0]->votes,
(*num[49]).name, (*num[49]).curatornum, (*num[49]).votes);
}
void InputData(Movie* (*num)[50])
{
for (size_t i = 0; i < 50; i += 1)
(*num)[i] = factory();
}
Movie* factory()
{
static int num = 1;
Movie* movie = malloc(sizeof(Movie));
if (movie == NULL) return NULL; // malloc failed
int value = rand();
movie->curatornum = num * 1000 + value % 1000;
movie->votes = 1 + value % 10;
sprintf(movie->name, "Title %07d", num);
num += 1;
return movie;
}
First movie is "Title 0000001", num = 1650, votes = 1
Last movie is "Title 0000050", num = 50161, votes = 2
Here is a common way to write this kind of program, one that is easier and extensible.
Here we have a Movie
thing and some functions to operate on Movie
typedef struct
{
char name[50];
int curatornum;
int votes;
} Movie;
Movie* factory();
int show_movie(Movie*, const char*);
But our data is not a Movie
, it is a collection of Movie
. java uses this name, C++ uses container.
Here we have Movies
and some functions to operate on the collection itself
typedef struct
{
size_t capacity;
size_t size;
Movie** M; // pointers to movies
} Movies;
Movies* InputData(size_t);
int show_movies(Movies*, const char*);
Movies* free_movies(Movies*);
It is easier to understand if InputData
returns a set of Movie
, but we do not see Movie
here. It is the concept of encapsulation: we can change the content of Movies
at will.
Movies* cat = InputData(10);
returns cat
with 10 Movie
using factory
and it is very easy to follow
Here is main
for the second example
int main(void)
{
srand(241012);
Movies* cat = InputData(10);
show_movies(cat, "***** A simple test *****\n\n");
free_movies(cat);
return 0;
}
srand()
is perfect for testing since it will give always the same sequence during testingInputData
will get the address of a collection of Movies
.show_movies
will do his thing and accepts a handy message for testingfree_movies
will erase the catalog***** A simple test *****
10 of 10 movies:
0 "Title 0000001", num = 1650, votes = 1
1 "Title 0000002", num = 2399, votes = 10
2 "Title 0000003", num = 3107, votes = 8
3 "Title 0000004", num = 4023, votes = 4
4 "Title 0000005", num = 5744, votes = 5
5 "Title 0000006", num = 6209, votes = 10
6 "Title 0000007", num = 7350, votes = 1
7 "Title 0000008", num = 8950, votes = 1
8 "Title 0000009", num = 9856, votes = 7
9 "Title 0000010", num = 10703, votes = 4
// https://stackoverflow.com/questions/77809883/passing-structure-through-function
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
char name[50];
int curatornum;
int votes;
} Movie;
Movie* factory();
int show_movie(Movie*, const char*);
typedef struct
{
size_t capacity;
size_t size;
Movie** M; // pointers to movies
} Movies;
Movies* InputData(size_t);
int show_movies(Movies*, const char*);
Movies* free_movies(Movies*);
int main(void)
{
srand(241012);
Movies* cat = InputData(10);
show_movies(cat, "***** A simple test *****\n\n");
free_movies(cat);
return 0;
}
Movies* InputData(size_t cap)
{
Movies* cat = malloc(sizeof(Movies));
if (cat == NULL) return NULL;
cat->capacity = cap;
cat->size = cap; // will return the catalog full
cat->M = (Movie**)malloc(cap * sizeof(Movie*));
if (cat->M == NULL)
{
free(cat);
return NULL;
}
// get the movies into the catalog
for (size_t i = 0; i < cap; i += 1)
cat->M[i] = factory();
return cat;
}
int show_movies(Movies* cat, const char* msg)
{
if (cat == NULL) return -1;
if (msg != NULL) printf("%s", msg); // optional title
printf(
" %llu of %llu movies:\n\n", cat->size, cat->capacity);
for (size_t i = 0; i < cat->size; i += 1)
{
char index[8];
sprintf(index, " %llu\t", i);
show_movie(cat->M[i], index);
}
return 0;
}
Movies* free_movies(Movies* cat)
{
if (cat == NULL) return NULL;
for (size_t i = 0; i < cat->size; i += 1)
free(cat->M[i]);
free(cat->M);
free(cat);
return NULL;
}
Movie* factory()
{
static int num = 1;
Movie* movie = malloc(sizeof(Movie));
if (movie == NULL) return NULL; // malloc failed
int value = rand();
movie->curatornum = num * 1000 + value % 1000;
movie->votes = 1 + value % 10;
sprintf(movie->name, "Title %07d", num);
num += 1;
return movie;
}
int show_movie(Movie* m, const char* msg)
{
if (m == NULL) return -1;
if (msg != NULL) printf("%s", msg); // optional title
printf(
"\"%s\", num = %6d, votes = %2d\n", m->name,
m->curatornum, m->votes);
return 0;
}