2011-02-14 58 views
9

Tôi muốn kết thúc sự nhầm lẫn tôi với char **char ** và dereferencing con trỏ

Khi một lần tạo ra một mảng của mảng ký tự (chuỗi) như thế nào char ** thực hiện việc này?

tôi nhận được rằng char * là một con trỏ đến một char và rằng char *array[] là một mảng con trỏ char, nhưng chính xác những gì char ** làm và làm thế nào để làm điều đó?

Ngoài ra khi tôi nghe thấy từ dereferences nó làm cho tôi nghĩ rằng con trỏ được loại bỏ chính xác những gì dereference một con trỏ có nghĩa là gì? Thay đổi giá trị mà con trỏ trỏ đến?

Cảm ơn

Trả lời

10

"dereferencing" một con trỏ có nghĩa là truy cập vào các giá trị điểm con trỏ đến. Giả sử các tờ khai sau:

int a = 10; 
int *p = &a; 

Đây là bản đồ bộ nhớ giả của hai biến:

 
Item  Address  0x00 0x01 0x02 0x03 
----  -------  ---- ---- ---- ---- 
    a  0x80001000 0x00 0x00 0x00 0x0A 
    p  0x80001004 0x80 0x00 0x10 0x00 

a chứa giá trị số nguyên 10 p chứa địa chỉ của a (0x80001000). Nếu chúng tôi muốn truy cập nội dung của a thông qua p, chúng tôi dereferencep với nhà điều hành gián tiếp *. Do đó, biểu thức *p tương đương với biểu thức a. Nếu chúng tôi đã viết

*p = 16; 

đó là giống như viết

a = 16; 

Dưới đây là một đoạn mã ngắn cho thấy làm thế nào để sử dụng một đối tượng kiểu char ** để tạo ra một mảng các chuỗi:

#include <stdlib.h> 
#define N  20 // For this example, we will allocate 20 strings 
#define LENGTH 10 // of 10 characters each (not counting 0 terminator) 
... 
char **arr = malloc(sizeof *arr * N); 
if (arr) 
{ 
    size_t i; 
    for (i = 0; i < N; i++) 
    { 
    arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1)); 
    strcpy(arr[i], "   "); 
    } 
} 

Đi qua từng dòng một,

char **arr = malloc(sizeof *arr * N); 

phân bổ một khối phần tử N, mỗi phần tử đủ lớn để lưu trữ con trỏ đến char (sizeof *arr == sizeof (char *) từ loại *arr == char *) và gán giá trị con trỏ kết quả thành arr. IOW, arr trỏ đến con trỏ đầu tiên đến char, do đó loại char **. Lưu ý rằng nếu bạn tách ra việc khai báo và gọi hàm, nó sẽ trông như

char **arr; 
... 
arr = malloc(sizeof *arr * N); 

Chúng tôi muốn gán kết quả của malloc-arr, không phải những gì arrđiểm để.

if (arr) 

Có thể malloc không thành công, vì vậy chúng tôi muốn kiểm tra kết quả trước khi sử dụng. Trong trường hợp malloc không thành công, nó sẽ trả về giá trị con trỏ NULL.

