Tại sao sizeof (my_arr) [0] biên dịch và sizeof bằng nhau (my_arr [0])?


129

Tại sao mã này biên dịch?

_Static uint32_t my_arr[2];
_Static_assert(sizeof(my_arr) == 8, "");
_Static_assert(sizeof(my_arr[0]) == 4, "");
_Static_assert(sizeof(my_arr)[0] == 4, "");

2 khẳng định đầu tiên rõ ràng là chính xác, nhưng tôi đã dự đoán dòng cuối cùng sẽ thất bại, vì sự hiểu biết của tôi là sizeof()nên đánh giá theo một số nguyên, không thể được coi là một mảng. Nói cách khác, nó sẽ thất bại theo cách tương tự như dòng sau:

_Static_assert(4[0] == 4, "");

Thật thú vị, những điều sau đây thực sự không biên dịch (mà nên làm điều tương tự, phải không?):

_Static_assert(*sizeof(my_arr) == 4, "");

lỗi: đối số loại không hợp lệ của unary '*' (có 'long unsign int') _Static_assert (* sizeof (my_arr) == 4, "");

Nếu có vấn đề, tôi đang sử dụng gcc 5.3.0


15
Tôi nghi ngờ ( sizeof( my_arr ) )[ 0 ]thất bại.
Andrew Henle

Một bản sao gần đây có một biến thể khác về cú pháp này gây bất ngờ: Tại sao sizeof (x) ++ biên dịch?
Peter Cordes

Câu trả lời:


197

sizeofkhông phải là một chức năng. Đó là một toán tử đơn nguyên như !hay ~.

sizeof(my_arr)[0]phân tích cú pháp như sizeof (my_arr)[0], chỉ sizeof my_arr[0]với dấu ngoặc đơn thừa.

Điều này cũng giống như !(my_arr)[0]phân tích như !(my_arr[0]).

Nói chung, các toán tử postfix có độ ưu tiên cao hơn các toán tử tiền tố trong C. sizeof *a[i]++Parses là sizeof (*((a[i])++))(các toán tử postfix []++được áp dụng cho ađầu tiên, sau đó là các toán tử tiền tố *sizeof).

(Đây là phiên bản biểu thức của sizeof. Ngoài ra còn có một phiên bản loại, có tên loại được ngoặc đơn : sizeof (TYPE). Trong trường hợp đó, các parens sẽ được yêu cầu và là một phần của sizeofcú pháp.)


14
Tôi chắc chắn biết rằng sizeof là một toán tử đơn nguyên và không phải là một hàm, nhưng hoàn toàn quên mất. Ái chà. Cảm ơn vì lời giải thích chi tiết. Thực tế là [] được ưu tiên cao hơn * là thú vị bất kể.
beimberg

@melpomene Thú vị. Tôi chưa bao giờ nghĩ sizeoflà một nhà điều hành đơn nguyên.
mutantkeyboard

5
Ý bạn là "... parses as sizeof (my_arr[0])"sao? Chỉ cần thêm một không gian không thực sự thay đổi bất cứ điều gì.
Bernhard Barker

Tôi muốn giới thiệu sizeof((my_array)[0])thay thế
bolov


46

sizeofcó hai "phiên bản": sizeof(type name)sizeof expression. Cái trước đòi hỏi một cặp ()xung quanh đối số của nó. Nhưng cái sau - cái có biểu thức là một đối số - không có ()xung quanh đối số của nó. Bất cứ điều gì ()bạn sử dụng trong đối số được xem là một phần của biểu thức đối số, không phải là một phần của sizeofchính cú pháp.

Do my_arrtrình biên dịch được biết đến như là một tên đối tượng, không phải tên loại, sizeof(my_arr)[0]trình biên dịch của bạn thực sự được xem như sizeofđược áp dụng cho một biểu thức : sizeof (my_arr)[0], trong đó (my_arr)[0]là biểu thức đối số. Tên ()xung quanh là hoàn toàn thừa. Toàn bộ biểu thức được hiểu là sizeof my_arr[0]. Điều này tương đương với trước đây của bạn sizeof(my_arr[0]).

(Điều này có nghĩa là, BTW, rằng trước đây của bạn sizeof(my_arr[0])cũng chứa một cặp không cần thiết ().)

Đó là một quan niệm sai lầm khá phổ biến rằng sizeofcú pháp bằng cách nào đó đòi hỏi một cặp ()xung quanh đối số của nó. Quan niệm sai lầm này là những gì đánh lừa trực giác của mọi người khi diễn giải các biểu thức như sizeof(my_arr)[0].


1
Phiên bản đầu tiên tồn tại để bạn có thể kiểm tra kích thước của một số nguyên nói chung trên máy (từ trở lại khi không có máy 64 bit!), Nhưng intkhông phải là biểu thức hợp lệ, vì vậy bạn không thể sử dụng hình thức thứ hai với nó.
NH.

25

[]có độ ưu tiên cao hơn sizeof. Như vậy sizeof(my_arr)[0]là giống như sizeof((my_arr)[0]).

Đây là một liên kết đến một bảng ưu tiên.


8

Bạn đang sử dụng phiên bản của sizeoftoán tử lấy một biểu thức làm tham số. Không giống như kiểu lấy một kiểu, nó không yêu cầu dấu ngoặc đơn. Do đó, toán hạng chỉ đơn giản (my_arr)[0], với dấu ngoặc đơn là dự phòng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.