2012-04-20 40 views
17

Mã sau đây đang làm gì?trỏ tới mảng C++

int g[] = {9,8}; 
int (*j) = g; 

Từ sự hiểu biết của tôi, việc tạo con trỏ tới một mảng gồm 2 int. Nhưng sau đó tại sao công việc này:

int x = j[0]; 

và điều này không làm việc:

int x = (*j)[0]; 
+15

@mjfgates: mảng không phải là con trỏ. Đừng nói với mọi người điều đó. –

+7

@mjfgates Thật vậy, có một đối tượng mảng trong C và C++. Nó chỉ xảy ra rằng có những quy tắc lạ, ví dụ, gây ra các chuyển đổi không chính xác từ một mảng tới một con trỏ đến phần tử đầu tiên của mảng. – bames53

Trả lời

37

Dấu ngoặc đơn thừa trong ví dụ của bạn. Con trỏ không quan tâm liệu có là một mảng có liên quan - nó chỉ biết rằng trỏ của mình cho một int

int g[] = {9,8}; 
    int (*j) = g; 

cũng có thể được viết lại như

int g[] = {9,8}; 
    int *j = g; 

mà cũng có thể được viết lại như

int g[] = {9,8}; 
    int *j = &g[0]; 

một con trỏ tới một mảng sẽ trông giống như

int g[] = {9,8}; 
    int (*j)[2] = &g; 

    //Dereference 'j' and access array element zero 
    int n = (*j)[0]; 

Có một đọc tốt trên tờ khai con trỏ (và làm thế nào để grok họ) tại liên kết này ở đây: http://www.codeproject.com/Articles/7042/How-to-interpret-complex-C-C-declarations

+0

bài viết này rất hữu ích, đặc biệt là quy tắc từ phải sang trái, cảm ơn. –

+0

là '2' cần thiết trong' int (* j) [2] = & g; '? Nó sẽ không còn là một con trỏ đến một mảng nếu '2' được bỏ qua? – johnbakers

+0

@ johnbakers có, nó là hoàn toàn cần thiết để xác định chiều dài của mảng, nếu không bạn sẽ không có một con trỏ-to-mảng, và sau đó mã sẽ không biên dịch trừ khi bạn sử dụng một diễn viên. Việc trỏ tới một mảng ngụ ý rằng bạn có kiến ​​thức về độ dài của nó tại thời gian biên dịch. Thông báo trình biên dịch kết quả từ việc bỏ qua chiều dài mảng trong MSVC++ lần đọc 'lỗi C2440: 'khởi tạo': không thể chuyển đổi từ 'int (*) [2]' thành 'int (*) []'' –

9

j[0]; dereferences một con trỏ đến int, vì vậy loại của nó là int.

(*j)[0] không có loại. *j chỉ định con trỏ đến một số int, do đó, nó trả về một số int(*j)[0] các nỗ lực để coi trọng một số int. Nó giống như cố gắng int x = 8; x[0];.

+3

Ngoài ra, hãy nhớ rằng 'j [0]' tương đương với '* (j + 0)'. '(* j) [0]' do đó tương đương với 'j [0] [0]', tương đương với '* (* (j + 0) + 0)', nó hoạt động là '** j '. Bất kỳ cách nào bạn nhìn vào nó, nó sẽ không hoạt động. – chris

+0

vì vậy sau đó làm thế nào bạn sẽ tạo một con trỏ đến một nhóm các phần tử mảng? –

+0

Bạn đặt con trỏ đến phần tử đầu tiên và sử dụng nó như một mảng, vì 'j [1] == * (j + 1) == phần tử tiếp theo trong mảng'. – chris

19
int g[] = {9,8}; 

này tuyên bố một đối tượng kiểu int [2], và khởi yếu tố của nó để {9 , 8}

int (*j) = g; 

này tuyên bố một đối tượng kiểu int *, và khởi tạo nó với một con trỏ đến phần tử đầu tiên của g.

Thực tế là khai báo thứ hai khởi tạo j với một cái gì đó khác với g là khá lạ. C và C++ chỉ có những quy tắc kỳ lạ về mảng, và đây là một trong số chúng. Ở đây, biểu thức g được chuyển đổi hoàn toàn từ một giá trị tham chiếu đến đối tượng g thành một rvalue thuộc loại int* trỏ vào phần tử đầu tiên của g.

Chuyển đổi này xảy ra ở một số nơi. Trong thực tế nó xảy ra khi bạn làm g[0]. Toán tử chỉ mục mảng không thực sự làm việc trên các mảng, chỉ trên con trỏ. Vì vậy, các tuyên bố int x = j[0]; hoạt động vì g[0] xảy ra để làm điều đó cùng một chuyển đổi tiềm ẩn đã được thực hiện khi j được khởi tạo.

Một con trỏ đến một mảng được khai báo như thế này

int (*k)[2]; 

và bạn hoàn toàn đúng về cách thức này sẽ được sử dụng

int x = (*k)[0]; 

(lưu ý cách "declaration follows use", tức là cú pháp cho tuyên bố biến của loại bắt chước cú pháp cho sử dụng biến số đó.)

Tuy nhiên, người ta thường không sử dụng một con trỏ đến một mảng. Toàn bộ mục đích của các quy tắc đặc biệt xung quanh mảng là để bạn có thể sử dụng một con trỏ tới một phần tử mảng như thể nó là một mảng. Vì vậy, thành ngữ C thường không quan tâm đến mảng và con trỏ không giống nhau, và các quy tắc ngăn bạn làm nhiều thứ hữu ích trực tiếp với mảng. (Ví dụ như bạn không thể sao chép một mảng như: int g[2] = {1,2}; int h[2]; h = g;)


Ví dụ:

void foo(int c[10]); // looks like we're taking an array by value. 
// Wrong, the parameter type is 'adjusted' to be int* 

int bar[3] = {1,2}; 
foo(bar); // compile error due to wrong types (int[3] vs. int[10])? 
// No, compiles fine but you'll probably get undefined behavior at runtime 

// if you want type checking, you can pass arrays by reference (or just use std::array): 
void foo2(int (&c)[10]); // paramater type isn't 'adjusted' 
foo2(bar); // compiler error, cannot convert int[3] to int (&)[10] 

int baz()[10]; // returning an array by value? 
// No, return types are prohibited from being an array. 

int g[2] = {1,2}; 
int h[2] = g; // initializing the array? No, initializing an array requires {} syntax 
h = g; // copying an array? No, assigning to arrays is prohibited 

Vì mảng rất không phù hợp với các loại khác trong C và C++, bạn nên chỉ cần tránh chúng. C++ có std::array phù hợp hơn và bạn nên sử dụng nó khi bạn cần các mảng có kích thước tĩnh. Nếu bạn cần mảng có kích thước động, tùy chọn đầu tiên của bạn là std :: vector.

+0

+1 để biết mô tả chính xác về mối quan hệ mảng/con trỏ, mà không sử dụng từ phân rã. –

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