ตั้งค่าถ่านเป็น CHAR_MAX รับประกันว่าจะพันไปที่ CHAR_MIN หรือไม่?


10

รหัสของฉัน:

#include <stdio.h>
#include <limits.h>

int main()
{
    char c = CHAR_MAX;
    c += 1;
    printf("CHAR_MIN=%d CHAR_MAX=%d c=%d (%c)\n", CHAR_MIN, CHAR_MAX, c, c);
}

เอาท์พุท:

CHAR_MIN=-128 CHAR_MAX=127 c=-128 ()

เราจะเห็นว่าเมื่อเราเพิ่มcharชุดตัวแปรก็ล้อมรอบไปCHAR_MAX CHAR_MINพฤติกรรมนี้รับประกันหรือไม่ หรือมันจะเป็นพฤติกรรมที่ไม่ได้กำหนดหรือพฤติกรรมที่ระบุการใช้งาน? มาตรฐาน C99 พูดถึงอะไรเกี่ยวกับเรื่องนี้

[หมายเหตุ: จะเกิดอะไรขึ้นเมื่อให้ค่าที่มากกว่า CHAR_MAX (127) ถึงถ่านหรือC- ทำไมถ่าน c = 129 จะเปลี่ยนเป็น -127 ไม่ตอบคำถามนี้เพราะพวกเขาพูดถึงการกำหนดค่านอกช่วงไม่เพิ่มมูลค่าให้กับค่านอกช่วง]


การเพิ่มขึ้นคือการมอบหมาย
William Pursell

2
ขึ้นอยู่กับว่าถ่านถูกเซ็นชื่อหรือไม่ได้ลงนาม การโอเวอร์โฟลว์ของจำนวนเต็มที่ลงนามเป็นพฤติกรรมที่ไม่ได้กำหนด ดังนั้นเอาต์พุตสามารถเป็นอะไรก็ได้
DaBler

คำตอบ:


15

คำถามคือสองเท่า: ประการแรกคือ

char c = CHAR_MAX;
c += 1;

ประเมินแตกต่างจาก

char c = CHAR_MAX;
c = c + 1;

และคำตอบคือไม่มันไม่ใช่เพราะC11 / C18 6.5.16.2p3 :

  1. การกำหนดแบบผสมของแบบฟอร์มE1 op = E2นั้นเทียบเท่ากับการแสดงออกแบบง่าย ๆE1 = E1 op (E2)ยกเว้นว่าค่า lvalue E1จะถูกประเมินเพียงครั้งเดียวและสำหรับการเรียกใช้ฟังก์ชันตามลำดับแบบไม่กำหนดการดำเนินการของการมอบหมายแบบผสมเป็นการประเมินแบบครั้งเดียว หากE1มีประเภทอะตอมการกำหนดผสมเป็นการดำเนินการอ่าน - แก้ไข - เขียนด้วยซีแมนทิกส์memory_order_seq_cstลำดับความจำ 113)

c = c + 1จากนั้นคำถามคือสิ่งที่เกิดขึ้นใน ที่นี่ถูกดำเนินการที่จะ+ได้รับการแปลงทางคณิตศาสตร์ตามปกติและcและ1ดังนั้นจึงมีการส่งเสริมการintเว้นแต่สถาปัตยกรรมที่แปลกประหลาดจริงๆต้องการให้มีการเลื่อนตำแหน่งให้เป็นchar unsigned intการคำนวณ+จะถูกประเมินแล้วและผลที่ได้จากประเภทint/ unsigned intจะถูกแปลงกลับไปและเก็บไว้ในcharc

