【问题标题】:Allocating memory for a string inside a struct为结构内的字符串分配内存
【发布时间】:2011-02-24 13:50:39
【问题描述】:

我知道这个问题一直存在,但我发现答案有点模糊、冗长或/和令人困惑;所以我将专门参考我的代码以充分理解这一点。

所以我得到了这个结构:

typedef struct album {
  unsigned int year;
  char *artist;
  char *title;
  char **songs;
  int songs_c;
} album ;

以下功能:

struct album* init_album(char *artist, char *album, unsigned int year){
  struct album *a;
  a= malloc( sizeof(struct album) );
  a->artist = malloc( strlen(artist) + 1);
  strncpy(a->artist, artist, strlen(artist));
  a->title = malloc( strlen(album) +  1);
  strncpy(a->title, album, strlen(album));
  a->year = year;
  return a;
}

void add_song(struct album *a, char *song){
  int index = a->songs_c;
  if (index == 0){
    a->songs = malloc( strlen(song) );
  } else a->songs[index] = malloc( strlen(song)+1 );

  strncpy(a->songs[index], song, strlen(song));
  a->songs_c= a->songs_c+1;
}

还有一个主要功能:

int main(void){
  char *name;
  char artist[20] = "The doors";
  char album[20] = "People are strange";
  int year = 1979;

  struct album *a;
  struct album **albums;

  albums = malloc( sizeof(struct album));

  albums[0] = init_album((char *)"hihi", (char *)"hoho", 1988);

  albums[1] = init_album((char *)"hihi1", (char *)"hoho1", 1911);

  printf("%s, %s, %d\n", albums[0]->artist, albums[0]->title, albums[0]->year);
  printf("%s, %s, %d\n", albums[1]->artist, albums[1]->title, albums[1]->year);

  char song[] = "song 1\0";

  add_song(albums[1], song);

  free(albums[0]);
  free(albums[1]);
}

发出 strncpy 以在“add_song()”中添加歌曲时出现分段错误。

我在做什么严重错误?正如多次听到的那样,c 中没有“正确”的实现方式,只要它有效并且没有错误,没关系,但作为初学者,获得一些关于使用内存分配的谨慎反馈或建议会很棒以及复杂的数据结构。

非常感谢! /s

【问题讨论】:

  • char song[] = "song 1\0";这会添加两个空终止字符。一旦你使用“”,编译器就会为你添加一个不可见的空终止,你不必手动做。 "x" 与 {'x', '\0'} 相同。
  • @Vlad 这是一个艰难的选择。要么用 C/C++ 编写,将程序员 90% 的时间用于内存管理,要么选择另一种语言,将 90% 的程序执行时间用于同一目的。 =)
  • @Lundin:不完全正确。我想说,如果您需要在关键路径上分配/释放内存 - 这是一个糟糕的设计,无论您使用 C 还是 C++ 都是一个问题,否则使用 C++ 中的std::string 不会损害您的性能,特别是如果你有一个对象池(甚至是无锁对象池)。

标签: c malloc struct


【解决方案1】:
if (index == 0) {
    a->songs = malloc( strlen(song) );
} else a->songs[index] = malloc( strlen(song)+1 );

这不是一个好主意。您必须通过a->songs[x] 发送x,因此您需要将a->songs 分配为(char**)malloc(sizeof(char*)*numsongs)。当只有一首歌曲时,仍应将其放入子指针中。

你出现段错误的一个原因是因为上面没有像你在其他地方一样的 NUL 的 +1...另一个是你没有将 +1 添加到 strncpy 长度,因此实际上没有任何内容被终止。

【讨论】:

  • 我打算用这个功能动态加歌,每次加歌都malloc有效吗?我之前的分配/使用的内存不会丢失吗?
【解决方案2】:

问题是strncpy() 不会为您终止字符串:

a->artist = malloc( strlen(artist) + 1);
strncpy(a->artist, artist, strlen(artist)); // null terminator is not placed

因为你告诉它缓冲区只有字符串本身的空间。这里的解决方案是只使用strcpy() - 你肯定知道缓冲区足够大。

还有这个:

free(albums[0]);
free(albums[1]);

只会释放结构,但不会释放从这些结构指向的字符串,并且会出现内存泄漏。

【讨论】:

  • 为什么 strcpy 而不是 strncpy 与 strlen(artist)+1 ?
  • @Smokie:会的,但这没有任何意义——你会沿着字符串和过度设计的代码进行额外的扫描。 strcpy() 就是你的意思,所以就用它吧。
【解决方案3】:

在我看来,你做的严重错误是没有使用正确的工具:-)

一个问题是下面这行:

a->songs = malloc( strlen(song) );

您分配的字节数等于第一首歌曲的长度,但您需要一个 char 指针数组。这可能是靠运气,第一首歌曲标题的字符数超过了使用的 char 指针数量所需的字节数。

但是这样做会更好

a->songs = calloc(max_number_of_songs, sizeof(char*));

甚至在需要时动态扩展“歌曲”数组和realloc

顺便说一句,您从未将songs_c 初始化为任何东西,这意味着您可能根本没有分配songs

此外,您将albums 分配给

albums = malloc( sizeof(struct album));

同样,这可能是靠运气,因为两个指针的大小可能小于struct album 的大小,但我认为你的意思是

albums = calloc(2, sizeof(struct album *));

所有这些问题都应该通过静态代码分析或运行时分析工具来捕获。

【讨论】:

    【解决方案4】:

    在initalbum 函数中,album[1] 的songs_c 变量未初始化。这将有一个垃圾值。

    在函数add_song中因为index没有初始化,导致sEGV。

    【讨论】:

      【解决方案5】:

      认真考虑替换这个:

      a->artist = malloc(strlen(artist) + 1);
      strncpy(a->artist, artist, strlen(artist));
      

      用这个:

      a->artist = my_strdup(artist);
      

      地点:

      char * my_strdup(const char *s)
      {
        char *out = NULL;
      
        if(s != NULL)
        {
            const size_t len = strlen(s);
            if((out = malloc(len + 1)) != NULL)
               memcpy(out, s, len + 1);
        }
        return out;  
      }
      

      我确实认为后者更清晰。它在功能方面也更好,因为strncpy() 具有可怕的语义,在我看来确实应该避免。此外,我的解决方案很可能更快。如果您的系统有strdup(),您可以直接使用它,但它不是 100% 可移植的,因为它没有很好地标准化。当然,您应该替换所有需要将字符串复制到动态分配的内存中的地方。

      【讨论】:

        猜你喜欢
        • 2022-11-28
        • 2018-08-05
        • 1970-01-01
        • 1970-01-01
        • 2021-11-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-27
        相关资源
        最近更新 更多