2013-03-14 65 views
5

Trong dưới đây có thể một người nào đó giải thích các kết quả đầu ra thu được khi thực hiện:Hai mảng chiều với con trỏ

#include<stdio.h> 
void main() 
{ 
char a[5][10]={"one","two","three","four","five"}; 
char **str=a; 
printf("%p ", &a[0]); 
printf("\n%p ", &str[0]); 
printf("\n%p ", &str[3]); 
printf("\n%p ", &str[1][56]); 
printf("\n%p ", &(*(*(str+4)+1))); 
} 

Dưới đây là kết quả quan sát:

0xbf7f6286 
0xbf7f6286 
0xbf7f6292 
0x38 
0x1 
  • & a [0] là địa chỉ của địa chỉ bắt đầu của mảng
  • & str [0] là thesame

    Một số người có thể giải thích cách địa chỉ của str [3] là 0xbf7f6292. Theo hiểu biết của tôi, số & str [0] và & str [3] phải chênh lệch 30 byte.

    Cũng vui lòng ai đó giải thích cách đầu ra cho hai trường hợp khác.

Cảm ơn trước

+0

mảng của bạn 'a', ngay cả với hai chiều, không đồng nghĩa với một con trỏ đến con trỏ. – WhozCraig

Trả lời

3

Đầu tiên hai printf rõ ràng là in cùng một địa chỉ, ví dụ: elememt đầu tiên của mảng hai d đó là một mảng một d. do đó, không có nghi ngờ gì về việc cả hai đều trỏ đến bắt đầu mảng, đó là [0] [0].

char ** str là một con trỏ vì vậy khi bạn làm str [3] nó có nghĩa là (str + 3) và có lẽ mã của bạn đang chạy trên một máy 32 bit để nó sẽ nhảy tới 12 byte phía trước theo con trỏ không khí. không bị lẫn lộn với mảng, str là một con trỏ 2-d không phải là một mảng.

0xbf7f6286 str [0] 0xbf7f6292 str [3] diff là 0xc đúng như mong đợi.

printf ("\ n% p", & str [1] [56]); printf ("\ n% p", & (( (str + 4) +1)));

Trên hai tuyên bố cuối cùng bạn đang cố gắng in vì sai sót dưới của con trỏ không khí.

Tôi hy vọng nó sẽ giúp giải quyết sự nghi ngờ của bạn.

+0

Tôi nghi ngờ: Vì là một mảng 2D, nó trỏ đến địa chỉ bắt đầu của mảng 2D. Nhưng, str là một con trỏ đến một con trỏ. Như chúng ta đã sao chép str = một biến con trỏ như vậy có chứa địa chỉ của một biến con trỏ và str sẽ được tạo ra tại một số vị trí 'x'. vì vậy, khi chúng ta in & str [0] nó sẽ in 'x' thay vì địa chỉ của một quyền? Xin vui lòng giải thích nếu sai một nơi nào đó – user2165725

0

Khi bạn nói str + 3, nó có nghĩa là str + (3 * sizeof (char **)). Vì vậy, nếu kích thước của 'char **' là 4byte thì str được tăng thêm 12 byte.

0

Nếu bạn muốn in ra một mảng 2D, bạn nên sử dụng hai vòng (tôi sẽ đề nghị hai 'cho' vòng), và có một lồng nhau bên trong khác.

Đối với đầu ra, chúng (tôi không có ý nói rõ ràng) tất cả địa chỉ vì khi bạn nhập '&' trước khi truy cập phần tử mảng, bạn đang yêu cầu địa chỉ của nó.

Vì bạn đang thiết lập mảng bằng str, bạn thực sự đặt đường con trỏ tới địa chỉ mà mảng 'a' trỏ tới.

Khi bạn sử dụng [int n], toán tử truy cập, bạn đang thực sự yêu cầu máy tính của bạn xem các khe địa chỉ 'n' qua vị trí thực sự trỏ đến mảng (hoặc con trỏ). Vì vậy, tại sao nó 16 hơn hai đầu.

