为结构内的字符串分配内存

时间:2011-02-24 13:50:40

标签: c malloc struct

我知道这个问题已经存在,但我发现答案有点模糊,长时间或/和令人困惑;所以我将专门提到我的代码,以便完全明白这一点。

所以我得到了这个结构:

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的

5 个答案:

答案 0 :(得分:3)

问题是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]);

只会释放结构,但不会释放那些结构所指向的字符串,而且你会发生内存泄漏。

答案 1 :(得分:3)

if (index == 0) {
    a->songs = malloc( strlen(song) );
} else a->songs[index] = malloc( strlen(song)+1 );

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

你是segfaulting的一个原因是因为上面的NUL没有+1,就像你在其他地方一样......另一个原因是你没有将+1添加到strncpy长度,因此实际上没有终止。

答案 2 :(得分:1)

在我看来,你做错的是不使用适当的工具: - )

一个问题是以下一行:

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

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

但是最好做

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

甚至可以动态扩展'songs'数组,并在需要时展开realloc

顺便说一下,您永远不会将songs_c初始化为任何内容,这意味着您可能根本没有分配songs

此外,您使用

分配albums
albums = malloc( sizeof(struct album));

同样,这可能会因运气不好而起作用,因为两个指针的大小可能小于struct album的大小,但我认为你的意思是

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

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

答案 3 :(得分:0)

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

在函数add_song中因为index未初始化,所以它导致sEGV。

答案 4 :(得分:0)

认真考虑更换:

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%可移植的,因为它没有很好地标准化。当然,您应该替换需要将字符串复制到动态分配的内存中的所有位置。

相关问题