When you have a pointer to a pointer in C, you have to know how the data is going to be used and laid out in the memory. Now, the first point is obvious, and true for any variable in general: if you don't know how some variable is going to be used in a program, why have it? :-). The second point is more interesting.
At the most basic level, a pointer to type T
points to one object of type T
. For example:
int i = 42;
int *pi = &i;
Now, pi
points to one int
. If you wish, you can make a pointer point to the first of many such objects:
int arr[10];
int *pa = arr;
int *pb = malloc(10 * sizeof *pb);
pa
now points to the first of a sequence of 10 (contiguous) int
values, and assuming that malloc()
succeeds, pb
points to the first of another set of 10 (again, contiguous) int
s.
The same applies if you have a pointer to a pointer:
int **ppa = malloc(10 * sizeof *ppa);
Assuming that malloc()
succeeds, now you have ppa
pointing to the first of a sequence of 10 contiguous int *
values.
So, when you do:
char **tmp = malloc(sizeof(char *)*CR_MULTIBULK_SIZE);
tmp
points to the first char *
object in a sequence of CR_MULTIBULK_SIZE
such objects. Each of the pointers above is not initialized, so tmp[0]
to tmp[CR_MULTIBULK_SIZE-1]
all contain garbage. One way to initialize them would be to malloc()
them:
size_t i;
for (i=0; i < CR_MULTIBULK_SIZE; ++i)
tmp[i] = malloc(...);
The ...
above is the size of the i
th data we want. It could be a constant, or it could be a variable, depending upon i
, or the phase of the moon, or a random number, or anything else. The main point to note is that you have CR_MULTIBULK_SIZE
calls to malloc()
in the loop, and that while each malloc()
is going to return you a contiguous block of memory, the contiguity is not guaranteed across malloc()
calls. In other words, the second malloc()
call is not guaranteed to return a pointer that starts right where the previous malloc()
's data ended.
To make things more concrete, let's assume CR_MULTIBULK_SIZE
is 3. In pictures, your data might look like this:
+------+ +---+---+
tmp: | |--------+ +----->| a | 0 |
+------+ | | +---+---+
| |
| |
| +------+------+------+
+-------->| 0 | 1 | 2 |
+------+------+------+
| |
| | +---+---+---+---+---+
| +--->| t | e | s | t | 0 |
+------+ +---+---+---+---+---+
|
|
| +---+---+---+
+--->| h | i | 0 |
+---+---+---+
tmp
points to a contiguous block of 3 char *
values. The first of the pointers, tmp[0]
, points to a contiguous block of 3 char
values. Similarly, tmp[1]
and tmp[2]
point to 5 and 2 char
s respectively. But the memory pointed to by tmp[0]
to tmp[2]
is not contiguous as a whole.
Since memcpy()
copies contiguous memory, what you want to do can't be done by one memcpy()
. Further, you need to know how each tmp[i]
was allocated. So, in general, what you want to do needs a loop:
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);
}
As above, you can call memcpy()
inside the loop, so you don't need nested loop in your code. (Most likely memcpy()
is implemented with a loop, so the effect is as if you had nested loops.)
Now, if you had code like:
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;
I.e., you allocated contiguous space for all the pointers in one malloc()
call, then you can copy all the data without a loop in your code:
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;
From the above, the simple answer is, if you had more than one malloc()
to allocate memory for tmp[i]
, then you will need a loop to copy all the data.