Выделение памяти для строки внутри структуры

Я знаю, что этот вопрос был вокруг, но я нашел ответы немного туманными, длинными или/и запутанными; поэтому я собираюсь специально ссылаться на свой код, чтобы полностью понять суть.

Так я получил эту структуру:

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, %dn", albums[0]->artist, albums[0]->title, albums[0]->year);
  printf("%s, %s, %dn", albums[1]->artist, albums[1]->title, albums[1]->year);

  char song[] = "song 1";

  add_song(albums[1], song);

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

ошибка сегментации при выдаче strncpy для добавления песни в " add_song ()".

что я делаю не так критично? как услышала куча раз нет" правильного " способа реализации в c, пока он работает, и он не глючит, все в порядке, но как Новичок было бы здорово получить некоторую осторожную обратную связь или советы по использованию распределения памяти вместе со сложными структурами данных.

спасибо! /s

5 ответов


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

это не очень хорошая идея. Вы должны петь x via a->songs[x], поэтому вам нужно выделить a->songs as (char**)malloc(sizeof(char*)*numsongs). Когда есть только одна песня, вы все равно должны поместить ее в под-указатель.

одна из причин, по которой вы segfaulting потому, что выше не имеет +1 для нуль, как у вас есть везде... Другое, что вы не добавили +1 до strncpy длина, так что на самом деле ничего не прекращается.


проблема strncpy() не будет null-завершить строку для вас:

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

так как вы говорите, что буфер имеет место только для самой строки. Решение здесь-просто использовать strcpy() - вы уверены, что буфер достаточно большой.

кроме этого:

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

освобождает структур, но не строки, на которые указывают эти структуры и у вас есть утечка памяти.


на мой взгляд, то, что вы делаете критически неправильно, не использует правильные инструменты : -)

одна проблема следующая строка:

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

вы выделяете количество байтов, равное длине первой песни, но вам нужен массив указателей символов. Это может работать по немой удаче, это первое название песни имеет больше символов, чем количество байтов, необходимых для количества указателей символов.

но лучше бы do

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

или даже расширить массив "песни" динамически и realloc при необходимости.

кстати, вы никогда не intialize songs_c ко всему, что означает, что вы, возможно, не выделили songs на всех.

Далее, вы выделяете albums С

albums = malloc( sizeof(struct album));

опять же, это может сработать по немой удаче, так как размер двух указателей может быть меньше размера struct album, но я думаю, что вы действительно имел в виду

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

все эти проблемы должны быть пойманы либо статическим анализом кода, либо инструментами анализа времени выполнения.


в функции initalbum переменная songs_c для альбома[1] не инициализируется. Это будет иметь значение мусора.

в функции add_song, поскольку индекс не инициализирован, он вызывает sEGV.


серьезно подумайте о замене этого:

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% портативный, так как он не стандартизирован. Конечно, ты следует сделать замену для всех мест, где вам нужно скопировать строку в динамически выделенную память.