2016-03-10 16 views
5

Tôi biết rằng khi tôi muốn truyền một mảng tới một hàm, nó sẽ phân hủy thành con trỏ, vì vậy kích thước của nó sẽ không được biết đến và hai tờ khai đây là tương đương:Tại sao kích thước của một mảng được truyền vào một hàm bằng tham chiếu được biết tới trình biên dịch trong C++?

void funtion(int *tab, int size); 

void funtion(int tab[], int size); 

Và tôi hiểu tại sao. Tuy nhiên, tôi đã kiểm tra rằng khi tôi vượt qua một mảng như một tài liệu tham khảo:

void funtion(int (&tab)[4]); 

trình biên dịch sẽ biết kích thước của mảng và sẽ không cho phép tôi vượt qua một loạt các kích thước khác nhau như một cuộc tranh cãi của chức năng này.

Tại sao lại như vậy? Tôi biết rằng khi tôi chuyển một mảng theo địa chỉ, kích thước không được tính đến khi tính toán vị trí của phần tử ith trong mảng, do đó, nó bị hủy ngay cả khi tôi đưa nó vào khai báo hàm:

void funtion(int tab[4], int size); 

Nhưng điều gì khác khi tôi chuyển mảng bằng tham chiếu? Tại sao kích thước của nó được biết đến với trình biên dịch?

Lưu ý: Tôi quan tâm đến các mảng có kích thước được biết tại thời gian biên dịch, vì vậy tôi không sử dụng bất kỳ mẫu nào.

tôi thấy a similar question trên Stack Overflow, tuy nhiên nó không trả lời câu hỏi của tôi - nó không giải thích lý do tại sao trình biên dịch biết kích thước của mảng, đó chỉ là một số thông tin về làm thế nào để vượt qua các mảng chức năng.

Trả lời

6

Vì có thể và vì việc kiểm tra bổ sung thêm sự an toàn. Trình biên dịch biết kích thước của mảng vì bạn nói nó như vậy, ngay tại đó trong khai báo hàm. Và vì thông tin đó có sẵn, tại sao nó không sử dụng nó để báo hiệu lỗi trong mã nguồn của bạn?

Câu hỏi thực sự là lý do tại sao ví dụ cuối cùng của bạn sẽ không thực hiện cùng một kiểm tra. Điều này, thật không may, một phần của di sản C - bạn không bao giờ đi qua một mảng, nó luôn luôn phân rã thành một con trỏ. Kích thước của mảng sau đó trở nên không liên quan.

Bạn có thể thêm séc không? Có thể, nhưng nó sẽ được sử dụng hạn chế (vì tất cả chúng ta đều sử dụng std :: array now - right !?), và bởi vì nó chắc chắn sẽ phá vỡ một số mã. Điều này, ví dụ:

void func (char Values [4]); 
func ("x"); 

Điều này hiện hợp pháp, nhưng sẽ không được kiểm tra thêm về kích thước mảng.

+0

Cảm ơn :) Nó có thể là một câu hỏi ngu ngốc, nhưng tại sao mảng không được chuyển qua phân rã tham chiếu thành con trỏ? Và tại sao thêm một ngắt kiểm tra một số mã giả định nó hoạt động tốt ngay bây giờ? – user2738748

+3

@ user2738748 Phân rã thành con trỏ là một di sản khủng khiếp của C. Không ai thích nó, nhưng thật khó để thay đổi nó trong C++, vì có một mong muốn nhất định làm cho càng nhiều mã C biên dịch với trình biên dịch C++ càng tốt. Nhưng vì không có tài liệu tham khảo trong C, C++ có thể đặt bất kỳ quy tắc nào liên quan đến chúng, không bị ràng buộc. Và các quy tắc lành mạnh đã được thiết lập. – SergeyA

0

Bởi vì không có thay đổi loại tiềm ẩn nào do trình biên dịch thực hiện trong trường hợp đó.Thông thường khi bạn viết:

