Những gì bạn đang nói trong bài viết của bạn là hoàn toàn chính xác. Tôi muốn nói rằng mọi nhà phát triển C đều có cùng một khám phá và đưa ra kết luận chính xác như nhau khi (nếu) họ đạt đến mức độ thành thạo nhất định với ngôn ngữ C.
Khi các chi tiết cụ thể của khu vực ứng dụng của bạn gọi một mảng có kích thước cố định cụ thể (kích thước mảng là hằng số thời gian biên dịch), cách duy nhất thích hợp để truyền một mảng như vậy cho một hàm là sử dụng tham số con trỏ đến mảng
void foo(char (*p)[10]);
(trong ngôn ngữ C ++, điều này cũng được thực hiện với các tài liệu tham khảo
void foo(char (&p)[10]);
).
Điều này sẽ cho phép kiểm tra loại cấp độ ngôn ngữ, điều này sẽ đảm bảo rằng mảng có kích thước chính xác được cung cấp dưới dạng đối số. Trong thực tế, trong nhiều trường hợp, mọi người sử dụng kỹ thuật này một cách ngầm định, thậm chí không nhận ra nó, ẩn kiểu mảng phía sau một tên typedef
typedef int Vector3d[3];
void transform(Vector3d *vector);
/* equivalent to `void transform(int (*vector)[3])` */
...
Vector3d vec;
...
transform(&vec);
Ngoài ra, lưu ý rằng đoạn mã trên là bất biến liên quan đến Vector3d
kiểu là một mảng hoặc a struct
. Bạn có thể chuyển định nghĩa Vector3d
bất cứ lúc nào từ một mảng sang một struct
trở lại và bạn sẽ không phải thay đổi khai báo hàm. Trong cả hai trường hợp, các hàm sẽ nhận được một đối tượng tổng hợp "bằng cách tham chiếu" (có trường hợp ngoại lệ cho điều này, nhưng trong bối cảnh của cuộc thảo luận này, điều này là đúng).
Tuy nhiên, bạn sẽ không thấy phương thức truyền mảng này được sử dụng quá thường xuyên, đơn giản vì quá nhiều người bị nhầm lẫn bởi một cú pháp khá phức tạp và đơn giản là không đủ thoải mái với các tính năng như vậy của ngôn ngữ C để sử dụng chúng đúng cách. Vì lý do này, trong cuộc sống thực trung bình, việc truyền một mảng như một con trỏ đến phần tử đầu tiên của nó là một cách tiếp cận phổ biến hơn. Nó chỉ trông "đơn giản hơn".
Nhưng trong thực tế, sử dụng con trỏ đến phần tử đầu tiên để truyền mảng là một kỹ thuật rất thích hợp, một mẹo, phục vụ một mục đích rất cụ thể: mục đích duy nhất của nó là để tạo điều kiện cho các mảng có kích thước khác nhau (tức là kích thước thời gian chạy) . Nếu bạn thực sự cần có khả năng xử lý các mảng có kích thước thời gian chạy, thì cách thích hợp để vượt qua một mảng như vậy là bằng một con trỏ đến phần tử đầu tiên của nó với kích thước cụ thể được cung cấp bởi một tham số bổ sung
void foo(char p[], unsigned plen);
Trên thực tế, trong nhiều trường hợp, rất hữu ích để có thể xử lý các mảng có kích thước thời gian chạy, điều này cũng góp phần vào sự phổ biến của phương thức. Nhiều nhà phát triển C chỉ đơn giản là không bao giờ gặp phải (hoặc không bao giờ nhận ra) nhu cầu xử lý một mảng có kích thước cố định, do đó vẫn không biết gì về kỹ thuật kích thước cố định thích hợp.
Tuy nhiên, nếu kích thước mảng là cố định, chuyển nó dưới dạng con trỏ đến một phần tử
void foo(char p[])
là một lỗi cấp độ kỹ thuật lớn, không may là khá phổ biến ngày nay. Một kỹ thuật con trỏ đến mảng là một cách tiếp cận tốt hơn nhiều trong những trường hợp như vậy.
Một lý do khác có thể cản trở việc áp dụng kỹ thuật chuyển mảng có kích thước cố định là sự thống trị của cách tiếp cận ngây thơ đối với việc gõ các mảng được phân bổ động. Ví dụ: nếu chương trình gọi các mảng cố định loại char[10]
(như trong ví dụ của bạn), một nhà phát triển trung bình sẽ có malloc
các mảng như
char *p = malloc(10 * sizeof *p);
Mảng này không thể được truyền cho một hàm được khai báo là
void foo(char (*p)[10]);
điều này gây nhầm lẫn cho nhà phát triển trung bình và khiến họ từ bỏ khai báo tham số kích thước cố định mà không suy nghĩ thêm. Trong thực tế, gốc rễ của vấn đề nằm ở malloc
cách tiếp cận ngây thơ . Các malloc
định dạng hiển thị ở trên nên được dành cho mảng kích thước thời gian chạy. Nếu kiểu mảng có kích thước thời gian biên dịch, một cách tốt hơn để malloc
nó trông như sau
char (*p)[10] = malloc(sizeof *p);
Điều này, tất nhiên, có thể dễ dàng được chuyển đến tuyên bố ở trên foo
foo(p);
và trình biên dịch sẽ thực hiện kiểm tra loại thích hợp. Nhưng một lần nữa, điều này quá khó hiểu với một nhà phát triển C chưa chuẩn bị, đó là lý do tại sao bạn sẽ không thấy nó quá thường xuyên trong mã trung bình hàng ngày "điển hình".
10
có thể được thay thế bằng bất kỳ biến nào trong phạm vi