1

I'm building a struct to hold a movie's information. When the movie is printed, it recursively prints the information of it's sequel.

    struct movie
    {
        char name[28];
        int year;
        struct movie* sequel;
    };

void print_movie(struct movie film)
{
    printf("Name: %s \n", film.name);
    printf("Year of Release: %d \n", film.year);
    if (film.sequel == 0)
    {
        printf("%s jas no sequel, yet! \n\n", film.name);
    }
    else
    {
        printf("%s's sequel was... \n\n", film.name);
        print_movie(*film.sequel);
    }
}

int main(void)
{
    struct movie jurassic;
    struct movie lostworld;

    strcpy(jurassic.name, "Jurassic Park");
    jurassic.year = 1993;
    jurassic.sequel = &lostworld;

All works as intended, until this part:

strcpy(lostworld.name, "The Lost World: Jurassic Park");
lostworld.year = 1997;
lostworld.sequel = 0;

print_movie(jurassic);

return 0;
}

I've counted the number of characters in 'The Lost World: Jurassic Park' (28) and used that as the maximum buffer. problem is, It's not dynamic and when I execute the program, It prints The Lost World: Jurassic Par- and makes an error sound.

If increase the char buffer, for example, to 29, I get Warning C4820: 'movie' : '3' bytes padding added after data member 'name' I'm working in Visual Studio, I've set the compiler to assess warnings as errors, for learning purposes and I've used _CRT_SECURE_NO_WARNINGS.

What's happening here? Should I use malloc for the char? Thanks

batPerson
  • 59
  • 1
  • 9
  • 4
    `I've counted the number of characters in 'The Lost World: Jurassic Park' (28)` count again. – tkausl Jun 15 '17 at 02:08
  • 2
    length of `"The Lost World: Jurassic Park"` is **29**. And Need +1 i.e. **30**. – BLUEPIXY Jun 15 '17 at 02:11
  • 1
    Not directly related to your problem - but for practical reasons you should probably make the `print_movie` function accept a pointer rather than copying the entire list into the stack – M.M Jun 15 '17 at 02:32
  • You are using an array of characters to hold a string. But strings have overhead and you need space for that overhead. – David Schwartz Jun 15 '17 at 02:48

3 Answers3

2

C tries to keep things bounded so four byte variables (such as ints) start on an even multiple of four. The compiler will start the structure on an appropriate boundary so all you need to worry about is the variables inside the structure. When you ran your name field up to 30 to fit the 29 characters of "The Lost World: Jurassic Park" and the terminating null, the 32 bit year field got pushed forward to an even multiple of four leaving you with a few bytes of wasted space.

If I were doing this, I would order the structure by size, pointer first, integer second and array last. I would also make sure my array was sized such that the whole thing comes out to an even multiple of eight. That way, we can have an array of structures and everything will be aligned.

Yes, a malloc would work but then you have to remember to free the memory when you are done with it and worry about heap fragmentation.

user1683793
  • 1,213
  • 13
  • 18
  • "C tries to keep things bounded so four byte variables ... start on an even multiple of four" --> C does not specify this. It is architecture/compiler and its option dependent. "order the structure by size, ... That way, we can have an array of structures" --> leading a struct with aligned members is good but does not impact the ability/alignment of having an array of the structure. Unfortunately the focus on such small optimizations tales away from the larger inefficiencies of OP's approach. – chux - Reinstate Monica Jun 15 '17 at 13:18
  • Quite so. "C" does not worry about such things. Your compiler tries to keep things on boundaries for performance reasons. The compiler would rather waste a few bytes here and there to make some big performance gains. The Intel processor you are running on is pretty forgiving about such things. Some machines cause programs to crash horribly if you try to access data unaligned. The compilers have to go to great lengths to avoid unaligned accesses. – user1683793 Jun 15 '17 at 22:46
1

Should I use malloc for the char?

Oh yeah! With such wide ranging title lengths of movies, allocating memory for the title is the way to go. Some titles are long: Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb. Some are short: Pi. Further, I would allocate struct movie. Allocating will avoid the "'3' bytes padding" issue too as the members (int and 2 pointers) are certainly a multiple of non-padded sizes.

"12345678901234567890123456789"
"The Lost World: Jurassic Park" needs 29+1 for the '\0' to be saved as a string. Inadequate buffer size and no protection against buffer over-run caused OP's problem. @tkausl @BLUEPIXY

struct movie {
    // char name[28];
    char *name;
    int year;
    struct movie* sequel;
};

Consider using pointer to structure rather than a copy of the structure. @M.M

// void print_movie(struct movie film)
void print_movie(struct movie *film) { ...

When its time to allocate/copy the string, use strdup(). This function is not part of standard C yet is common. If you need to roll your own: sample code

struct movie *movie_create(char *name, int year, struct movie *sequel) {
  struct movie *m = malloc(sizeof *m);
  if (m) {
    m->name = strdup(name);
    if (m->name) {
      m->year = year;
      m->sequel = sequel;
    } else { // failed to allocate string
      free(m);
      m = NULL;   
    }
  }
  return m;
}

void movie_destroy(struct movie *m) {
  if (m) {
    free(m->name);
  }
  free(m);
}

int main(void) {
  struct movie *lostworld = movie_create("The Lost World: Jurassic Park", 1997, NULL);
  struct movie *jurassic = movie_create("Jurassic Park", 1993, lostworld);

  print_movie(jurassic);

  movie_destroy(lostworld);
  movie_destroy(jurassic);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

You should add the '\0' in the end of the name array when you use the 'printf' function. So the memory space of 'name' is not enough to store the name of the movie. The reason of the warning C4820 is that the structure of movie is 4 bytes aligned. Size of the struct movie should be equal to the multiple of 4. You can define the struct movie as following:

struct movie
{
    //maximum of length that you can save in name is 31. Last space of name array is '\0'. If the memory space is small, the length of array should be equal to  the multiple of 4. length % 4 must be 0 so that  the warning will be resolved.
    char name[32];
    int year;
    struct movie* sequel;
};
....
int main()
{
     strut movie lostworld;
     //append the '\0' in the last of the name array.
     memset(&lostworld,0,sizeof(lostworld));
     ....
     print_movie(jurassic);
     return 0;

}
WangYang
  • 466
  • 1
  • 5
  • 15
  • it's not the alignment of the structure is 4 bytes but the alignment of `int year;` is 4 bytes – phuclv Jun 15 '17 at 03:22
  • You are right. Maximum of type in struct is int. Thus the alignment of struct is sizeof(int) bytes---sizeof(int) is usually 4 bytes. Thank you for reminding. – WangYang Jun 18 '17 at 14:17