ทำไม sizeof int จึงผิดในขณะที่ sizeof (int) ถูกต้อง


97

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

int main()
{
        int a;

        sizeof int;
        sizeof( int );
        sizeof a;
        sizeof( a );

        return 0;
}

การใช้ครั้งแรกsizeofผิดในขณะที่คนอื่นถูก

เมื่อคอมไพล์โดยใช้ gcc ข้อความแสดงข้อผิดพลาดต่อไปนี้จะได้รับ:

main.c:5:9: error: expected expression beforeint

คำถามของฉันคือเหตุใดมาตรฐาน C จึงไม่อนุญาตให้มีการทำงานแบบนี้ จะsizeof intทำให้เกิดความคลุมเครือหรือไม่


5
สิ่งที่ตลกก็คือว่าไม่แสดงออกทั้งหมดได้รับการยอมรับโดยไม่ต้องวงเล็บ: sizeof (int)aลอง
Fred Foo

2
@MikkelK: OP กำลังถามเหตุผลที่อยู่เบื้องหลังคำพูดมาตรฐานที่เขารู้อยู่แล้วว่าคำพูดที่กล่าวถึงในคำตอบที่ทำเครื่องหมายไว้
Alok บันทึก

2
@Lundin: sizeof +(int)aมีข้อได้เปรียบใน*&การรวบรวมจริงๆ ;-)
Steve Jessop

2
@SteveJessop อ่าใช่แล้วมันไม่ใช่ lvalue แม้ว่ามันจะรวบรวมใน Embarcadero C ++ แต่ก็แปลกพอสมควร อย่างไรก็ตามฉันเชื่อว่าตอนนี้เราพบการใช้งานสำหรับตัวดำเนินการ unary + แล้ว! นั่นจะต้องเป็นครั้งแรกในประวัติศาสตร์ของการเขียนโปรแกรม C :)
Lundin

2
@Lundin: +คุณยังคงต้องระมัดระวังกับเอก ตัวอย่างเช่นsizeof +(char)a == sizeof(int)เนื่องจากการส่งเสริมจำนวนเต็มอาจมีข้อผิดพลาดน้อยกว่าที่จะใส่ในวงเล็บหากมีความกังวลเกี่ยวกับนิพจน์ที่คุณพยายามใช้ขนาด ดังนั้นฉันไม่แน่ใจว่าฉันจะเรียกมันว่า "การใช้งาน" แม้ว่าฉันจะยอมรับว่าฉันใช้มัน ...
Steve Jessop

คำตอบ:


101

สิ่งต่อไปนี้อาจคลุมเครือ:

sizeof int * + 1

นั่น(sizeof (int*)) + 1หรือ(sizeof(int)) * (+1)?

เห็นได้ชัดว่าภาษา C สามารถนำกฎมาใช้เพื่อแก้ไขความคลุมเครือได้ แต่ฉันนึกได้ว่าทำไมมันถึงไม่น่ารำคาญ เมื่อใช้ภาษาตามที่ระบุตัวระบุประเภทจะไม่ปรากฏ "เปล่า" ในนิพจน์ดังนั้นจึงไม่จำเป็นต้องมีกฎเพื่อระบุว่าวินาที*นั้นเป็นส่วนหนึ่งของประเภทหรือตัวดำเนินการทางคณิตศาสตร์

ไวยากรณ์ที่มีอยู่สามารถแก้ไขความคลุมเครือที่อาจเกิดขึ้นsizeof (int *) + 1ได้ มันเป็น(sizeof(int*))+1, ไม่ sizeof((int*)(+1))

C ++ มีปัญหาที่ค่อนข้างคล้ายกันในการแก้ไขด้วยไวยากรณ์การแคสต์สไตล์ฟังก์ชัน คุณสามารถเขียนint(0)และคุณสามารถเขียนแต่คุณไม่สามารถเขียนtypedef int *intptr; intptr(0); int*(0)ในกรณีนี้วิธีแก้ปัญหาคือประเภท "ไม่มี" ต้องเป็นชื่อประเภทที่เรียบง่ายไม่ได้เป็นเพียงรหัสประเภทเก่าที่อาจมีช่องว่างหรือมีเครื่องหมายวรรคตอนต่อท้าย บางทีsizeofอาจถูกกำหนดด้วยข้อ จำกัด เดียวกันฉันไม่แน่ใจ


1
C ++ new int*(X)จะมีความคลุมเครือหาก C ++ ไม่ได้ระบุว่าตัวดำเนินการใหม่รับสตริงที่ยาวที่สุดซึ่งอาจเป็นชนิด ดังนั้นใน C ++ พวกเขาสามารถสร้างแบบเดียวกันกับ sizeof ได้ฉันเดาว่า แต่ใน C ++ คุณสามารถพูดได้ว่าsizeof int()สิ่งใดที่ไม่ชัดเจน (ประเภทหรือค่าเริ่มต้น int?)
Johannes Schaub - litb