void func(int[4]); 

hoặc

void func(void()); 

Trình biên dịch quyết định "giúp" bạn và chuyển những thành:

void func(int *); 

hoặc

void func(void(*)()); 

vui điều mặc dù - tôi t sẽ không giúp bạn theo cách như vậy khi bạn thử trả lại một trong số đó. Hãy thử viết:

int func()[4], func1()(); 

Lỗi - bất ngờ - lỗi trình biên dịch.

Nếu không, mảng là mảng và có kích thước không đổi có thể được mua bằng cách sử dụng toán tử sizeof.

Tuy nhiên, điều này thường bị lãng quên vì hành vi của trình biên dịch được lưu ý ở trên và cũng do chuyển đổi con trỏ ẩn được áp dụng cho các đối tượng kiểu mảng khi không như mong đợi. Và điều này là rất thường xuyên. Mặc dù đây là một vài trường hợp ngoại lệ khi không có chuyển đổi đối tượng mảng ngầm được áp dụng:

size_t arr[4], 

    (*parr)[3] = &arr, //taking the address of array 

    (&refarr)[3] = arr, //storing reference to array 

    sizearrobject = sizeof(arr); //taking the array object size 

Những ví dụ trên sẽ làm xuất hiện lỗi biên dịch bởi vì các loại không tương thích trên dòng thứ hai và thứ ba.

tôi đang nói về các trường hợp khi arr đối tượng không tự động chuyển đổi sang một cái gì đó như thế này:

(size_t*)&arr 
0

Vâng, có một số cách để vượt qua một mảng hoạt động. Bạn có thể truyền nó bằng con trỏ theo tham chiếu và có nhiều cách để xác định hoặc không xác định kích thước của nó một cách rõ ràng cho cả hai cách.


Trong câu hỏi của bạn, bạn so sánh các 2 cách sau:

  • Con trỏ trỏ tới phần tử đầu tiên: void f(int *arr)
  • Tham chiếu đến toàn bộ một mảng: void f(int (&arr)[size])

Bạn hỏi tại sao bạn cần phải xác định kích thước chỉ trong một trong những trường hợp này.

Dường như bạn giả định rằng sự khác biệt duy nhất giữa chúng là thực tế là người ta sử dụng con trỏ và một sử dụng tham chiếu. Nhưng tuyên bố này là không chính xác, chúng có nhiều sự khác biệt: Một là con trỏ đến thành phần đầu tiên, nhưng thứ hai là tham chiếu đến toàn bộ mảng.


Bạn có thể vượt qua một mảng bằng con trỏ đến toàn bộ một mảng:

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

Hãy so sánh nó với ví dụ của bạn, với đi qua bởi refence cho toàn bộ một mảng:

void f(int (&arr)[size])  

Chúng được tương tự, họ có cú pháp tương tự, cả hai đều xác định rõ ràng kích thước mảng.


Ngoài ra, xem xét việc này:

void f(int &arr) 

Dường như đi qua một int duy nhất bằng cách tham khảo, nhưng bạn có thể vượt qua một loạt các kích thước không biết đến nó.

Pointer thay thế cho nó là

void f(int *arr) 

Bạn hỏi tại sao bạn cần phải xác định kích thước mảng duy nhất tại một trong những trường hợp đó. Đó là vì cú pháp bạn đã sử dụng, không phải vì cú pháp là con trỏ và tham chiếu khác.

Như tôi đã nói, bạn có thể sử dụng con trỏ hoặc tham chiếu. Và bạn có thể chỉ định kích thước mảng hoặc bạn có thể cho phép một mảng có kích thước bất kỳ được sử dụng. Hai cái này không được kết nối.

//      by pointer    by reference 
/* Any size */ void f(int *arr)  void f(int &arr) 
/* Specific size */ void f(int (*arr)[x]) void f(int (&arr)[x]) 
Các vấn đề liên quan