Hai cái nhìn cuối cùng giống như Hex, tôi không hoàn toàn chắc chắn về hai dòng đầu ra cuối cùng.

1
char a[5][10]={"one","two","three","four","five"}; 
char **str=a; 

Hãy để tôi giải thích làm thế nào một công trình hai con trỏ bắt đầu, và mối quan hệ của nó với một mảng là gì, để bạn có thể có một cơ hội tốt hơn cố gắng giải quyết các câu hỏi cho mình.

Con trỏ sao đôi thông thường được gán với "địa chỉ" của một con trỏ khác.

Ví dụ

char a = 5; 
char *b = &a; 
char **c = &b; 

vì vậy khi bạn de-tham khảo c một lần

printf("\n%d",*c) 

, bạn sẽ nhận được giá trị chứa bởi 'b', đó là gì, nhưng các địa chỉ của 'a' .

Khi bạn bỏ tham khảo c hai lần

printf("\n%d",**c) 

, bạn sẽ nhận được giá trị của 'a' trực tiếp.

Bây giờ đến mối quan hệ giữa mảng và con trỏ.

Tên của mảng là một từ đồng nghĩa với địa chỉ của mảng, có nghĩa là tên của mảng là vị trí của chính nó.

Ví dụ:

int array [5] = {1,2,3,4,5}; 
printf("\n%p",array); 
printf("\n%p",&array); 
printf("\n%p",&array[0]); 

tất cả trong số họ sẽ in cùng một địa chỉ.

Do đó đây là một bài tập con trỏ không hợp lệ

char **str=a; 

Bởi vì ** str hy vọng một địa chỉ của một con trỏ giữ một địa chỉ, nhưng bạn chỉ cần đi qua chỉ là một địa chỉ [tên của mảng = a].

Vì vậy, trong ngắn hạn, một con trỏ hai sao, không nên được sử dụng để xử lý một mảng hai chiều.

Điều này question xử lý việc xử lý mảng hai chiều với con trỏ.

8

Trả lời Rất Ngắn:

Thay đổi này:

char **str; 

Để có này:

char (*str)[10]; 

Và sửa chữa bất kỳ lỗi trong mã của bạn sau đó. Loại str không tương thích với loại mảng hai chiều của bạn. Tôi đã tự do sửa chữa loại trả về là int cho main() (khoảng trống không được phép theo tiêu chuẩn C).Tôi cũng đánh dấu nguồn nơi bạn về mặt kỹ thuật có hành vi không xác định:

#include <stdio.h> 
int main() 
{ 
    char a[5][10]={"one","two","three","four","five"}; 
    char (*str)[10] = a; 
    printf("%p ", &a[0]); 
    printf("\n%p ", &str[0]); 
    printf("\n%p ", &str[3]); 
    printf("\n%p ", &str[1][56]);  // this is undefined behaviour 
    printf("\n%p ", &(*(*(str+4)+1))); 
    return 0; 
} 

Bạn cũng nên lưu ý các printf() tuyên bố cuối cùng có một thừa &(*(...)) rằng không cần thiết. Nó có thể bị tước trông như thế này, đó là tương đương:

printf("\n%p ", *(str+4)+1); 

Output (Hệ thống phụ thuộc)

0x7fff5fbff900 
0x7fff5fbff900 
0x7fff5fbff91e 
0x7fff5fbff942 
0x7fff5fbff929 

The Very (Rất, Rất) dài trả lời

Bạn không đúng với giả thiết rằng hai thứ nguyên al mảng tương đương với một con trỏ trỏ tới. Chúng tương tự như chỉ sử dụng cú pháp. Sau đây là một cách bố trí sơ bộ như thế nào mảng a được miêu tả trong bộ nhớ

char a[5][10] 

    0 1 2 3 4 5 6 7 8 9 
    ----------------------------------------- 