{ 
    size_t i; 
    for (i = 0; i < N; i++) 
    { 
    arr[i] = malloc(sizeof *arr[i] * (LENGTH + 1)); 

Đối với mỗi con trỏ vật arr[i], chúng tôi phân bổ một khối bộ nhớ đủ lớn LENGTH + 1 yếu tố, mỗi đủ lớn để chứa một giá trị char (sizeof *arr[i] == sizeof (char), vì loại *arr[i] == char; lưu ý rằng sizeof (char) luôn là 1) và gán kết quả cho arr[i].

Vì chúng tôi phân bổ từng chuỗi với một cuộc gọi malloc riêng biệt, có thể chúng không tiếp giáp trong bộ nhớ. Đây là một bản đồ bộ nhớ cho thấy một thể kết quả của đoạn code trên: "* p1 == c, ví dụ: dereferencing p1 cho phép chúng ta đọc từ/viết thư cho c"

 
Item   Address  0x00 0x01 0x02 0x03 
----   -------  ---- ---- ---- ---- 
arr   0x80001000  0xA0 0xCC 0x00 0x00 
      ... 
arr[0]  0xA0CC0000  0xA0 0xCC 0x20 0x00  
arr[1]  0xA0CC0004  0xA0 0xCC 0x20 0x40 
arr[2]  0xA0CC0008  0xA0 0xCC 0x21 0x28 
      ... 
arr[19]  0xA0CC0014  0xA0 0xCC 0x23 0x10 
      ... 
arr[0][0] 0xA0CC2000  ' ' ' ' ' ' ' ' 
arr[0][4] 0xA0CC2004  ' ' ' ' ' ' ' ' 
arr[0][8] 0xA0CC2008  ' ' ' ' 0x00 0x?? 
      ... 
arr[1][0] 0xA0CC2040  ' ' ' ' ' ' ' ' 
arr[1][4] 0xA0CC2044  ' ' ' ' ' ' ' ' 
arr[1][8] 0xA0CC2048  ' ' ' ' 0x00 0x?? 
      ... 
+1

Xin lỗi tôi là một người mới, vì vậy tôi có thể sai, nhưng để hoàn toàn làm sạch sau khi phân bổ, bạn sẽ phải lặp qua tổng số chuỗi và arr miễn phí [i ] cũng như giải phóng toàn bộ mảng miễn phí (arr), đúng không? Hoặc nó sẽ đủ để chỉ 'miễn phí (arr);'? –

3

dereferencing một con trỏ có nghĩa là truy cập vào các giá trị con trỏ điểm tới. Ví dụ:

char c = 'c'; // This is a primitive value. You cannot dereference it. 
char* p1 = &c; // A pointer to the address of c 
char** p2 = &p1; // A pointer to the address of p1 
/* Now, the following is true: 
*p1 == c, i.e. dereferencing p1 allows us to read from/write to c. 
*p2 == p1 
**p2 == *(*p2) == *p1 = c - dereferencing p2 twice is c, too */ 

Lý do tại sao bạn sử dụng con trỏ đến c thay vì c trực tiếp là con trỏ cho phép bạn truy cập nhiều hơn 1 giá trị. Lấy ví dụ này:

char[4] str; 
char c0 = 'a', c1 = 'b', c3 = 'c', c4 = '\0'; 
str[0] = c0; str[1] = c1; str[2] = c2; str[3] = c3; 
str = "abc"; // Same as the above line 

Bây giờ giả sử chúng ta cần ký tự thứ hai. Chúng tôi có thể truy cập nó với c1. Nhưng như bạn có thể thấy, ký hiệu này thực sự rườm rà. Thêm vào đó, nếu chúng ta đọc chuỗi từ một tập tin thay vì viết nó, chúng ta sẽ phải làm những điều phức tạp. Thay vào đó, chúng ta chỉ cần viết

str[1] /* or */ *(str+1) 

Lưu ý rằng phần tử đầu tiên có index 0, thứ hai 1 - đó là lý do tại sao chúng tôi đang sử dụng 1 tại đây. Một char** biến điều này lên đến mười một - chúng tôi có một mảng của một loạt các ký tự. Giả sử chúng ta có một mảng như vậy, hãy gọi nó là input và cần phải tìm hiểu chiều dài của tất cả các chuỗi trong đó. Đây là cách chúng tôi sẽ thực hiện:

int lensum(char **input) { 
    int res = 0; 
    while (*input) { // This loops as long as the value input points to is not 0. 
     char* p = *input; // Make a copy of the value input currently points to 
     while (*p != '\0') { // Loop while the copy does not point to a char '\0' 
      res += 1; // We found a character 
      p++; // Check next character in the next iteration 
     } 
     input++; // Check next string in the next iteration 
    } 
    return res; 
} 
+0

Những gì dòng này được thực hiện trên? char * p1 = & c; ? – jarryd

+0

@alJaree Chính xác. & c là địa chỉ của c. – phihag

9

Con trỏ là loại chứa địa chỉ giá trị thay vì giữ giá trị thực. Vì vậy, trong trường hợp của char * p, một khi được phân bổ, p sẽ chứa một địa chỉ A. Dereferencing con trỏ có nghĩa là truy cập vào giá trị được lưu trữ tại địa chỉ A. Lý do bạn có thể lưu trữ các chuỗi trong một char * là bởi vì bộ nhớ được cấp phát là tiếp giáp. Vì vậy, A là một địa chỉ lưu trữ ký tự đầu tiên, A + 1 là một địa chỉ lưu trữ ký tự thứ hai và vân vân.

Trong trường hợp char ** pp, nó lưu trữ địa chỉ của một char *. Gọi địa chỉ này B. Vì vậy, dereferencing pp có nghĩa là truy cập giá trị tại địa chỉ B, xảy ra là một char *, điều này xảy ra để giữ một chuỗi. Trong cùng một cách, B + 1 (thực sự B + sizeof (char *)) lưu trữ giá trị tiếp theo, là một chuỗi khác.

Dereferencing pp hai lần (tức là ** pp) có nghĩa là bạn lần đầu tiên truy cập giá trị tại địa chỉ B, ví dụ là A và sau đó dereferencing một lần nữa để nhận giá trị tại địa chỉ A, là một số ký tự.

+0

Giải thích thực sự thú vị. Cảm ơn. – jarryd

3

Sơ đồ đáng giá 1000 từ. Hãy xem here

char, char * và char ** chỉ đơn giản là các loại mô tả biến (vùng bộ nhớ) là gì.

Sử dụng dereferencing như * variable thực sự nói để xử lý giá trị trong biến dưới dạng địa chỉ bộ nhớ và thực sự trả về giá trị tại địa chỉ đó. Đây là sự vô đạo đức.

** biến chỉ đơn giản là hai cấp độ gián tiếp. nghĩa là giá trị trong biến là địa chỉ bộ nhớ của một địa chỉ bộ nhớ khác của dữ liệu sẽ được trả lại.

Địa chỉ thường đến từ địa chỉ của nhà điều hành, & hoặc từ một cấp phát bộ nhớ chức năng/điều hành như new

+1

Cảm ơn :) 5 nữa để đi ..: P – jarryd

Các vấn đề liên quan