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