0 | o | n | e | 0 | | | | | | | 
    ----------------------------------------- 
1 | t | w | o | 0 | | | | | | | 
    ----------------------------------------- 
2 | t | h | r | e | e | 0 | | | | | 
    ----------------------------------------- 
3 | f | o | u | r | 0 | | | | | | 
    ----------------------------------------- 
4 | f | i | v | e | 0 | | | | | | 
    ----------------------------------------- 

Lưu ý rằng địa chỉ bắt đầu của hàng thứ hai, đó là &a[1], là mười byte qua địa chỉ bắt đầu của dòng đầu tiên, &a[0] (không phải ngẫu nhiên là địa chỉ bắt đầu của toàn bộ mảng). Sau đây chứng tỏ điều này:

int main() 
{ 
    char a[5][10] = { "one", "two", "three", "four", "five" }; 

    printf("&a = %p\n", &a); 
    printf("&a[0] = %p\n", &a[0]); 
    printf("a[0] = %p\n", a[0]); 
    printf("&a[1] = %p\n", &a[1]); 
    printf("a[1] = %p\n", a[1]); 
    return 0; 
} 

Output

&a = 0x7fff5fbff8e0 
&a[0] = 0x7fff5fbff8e0 
a[0] = 0x7fff5fbff8e0 
&a[1] = 0x7fff5fbff8ea 
a[1] = 0x7fff5fbff8ea 

Lưu ý rằng địa chỉ tại a[1] là 10 byte (0x0a hex) qua đầu của mảng. Nhưng tại sao? Để trả lời câu hỏi này, bạn phải hiểu các khái niệm cơ bản về số học con trỏ đã nhập.


Cách tính toán số học con trỏ?

Trong C và C++, tất cả các con trỏ ngoại trừ void được nhập. Có một kiểu dữ liệu cơ bản được liên kết với con trỏ. Ví dụ.

int *iptr = malloc(5*sizeof(int)); 

điểm trên vào vùng bộ nhớ được phân bổ là kích thước năm số nguyên. Phát biểu nó như chúng ta thường giải quyết các mảng trông như thế này:

iptr[1] = 1; 

nhưng chúng tôi có thể chỉ là một cách dễ dàng giải quyết nó như thế này:

*(iptr+1) = 1; 

và kết quả sẽ là như nhau; lưu trữ giá trị 1 trong khe mảng thứ hai (0 là vị trí đầu tiên). Chúng ta biết toán tử dereference * cho phép truy cập thông qua một địa chỉ (địa chỉ được lưu trữ trong con trỏ). Nhưng làm thế nào để (iptr+1) biết bỏ qua bốn byte (nếu các loại int của bạn là 32 bit, tám byte nếu chúng là 64bit) để truy cập vào khe nguyên tiếp theo?

Trả lời: vì số học con trỏ đã nhập.Trình biên dịch biết độ rộng, tính bằng byte, loại nằm dưới của con trỏ là (trong trường hợp này, chiều rộng của loại int). Khi bạn cộng hoặc trừ các giá trị scaler thành/từ con trỏ, trình biên dịch sẽ tạo ra mã thích hợp cho tài khoản cho "chiều rộng kiểu" này. Nó cũng làm việc với các loại người dùng. Đây được thể hiện dưới đây:

#include <stdio.h> 

typedef struct Data 
{ 
    int ival; 
    float fval; 
    char buffer[100]; 
} Data; 

int main() 
{ 
    int ivals[10]; 
    int *iptr = ivals; 
    char str[] = "Message"; 
    char *pchr = str; 
    Data data[2]; 
    Data *dptr = data; 

    printf("iptr = %p\n", iptr); 
    printf("iptr+1 = %p\n", iptr+1); 

    printf("pchr = %p\n", pchr); 
    printf("pchr+1 = %p\n", pchr+1); 

    printf("dptr = %p\n", dptr); 
    printf("dptr+1 = %p\n", dptr+1); 

    return 0; 
} 