การกำหนดการนำไปปฏิบัติมี3วิธีซึ่งสิ่งนี้สามารถประเมินได้

  • CHAR_MINเป็น 0 และcharไม่ได้ลงนามดังนั้น

    อย่างใดอย่างหนึ่งcharคือการส่งเสริมไปแล้วintหรือunsigned intและหากมีการส่งเสริมให้intแล้วCHAR_MAX + 1จำเป็นจะพอดีกับintเกินไปและจะไม่ล้นหรือถ้าunsigned intมันอาจจะพอดีหรือห่อรอบให้เป็นศูนย์ เมื่อค่าผลลัพธ์ซึ่งเป็นตัวเลขทั้งCHAR_MAX + 1หรือ0หลังจากการลดแบบโมดูโลกลับไปที่cหลังจากการลดแบบโมดูโลมันจะกลายเป็น 0 นั่นคือCHAR_MIN

  • มิฉะนั้นcharจะได้รับการลงชื่อจากนั้นถ้าCHAR_MAX น้อยกว่าINT_MAXผลลัพธ์ของCHAR_MAX + 1จะพอดีintและมาตรฐานC11 / C18 6.3.1.3p3จะใช้กับการแปลงที่เกิดขึ้นเมื่อมีการมอบหมาย :

    1. มิฉะนั้นจะมีการลงชื่อประเภทใหม่และไม่สามารถแสดงค่าได้ ผลที่ได้คือการดำเนินการที่กำหนดไว้หรือสัญญาณที่กำหนดการดำเนินงานจะเพิ่มขึ้น
  • หรือIFF sizeof (int) == 1 และ charมีการลงนามแล้วcharจะส่งเสริมให้intและCHAR_MAX == INT_MAX=> CHAR_MAX + 1จะทำให้เกิดการล้นจำนวนเต็มและพฤติกรรมจะไม่ได้กำหนด

นั่นคือผลลัพธ์ที่เป็นไปได้คือ:

  • ถ้าcharเป็นชนิดจำนวนเต็มไม่ได้ลงนามผลเสมอคือ0CHAR_MIN

  • มิฉะนั้นcharเป็นชนิดจำนวนเต็มที่ลงนามและพฤติกรรมคือการใช้งานที่กำหนด / ไม่ได้กำหนด:

    • CHAR_MIN หรือค่าที่กำหนดโดยการนำไปปฏิบัติบางอย่าง
    • สัญญาณที่ถูกนำไปใช้งานถูกยกขึ้นอาจเป็นการยกเลิกโปรแกรม
    • sizeof (char) == sizeof (int)หรือพฤติกรรมจะไม่ได้กำหนดในบางแพลตฟอร์มที่

การดำเนินงานที่เพิ่มขึ้นทั้งหมดc = c + 1, c += 1, c++และ++cมีผลข้างเคียงที่เดียวกันบนเวทีเดียวกัน ค่าที่ประเมินของนิพจน์c++จะเป็นค่าcก่อนการเพิ่มขึ้น สำหรับอีกสามค่าจะเป็นค่าcหลังจากการเพิ่มขึ้น


1
sizeof(int) == 1จะต้องCHAR_BITS >= 16ใช่มั้ย
sepp2k

3
@ sepp2k <pedantic>IDK เกี่ยวCHAR_BITSแต่จะCHAR_BIT >= 16</pedantic>
Antti Haapala

2
อีกเหตุผลหนึ่งที่ทำไมcharไม่ควรลงชื่อโดยค่าเริ่มต้น
chqrlie

1
@chqrlie ฉันเห็นด้วยโชคไม่ดีที่อาจมีการลงชื่อโดยค่าเริ่มต้นเนื่องจากไม่ได้ลงชื่อในประวัติศาสตร์อาจเป็นเรื่องยากที่จะเปลี่ยนแปลงในระบบ cr * ppy เนื่องจากโปรแกรมที่เสียหายจำนวนมากคาดว่า EOF จะพอดีกับ char ..
Antti Haapala

1
บางครั้งก็ชัดเจนที่จะเพิ่มคำตอบโดยตรงเช่นกัน: "มีการตั้งค่าถ่านเป็น CHAR_MAX รับประกันว่าจะล้อมรอบไปที่ CHAR_MIN หรือไม่" -> ไม่
chux - Reinstate Monica
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.