เหตุใดค่า int ที่เป็นลบมากที่สุดจึงทำให้เกิดข้อผิดพลาดเกี่ยวกับการโอเวอร์โหลดของฟังก์ชันที่ไม่ชัดเจน


91

ฉันกำลังเรียนรู้เกี่ยวกับการทำงานมากเกินไปใน C ++ และเจอสิ่งนี้:

void display(int a)
{
    cout << "int" << endl;
}

void display(unsigned a)
{
    cout << "unsigned" << endl;
}

int main()
{
    int i = -2147483648;
    cout << i << endl; //will display -2147483648
    display(-2147483648);
}

จากสิ่งที่ฉันเข้าใจค่าใด ๆ ที่กำหนดในintช่วง (ในกรณีของฉันintคือ 4 ไบต์) จะเรียกdisplay(int)และค่าใด ๆ ที่อยู่นอกช่วงนี้จะไม่ชัดเจน (เนื่องจากคอมไพเลอร์ไม่สามารถตัดสินใจได้ว่าจะเรียกใช้ฟังก์ชันใด) มันถูกต้องสำหรับช่วงที่สมบูรณ์ของintค่ายกเว้นค่าต่ำสุดเช่น-2147483648ที่การคอมไพล์ล้มเหลวด้วยข้อผิดพลาด

การโทรเกินdisplay(long int)กำลังมีความคลุมเครือ

แต่การค่าเดียวกันไปยังและการพิมพ์ค่าที่จะช่วยให้int 2147483648ฉันสับสนกับพฤติกรรมนี้อย่างแท้จริง

เหตุใดจึงสังเกตพฤติกรรมนี้เฉพาะเมื่อมีการส่งผ่านจำนวนลบมากที่สุด (ลักษณะการทำงานจะเหมือนกันหากshortใช้กับ-32768- ในความเป็นจริงไม่ว่าในกรณีใดก็ตามที่จำนวนลบและจำนวนบวกมีการแสดงไบนารีเหมือนกัน)

คอมไพเลอร์ที่ใช้: g ++ (GCC) 4.8.5


4
ค่าต่ำสุดของ Int คือ "ส่งข้อผิดพลาดของคอมไพเลอร์" ผิดพลาดประการใด คุณควรรวมไว้ในคำถาม
จัสติน

11
call of overloaded ‘display(long int)’ is ambiguousฉันเห็น
crashmstr

6
ไม่เกี่ยวข้อง แต่คุณควรอัปเดตคอมไพเลอร์ มี GCC 7.1 อยู่แล้ว
HolyBlackCat

4
นี่คือการคาดเดาของฉัน: typeof(-2147483648) != int. ตัวอักษรมี2147483648ขนาดใหญ่เกินไปสำหรับ an intดังนั้นมันจึงlongถูกปฏิเสธ
Justin

3
ที่น่าสนใจ g ++ (อย่างน้อย 6.4 และ 7.1) อย่าบ่นว่าint j{-2147483648};เป็นการแปลงที่แคบลง เกือบจะคุ้มค่ากับคำถามในตัวเองว่า อาจเกี่ยวข้องกับการอนุญาต (เช่น) long longค่า constexpr เช่น2147483647LLการ จำกัด การเริ่มต้น
Toby Speight

คำตอบ:


145

นี่เป็นข้อผิดพลาดที่ละเอียดอ่อนมาก สิ่งที่คุณเห็นเป็นผลมาจากไม่มีตัวอักษรจำนวนเต็มลบใน C ++ ถ้าเราดู [lex.icon] เราจะได้จำนวนเต็ม -ลิเทอรัล

integer-literal decimal
        -literal integer-suffix opt
        [... ]

สามารถเป็นทศนิยม - ลิเทอรั

ทศนิยมอักษร:
        ภัณฑ์หลัก
        ทศนิยมที่แท้จริง ' การเลือกหลัก

ที่หลักคือ[0-9]และเป็นศูนย์หลักคือ[1-9]และที่ตราไว้ต่อท้ายสามารถเป็นหนึ่งu, U, l, L, หรือll LLไม่มีที่ไหนในที่นี้รวมถึง-เป็นส่วนหนึ่งของลิเทอรัลทศนิยม

ใน§2.13.2เรายังมี:

ลิเทอรัลจำนวนเต็มคือลำดับของตัวเลขที่ไม่มีจุดหรือส่วนเลขชี้กำลังโดยมีเครื่องหมายคำพูดเดี่ยวที่เป็นทางเลือกซึ่งจะถูกละเว้นเมื่อกำหนดค่า ลิเทอรัลจำนวนเต็มอาจมีคำนำหน้าที่ระบุฐานและส่วนต่อท้ายที่ระบุประเภท ตัวเลขหลักแรกของคำศัพท์มีความสำคัญที่สุด ลิเทอรัลจำนวนเต็มทศนิยม (ฐานสิบ) เริ่มต้นด้วยตัวเลขอื่นที่ไม่ใช่ 0 และประกอบด้วยลำดับของหลักทศนิยม

(เน้นเหมือง)

ซึ่งหมายความว่า-ในการเป็นเอก-2147483648 operator -นั่นหมายถึงการได้รับการปฏิบัติจริงเป็น-2147483648 -1 * (2147483648)เนื่องจาก2147483648มีจำนวนมากเกินไปสำหรับคุณintจึงได้รับการเลื่อนระดับเป็น a long intและความคลุมเครือมาจากการไม่ตรงกัน

หากคุณต้องการรับค่าต่ำสุดหรือสูงสุดสำหรับประเภทในลักษณะพกพาคุณสามารถใช้:

std::numeric_limits<type>::min();  // or max()

2
-2147483647 - 1จะทำงานโดยไม่มีการเตือนเป็นนิพจน์เชิงลบ
Cœur

2
หรือINT_MINสำหรับตัวเลือก verbose น้อยที่สุด ทั่วไปน้อยกว่าแม้ว่า
MSalters

@NathanOliver, display(2147483649);คุณสามารถกรุณาอธิบายให้ฉันกรณีนี้ เหตุใดจึงไม่เรียกฟังก์ชัน int int ที่ไม่ได้ลงชื่อในกรณีนี้ และเหตุใดจึงถือว่าอาร์กิวเมนต์2147483649เป็น int ยาวแทนที่จะเป็น int ที่ไม่ได้ลงนาม
infinite loop

2
@infiniteloop ทศนิยมจำนวนเต็มอักษรไปจากที่intที่จะไปlong int long long intคุณ wlll จะไม่ได้รับประเภทที่ไม่ได้ลงชื่อสำหรับลิเทอรัลทศนิยมเว้นแต่คุณจะใช้u/ Uต่อท้าย
NathanOliver

2
ในตัวอย่างนี้ใช่ หากต้องการโทรหาdisplay(unsigned a)คุณต้องdisplay(1234u);หรือdisplay(static_cast<unsigned>(1234));หรือunsigned foo = 1234; display(foo);
NathanOliver

36

การแสดงออก-2147483648ที่เป็นจริงที่ใช้ประกอบการอย่างต่อเนื่อง- 2147483648บนแพลตฟอร์มของคุณintไม่สามารถจัดเก็บ2147483648ได้ต้องแสดงด้วยประเภทที่ใหญ่กว่า ดังนั้นการแสดงออก-2147483648ไม่ได้ที่จะอนุมานได้แต่มีขนาดใหญ่ลงนามชนิดsigned intsigned long int

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


4

ขยายคำตอบของผู้อื่น


เพื่อชี้แจงว่าเหตุใด OP จึงสับสนอันดับแรกให้พิจารณาการsigned intแสดงไบนารี2147483647ด้านล่าง

int ที่ลงนามที่ใหญ่ที่สุด




ถัดไปเพิ่มหนึ่งไปยังหมายเลขนี้ : giving อื่นsigned intของ-2147483648(ซึ่ง OP มีความประสงค์จะใช้งาน) Int ที่ลงชื่อน้อยที่สุด



สุดท้าย:เราจะเห็นว่าเหตุใด OP จึงสับสนเมื่อ-2147483648คอมไพล์เป็น a long intแทนที่จะเป็น a signed intเนื่องจากมันพอดีกับ 32 บิตอย่างชัดเจน

แต่ตามที่คำตอบปัจจุบันกล่าวถึงตัวดำเนินการยูนารี ( -) จะถูกนำไปใช้หลังจากการแก้ไข2147483648ซึ่งเป็น a long intและไม่พอดีกับ 32 บิต

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