многомерный массив символов?

То, что я пытаюсь сделать, это создать двумерный массив символьных строк. Следующие ошибки сегмента происходят мгновенно, что не так?

void add2(char***b, char *i)
{
    if (!i) {
       b[0][0] = (char*) malloc(120);
       sprintf(b[0][0], "%s", "hithere");
       b[0][1] = (char*) malloc(120);
       sprintf(b[0][1], "%s", "bithere");
    } else {
       strcat(b[0][0], "\\\\");
       strcat(b[0][0], i);
       strcat(b[0][1], "\\\\");
       strcat(b[0][1], i);
    }

}
void add1(char ***b)
{
 add2(b,NULL);
 add2(b,"one");
 add2(b,"two");
 add2(b,"three");
}

int main()
{
 char **keys[2] = {0};
 int i,j;

 add1(keys);

 for (i = 0; keys[i]; i++)
     for (j = 0; keys[j]; j++)
     {
         fprintf(stderr, "%s\n", keys[i][j]);
         free(keys[i][j]);
     }

}

person user105033    schedule 06.10.2009    source источник


Ответы (2)


Когда вы объявляете ключи массива, вы сообщаете компилятору, что хотите работать с массивом из двух указателей на указатели на символы, и просите его инициализировать эти указатели на указатель на char значением NULL. Все хорошо.

Затем вы звоните add1(). Все хорошо.

Затем вы вызываете add2() и пытаетесь поместить в первый элемент b[0] возвращаемое значение из malloc(). Но значение b[0] равно NULL. NULL был помещен туда в функции main(). b[0] не имеет элементов!

Когда у вас есть массивы массивов (массивов...) в виде указателей, вам нужно malloc()free()) каждый уровень отдельно.


Изменить

#include <stdlib.h>

int main()
{
    char **keys[2] = {0};
    keys[0] = malloc(20 * sizeof *keys[0]); /* 20 strings */
    keys[1] = malloc(20 * sizeof *keys[1]); /* 20 strings */

    for (int k=0; k<20; k++) {
        keys[0][k] = malloc(120); /* string with 119 chars + '\0' */
        keys[1][k] = malloc(120); /* string with 119 chars + '\0' */
    }

    /* use k[0][0] through k[0][19] */
    /* use k[1][0] through k[1][19] */

    for (int k=0; k<20; k++) {
        free(keys[0][k]);
        free(keys[1][k]);
    }
    free(keys[0]);
    free(keys[1]);

    return 0;
}

Я поместил все это в функцию main(), но это та же идея, если malloc()s и free()s находятся в своих собственных функциях.

person pmg    schedule 06.10.2009
comment
так как мне malloc от b[0] до b[n]? - person user105033; 07.10.2009
comment
@pxl: на самом деле, если быть точным, это должно быть malloc(sizeof(char**) * n);, хотя на практике sizeof(char**) совпадает с sizeof(char*). Это основная причина, по которой мне нравится использовать sizeof с тем, для чего я выделяю память: pointer = malloc(n * sizeof *pointer);. Мне все равно, указывает ли указатель на массив структур или структуру с массивами или что-то еще; malloc() всегда получает правильный размер :) - person pmg; 07.10.2009
comment
ну, вы никогда не можете быть слишком уверены... sizeof(char**) будет правильным способом сделать это. - person pxl; 07.10.2009

Общая процедура динамического выделения 2D-массива char * выглядит примерно так (для любого другого типа T замените char * на нужный тип):

char ***new2DArr(size_t rows, size_t cols)
{
  char ***newArr = NULL;
  size_t i;
  newArr = malloc(sizeof *newArr * rows);
  if (newArr)
  {
    for (i = 0; i < rows; i++)
    {
      newArr[i] = malloc(sizeof *newArr[i] * cols);
      if (newArr[i])
      {
        /* initialize or assign newArr[i][0] - newArr[i][cols-1] here */
      }
    }
  }
  return newArr;
}

Это предполагает, что вы заранее знаете, сколько строк и столбцов вам нужно. Обратите внимание, что вы выделили таблицу указателей; вам все равно придется выделять память для каждой записи строки, например:

char **myArr = new2DArr(10, 10);
myArr[0][0] = malloc(strlen("Hello, World") + 1);
if (myArr[0][0])
{
    strcpy(myArr[0][0], "Hello, World");
}

Если вы хотите добавить новые строки или новые записи в существующие строки, вам придется вести дополнительную бухгалтерию.

Вот один пример, который расширяет одну строку:

char **extendRow(char **row, char *newEntry, size_t *numEntries)
{
  char **tmp = realloc(row, *numEntries + 1);
  if (tmp)
  {
    row = tmp;
    row[*numEntries] = malloc(strlen(newEntry) + 1);
    if (row[*numEntries])
    {
      strcpy(row[*numEntries], newEntry);
      (*numEntries)++;
    }
  }
  return row;
}

И вы бы назвали это как

table[i] = extendRow(table[i], "This is a test", &entryCount);

Результат присваивается обратно table[i] в случае изменения значения указателя на realloc(). Я делаю это таким образом, вместо того, чтобы передавать указатель на table[i], просто чтобы свести гимнастику указателя к минимуму.

person John Bode    schedule 08.10.2009