Чтобы полностью скопировать указатели и значения ( на что они указывают ), вам необходимо знать три вещи:
- Сколько указателей я могу сделатьесть?
- Нужно ли мне также копировать то, на что они указывают ?;и если да, то
- Сколько указателей у меня есть, что указывает на то, что мне нужно скопировать.
Когда вы объявляете указатель на указатель на символ , онэто просто неинициализированный указатель.Чтобы обеспечить возможность адресации более чем одного адреса, вы затем выделяете некоторое необходимое количество указателей (скажем, MAXPTRS
).Например:
#define MAXPTRS 32
...
char **values = NULL;
values = malloc (MAXPTRS * sizeof *values); /* allocate MAXPTRS pointers */
if (values == NULL) { /* validate/handle error */
perror ("malloc-values");
/* handle error */
}
Теперь у вас есть MAXPTRS
(32
) указатели для работы.Если вы затем выделите хранилище для, скажем, 8
из них, например,
size_t count = 0;
while (fgets (buf, sizeof buf, stdin)) {
size_t len = strlen (buf);
values[count] = malloc (len + 1);
if (!values[count]) {
perror ("malloc-values[count]");
break;
}
memcpy (values[count++], buf, len + 1);
}
Итак, на этом этапе в примере у вас выделено 32
указателей и 8
из этих указателей, указывающих на блоки памятиудержание строк, считанных из stdin
.
Если вы хотите скопировать всю структуру, вы должны не только скопировать исходные указатели 32
, но и блоки памяти 8
, на которые указывают инициализированные указатели,Если вам не удастся скопировать указанные блоки памяти и просто назначить указатели (например, val[i] = values[i]
), то любые изменения в оригинале, например, values[2]
, будут автоматически отражены в val[2]
(это может или не может бытьто, что вы хотите)
Чтобы полностью скопировать указатель values
на указатель на символ и вернуть val
, содержащий такое же количество указателей, с копией каждой выделенной строки, содержащейся в исходном массиве values
указатели, вам нужна «глубокая копия», где вы копируете не только указатели, но и дублируете их содержимое, например,
char **copystrings (const char **values, size_t nptrs, size_t filled)
{
char **val = NULL;
val = malloc (nptrs * sizeof *val); /* allocate nptrs pointers */
if (!val) { /* validate */
perror ("malloc-val");
return NULL;
}
for (size_t i = 0; i < filled; i++) { /* loop over each filled ptr */
size_t len = strlen (values[i]); /* get length of string */
val[i] = malloc (len + 1) /* allocate storare val[i] */
if (!val[i]) { /* validate */
perror ("malloc-val[i]");
break;
}
memcpy (val[i], values[i], len + 1); /* copy to val[i] */
}
return val; /* return val (may contain less than filled allocated) */
}
( примечание: не скомпилировано, также будетРекомендуется передать filled
в качестве указателя и обновить filled
значением i
перед возвратом, чтобы обеспечить возможность проверки того, что все блоки памяти для filled
указателей были продублированы. В противном случае ошибка malloc
val[i]
приведет к тому, что будет выделено и скопировано менее filled
, и вы не сможете узнать - если вы не использовали calloc
для обнуления новой памяти при выделении для val
)
Глубокая копия позволяет изменять скопированные значения без изменения данных по адресам, указанным указателями в values
.Если вы не изменяете данные (например, вы просто хотите отсортировать указатели с помощью qsort
, тогда нет необходимости в «глубоком копировании», а вам нужно только выделить количество указателей filled
и назначить адрес изvalues
до val
.
Понимание того, что вам нужно, и различия в том, как вы добиваетесь его достижения, является ключевым.
(спасибо @ 4386427 за ловлюпара упущений)