Lý lịch
Câu lệnh khai báo biến trong C bao gồm ba phần: tên của biến, kiểu cơ sở của nó và (các) biến tố loại .
Có ba loại sửa đổi loại:
- Con trỏ
*
(tiền tố) - Mảng
[N]
(hậu tố) - Hàm
()
(hậu tố)- Bạn có thể chỉ định một danh sách các đối số hàm bên trong parens, nhưng vì lợi ích của thách thức này, chúng ta hãy bỏ qua nó và chỉ sử dụng
()
(về mặt kỹ thuật có nghĩa là "hàm có thể lấy bất kỳ loại đối số nào").
- Bạn có thể chỉ định một danh sách các đối số hàm bên trong parens, nhưng vì lợi ích của thách thức này, chúng ta hãy bỏ qua nó và chỉ sử dụng
Và một cách để đọc các ký hiệu như sau:
int i; // i is an int
float *f; // f is a pointer to a float
my_struct_t s[10]; // s is an array of 10 my_struct_t
int func(); // func is a function returning an int
Điều hấp dẫn là chúng ta có thể trộn tất cả những thứ này để tạo thành một loại phức tạp hơn, chẳng hạn như mảng mảng hoặc mảng con trỏ hàm hoặc con trỏ tới mảng con trỏ :
int arr[3][4];
// arr is an array of 3 arrays of 4 ints
int (*fptrs[10])();
// fptrs is an array of 10 pointers to functions returning an int
float *(*p)[16];
// p is a pointer to an array of 16 pointers to float
Làm thế nào tôi đọc những tuyên bố phức tạp này?
- Bắt đầu từ tên biến.
(name) is ...
- Chọn công cụ sửa đổi có quyền ưu tiên cao nhất.
- Đọc nó:
* -> pointer to ...
[N] -> array of N ...
() -> function returning ...
- Lặp lại 2 và 3 cho đến khi hết sửa đổi.
- Cuối cùng, đọc loại cơ sở.
... (base type).
Trong C, các toán tử postfix được ưu tiên hơn các toán tử tiền tố và các biến tố loại không phải là ngoại lệ. Do đó, []
và ()
ràng buộc đầu tiên, sau đó *
. Bất cứ điều gì bên trong một cặp parens (...)
(không bị nhầm lẫn với toán tử hàm) liên kết đầu tiên trên bất kỳ thứ gì bên ngoài.
Ví dụ minh họa:
int (*fptrs[10])();
fptrs fptrs is ...
[10] array of 10 ... // [] takes precedence over *
(* ) pointer to ...
() function returning ...
int int
Bài tập, nhiệm vụ
Đưa ra một dòng câu lệnh khai báo biến được viết bằng C, xuất ra biểu thức tiếng Anh mô tả dòng đó, sử dụng phương thức hiển thị ở trên.
Đầu vào
Đầu vào là một câu lệnh C duy nhất bao gồm một loại cơ sở duy nhất, một tên biến duy nhất, các biến tố loại 0 hoặc nhiều hơn và dấu chấm phẩy kết thúc. Bạn phải thực hiện tất cả các yếu tố cú pháp được đề cập ở trên, cộng với:
- Cả loại cơ sở và tên biến đều khớp với biểu thức chính quy
[A-Za-z_][A-Za-z0-9_]*
. - Về mặt lý thuyết, chương trình của bạn nên hỗ trợ số lượng sửa đổi loại không giới hạn.
Bạn có thể đơn giản hóa các yếu tố cú pháp C khác theo các cách sau (triển khai đầy đủ cũng được hoan nghênh):
- Các loại cơ sở luôn luôn là một từ duy nhất, ví dụ như
int
,float
,uint32_t
,myStruct
. Một cái gì đó giống nhưunsigned long long
sẽ không được thử nghiệm. - Đối với các ký hiệu mảng
[N]
, số lượngN
sẽ luôn luôn là một số nguyên dương đơn bằng văn bản trong cơ sở 10. Những điều nhưint a[5+5]
,int a[SIZE]
hoặcint a[0x0f]
sẽ không được kiểm tra. - Đối với ký hiệu hàm
()
, không có tham số nào được chỉ định cả, như đã chỉ ra ở trên. - Đối với khoảng trắng, chỉ ký tự khoảng trắng
0x20
sẽ được sử dụng. Bạn có thể giới hạn chương trình của mình để sử dụng cụ thể các khoảng trắng, ví dụ:- Chỉ sử dụng một khoảng trắng sau loại cơ sở
- Sử dụng khoảng trắng ở mọi nơi giữa các mã thông báo
- Tuy nhiên, bạn không thể sử dụng hai hoặc nhiều khoảng trắng liên tiếp để truyền tải nhiều thông tin hơn là dấu tách mã thông báo.
Theo cú pháp C, ba kết hợp sau không hợp lệ và do đó sẽ không được kiểm tra:
f()()
Chức năng trả vềf()[]
Hàm trả về mảnga[]()
Mảng hàm N
Các nhà phát triển C sử dụng các hình thức tương đương này thay thế (và tất cả các hình thức này được đề cập trong các trường hợp thử nghiệm):
(*f())()
Hàm trả về con trỏ cho hàm*f()
Hàm trả về con trỏ đến phần tử đầu tiên của mảng(*a[])()
Mảng con trỏ N hoạt động
Đầu ra
Đầu ra là một câu tiếng Anh. Bạn không cần (nhưng bạn có thể nếu bạn muốn) tôn trọng ngữ pháp tiếng Anh, ví dụ: việc sử dụng các a, an, the
dạng số ít / số nhiều và dấu chấm kết thúc (dấu chấm). Mỗi từ nên được phân tách bằng một hoặc nhiều khoảng trắng (dấu cách, tab, dòng mới) để kết quả có thể đọc được.
Một lần nữa, đây là quá trình chuyển đổi:
- Bắt đầu từ tên biến.
(name) is ...
- Chọn công cụ sửa đổi có quyền ưu tiên cao nhất.
- Đọc nó:
* -> pointer to ...
[N] -> array of N ...
() -> function returning ...
- Lặp lại 2 và 3 cho đến khi hết sửa đổi.
- Cuối cùng, đọc loại cơ sở.
... (base type).
Các trường hợp thử nghiệm
int i; // i is int
float *f; // f is pointer to float
my_struct_t s[10]; // s is array of 10 my_struct_t
int func(); // func is function returning int
int arr[3][4]; // arr is array of 3 array of 4 int
int (*fptrs[10])(); // fptrs is array of 10 pointer to function returning int
float *(*p)[16]; // p is pointer to array of 16 pointer to float
_RANdom_TYPE_123 (**(*_WTH_is_TH15)())[1234][567];
/* _WTH_is_TH15 is pointer to function returning pointer to pointer to array of
1234 array of 567 _RANdom_TYPE_123 */
uint32_t **(*(**(*(***p)[2])())[123])[4][5];
/* p is pointer to pointer to pointer to array of 2 pointer to function returning
pointer to pointer to array of 123 pointer to array of 4 array of 5 pointer to
pointer to uint32_t */
uint32_t (**((*(**(((*(((**(*p)))[2]))())))[123])[4])[5]);
// Same as above, just more redundant parens
some_type (*(*(*(*(*curried_func())())())())())();
/* curried_func is function returning pointer to function returning pointer to
function returning pointer to function returning pointer to
function returning pointer to function returning some_type */
Tiêu chí chấm điểm & chiến thắng
Đây là một thách thức mã golf . Chương trình có số byte nhỏ nhất sẽ thắng.
int arr[3][4];
là an array of 3 arrays of 4 ints
(như bạn nói), hay an array of 4 arrays of 3 ints
?
;
ở cuối dòng không?