Output

iptr = 0x7fff5fbff900 
iptr+1 = 0x7fff5fbff904 
pchr = 0x7fff5fbff8f0 
pchr+1 = 0x7fff5fbff8f1 
dptr = 0x7fff5fbff810 
dptr+1 = 0x7fff5fbff87c 

Chú ý sự khác biệt giữa địa chỉ iptriptr+1không một byte; nó là bốn byte (chiều rộng của int trên hệ thống của tôi). Tiếp theo, chiều rộng của một đơn char được thể hiện với pchrpchr+1 dưới dạng một byte. Cuối cùng, kiểu dữ liệu tùy chỉnh của chúng tôi Data với hai giá trị con trỏ của nó, dptrdptr+1 cho biết rằng nó là 0x6C hoặc rộng 108 byte. (nó có thể lớn hơn do đóng gói cấu trúc và sắp xếp trường, nhưng chúng tôi may mắn với ví dụ này là không). Điều đó có ý nghĩa, vì cấu trúc chứa hai trường dữ liệu 4 byte (một intfloat) và một bộ đệm char 100 phần tử rộng.

Điều ngược lại cũng đúng, bằng cách này và thường là không phải được các lập trình viên C/C++ có kinh nghiệm xem xét. Nó được nhập vào con trỏ differencing. Nếu bạn có hai con trỏ hợp lệ của một loại quy định trong một khu vực tiếp giáp của bộ nhớ có giá trị:

int ar[10]; 
int *iptr1 = ar+1; 
int *iptr5 = ar+5; 

gì bạn cho rằng bạn sẽ có được kết quả là từ:

printf("%lu", iptr5 - iptr1); 

Câu trả lời là .. 4. Whoopee, bạn nói. Không phải là một thỏa thuận lớn? Bạn không tin rằng nó. Đây là cực kỳ tiện dụng khi sử dụng số học con trỏ để xác định độ lệch trong bộ đệm của một phần tử cụ thể.

Nói tóm lại, khi bạn có một biểu hiện như thế này:

int ar[5]; 
int *iptr = ar; 

iptr[1] = 1; 

Bạn có thể biết nó tương đương với:

*(iptr+1) = 1; 

nào, về lay-con người, có nghĩa là "Hãy địa chỉ được giữ trong biến số iptr, thêm 1 * (chiều rộng của một byte là int byte), sau đó lưu trữ giá trị 1 trong bộ nhớ được đăng ký lại thông qua địa chỉ được trả lại. "

Thanh trang web: Thao tác này cũng sẽ hoạt động.Xem nếu bạn có thể nghĩ tại sao

1[iptr] = 1; 

Về mảng mẫu của bạn (chúng tôi), Bây giờ hãy nhìn vào những gì sẽ xảy ra khi chúng tôi đề cập đến cùng một địa chỉ của a, nhưng thông qua một con trỏ kép (hoàn toàn không chính xác và trình biên dịch của bạn ít nhất phải cảnh báo bạn về việc chuyển nhượng):

char **str = a; // Error: Incompatible pointer types: char ** and char[5][10] 

Điều đó không hiệu quả. Nhưng cho phép giả sử một lúc nó đã làm. char ** là một con trỏ trỏ đến một con trỏ tới char. Điều này có nghĩa là bản thân biến không giữ gì hơn một con trỏ. Nó không có khái niệm về chiều rộng hàng cơ sở, vv Vì vậy giả sử bạn đặt địa chỉ của a trong con trỏ kép str.

char **str = (char **)(a); // Should NEVER do this, here for demonstration only. 
char *s0 = str[0]; // what do you suppose this is? 

nhẹ cập nhật cho chương trình thử nghiệm của chúng tôi:

