-1

Our teacher asked us to make a video club menu and he gave us those structs to work with:

typedef struct date
{
    int day, month, year;
}date;


typedef struct directorInfo
{
    const char* directorSurname;
    const char* directorName;
}directorInfo;


typedef struct movie
{
    int id;
    const char* title;
    directorInfo* director;
    date* releaseDate;
}movie;

I am very confused and I do not know how to malloc every single one of those. This is what I have done:

int main() 
{
    
    int totalMovies = 50;
    
    movie *movie_ptr = (movie*)malloc( totalMovies * sizeof(movie) );                               
    date *date_ptr = (date*)malloc( totalMovies * sizeof(date) );                                   
    directorInfo *directorInfo_ptr = (directorInfo*)malloc( totalMovies * sizeof(directorInfo) );   
    
    
    
    movie_ptr->title = (char*)malloc( 200 * sizeof(char) );                                                     
    directorInfo_ptr->directorName = (char*)malloc( 200 * sizeof(char) );
    
    movie_ptr[0].director->directorName = "John";

    printf("%s", movie_ptr[0].director->directorName);

Is there a faster and more professional way of doing this? I feel like it is all wrong, plus when I try to print directorName it does not print anything. (I am sorry for any senior coders here that see this code and make their eyes pop)

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
  • 1
    There is no such thing as a typedef struct – user253751 Apr 01 '21 at 15:18
  • why not? @ user253751 i used it several times – Asaf Itach Apr 01 '21 at 15:20
  • This is equally for C++ and C: there's very little that the core language will do for you. You have to do all the work, and write all the code to do it. Neither C, nor C++, have a shortcut somewhere, that only needs to be employed in order to make everything happen. If you have to `malloc` several different objects, and each object has pointers that also, themselves, must be `malloc`ed in turn, then the code to do so must be written. – Sam Varshavchik Apr 01 '21 at 15:20
  • 2
    To elaborate... "typedef struct" is not a term. It's two keyword in succession, with each having its own effect. The presence of either one does not affect the behavior of the other. So "typedef struct" isn't conveying anything meaningful. – StoryTeller - Unslander Monica Apr 01 '21 at 15:21
  • normally you can use a function which do it for you (obviously you will have to write it) – Asaf Itach Apr 01 '21 at 15:22
  • yeah, but how can I malloc the struct within a struct? `directorInfo_ptr->directorName = (char*)malloc( 200 * sizeof(char) );` this is where I get the error – cannotcode Apr 01 '21 at 15:30
  • you need to malloc every element of `directorInfo` – Asaf Itach Apr 01 '21 at 15:35
  • You [don't need to cast `malloc`](https://stackoverflow.com/a/605856/14947044), because it returns `void *`, which is automatically and safely promoted to any other pointer. – Andy Sukowski-Bang Apr 01 '21 at 16:03
  • Tip: simplify: `movie *movie_ptr = (movie*)malloc( totalMovies * sizeof(movie) );` --> `movie *movie_ptr = malloc(sizeof *movie_ptr * totalMovies);`. Notice no _type_ coded in `malloc(sizeof *movie_ptr * totalMovies);`. – chux - Reinstate Monica Apr 01 '21 at 18:36

2 Answers2

2

Unless you're compiling this code as C++ or using an ancient K&R C implementation, you can leave the casts off of the malloc calls - saves some clutter and maintenance headaches. Also, you can use your target as your sizeof operand:

movie *movie_ptr = malloc( totalMovies * sizeof *movie_ptr );      

Since the type of the expression *movie_ptr is movie, sizeof *movie_ptr == sizeof (movie)1. This is especially helpful when the declaration of movie_ptr is separate from the actual malloc call:

movie *movie_ptr;

// a bunch of code here

movie_ptr = malloc( totalMovies * sizeof *movie_ptr );

This way you don't have to constantly repeat the type name, which makes maintenance a little easier.

Now, as far as the larger issue - how to better organize your code for this kind of problem - here are some suggestions:

I would start by abstracting out the logic for reading, allocating, and assigning data to your various types into separate functions.

For example, have a function devoted to reading dates from the standard input stream:

int getDate( int *day, int *month, int *year )
{
  int success = 1;

  printf( "Release year: " );  
  if ( scanf( "%d", year ) != 1 )  // this is the barest minimum level
    success = 0;                   // of error handling, but it's better than nothing
  ...
  return success;
}

then have another function which takes that data and allocates a date object:

date *createDate( int day, int month, int year )
{
  date *d = malloc( sizeof *d );
  /**
   * ALWAYS check the result of malloc, calloc, or realloc before attempting
   * to use the pointer, since they will return NULL if they cannot 
   * satisfy the request.
   */
  if ( d ) // or if ( d != NULL )
  {
    d->day = day;
    d->month = month;
    d->year = year;
  }
  return d;
}

Then in your main code, you'd call them like so:

int day, month, year;
date *d = NULL;

if ( getDate( &day, &month, &year ) )
  d = createDate( day, month, year );

Alternately could combine those operations in a third function:

date *dateFromStdInput( void )
{
  int day, month, year;
  date *d = NULL;

  if ( getDate( &day, &month, &year ) )
    d = createDate( day, month, year );

  return d;
}

and then in your main code just call:

date *d = dateFromStdInput();

I/O and memory allocation are completely separate operations, so as a rule you want to write separate functions to handle each such that you can change one without affecting the other. It takes some forethought, but in the end it makes code much easier to maintain and update.

Do something similar for your director info, and then you can create a new movie object like so:

date *d = dateFromStdInput();
directorInfo *dir = dirFromStdInput();

char title[MAX_TITLE_LEN + 1];

/**
 * Make sure we have successfully read title, director, and date info
 * before we attempt to create a new movie object:
 */
if ( fgets( title, sizeof title, stdin ) && dir && d )
{
  movie *m = createMovie( title, dir, d );
}

"But," you say, "I want to create an array of movies." Well, what you can do is create an array of pointers to movie, and then allocate a new movie object for each element:

movie **m = malloc( totalMovies * sizeof *m ); // sizeof *m == sizeof (movie *)
if ( m )
{
  for ( int i = 0; i < totalMovies; i++ )
  {
    date *d = dateFromStdInput();
    director *dir = dirFromStdInput();
    char title[MAX_TITLE_LEN+1];

    if ( fgets( title, sizeof title, stdin ) && dir && d )    
      m[i] = createMovie( title, dir, d );
  }
}

To deallocate, you'd want to write a separate deallocation function that knows how to deallocate stuff in the right order:

void freeMovie( movie *m )
{
  free( m->releaseDate );
  free( m->director->name );
  free( m->director->surname );
  free( m->director );
  free( m->title );
  free( m );
}
  

Again, these are suggestions off the top of my head; there are undoubtedly better ways to structure this code, but it should get you started anyway.


  1. Remember that sizeof is an operator, not a function; parens are only required if the operand is a type name like int or movie. It doesn't hurt to add them; sizeof (*movie_ptr) will work the same way. But again, it saves some clutter.
John Bode
  • 119,563
  • 19
  • 122
  • 198
0

memory allocations for structures, nested structures, needs to follow allocations from top most structure and drill down into nested structures if they are pointers memory is not allocated in which case each structure pointer needs to be allocated memory explicitly, for example in your case something like this

This following code will allocate an un-initialized array of structures of type movie

// this created/allocates an array of 50 movies
movie *m = malloc( sizeof(movie) * 50);

// for each movie m[i]
for (int i = 0; i < 50; ++i)
{
    m[i].title = malloc( sizeof(char) * 128 ); // say title size is 128 characters;
    m[i].director = malloc( sizeof(director));
    m[i].director->directorName = malloc( sizeof(char) * 128 );
    m[i].director->directorSurname = malloc( sizeof(char) * 128 );
    m[i].releaseDate = malloc( sizeof(date));
} // repeat for others

also the freeing follows a reverse path , free all structure elements first and then free up the actual structure for example

// Free loop

for (int i = 0; i < 50; ++i)
{
    free(m[i].releaseDate);
    free(m[i].directorName->directorName);
    free(m[i].directorName->directorSurname);
    free(m[i],director);
    free(m[i]);
} // repeat for others

free(m);
asio_guy
  • 3,667
  • 2
  • 19
  • 35