2010-01-09 30 views
6

khi tôi cần phải vượt qua một mảng tới một hàm, có vẻ như tất cả các tờ khai sau của hàm sẽ làm việcmảng Type - Quy định chuyển nhượng/sử dụng như tham số chức năng

void f(int arr[]) 
void f(int arr[4]) // is this one correct? 

cho việc này:

int a[]={1,2,3,4}; 
f(a); 

Nhưng khi tôi gán một mảng vào mảng khác, nó không

int a[]={1,2,3,4}; 
int b[4] = a; // error: array must be initialized with a brace-enclosed initializer 

Vậy tại sao một mảng thông qua như là một argum ent của một chức năng là okay, nhưng được sử dụng trên các rhs của nhiệm vụ đơn giản là sai?

+3

Tiêu đề của câu hỏi cần phải được viết lại, tên hiện tại quá chung chung và chữ thường. –

Trả lời

12

Đối với sự hiểu biết sự khác biệt, chúng ta cần phải hiểu hai ngữ cảnh khác nhau.

  • Trong giá trị bối cảnh, tên của một loạt các loại T là tương đương với một con trỏ để gõ T, và tương đương với một con trỏ đến phần tử đầu tiên của mảng.
  • Trong các đối tượng đối tượng, tên của một loại kiểu T không giảm xuống con trỏ.

Ngữ cảnh đối tượng là gì?

Trong a = b;, a nằm trong ngữ cảnh đối tượng. Khi bạn lấy địa chỉ của một biến, nó được sử dụng trong ngữ cảnh đối tượng. Cuối cùng, khi bạn sử dụng toán tử sizeof trên một biến, nó được sử dụng trong ngữ cảnh đối tượng. Trong tất cả các trường hợp khác, một biến được sử dụng trong ngữ cảnh giá trị.

Bây giờ chúng ta có kiến ​​thức này, khi chúng ta làm:

void f(int arr[4]); 

Đó là chính xác tương đương với

void f(int *arr); 

Như bạn phát hiện ra, chúng ta có thể bỏ qua các kích thước (4 ở trên) từ khai báo hàm. Điều này có nghĩa là bạn không thể biết kích thước của "mảng" được chuyển đến f().Sau đó, khi bạn làm:

int a[]={1,2,3,4}; 
f(a); 

Trong cuộc gọi chức năng, tên a là trong bối cảnh giá trị, vì vậy nó làm giảm đến một con trỏ đến int. Điều này là tốt, bởi vì f mong đợi một con trỏ đến một int, do đó, định nghĩa chức năng và sử dụng phù hợp. Những gì được chuyển đến f() là con trỏ đến phần tử đầu tiên của a (&a[0]).

Trong trường hợp của

int a[]={1,2,3,4}; 
int b[4] = a; 

Tên b được sử dụng trong một bối cảnh đối tượng, và không làm giảm tới một con trỏ. (Ngẫu nhiên, a đây trong bối cảnh giá trị, và giảm đến một con trỏ.)

Bây giờ, int b[4]; gán giá trị lưu trữ của 4 int s và cung cấp cho các tên b với nó. a cũng được chỉ định bộ nhớ tương tự. Vì vậy, trên thực tế, nhiệm vụ trên có nghĩa là "Tôi muốn đặt vị trí lưu trữ giống như vị trí trước đó". Điều này không có ý nghĩa.

Nếu bạn muốn bản sao nội dung của a vào b, sau đó bạn có thể làm:

#include <string.h> 
int b[4]; 
memcpy(b, a, sizeof b); 

Hoặc, nếu bạn muốn có một con trỏ b mà chỉ vào a:

int *b = a; 

Tại đây, a có ngữ cảnh giá trị và giảm xuống con trỏ thành int, vì vậy chúng tôi có thể gán a cho an int *.

Cuối cùng, khi khởi tạo một mảng, bạn có thể gán cho nó giá trị rõ ràng:

int a[] = {1, 2, 3, 4}; 

Ở đây, một đã 4 yếu tố, khởi tạo 1, 2, 3 và 4. Bạn cũng có thể làm :

int a[4] = {1, 2, 3, 4}; 

