ทำไม sizeof (my_arr) [0] จึงคอมไพล์และเท่ากับ sizeof (my_arr [0])


129

ทำไมโค้ดนี้จึงคอมไพล์

_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 รายการแรกนั้นถูกต้อง แต่ฉันคาดว่าบรรทัดสุดท้ายจะล้มเหลวเนื่องจากความเข้าใจของฉันคือsizeof()ควรประเมินเป็นลิเทอรัลจำนวนเต็มซึ่งไม่สามารถถือเป็นอาร์เรย์ได้ กล่าวอีกนัยหนึ่งมันจะล้มเหลวในลักษณะเดียวกับที่บรรทัดต่อไปนี้ล้มเหลว:

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

ที่น่าสนใจสิ่งต่อไปนี้ไม่สามารถรวบรวมได้อย่างแท้จริง (ซึ่งควรทำในสิ่งเดียวกันไม่ใช่หรือ):

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

ข้อผิดพลาด: อาร์กิวเมนต์ประเภทที่ไม่ถูกต้องของ unary '*' (have 'long unsigned int') _Static_assert (* sizeof (my_arr) == 4, "");

ถ้าเป็นเรื่องสำคัญฉันใช้ gcc 5.3.0


15
ฉันสงสัยว่า( sizeof( my_arr ) )[ 0 ]ล้มเหลว
Andrew Henle

รายการที่ซ้ำกันล่าสุดมีรูปแบบอื่นที่แปลกใจเกี่ยวกับไวยากรณ์นี้: ทำไม sizeof (x) ++ จึงคอมไพล์
Peter Cordes

คำตอบ:


197

sizeofไม่ใช่ฟังก์ชัน มันเป็นผู้ประกอบการเช่นเอกหรือ!~

sizeof(my_arr)[0]แยกวิเคราะห์เป็นsizeof (my_arr)[0]ซึ่งเป็นเพียงsizeof my_arr[0]วงเล็บที่ซ้ำซ้อน

นี้เป็นเช่นเดียวกับแยกวิเคราะห์เป็น!(my_arr)[0]!(my_arr[0])

โดยทั่วไปแล้วตัวดำเนินการ postfix จะมีลำดับความสำคัญสูงกว่าตัวดำเนินการคำนำหน้าใน C. sizeof *a[i]++แยกวิเคราะห์เป็นsizeof (*((a[i])++))(ตัวดำเนินการ postfix []และ++ถูกนำไปใช้กับaตัวดำเนินการคำนำหน้า*และsizeof)

(นี่คือเวอร์ชันนิพจน์ของsizeofนอกจากนี้ยังมีเวอร์ชันประเภทซึ่งใช้ชื่อประเภทในวงเล็บ: sizeof (TYPE)ในกรณีนี้จำเป็นต้องใช้ parens และเป็นส่วนหนึ่งของsizeofไวยากรณ์)


14
ฉันรู้แน่นอนว่า sizeof เป็นตัวดำเนินการแบบยูนารีไม่ใช่ฟังก์ชัน แต่ลืมไปโดยสิ้นเชิง woops ขอบคุณสำหรับคำอธิบายโดยละเอียด ความจริงที่ว่า [] มีความสำคัญสูงกว่า * เป็นสิ่งที่น่าสนใจไม่ว่า
bgomberg

@melpomene น่าสนใจ. ฉันไม่เคยคิดที่sizeofจะเป็นผู้ดำเนินการยูนารี
mutantkeyboard

5
คุณไม่ได้หมายความว่า"... parses as sizeof (my_arr[0])"? แค่เพิ่มช่องว่างก็ไม่ได้เปลี่ยนอะไรจริงๆ
Bernhard Barker

ฉันอยากจะแนะนำsizeof((my_array)[0])แทน
bolov


46

sizeofมีสอง "เวอร์ชัน": sizeof(type name)และsizeof expression. อดีตต้องการคู่ของ()การโต้แย้ง แต่อย่างหลัง - อันที่มีนิพจน์เป็นอาร์กิวเมนต์ - ไม่มี()ข้อโต้แย้ง สิ่งที่()คุณใช้ในอาร์กิวเมนต์จะถูกมองว่าเป็นส่วนหนึ่งของนิพจน์อาร์กิวเมนต์ไม่ใช่ส่วนหนึ่งของsizeofไวยากรณ์

เนื่องจากmy_arrเป็นที่รู้จักกันคอมไพเลอร์เป็นชื่อวัตถุไม่ใช่ชื่อประเภทของคุณsizeof(my_arr)[0]จะเห็นจริงโดยเรียบเรียงเป็นsizeofนำไปใช้กับการแสดงออก: sizeof (my_arr)[0]ที่(my_arr)[0]แสดงออกโต้แย้ง ()รอบชื่ออาร์เรย์เป็นฟุ่มเฟือยอย่างหมดจด sizeof my_arr[0]การแสดงออกทั้งหมดจะถูกตีความว่าเป็น sizeof(my_arr[0])นี้จะเทียบเท่ากับก่อนหน้านี้

(ซึ่งหมายความว่า BTW ก่อนหน้านี้ของคุณsizeof(my_arr[0])มีคู่ของฟุ่มเฟือย()อยู่ด้วย)

มันเป็นความเข้าใจผิดที่ค่อนข้างแพร่หลายว่าsizeofไวยากรณ์ต้องใช้คู่ของ()อาร์กิวเมนต์ sizeof(my_arr)[0]ความเข้าใจผิดนี่คือสิ่งที่เข้าใจผิดของผู้คนสัญชาตญาณเมื่อแปลแสดงออกเช่น


1
เวอร์ชันแรกมีอยู่เพื่อให้คุณสามารถตรวจสอบขนาดของจำนวนเต็มโดยทั่วไปบนเครื่องได้ (จากด้านหลังเมื่อไม่มีเครื่อง 64 บิต!) แต่intไม่ใช่นิพจน์ที่ถูกต้องดังนั้นคุณจึงไม่สามารถใช้ รูปแบบที่สองกับมัน
เอช.

25

[]มี precendence sizeofสูงกว่า ดังนั้นเป็นเช่นเดียวกับsizeof(my_arr)[0]sizeof((my_arr)[0])

นี่คือลิงค์ไปยังตารางลำดับความสำคัญ


8

คุณกำลังใช้ตัวดำเนินการเวอร์ชันsizeofที่รับนิพจน์เป็นพารามิเตอร์ ต่างจากประเภทที่เป็นประเภทที่ไม่ต้องใช้วงเล็บ ดังนั้นตัวถูกดำเนินการจึงเป็นเพียง(my_arr)[0]วงเล็บที่ซ้ำซ้อน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.