int main() 
{ 
    char a[5][10] = { "one", "two", "three", "four", "five" }; 
    char **str = (char **)a; 
    char *s0 = str[0]; 
    char *s1 = str[1]; 

    printf("&a = %p\n", &a); 
    printf("&a[0] = %p\n", &a[0]); 
    printf("a[0] = %p\n", a[0]); 
    printf("&a[1] = %p\n", &a[1]); 
    printf("a[1] = %p\n", a[1]); 

    printf("str = %p\n", str); 
    printf("s0 = %p\n", s0); 
    printf("s1 = %p\n", s1); 

    return 0; 
} 

Cung cấp cho chúng ta những kết quả sau:

&a = 0x7fff5fbff900 
&a[0] = 0x7fff5fbff900 
a[0] = 0x7fff5fbff900 
&a[1] = 0x7fff5fbff90a 
a[1] = 0x7fff5fbff90a 
str = 0x7fff5fbff900 
s0 = 0x656e6f 
s1 = 0x6f77740000 

Vâng, str có vẻ như nó là những gì chúng ta muốn, nhưng điều đó là những gì trong s0? Tại sao, đó là giá trị ký tự ASCII cho chữ cái. Cái nào? Một kiểm tra nhanh chóng của một phong nha ASCII table cho thấy là:

0x65 : e 
0x6e : n 
0x6f : o 

Thats từ "một" ngược lại (ngược lại là do việc xử lý endian các giá trị đa byte trên hệ thống của tôi, nhưng tôi hy vọng vấn đề là rõ ràng gì về điều đó giá trị thứ hai:.

0x6f : o 
0x77 : w 
0x74 : t 

Yep, đó là "hai" Vậy tại sao chúng ta nhận được byte trong mảng của chúng tôi như con trỏ

Hmmm .. Yeah, như tôi đã nói ở.? ai đó nói với bạn hoặc hin ted với bạn rằng con trỏ đôi và mảng hai chiều đồng nghĩa là không chính xác. Hãy nhớ lại sự gián đoạn của chúng tôi vào số học con trỏ đã nhập. Hãy nhớ rằng, điều này:

str[1] 

và điều này:

*(str+1) 

là đồng nghĩa. Tốt. loại loại của con trỏ str là gì? Loại nó trỏ đến là con trỏ char. Vì vậy, sự khác biệt đếm byte giữa này:

str + 0 

và điều này

str + 1 

sẽ là kích thước của một char*. Trên hệ thống của tôi là 4 byte (tôi có con trỏ 32 bit). Điều này giải thích lý do tại sao địa chỉ rõ ràng trong str[1]dữ liệu bốn byte vào cơ sở mảng ban đầu của chúng tôi.

Do đó, trả lời câu hỏi cơ bản đầu tiên của bạn (có, chúng tôi cuối cùng là đã nhận được điều đó).

Tại sao str[3]0xbf7f6292

trả lời: Đây:

&str[3] 

tương đương với điều này:

(str + 3) 

Nhưng chúng ta biết từ trên (str + 3) đó chỉ là địa chỉ lưu trữ trong str, sau đó thêm 3x chiều rộng của loại str điểm vào, là char *, tính bằng byte đến địa chỉ đó. Tốt. chúng ta đã biết từ bạn thứ hai printf gì địa chỉ đó là:

0xbf7f6286 

Và chúng ta biết chiều rộng của một con trỏ trên hệ thống của bạn là 4 byte (32 con trỏ bit). Do đó ...

0xbf7f6286 + (3 * 4) 

hay ....

0xbf7f6286 + 0x0C = 0xbf7f6292 
+1

câu trả lời tuyệt vời !! OP phải có một ngày tuyệt vời để đi qua câu trả lời như vậy, tôi sẽ được yêu thích, câu hỏi này, chỉ dành cho bài đăng này !!! –

+0

Câu trả lời tuyệt vời ... Thực sự hữu ích – user2165725

+0

@ user2165725, nếu bạn tìm thấy bất kỳ câu trả lời nào hữu ích, bạn nói như vậy bằng cách chấp nhận câu trả lời –