2010-10-22 43 views
9

Khi chúng ta chuyển một phần tử của một mảng vào một hàm, nó được coi là một biến bình thường và hàm được gọi tạo ra một bản sao của đối số thực tế và hoạt động trên nó. Bất kỳ thay đổi nào được thực hiện trong các đối số chính thức không ảnh hưởng đến các đối số thực tế.Truyền toàn bộ mảng tới hàm

Nhưng điều này không xảy ra khi chúng tôi vượt qua toàn bộ mảng. Trong trường hợp này nó (gọi là hàm) được truy cập vào các đối số thực tế và bất kỳ thay đổi nào được thực hiện trong các đối số chính thức ảnh hưởng đến các đối số thực tế. Tại sao điều này xảy ra?

+2

Tôi tin câu trả lời (chi tiết) của tôi cho câu hỏi khác này http://stackoverflow.com/questions/3613302/passing-array-of-struc tures-to-function-c/3613350 # 3613350 cũng đề cập đến điều này. Về cơ bản đó là sự khác biệt giữa vấn đề mảng và con trỏ. – kriss

+1

Bạn không thể sử dụng const, nếu bạn không muốn sửa đổi nội dung mảng? – Nyan

Trả lời

16

Mảng được truyền dưới dạng con trỏ tới các phần tử.

Nếu tôi viết:

void doStuff(int *ptr) 
{ 
    ptr[1] = 5; 
} 

int main() 
{ 
    int arr[] = {0, 1, 2}; 
    doStuff(arr); 
    printf("%d %d %d\n", arr[0], arr[1], arr[2]); 
} 

đầu ra sẽ là "0 5 2". Đó là bởi vì C truyền bất cứ điều gì thực sự là tham số theo giá trị. Tham số là một con trỏ tới một int. Vì vậy, một con trỏ đến một int được truyền theo giá trị. Do đó, doStuff nhận được một bản sao của một con trỏ tới bộ nhớ trong khung stack chính. Khi nó dereferences con trỏ với ptr[1], nó là sau con trỏ đến bộ nhớ chính và sửa đổi các mảng đó.

C chỉ có thể truyền theo giá trị, nhưng nó có thể "cạn". Nếu bạn yêu cầu nó vượt qua một int *, nó sẽ truyền một int *. Nó chỉ sao chép giá trị của con trỏ, chứ không phải giá trị của bất kỳ giá trị nào trỏ tới.

Nếu bạn muốn doStuff để có được bản sao riêng của mảng, hoặc quấn mảng trong một cấu trúc như những người khác đã gợi ý, hoặc sử dụng memcpy tự sâu sao chép các mảng như thế này:

void doStuff(int *ptr, int nElems) 
{ 
    int myCopyOfTheArray[nElems]; 
    memcpy(myCopyOfTheArray, ptr, sizeof(int) * nElems); 
    /* do stuff with the local copy */ 
} 

Không giống như sử dụng một cấu trúc, cách tiếp cận memcpy hoạt động nếu nElems chỉ biết trong thời gian chạy.

+0

Đoạn mã thứ 2, là nó hợp lệ C hay chỉ là giả C ??? – Nyan

+0

Ok, bạn đang sử dụng trình biên dịch nào? chỉ tò mò thôi. – Nyan

+0

phiên bản gcc 4.1.2 – AlcubierreDrive

0

Một mảng trong C có thể được coi là con trỏ đến phần tử đầu tiên của mảng. Con trỏ được truyền theo giá trị (bạn không thể sửa đổi giá trị được truyền vào), nhưng bạn có thể dereference nó và sửa đổi bộ nhớ mà nó trỏ vào.

+4

Một mảng trong C không phải là một con trỏ, thường là * bị giáng cấp * thành một con trỏ. –

+0

@Matteo, @paxdiablo Đã sửa lỗi. – Jonathan

8

Mảng không thể được truyền theo giá trị trong C. Chúng được giảm hạng xuống con trỏ. Vì vậy, hàm được gọi thấy một con trỏ tới mảng (được truyền theo tham chiếu) và hoạt động trên nó. Nếu bạn muốn tạo một bản sao, bạn phải làm như vậy một cách rõ ràng, hoặc đặt mảng của bạn bên trong một cấu trúc (có thể được truyền theo giá trị cho hàm.)

0

