ตัวดำเนินการบูล ++ และ -


104

วันนี้ในขณะที่เขียนโค้ด Visual C ++ ฉันเจอสิ่งที่ทำให้ฉันประหลาดใจ ดูเหมือนว่า C ++ รองรับ ++ (เพิ่มขึ้น) สำหรับบูล แต่ไม่ใช่ - (ลดลง) นี่เป็นเพียงการตัดสินใจแบบสุ่มหรือมีเหตุผลบางอย่างอยู่เบื้องหลัง?

สิ่งนี้รวบรวม:

static HMODULE hMod = NULL;
static bool once = false;
if (!once++)
    hMod = LoadLibrary("xxx");

สิ่งนี้ไม่:

static HMODULE hMod = NULL;
static bool once = true;
if (once--)
    hMod = LoadLibrary("xxx");

2
hm เหมือนกันสำหรับคอมไพเลอร์ xcode และ gcc
Vladimir

ใช่++onceและonce++ทำงานกับ gcc แต่ไม่ใช่การลดลง
Justin Ardini

อาจจะติดแท็ก "history" ใหม่แทน "ตัวดำเนินการ - คีย์เวิร์ด" ดังนั้นจึงถูกจัดกลุ่มพร้อมกับคำอธิบายสนุก ๆ อื่น ๆ ว่าทำไมเรื่องบ้าๆต่างๆจึงสมเหตุสมผลถ้าคุณพิจารณาประวัติศาสตร์? :)
จอนฮันนา

หมายเหตุ ณ C ++ 17 ผู้ประกอบการก่อนที่เพิ่มขึ้นสำหรับการboolถูกเลิกsouce
cogle

สิ่งนี้สามารถแทนที่ได้ด้วยstd::exchange(once,false)(หมายเหตุ: ไม่ใช่อะตอม) หากคุณต้องการสิ่งที่ไม่เลิกใช้
golvok

คำตอบ:


91

มาจากประวัติของการใช้ค่าจำนวนเต็มเป็นบูลีน

ถ้าxเป็น an intแต่ฉันกำลังใช้มันเป็นบูลีนตามการif(x)...เพิ่มขึ้นจะหมายความว่าค่าความจริงใด ๆ ก็ตามก่อนการดำเนินการมันจะมีค่าความจริงตามtrueหลัง (ยกเว้นการล้น)

อย่างไรก็ตามเป็นไปไม่ได้ที่จะทำนายผลลัพธ์ของ--ความรู้ที่กำหนดให้เป็นค่าความจริงxเท่านั้นเนื่องจากอาจทำให้เกิดfalse(ถ้าค่าอินทิกรัลเป็น 1) หรือtrue(ถ้าค่าอินทิกรัลเป็นอย่างอื่นโดยเฉพาะอย่างยิ่งซึ่งรวมถึง 0 [ false] และ 2 หรือ เพิ่มเติม [ true])

เช่นเดียวกับมือสั้น++ทำงานและ--ไม่ได้ผล

++ ได้รับอนุญาตในบูลเพื่อให้เข้ากันได้กับสิ่งนี้ แต่การใช้งานถูกเลิกใช้ในมาตรฐานและถูกลบออกใน C ++ 17


นี้อนุมานว่าฉันเพียงใช้xเป็นบูลีนความหมายล้นที่ไม่สามารถเกิดขึ้นจนกว่าฉันได้ทำ++บ่อยมากพอที่จะทำให้เกิดการล้นบนเป็นของตัวเอง แม้ว่าจะมี char เป็นประเภทที่ใช้และCHAR_BITSบางอย่างที่ต่ำเช่น 5 แต่ 32 เท่าก่อนหน้านี้ก็ไม่ได้ผลอีกต่อไป (นั่นยังคงมีข้อโต้แย้งเพียงพอสำหรับการปฏิบัติที่ไม่ดีฉันไม่ได้ปกป้องการฝึกฝนเพียงแค่อธิบายว่าเหตุใดจึงได้ผล) สำหรับ 32 บิตintเราจะต้องใช้++2 ^ 32 ครั้งก่อนที่จะเป็นปัญหา ด้วย--แม้ว่ามันจะส่งผลให้falseถ้าผมเริ่มด้วยค่าตัว 1 trueหรือเริ่มต้นด้วย 0 และใช้งาน++ได้อย่างแม่นยำอีกครั้งก่อน

สิ่งนี้จะแตกต่างกันถ้าเราเริ่มต้นด้วยค่าที่ต่ำกว่า 0 เพียงไม่กี่อันอันที่จริงในกรณีนี้เราอาจต้องการ ++ให้ผลลัพธ์เป็นfalseค่าในที่สุดเช่นใน:

int x = -5;
while(++x)
  doSomething(x);

อย่างไรก็ตามตัวอย่างนี้ถือว่า xเป็นintทุกที่ยกเว้นเงื่อนไขดังนั้นจึงเทียบเท่ากับ:

int x = -5;
while(++x != 0)
  doSomething(x);

ซึ่งแตกต่างกันเพียงการใช้xเป็นบูลีน


1
ขอบคุณ. ดีใจที่ได้ทราบว่าฉันยังสามารถให้คำตอบกับผู้คนเช่นนี้ได้โดยพิจารณาจากระยะเวลาที่ฉันเขียน C ++ จริง ๆ :)
จอนฮันนา

8
แต่ถ้า x เป็น -1 (TRUE ในบางแพลตฟอร์มเช่น VB) ++ x จะเป็น FALSE
James Curran

