Когда у вас есть указатель на указатель в C, вы должны знать, как данные будут использоваться и располагаться в памяти. Итак, первый пункт очевиден и справедлив для любой переменной в целом: если вы не знаете, как какая-то переменная будет использоваться в программе, зачем она вам? :-). Второй момент интереснее.
На самом базовом уровне указатель типа T
указывает на один объект типа T
. Например:
int i = 42;
int *pi = &i;
Теперь pi
указывает на один int
. При желании вы можете сделать так, чтобы указатель указывал на первый из многих таких объектов:
int arr[10];
int *pa = arr;
int *pb = malloc(10 * sizeof *pb);
pa
теперь указывает на первое из последовательности из 10 (непрерывных) значений int
, и если malloc()
выполняется успешно, pb
указывает на первое из другого набора из 10 (опять же, смежных) int
s.
То же самое применимо, если у вас есть указатель на указатель:
int **ppa = malloc(10 * sizeof *ppa);
Предполагая, что malloc()
выполнено успешно, теперь у вас есть ppa
, указывающий на первое из последовательности из 10 смежных значений int *
.
Итак, когда вы делаете:
char **tmp = malloc(sizeof(char *)*CR_MULTIBULK_SIZE);
tmp
указывает на первый char *
объект в последовательности из CR_MULTIBULK_SIZE
таких объектов. Каждый из приведенных выше указателей не инициализирован, поэтому все указатели с tmp[0]
по tmp[CR_MULTIBULK_SIZE-1]
содержат мусор. Один из способов инициализировать их — malloc()
их:
size_t i;
for (i=0; i < CR_MULTIBULK_SIZE; ++i)
tmp[i] = malloc(...);
...
выше — это размер i
th данных, которые нам нужны. Это может быть константа или переменная, зависящая от i
, или фазы луны, или случайного числа, или чего-то еще. Важно отметить, что у вас есть CR_MULTIBULK_SIZE
вызовов malloc()
в цикле, и хотя каждый malloc()
будет возвращать вам непрерывный блок памяти, непрерывность не гарантируется для malloc()
вызовов. Другими словами, второй вызов malloc()
не гарантирует возврата указателя, который начинается точно там, где заканчивались данные предыдущего malloc()
.
Для большей конкретики предположим, что CR_MULTIBULK_SIZE
равно 3. На картинках ваши данные могут выглядеть так:
+------+ +---+---+
tmp: | |--------+ +----->| a | 0 |
+------+ | | +---+---+
| |
| |
| +------+------+------+
+-------->| 0 | 1 | 2 |
+------+------+------+
| |
| | +---+---+---+---+---+
| +--->| t | e | s | t | 0 |
+------+ +---+---+---+---+---+
|
|
| +---+---+---+
+--->| h | i | 0 |
+---+---+---+
tmp
указывает на непрерывный блок из 3 значений char *
. Первый из указателей, tmp[0]
, указывает на непрерывный блок из 3 значений char
. Точно так же tmp[1]
и tmp[2]
указывают на 5 и 2 char
соответственно. Но память, на которую указывает от tmp[0]
до tmp[2]
, не является непрерывной в целом.
Поскольку memcpy()
копирует непрерывную память, то, что вы хотите сделать, не может быть выполнено одним memcpy()
. Кроме того, вам нужно знать, как каждый tmp[i]
был выделен. Итак, в общем, то, что вы хотите сделать, нуждается в цикле:
char **realDest = malloc(CR_MULTIBULK_SIZE * sizeof *realDest);
/* assume malloc succeeded */
size_t i;
for (i=0; i < CR_MULTIBULK_SIZE; ++i) {
realDest[i] = malloc(size * sizeof *realDest[i]);
/* again, no error checking */
memcpy(realDest[i], tmp[i], size);
}
Как и выше, вы можете вызвать memcpy()
внутри цикла, поэтому вам не нужен вложенный цикл в вашем коде. (Скорее всего, memcpy()
реализован с циклом, поэтому эффект такой, как если бы у вас были вложенные циклы.)
Теперь, если у вас есть код вроде:
char *s = malloc(size * CR_MULTIBULK_SIZE * sizeof *s);
size_t i;
for (i=0; i < CR_MULTIBULK_SIZE; ++i)
tmp[i] = s + i*CR_MULTIBULK_SIZE;
То есть, вы выделили непрерывное пространство для всех указателей в одном вызове malloc()
, тогда вы можете скопировать все данные без цикла в своем коде:
size_t i;
char **realDest = malloc(CR_MULTIBULK_SIZE * sizeof *realDest);
*realDest = malloc(size * CR_MULTIBULK_SIZE * sizeof **realDest);
memcpy(*realDest, tmp[0], size*CR_MULTIBULK_SIZE);
/* Now set realDest[1]...realDest[CR_MULTIBULK_SIZE-1] to "proper" values */
for (i=1; i < CR_MULTIBULK_SIZE; ++i)
realDest[i] = realDest[0] + i * CR_MULTIBULK_SIZE;
Из вышесказанного простой ответ: если у вас было более одного malloc()
для выделения памяти для tmp[i]
, то вам понадобится цикл для копирования всех данных.
person
Alok Singhal
schedule
09.02.2010
void * memcpy(void *dst, const void *src, size_t len);
Вы уверены, что используете его правильно? - person dmckee --- ex-moderator kitten   schedule 09.02.2010tmp
— этоchar **
, а аргументmemcpy
— этоvoid *
. - person dmckee --- ex-moderator kitten   schedule 09.02.2010tmp
?memcpy
, что вы делаете, неправильно почти в любом случае, но мы не можем сказать вам, почему, если вы не уточните, что находится на другом конце. И я не собираюсь копаться в тысяче строк библиотечного кода, чтобы понять это за вас. Я предполагаю, что вещь, возвращаемая библиотекой, должна быть непрозрачной, и вы должны попросить библиотеку передать вам нужные вам строки. - person dmckee --- ex-moderator kitten   schedule 09.02.2010