Khi bạn có con trỏ đến con trỏ trong C, bạn phải biết cách dữ liệu sẽ được sử dụng và được trình bày trong bộ nhớ. Bây giờ, điểm đầu tiên là hiển nhiên, và đúng cho bất kỳ biến nói chung: nếu bạn không biết làm thế nào một số biến sẽ được sử dụng trong một chương trình, tại sao có nó? :-). Điểm thứ hai thú vị hơn.
Ở cấp độ cơ bản nhất, con trỏ nhập T
trỏ tới một đối tượng loại T
. Ví dụ:
int i = 42;
int *pi = &i;
Bây giờ, pi
trỏ đến một int
. Nếu bạn muốn, bạn có thể làm cho một điểm con trỏ đến đầu tiên của nhiều đối tượng như:
int arr[10];
int *pa = arr;
int *pb = malloc(10 * sizeof *pb);
pa
tại chỉ để là người đầu tiên của một chuỗi các 10 (liền kề) int
giá trị, và giả định rằng malloc()
thành công, pb
điểm đến tập đầu tiên của bộ 10 (một lần nữa, tiếp giáp) int
s.
cũng áp dụng nếu bạn có một con trỏ đến một con trỏ:
int **ppa = malloc(10 * sizeof *ppa);
Giả sử rằng malloc()
thành công, bây giờ bạn có ppa
trỏ đến đầu tiên trong một chuỗi 10 int *
giá trị tiếp giáp.
Vì vậy, khi bạn làm:
char **tmp = malloc(sizeof(char *)*CR_MULTIBULK_SIZE);
tmp
điểm đến đối tượng đầu tiên char *
trong một chuỗi các CR_MULTIBULK_SIZE
đối tượng như vậy. Mỗi con trỏ ở trên không được khởi tạo, vì vậy, tmp[0]
đến tmp[CR_MULTIBULK_SIZE-1]
tất cả đều chứa rác. Một cách để khởi tạo chúng sẽ được malloc()
họ:
size_t i;
for (i=0; i < CR_MULTIBULK_SIZE; ++i)
tmp[i] = malloc(...);
Các ...
trên là kích thước của dữ liệu thứ i
chúng ta muốn. Nó có thể là một hằng số, hoặc nó có thể là một biến, tùy thuộc vào i
, hoặc giai đoạn của mặt trăng, hoặc một số ngẫu nhiên, hoặc bất cứ điều gì khác. Điểm chính cần lưu ý là bạn có các cuộc gọi CR_MULTIBULK_SIZE
tới malloc()
trong vòng lặp và rằng trong khi mỗi malloc()
sẽ trả về cho bạn một khối bộ nhớ liền kề, tiếp giáp không được đảm bảo qua các cuộc gọi malloc()
. Nói cách khác, cuộc gọi thứ hai malloc()
không được đảm bảo để trả lại con trỏ bắt đầu ngay khi dữ liệu của malloc()
trước đó kết thúc.
Để làm cho mọi việc cụ thể hơn, chúng ta hãy giả CR_MULTIBULK_SIZE
là 3. Trong hình ảnh, dữ liệu của bạn có thể trông như thế này:
+------+ +---+---+
tmp: | |--------+ +----->| a | 0 |
+------+ | | +---+---+
| |
| |
| +------+------+------+
+-------->| 0 | 1 | 2 |
+------+------+------+
| |
| | +---+---+---+---+---+
| +--->| t | e | s | t | 0 |
+------+ +---+---+---+---+---+
|
|
| +---+---+---+
+--->| h | i | 0 |
+---+---+---+
tmp
điểm đến một khối tiếp giáp của 3 char *
giá trị. Đầu tiên của con trỏ, tmp[0]
, trỏ đến khối liền kề 3 char
giá trị.Tương tự, tmp[1]
và tmp[2]
trỏ đến 5 và 2 char
s tương ứng. Nhưng bộ nhớ được trỏ đến bởi tmp[0]
đến tmp[2]
không tiếp giáp với tổng thể.
Vì memcpy()
bản sao bộ nhớ liền kề, những gì bạn muốn làm không thể được thực hiện bởi một memcpy()
. Hơn nữa, bạn cần phải biết cách phân bổ mỗi tmp[i]
. Vì vậy, nói chung, những gì bạn muốn làm cần có một vòng lặp:
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);
}
Như trên, bạn có thể gọi memcpy()
bên trong vòng lặp, do đó bạn không cần vòng lặp lồng nhau trong mã của bạn. (Nhiều khả năng memcpy()
được thực hiện với một vòng lặp, do đó hiệu quả là như nếu bạn đã lồng vòng.)
Bây giờ, nếu bạn có mã như:
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;
Ie, bạn phân bổ không gian tiếp giáp cho tất cả các con trỏ trong một malloc()
cuộc gọi, sau đó bạn có thể sao chép tất cả dữ liệu mà không có một vòng lặp trong mã của bạn:
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;
Từ trên, câu trả lời đơn giản là, nếu bạn có nhiều hơn một malloc()
phân bổ bộ nhớ cho tmp[i]
, sau đó bạn sẽ cần một vòng lặp để sao chép tất cả dữ liệu.
Nó hoàn toàn phụ thuộc vào cách "mảng đa chiều" của bạn được xây dựng. Hiển thị mã tạo ra nó. – caf
nếu bạn không có thứ nguyên mảng, thì bạn cũng không thể sao chép kích thước đó bằng vòng lặp. –
@ John Knoeller: Cảm ơn. Tôi đã cập nhật mô tả. – dan