4
@ James ในภาษา C และ C ++ นั่นคงเป็นกรณีที่ฉันนึกถึงตอนที่ฉันพูด ("barring overflow") ที่จริงแล้วใน VB ใด ๆ ที่ไม่ใช่ศูนย์มีค่าความจริง TRUE (เช่นใน C) แต่มี -1 มากกว่า 1 เนื่องจากการดำเนินการบูลีนที่แท้จริงดังนั้น NOT (TRUE) เป็น FALSE, NOT (FALSE) เป็น TRUE, x หรือ TRUE คือ TRUE, x หรือ FALSE คือ x, x และ FALSE คือ FALSE และ x AND TRUE คือ x เป็นต้นโดยใช้ตัวดำเนินการเดียวกันสำหรับการดำเนินการแบบบูลีนและบิตที่ชาญฉลาด (เนื่องจาก VB ถือว่าสองส่วนเติมเต็มดังนั้น -1 คือ 1 บิตทั้งหมด) อย่างไรก็ตามสิ่งนี้อาจทำให้เกิดบั๊กแปลก ๆ ใน VB หาก coder ไม่จับ 2 (true) และ 4 (true) ให้ผลลัพธ์เป็น 0 (false)
จอนฮันนา

2
@ จอนฮันนา: ANSI C89 เป็นมาตรฐาน C ตัวแรก คณะกรรมการ ANSI C ได้คิดค้น<limits.h>ส่วนหัวและCHAR_BITมาโคร ก่อนหน้านั้นฉันคิดว่าในทางทฤษฎีอาจเป็นการใช้งานที่charแคบกว่า 8 บิต แต่เท่าที่ฉันรู้ว่าไม่มีเลย โดยเฉพาะอย่างยิ่ง K & R1 (ตีพิมพ์ในปี 1978) รายการที่ 4 การใช้งานตัวอย่างทั้งหมดที่มี 8 บิตหรือ char9
Keith Thompson

1
@JonHanna: เป็นไปตามกลไกการดำเนินงาน C จะต้องCHAR_BIT >= 8มี มาตรฐานไม่ได้ให้ค่าเผื่อสำหรับเป้าหมายที่ยาก (แน่นอนคุณอาจมีการใช้งานที่ไม่เป็นไปตามข้อกำหนด)
Keith Thompson

29

ANSI ISO IEC 14882 2003 (c ++ 03):

5.2.6-2

ตัวถูกดำเนินการของ postfix - จะลดลงในลักษณะคล้ายคลึงกับตัวดำเนินการ postfix ++ ยกเว้นว่าตัวถูกดำเนินการจะต้องไม่เป็นประเภทบูล [หมายเหตุ: สำหรับการเพิ่มและลดคำนำหน้าโปรดดูที่ 5.3.2 ]

และไม่แปลกใจเลย ...

5.3.2-2

ตัวถูกดำเนินการของคำนำหน้า - ถูกแก้ไขโดยการลบ 1 ตัวถูกดำเนินการต้องไม่เป็นประเภทบูล ข้อกำหนดเกี่ยวกับตัวถูกดำเนินการของคำนำหน้า - และคุณสมบัติของผลลัพธ์จะเหมือนกับคำนำหน้า ++ [หมายเหตุ: สำหรับการเพิ่มและการลดหลังการแก้ไขโปรดดูที่ 5.2.6 ]

นอกจากนี้ข้อ 5.6.2-1 และ 5.3.2-1 ยังระบุว่า ++ สำหรับบูลจะต้องเป็นจริงและภาคผนวก D-1 ระบุว่า ++ ในบูลที่เลิกใช้แล้ว


3
@BlueRaja: ดูคำตอบของจอนฮันนา
Justin Ardini

9

เนื่องจากเหตุผลทางประวัติศาสตร์จึงได้รับการสนับสนุน แต่โปรดทราบว่า ... การใช้ตัวถูกดำเนินการประเภทบูลกับตัวดำเนินการ ++ นั้นเลิกใช้แล้วดูส่วน 5.3.2 ในมาตรฐาน C ++ (n3092)

5.3.2 การเพิ่มและลด [expr.pre.incr]

  • ตัวถูกดำเนินการของคำนำหน้า ++ ถูกแก้ไขโดยการเพิ่ม 1 หรือตั้งค่าเป็นจริงหากเป็นบูล (เลิกใช้งานการใช้นี้) ตัวถูกดำเนินการจะต้องเป็นค่า lvalue ที่ปรับเปลี่ยนได้ ประเภทของตัวถูกดำเนินการต้องเป็นประเภทเลขคณิตหรือตัวชี้ไปยังประเภทวัตถุที่กำหนดไว้อย่างสมบูรณ์ ผลลัพธ์คือตัวถูกดำเนินการที่อัปเดต มันเป็นค่า lvalue และเป็นบิตฟิลด์ถ้าตัวถูกดำเนินการเป็นบิตฟิลด์ ถ้า x ไม่ใช่ประเภทบูลนิพจน์ ++ x จะเทียบเท่ากับ x + = 1 [หมายเหตุ: ดูการอภิปรายเกี่ยวกับการเพิ่ม (5.7) และตัวดำเนินการกำหนด (5.17) สำหรับข้อมูลเกี่ยวกับการแปลง - ส่งหมายเหตุ]
  • ตัวถูกดำเนินการของคำนำหน้า - ถูกแก้ไขโดยการลบ 1 ตัวถูกดำเนินการต้องไม่เป็นประเภทบูล ข้อกำหนดเกี่ยวกับตัวถูกดำเนินการของคำนำหน้า - และคุณสมบัติของผลลัพธ์จะเหมือนกับคำนำหน้า ++

3
  • ด้วยมาตรฐานเก่า (C ++ 98) ไม่ใช่ข้อผิดพลาด
  • ด้วยมาตรฐานใหม่ที่เพิ่มบูลีนจึงเลิกใช้งาน (C ++ 11)
  • คุณสามารถใช้การเพิ่มบูลีนได้จนถึง C ++ 17
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.