2012-04-01 74 views
7

xem xét mã:Đối số mảng có được chuyển đến một hàm không phải là một con trỏ không?

void foo(char a[]){ 
    a++;   // works fine, gets compiled 
    //... 
} 

Bây giờ, hãy xem xét điều này:

void foo(){ 
    char a[50]; 
    a++;   // Compiler error 
    //... 
} 

tôi nghe một mảng tương đương với một con trỏ liên tục và không thể tăng lên vì nó không phải là một giá trị trái ...

Sau đó, tại sao mã đầu tiên được biên dịch, có phải vì đối số mảng cho hàm được truyền dưới dạng con trỏ hay không, tức là T [] được chuyển thành T * để chuyển .. Vì vậy, foo (a) chuyển thành con trỏ.

Nhưng là nó không trở lại được chuyển đổi để T [] một lần nữa vì được khai báo là:

void foo(char a[]); 
+2

Đọc phần 6 của [comp.lang.c FAQ] (http://c-faq.com). –

Trả lời

13

Khi bạn vượt qua một mảng như một tham số để một hàm, nó phân rã đến một con trỏ.
Vì vậy, thứ bạn tăng bên trong thân hàm là con trỏ chứ không phải mảng.

+0

Cụ thể, nó giống như bạn đã khai báo 'char * temp = a;' sau đó tăng lên 'temp'. –

4

tôi nghe một mảng tương đương với một con trỏ hằng

Bạn thể nghĩ về nó theo cách đó, nhưng chúng không tương đương.

Một mảng phân rã thành con trỏ khi được chuyển đến một hàm, đó là lý do tại sao bên trong hàm đó hợp lệ.

Chỉ vì chữ ký là void foo(char a[]) không làm cho a một mảng.

Bên ngoài chức năng, nó chỉ là một mảng và bạn không thể làm điểm số con trỏ trên đó.

1

Khi bạn chuyển mảng a [] vào hàm, nó chuyển giá trị của 'a', một địa chỉ, vào hàm. vì vậy bạn có thể sử dụng nó như một con trỏ trong hàm. Nếu bạn khai báo một mảng trong hàm, 'a' là hằng số vì bạn không thể thay đổi địa chỉ của nó trong bộ nhớ.

8

Đây là một tính năng khá đáng tiếc được kế thừa từ ngôn ngữ C, với tên khá tồi tệ của "phân rã". Vì C một lần không cho phép các kiểu hợp chất đi qua theo giá trị, chúng quyết định cho phép các lập trình viên chỉ định các mảng như các kiểu tham số hàm, nhưng chỉ có tính thẩm mỹ. Loại mảng phân rã thành kiểu con trỏ, thực hiện một loại ngữ nghĩa vượt qua tham chiếu khác với phần còn lại của ngôn ngữ. Xấu xí.

Để tóm tắt lại (và những người khác đã nói điều này), chữ ký

void foo(char a[]); // asking for trouble 

được unceremoniously đọc sai thành

void foo(char *a); 

... tất cả vì lợi ích của khả năng tương thích với mã C cổ đại. Vì bạn không viết mã C cổ, bạn không nên sử dụng tính năng "này".

Tuy nhiên, bạn có thể chuyển sạch tham chiếu vào một mảng.C++ yêu cầu kích thước của mảng được biết:

void foo(char (&a)[ 50 ]); 

Bây giờ a không thể được sửa đổi trong chức năng (chỉnh sửa: nội dung của nó có thể, tất nhiên - bạn biết những gì tôi có nghĩa), và chỉ có các mảng của đúng kích cỡ có thể được thông qua. Đối với mọi thứ khác, chuyển con trỏ hoặc loại cấp cao hơn.

-1

Bên trong hàm, a là chính địa chỉ, trỏ đến biến cục bộ. Bạn có thể tăng nó bởi vì nó là một địa chỉ. Ngoài ra, bạn có thể làm điều gì đó như sau:

char a[50]; 
int i; 
for(i=0;i<50;i++) 
    *(a+i)='b'; 

Không có lỗi.

+2

Không, mảng không được chuyển đến hàm, chỉ là địa chỉ của phần tử đầu tiên của nó. –

+0

Tôi không nghĩ vậy. Trong kiểu đầu tiên, tôi có nghĩa là 'void foo (char [] a) {..}', những thay đổi trong mảng không ảnh hưởng đến mảng chính nơi hàm trả về. – mtyurt

+1

Thay đổi thành 'a' chỉ ảnh hưởng đến tham số, là một đối tượng * con trỏ cục bộ *. Hãy thử sửa đổi 'a [0]' và xem điều gì xảy ra. Sau đó đọc phần 6 của [comp.lang.c FAQ] (http://c-faq.com). –

3

Trong C++, bất kỳ tham số chức năng nào của loại "mảng T" được điều chỉnh thành "con trỏ đến T". Hãy thử mã này:

void foo(char a[]) {} 
void foo(char* a) {} //error: redefinition 

Chúng thực sự là cùng một chức năng.

Vì vậy, tại sao chúng ta có thể chuyển đối số mảng tới hàm như một tham số con trỏ? Không phải vì một mảng tương đương với một con trỏ không đổi, nhưng vì một kiểu mảng có thể được chuyển đổi hoàn toàn thành một giá trị rvalue của kiểu con trỏ. Cũng lưu ý kết quả của việc chuyển đổi là một rvalue, đó là lý do tại sao bạn không thể áp dụng toán tử ++ vào một mảng, bạn chỉ có thể áp dụng toán tử này cho một giá trị.

1

tôi nghe một mảng tương đương với một con trỏ liên tục và không thể tăng lên vì nó không phải là một giá trị trái ...

Hầu.

Mảng biểu thứckhông thể sửa đổi lvalue; nó có thể không phải là toán hạng cho các toán tử như ++ hoặc -- và nó có thể không phải là đích của một biểu thức gán. Đây không phải là điều tương tự như một con trỏ liên tục (có nghĩa là, một con trỏ được khai báo là T * const).

Một biểu thức mảng sẽ được thay thế bằng một biểu thức con trỏ có giá trị là địa chỉ của phần tử đầu tiên của mảng trừ khi biểu thức mảng là một toán hạng của sizeof hoặc unary & nhà khai thác, hoặc khi biểu thức mảng là một chuỗi ký tự được sử dụng để khởi tạo một mảng khác trong một khai báo.

Khi bạn gọi một hàm với một cuộc tranh cãi mảng, chẳng hạn như

int a[N]; 
... 
foo(a); 

biểu thức a được chuyển đổi từ "mảng N phần tử của int" loại để "con trỏ đến int" và giá trị con trỏ này là những gì được chuyển đến foo; do đó, hàm nguyên mẫu tương ứng nên

void foo (int *arr) {...} 

Lưu ý rằng trong bối cảnh của một tuyên bố tham số chức năng, T a[]T a[N] là giống hệt nhau để T *a; trong cả ba trường hợp, a được khai báo là con trỏ đến T.Trong hàm foo, tham số arr là biểu thức con trỏ, trong đó giá trị có thể sửa đổi và do đó có thể được gán và có thể là toán hạng của các toán tử ++--.

Hãy nhớ rằng tất cả các chuyển đổi này nằm trên biểu thức mảng; nghĩa là, số nhận dạng mảng hoặc biểu thức khác đề cập đến mảng đối tượng trong bộ nhớ. Đối tượng mảng (phần bộ nhớ chứa các giá trị mảng) không được chuyển đổi.

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