【问题标题】:C structs and char array. Warning C4820: '3' bytes padding added after data memberC 结构和字符数组。警告 C4820:在数据成员之后添加了“3”字节填充
【发布时间】:2017-11-17 08:24:11
【问题描述】:

我正在构建一个结构来保存电影的信息。当电影被打印时,它会递归地打印它的续集信息。

    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;

所有工作都按预期进行,直到这部分:

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

print_movie(jurassic);

return 0;
}

我计算了《失落的世界:侏罗纪公园》(28) 中的字符数,并将其用作最大缓冲区。问题是,它不是动态的,当我执行程序时,它会打印 The Lost World: Jurassic Par- 并发出错误声音。

如果增加 char 缓冲区,例如增加到 29,我得到 Warning C4820: 'movie' : '3' bytes padding added after data member 'name' 我在 Visual Studio 中工作,出于学习目的,我已将编译器设置为将警告评估为错误,并且我使用了 _CRT_SECURE_NO_WARNINGS。

这里发生了什么?我应该使用 malloc 作为字符吗?谢谢

【问题讨论】:

  • I've counted the number of characters in 'The Lost World: Jurassic Park' (28) 再次计数。
  • "The Lost World: Jurassic Park" 的长度为 29。并且需要 +1 即 30.
  • 与您的问题没有直接关系 - 但出于实际原因,您可能应该让 print_movie 函数接受一个指针,而不是将整个列表复制到堆栈中
  • 您正在使用一个字符数组来保存一个字符串。但是字符串有开销,你需要空间来承受开销。

标签: c visual-studio recursion struct char


【解决方案1】:

使用“printf”函数时,应在名称数组的末尾添加“\0”。所以'name'的内存空间不足以存储电影的名字。 warning C4820的原因是movie的结构是4字节对齐的。 struct movie 的大小应该等于 4 的倍数。 您可以将结构电影定义如下:

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;

}

【讨论】:

  • 不是结构的对齐是4字节而是int year;的对齐是4字节
  • 你是对的。 struct 中的最大类型是 int。因此 struct 的对齐方式是 sizeof(int) bytes---sizeof(int) 通常是 4 bytes。谢谢提醒。
【解决方案2】:

我应该使用 malloc 作为 char 吗?

哦,是的!对于如此广泛的电影标题长度,为标题分配内存是要走的路。有些标题很长:Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb。有些很短:Pi。此外,我会分配struct movie。分配也将避免“'3'字节填充”问题,因为成员(int 和 2 个指针)肯定是非填充大小的倍数。

"12345678901234567890123456789"
"The Lost World: Jurassic Park" 需要 29+1 才能将 '\0' 保存为字符串。缓冲区大小不足和没有针对缓冲区溢出的保护导致了 OP 的问题。 @tkausl@BLUEPIXY

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

考虑使用指向结构的指针,而不是结构的副本。 @M.M

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

当分配/复制字符串时,使用strdup()。此函数不是标准 C 的一部分,但很常见。如果您需要自己滚动: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);
}

【讨论】:

    【解决方案3】:

    C 试图保持事物有界,因此四个字节变量(例如整数)以 4 的偶数倍数开始。编译器将在适当的边界上启动结构,因此您只需要担心结构内的变量。当您将名称字段最多运行到 30 以适应“失落的世界:侏罗纪公园”的 29 个字符和终止的 null 时,32 位年份字段被推到四的偶数倍,从而使您浪费了几个字节空间。

    如果我这样做,我会按大小排序结构,首先是指针,其次是整数,最后是数组。我还要确保我的数组的大小使得整个数组的大小达到八的偶数倍。这样,我们可以有一个结构数组,并且所有内容都会对齐。

    是的,malloc 可以工作,但你必须记住在完成后释放内存并担心堆碎片。

    【讨论】:

    • “C 试图保持事物有界,因此四个字节变量......从 4 的偶数倍数开始”--> C 没有指定这一点。它取决于架构/编译器及其选项。 “按大小对结构进行排序,......这样,我们可以拥有一个结构数组”-> 引导一个具有对齐成员的结构是好的,但不会影响拥有结构数组的能力/对齐。不幸的是,对这种小优化的关注远离了 OP 方法的更大效率低下。
    • 确实如此。 “C”不担心这些事情。出于性能原因,您的编译器会尝试将事物保持在边界上。编译器宁愿在这里和那里浪费几个字节来获得一些巨大的性能提升。您正在运行的英特尔处理器对此类事情非常宽容。如果您尝试访问未对齐的数据,某些机器会导致程序严重崩溃。编译器必须竭尽全力避免未对齐的访问。
    猜你喜欢
    • 2017-02-23
    • 2020-11-07
    • 1970-01-01
    • 2018-06-30
    • 2016-12-09
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 2015-09-23
    相关资源
    最近更新 更多