Nếu có ít yếu tố trong danh sách hơn số phần tử trong mảng, sau đó phần còn lại của các giá trị được đưa đến là 0:

int a[4] = {1, 2}; 

bộ a[2]a[3] để 0.

+0

Rất tốt bằng văn bản – benzado

+0

Bạn có thể cung cấp tham chiếu về nơi bạn đọc về ngữ cảnh đối tượng và giá trị không? –

+0

Tôi chưa bao giờ nghe nói về "ngữ cảnh đối tượng" và "ngữ cảnh giá trị". –

7
void f(int arr[]); 
void f(int arr[4]); 

Cú pháp gây hiểu lầm. Cả hai đều giống nhau như sau:

void f(int *arr); 

tức là, bạn chuyển con trỏ đến đầu mảng. Bạn không sao chép mảng.

+3

Có, tuy nhiên (trong C99) 'void f (int arr [tĩnh 4]) {...}' là đặc biệt vì điều này cho phép trình biên dịch giả định rằng 'arr' không phải là 'NULL' và có kích thước ít nhất 4. – Jed

6

C không hỗ trợ gán mảng. Trong trường hợp của một cuộc gọi hàm, mảng phân rã thành một con trỏ. C không hỗ trợ gán con trỏ. Điều này được yêu cầu ở đây chỉ là về mỗi ngày - những gì C văn bản cuốn sách là bạn guys đọc mà không giải thích điều này?

3

Thử ghi nhớ.

int a[]={1,2,3,4}; 
int b[4]; 
memcpy(b, a, sizeof(b)); 

Cảm ơn đã chỉ ra rằng, Steve, nó được một lúc kể từ khi tôi sử dụng C.

+1

'memcpy (b, a, 4 * sizeof (int)' Hoặc 'sizeof (b)' –

1

Để có được trực giác của bạn về điều đó, bạn phải hiểu những gì đang xảy ra trên mức máy.

Ngữ nghĩa khởi tạo (= {1,2,3,4}) có nghĩa là "đặt nó trên hình ảnh nhị phân của bạn chính xác theo cách này" để có thể biên dịch.

Phân công mảng sẽ khác nhau: trình biên dịch sẽ dịch nó thành vòng lặp, điều này thực tế sẽ là lặp lại qua các phần tử. Trình biên dịch C (hoặc C++, cho rằng vấn đề) không bao giờ làm một điều như vậy. Nó đúng là kỳ vọng bạn tự làm điều đó. Tại sao? Bởi vì bạn có thể. Vì vậy, nó phải là một chương trình con, được viết bằng C (memcpy).Đây là tất cả về sự đơn giản và gần gũi với vũ khí của bạn, đó là C và C++ tất cả về.

0

Lưu ý rằng loại a trong int a[4]int [4].

Nhưng TypeOf (&a) == int (*)[4]! = int [4].

Cũng lưu ý rằng loại giá trị của aint *, khác với tất cả những điều trên!

Dưới đây là một chương trình mẫu mà bạn có thể thử:

int main() { 
    // All of these are different, incompatible types!  
    printf("%d\n", sizeof (int[4])); // 16 
    // These two may be the same size, but are *not* interchangeable! 
    printf("%d\n", sizeof (int (*)[4])); // 4 
    printf("%d\n", sizeof (int *)); // 4 
} 
+0

Bạn nên sử dụng "% zu" để in size_t (trong C99). – Jens

0

Tôi muốn làm rõ. Có một số gợi ý sai lệch trong câu trả lời ... Tất cả các chức năng sau có thể mất mảng số nguyên:

void f(int arr[]) 
void f(int arr[4]) 
void f(int *arr) 

Nhưng luận chính thức là không giống nhau. Vì vậy, trình biên dịch có thể xử lý chúng khác nhau. Trong ý thức quản lý bộ nhớ trong, tất cả các đối số đều dẫn đến con trỏ.

void f(int arr[]) 

... f() có một mảng có kích thước bất kỳ.

void f(int arr[4]) 

... Đối số chính thức cho biết kích thước mảng.

void f(int *arr) 

... Bạn cũng có thể chuyển con trỏ nguyên. f() không biết gì về kích thước.

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