Sử dụng mảng tiêu chuẩn trong C với kiểu tự nhiên phân rã từ mảng thành ptr
@Bo Persson tuyên bố chính xác trong câu trả lời tuyệt vời của anh ấy ở đây :
Khi truyền một mảng dưới dạng tham số, điều này
void arraytest(int a[])
có nghĩa chính xác giống như
void arraytest(int *a)
Tuy nhiên, tôi xin nói thêm rằng hai biểu mẫu trên cũng có:
có nghĩa chính xác giống như
void arraytest(int a[0])
có nghĩa là giống hệt như
void arraytest(int a[1])
có nghĩa là giống hệt như
void arraytest(int a[2])
có nghĩa là giống hệt như
void arraytest(int a[1000])
Vân vân.
Trong mọi ví dụ về mảng ở trên, kiểu tham số đầu vào giảm dần thànhint *
và có thể được gọi mà không có cảnh báo và không có lỗi, ngay cả khi -Wall -Wextra -Werror
đã bật tùy chọn xây dựng (xem repo của tôi tại đây để biết chi tiết về 3 tùy chọn xây dựng này), như điều này:
int array1[2];
int * array2 = array1;
arraytest(array1);
arraytest(array2);
Như một vấn đề của thực tế, giá trị "kích thước" ( [0]
, [1]
, [2]
, [1000]
, vv) bên trong tham số mảng ở đây là dường như chỉ dành riêng cho mục đích thẩm mỹ / tự tài liệu, và có thể là bất kỳ số nguyên dương ( size_t
gõ tôi nghĩ) mà bạn muốn!
Tuy nhiên, trong thực tế, bạn nên sử dụng nó để chỉ định kích thước tối thiểu của mảng mà bạn mong đợi hàm nhận được, để khi viết mã, bạn có thể dễ dàng theo dõi và xác minh. Tiêu chuẩn MISRA-C-2012 ( mua / tải xuống bản PDF 236 trang / phiên bản 2012 của tiêu chuẩn với giá £ 15,00 tại đây ) cho đến nay (đã nhấn mạnh thêm):
Quy tắc 17.5 Đối số hàm tương ứng với một tham số được khai báo là có kiểu mảng phải có một số phần tử thích hợp.
...
Nếu một tham số được khai báo là một mảng với kích thước xác định, thì đối số tương ứng trong mỗi lệnh gọi hàm phải trỏ vào một đối tượng có ít nhất bao nhiêu phần tử như mảng.
...
Việc sử dụng bộ khai báo mảng cho một tham số hàm xác định giao diện hàm rõ ràng hơn so với việc sử dụng con trỏ. Số lượng phần tử tối thiểu mà hàm mong đợi được nêu rõ ràng, trong khi điều này là không thể với một con trỏ.
Nói cách khác, họ khuyên bạn nên sử dụng định dạng kích thước rõ ràng, mặc dù tiêu chuẩn C về mặt kỹ thuật không thực thi nó - nó ít nhất giúp làm rõ với bạn với tư cách là nhà phát triển và với những người khác sử dụng mã, mảng kích thước mà hàm đang mong đợi bạn để vượt qua.
Buộc loại an toàn trên các mảng trong C
Như @Winger Sendon đã chỉ ra trong một bình luận bên dưới câu trả lời của tôi, chúng ta có thể buộc C xử lý một kiểu mảng là khác nhau dựa trên kích thước mảng !
Đầu tiên, bạn phải nhận ra rằng trong ví dụ của tôi ở trên, sử dụng int array1[2];
như sau: các arraytest(array1);
nguyên nhân array1
tự động phân rã thành một int *
. TUY NHIÊN, nếu bạn lấy địa chỉ của array1
thay thế và gọi arraytest(&array1)
, bạn sẽ có hành vi hoàn toàn khác! Bây giờ, nó KHÔNG phân rã thành một int *
! Thay vào đó, kiểu &array1
là int (*)[2]
, có nghĩa là "con trỏ đến một mảng kích thước 2 của kiểu int" hoặc "con trỏ đến một mảng kích thước 2 kiểu int" . Vì vậy, bạn có thể FORCE C để kiểm tra độ an toàn của kiểu trên một mảng, như sau:
void arraytest(int (*a)[2])
{
}
Cú pháp này khó đọc, nhưng tương tự như cú pháp của con trỏ hàm . Công cụ trực tuyến, cdecl , cho chúng ta biết điều đó int (*a)[2]
có nghĩa là: "khai báo một con trỏ là con trỏ tới mảng 2 của int" (con trỏ tới mảng 2 int
s). Đừng nhầm lẫn điều này với phiên bản KHÔNG có ngoặc đơn:, int * a[2]
có nghĩa là: "khai báo a là mảng 2 của con trỏ tới int" (mảng 2 con trỏ tới int
).
Bây giờ, hàm này &
YÊU CẦU bạn gọi nó bằng toán tử địa chỉ ( ) như thế này, sử dụng làm tham số đầu vào là ĐIỂM ĐẾN MỘT SỐ ĐẾN ĐÚNG KÍCH THƯỚC !:
int array1[2];
arraytest(&array1);
Tuy nhiên, điều này sẽ tạo ra một cảnh báo:
int array1[2];
arraytest(array1);
Bạn có thể kiểm tra mã này ở đây .
Để buộc trình biên dịch C biến cảnh báo này thành lỗi, do đó bạn PHẢI luôn gọi arraytest(&array1);
chỉ sử dụng mảng đầu vào có kích thước và kiểu đúng ( int array1[2];
trong trường hợp này), hãy thêm -Werror
vào các tùy chọn xây dựng của bạn. Nếu chạy mã kiểm tra ở trên trên onlinegdb.com, hãy thực hiện việc này bằng cách nhấp vào biểu tượng bánh răng ở trên cùng bên phải và nhấp vào "Cờ trình biên dịch bổ sung" để nhập tùy chọn này. Bây giờ, cảnh báo này:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
sẽ chuyển thành lỗi bản dựng này:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1);
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
Lưu ý rằng bạn cũng có thể tạo con trỏ "loại an toàn" cho các mảng có kích thước nhất định, như sau:
int array[2];
int (*array_p)[2] = &array;
... nhưng tôi không nhất thiết phải khuyến nghị điều này, vì nó gợi cho tôi rất nhiều trò hề trong C ++ được sử dụng để buộc an toàn loại ở mọi nơi, với chi phí đặc biệt cao về độ phức tạp cú pháp ngôn ngữ, độ dài và khó khăn trong việc lưu trữ mã, và điều mà tôi không thích và đã nói nhiều lần trước đây (ví dụ: xem "Suy nghĩ của tôi trên C ++" tại đây ).
Để biết thêm các bài kiểm tra và thử nghiệm, hãy xem thêm liên kết ngay bên dưới.
Người giới thiệu
Xem các liên kết ở trên. Cũng thế:
- Thử nghiệm mã của tôi trực tuyến: https://onlinegdb.com/B1RsrBDFD