Bởi vì khi bạn chuyển toàn bộ mảng đến hàm Bạn vượt qua con trỏ đến mảng đó, có nghĩa là bạn cho hàm một vị trí trong bộ nhớ, nơi mảng này được đặt. Vì vậy, khi bạn thay đổi mảng trong chức năng Bạn đang thay đổi mảng ban đầu là tốt.

5

Thật tốn kém khi tạo bản sao của toàn bộ mảng, nói chung. Quay trở lại những ngày khi C được tạo lần đầu tiên, nó có thể dễ dàng làm cạn kiệt tài nguyên máy (đặc biệt là ngăn xếp).

Nếu bạn thực sự muốn vượt qua một mảng, quấn nó trong một cấu trúc:

struct wrap { int array[100]; }; 

int somefunc(struct wrap large) { ... } 

void anotherfunc(void) 
{ 
    struct wrap a; 
    ...add data to array... 
    printf("%d\n", somefunc(a)); 
} 
2

"Vượt qua bằng cách tham khảo" là một cá trích đỏ trong C. Tất cả các đối số cho hàm được "truyền theo giá trị" chỉ . Trong trường hợp mảng, bạn chuyển địa chỉ của phần tử đầu tiên dưới dạng một con trỏ. Sau đó, bạn có thể sử dụng con trỏ này để tham khảo vị trí bộ nhớ mong muốn & giá trị đọc hoặc ghi.

2

Từ online C standard:

6.3.2.1 lvalues, mảng, và chức năng định danh
...
3 Trừ khi nó là toán hạng của sizeof hành hoặc unary & điều hành, hoặc là một chuỗi chữ sử dụng để khởi tạo một mảng, một biểu thức có kiểu "mảng kiểu" được chuyển thành một biểu thức có kiểu "con trỏ để nhập" trỏ đến phần tử ban đầu của đối tượng mảng và không phải là một giá trị. Nếu đối tượng mảng có lớp lưu trữ đăng ký, hành vi là không xác định.

Giả sử đoạn mã sau:

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

Trong cuộc gọi đến foo, kiểu của biểu thức a là "mảng 10 phần tử của int". Tuy nhiên, vì biểu thức không phải là toán hạng của toán tử sizeof hoặc & và vì nó không phải là chuỗi ký tự được sử dụng để khởi tạo mảng khác trong khai báo, loại của nó được chuyển đổi hoàn toàn ("decays") từ "10- mảng phần tử của int "tới" con trỏ đến int "và giá trị của nó sẽ là địa chỉ của phần tử đầu tiên trong mảng (ví dụ: &a[0]).

Do đó, những gì foo nhận được là con trỏ đến int, không phải là mảng. Dereferencing hoặc subscripting con trỏ này cho phép bạn thay đổi các giá trị của mảng.

Đây là một tính năng của ngôn ngữ C; mảng không phải là đối tượng hạng nhất. Trong thực tế, trong hầu hết trường hợp (bao gồm cả chỉ số) mảng biểu thức sẽ được chuyển đổi thành loại con trỏ.

tôi tiếp tục nhấn mạnh từ biểu để phân biệt giữa các mảng thực tế đối tượng (mà luôn luôn và mãi mãi một kiểu mảng là) và bất kỳ tham khảo đến đối tượng đó trong các mã, trong đó có thể được chuyển đổi sang một con trỏ kiểu.

3

Tôi chưa thực sự thấy bất kỳ câu trả lời nào chưa bao gồm toàn bộ câu hỏi. Từ những gì tôi nhìn thấy, có vẻ như các câu hỏi yêu cầu một cái gì đó như thế này:

Với đoạn mã sau:

int a[5]; 

foo(a); 
bar(a[0]); 

Tại sao có thể foo thao tác các giá trị ban đầu trong khi bar chỉ nhận được một bản sao của giá trị được thông qua tại ?

Điều này là do việc sử dụng ký hiệu mảng: toán tử [] bỏ qua mục mà nó tham chiếu trong mảng và chuyển giá trị tại vị trí đó.

Vì vậy

int a[5]; 
bar(a[0]); 

khác với:

int* a; 
bar(a); 

Nếu bạn muốn vượt qua trong một tham chiếu đến một mục cụ thể trong một mảng, bạn cần phải sử dụng & điều hành:

bar(&a[5]); 
Các vấn đề liên quan