@ JohannesSchaub-litb: อืมบางที C ++ อาจบอกได้ว่าต้องการประเภทนี้ถ้าเป็นไปได้ที่จะแยกวิเคราะห์มิฉะนั้นจะเป็นการแสดงออก ความคลุมเครือจะได้รับการแก้ไข แต่sizeof int()จะเป็นรูปแบบที่ไม่ดี ("ไม่operator()สำหรับsize_t") ซึ่งฉันคาดว่าจะไม่เป็นที่พอใจ!
Steve Jessop

32

จากC99 Standard

6.5.3.4.2
ตัวsizeofดำเนินการให้ขนาด (เป็นไบต์) ของตัวถูกดำเนินการซึ่งอาจเป็นนิพจน์หรือชื่อในวงเล็บของชนิด

ในกรณีของคุณintคือนิพจน์หรือชื่อในวงเล็บ


10
ฉันไม่เข้าใจว่าทำไมถึงได้รับ 7 upvotes เช่นเดียวกับคำตอบที่ถูกลบทั้งสามคำตอบเพียงแค่ทำซ้ำกฎแทนที่จะอธิบายว่าเหตุใดจึงมีกฎ
Fred Foo

8
@larsmans ใช่ฉันเห็นด้วยกับคุณ แม้ว่าจะยังคงทำซ้ำกฎ แต่อย่างน้อยก็ให้การอ่านที่อนุญาต
Yishu Fang

3
@larsmans ถ้าเราเริ่มพยายามพิสูจน์ทุกการตัดสินใจใน C99 เราจะอยู่ที่นี่ตลอดทั้งปี การอ้างถึงมาตรฐานเป็นวิธีที่ดีพอ ๆ กับการสรุปการสนทนานี้
Perry

1
@Perry: หากคุณไม่ชอบคำถามประเภทนี้คุณสามารถโหวตเพื่อปิดคำถามแบบโต้แย้งได้
Fred Foo

4
ในฐานะที่ไม่ใช่ผู้ฝึก C ++ ฉันเข้าใจคำตอบนี้ แต่ไม่แน่ใจว่าปัญหาคืออะไรถ้าคุณสามารถอ่านและเข้าใจภาษาอังกฤษได้
Kev

6

มีสองวิธีในการใช้ตัวดำเนินการ sizeof ใน C ไวยากรณ์คือ:

C11 6.5.3 Unary operators
...
sizeof unary-expression
sizeof ( type-name )

เมื่อใดก็ตามที่คุณใช้ type เป็นตัวถูกดำเนินการคุณต้องมีวงเล็บตามนิยามไวยากรณ์ของภาษา หากคุณใช้ sizeof กับนิพจน์คุณไม่จำเป็นต้องมีวงเล็บ

มาตรฐาน C ให้ตัวอย่างที่คุณอาจต้องการใช้กับนิพจน์:

sizeof array / sizeof array[0]

อย่างไรก็ตามเพื่อความสอดคล้องและเพื่อหลีกเลี่ยงข้อบกพร่องที่เกี่ยวข้องกับลำดับความสำคัญของตัวดำเนินการฉันขอแนะนำให้ใช้ () เป็นการส่วนตัวเสมอไม่ว่าสถานการณ์จะเป็นอย่างไร


@ JohannesSchaub-litb ฉันยอมรับว่ามันไม่ได้ให้เหตุผลใด ๆ สำหรับวิธีที่คณะกรรมการมาตรฐาน C ให้เหตุผลเมื่อพวกเขาระบุมาตรฐาน C90 คุณต้องถามพวกเขา ... มันตอบโดยไม่มีเหตุผลใด ๆ ที่ระบุไว้อย่างไรก็ตาม C ไม่อนุญาตเนื่องจากไวยากรณ์เป็นไปตามที่ระบุใน 6.5.3 ดูเหมือนว่า OP จะไม่รู้ถึงความแตกต่างระหว่างsizeof expressionและsizeof(type)ซึ่งอธิบายไว้ในคำตอบนี้
Lundin

(สำหรับบันทึกนี้ฉันเพิ่งอ่านทั้งเหตุผล C90 และ C11 และไม่ได้ให้คำอธิบายใด ๆ )
Lundin

ฉันสงสัยว่าไม่มีเหตุผลที่จำเป็นสำหรับเรื่องนี้มันเป็นเพียงวิธีที่นักออกแบบ C ดั้งเดิมตัดสินใจทำ บางทีมันอาจทำให้ง่ายขึ้นสำหรับตัวแยกวิเคราะห์ในช่วงต้น ฉันจะโพสต์คำตอบเกี่ยวกับข้อเท็จจริงที่ว่า typedefs และตัวแปรใช้เนมสเปซเดียวกัน แต่ไม่ได้ขัดขวางการอนุญาตให้ใช้ไวยากรณ์แรก เพียงแค่ต้องการกฎการแยกวิเคราะห์เพื่อบอกว่ามันชอบตัวแปรเมื่อไม่มี parens ชอบประเภทเมื่อมีและสามารถใช้รูปแบบใดก็ได้เมื่อชื่อไม่คลุมเครือ เนื่องจากไม่จำเป็นต้องเปลี่ยนแปลงคณะกรรมการมาตรฐานจึงปล่อยให้มันอยู่คนเดียว
Barmar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.