ผลการดำเนินงานระดับบิตในขนาดตัวแปรที่ไม่คาดคิด


24

บริบท

เรากำลังพอร์ตโค้ด C ที่คอมไพล์แล้วโดยใช้คอมไพเลอร์ C แบบ 8 บิตสำหรับไมโครคอนโทรลเลอร์ PIC สำนวนทั่วไปที่ใช้เพื่อป้องกันตัวแปรโกลบอลที่ไม่ได้ลงชื่อ (ตัวอย่างเช่นตัวนับข้อผิดพลาด) จากการย้อนกลับเป็นศูนย์มีดังต่อไปนี้:

if(~counter) counter++;

ผู้ประกอบการระดับบิตที่นี่กลับค่าบิตทั้งหมดและคำสั่งเป็นจริงหากcounterน้อยกว่าค่าสูงสุด ที่สำคัญทำงานได้โดยไม่คำนึงถึงขนาดของตัวแปร

ปัญหา

ตอนนี้เรากำลังกำหนดเป้าหมายหน่วยประมวลผล ARM แบบ 32 บิตโดยใช้ GCC เราสังเกตเห็นว่ารหัสเดียวกันให้ผลลัพธ์ที่แตกต่าง เท่าที่เราสามารถบอกได้ดูเหมือนว่าการทำงานส่วนเสริม bitwise คืนค่าที่มีขนาดแตกต่างจากที่เราคาดไว้ ในการทำซ้ำสิ่งนี้เรารวบรวมใน GCC:

uint8_t i = 0;
int sz;

sz = sizeof(i);
printf("Size of variable: %d\n", sz); // Size of variable: 1

sz = sizeof(~i);
printf("Size of result: %d\n", sz); // Size of result: 4

ในบรรทัดแรกของเอาต์พุตเราได้สิ่งที่เราคาดหวัง: iคือ 1 ไบต์ อย่างไรก็ตามส่วนประกอบ bitwise ของiจริง ๆ แล้วสี่ไบต์ซึ่งทำให้เกิดปัญหาเนื่องจากการเปรียบเทียบกับตอนนี้จะไม่ให้ผลลัพธ์ที่คาดไว้ ตัวอย่างเช่นหากทำ (โดยที่iการเริ่มต้นอย่างถูกต้องuint8_t):

if(~i) i++;

เราจะเห็นi"ล้อมรอบ" จาก 0xFF กลับเป็น 0x00 พฤติกรรมนี้แตกต่างกันใน GCC เมื่อเทียบกับเมื่อก่อนเคยทำงานตามที่เราตั้งใจไว้ในคอมไพเลอร์ก่อนหน้าและไมโครคอนโทรลเลอร์ PIC 8 บิต

เราตระหนักดีว่าเราสามารถแก้ไขปัญหานี้ได้โดยการคัดเลือกนักแสดง:

if((uint8_t)~i) i++;

หรือโดย

if(i < 0xFF) i++;

อย่างไรก็ตามในการแก้ปัญหาทั้งสองนี้ขนาดของตัวแปรจะต้องเป็นที่รู้จักและเป็นข้อผิดพลาดสำหรับผู้พัฒนาซอฟต์แวร์ การตรวจสอบขอบเขตบนเหล่านี้เกิดขึ้นทั่วทั้งฐานข้อมูล ตัวแปรมีหลายขนาด (เช่น. uint16_tและunsigned charอื่น ๆ ) และการเปลี่ยนแปลงเหล่านี้ใน codebase ที่ใช้งานได้ไม่ใช่สิ่งที่เรารอคอย

คำถาม

ความเข้าใจของเราเกี่ยวกับปัญหานั้นถูกต้องและมีตัวเลือกสำหรับแก้ไขปัญหาที่ไม่จำเป็นต้องเยี่ยมชมแต่ละกรณีที่เราใช้สำนวนนี้หรือไม่ สมมติฐานของเราถูกต้องหรือไม่ว่าการดำเนินการเช่นส่วนเสริม bitwise ควรส่งคืนผลลัพธ์ที่มีขนาดเท่ากับตัวถูกดำเนินการหรือไม่ ดูเหมือนว่าสิ่งนี้จะพังขึ้นอยู่กับสถาปัตยกรรมของโปรเซสเซอร์ ฉันรู้สึกว่าฉันกำลังทานยาเม็ดบ้าและ C น่าจะพกพาได้มากกว่านี้ อีกครั้งความเข้าใจของเราเกี่ยวกับเรื่องนี้อาจผิด

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


หมายเหตุ:มีคำถามซ้ำกันที่ดูเหมือนจะคล้ายกัน แต่ไม่ตรงกับที่นี่: การดำเนินการบิตบนถ่านให้ผลลัพธ์ 32 บิต

ฉันไม่เห็นปัญหาที่แท้จริงของปัญหาที่กล่าวถึงนั่นคือขนาดของผลลัพธ์ของส่วนประกอบ bitwise ที่แตกต่างจากสิ่งที่ส่งผ่านไปยังผู้ประกอบการ


14
"ข้อสันนิษฐานของเราถูกต้องหรือไม่ว่าการดำเนินการเช่นส่วนเสริมระดับบิตควรส่งคืนผลลัพธ์ที่มีขนาดเท่ากับตัวถูกดำเนินการหรือไม่" ไม่ไม่ถูกต้องมีการใช้โปรโมชันจำนวนเต็ม
Thomas Jager

2
ในขณะที่มีความเกี่ยวข้องแน่นอนฉันไม่มั่นใจว่าสิ่งเหล่านี้ซ้ำซ้อนกับคำถามนี้เพราะพวกเขาไม่ได้ให้วิธีการแก้ปัญหา
Cody Gray

3
ฉันรู้สึกว่าฉันกำลังทานยาเม็ดบ้าและ C น่าจะพกพาได้มากกว่านี้ หากคุณไม่ได้รับโปรโมชันจำนวนเต็มสำหรับประเภท 8 บิตคอมไพเลอร์ของคุณไม่รองรับมาตรฐาน C ในกรณีนี้ฉันคิดว่าคุณควรดำเนินการคำนวณทั้งหมดเพื่อตรวจสอบและแก้ไขหากจำเป็น
694733

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

1
@puck คุณถูกต้องเราสามารถเปลี่ยนเป็น 4 ไบต์ แต่มันจะหยุดความเข้ากันได้เมื่อสื่อสารกับระบบที่มีอยู่ จุดประสงค์คือการรู้ว่าเมื่อมีข้อผิดพลาดใด ๆดังนั้นตัวนับ 1 ไบต์นั้นเพียงพอแล้ว แต่ยังคงเป็นเช่นนั้น
Charlie Salts

คำตอบ:


26

สิ่งที่คุณเห็นเป็นผลมาจากโปรโมชั่นจำนวนเต็ม ในกรณีส่วนใหญ่ที่ค่าจำนวนเต็มจะใช้ในการแสดงออกถ้าชนิดของค่าที่มีขนาดเล็กกว่าค่าจะเลื่อนตำแหน่งให้เป็นint intนี่คือเอกสารในส่วน 6.3.1.1p2 ของมาตรฐาน C :

ต่อไปนี้อาจถูกนำมาใช้ในการแสดงออกทุกที่intหรือ unsigned intอาจจะใช้

  • วัตถุหรือการแสดงออกที่มีชนิดจำนวนเต็ม (นอกเหนือintหรือunsigned int) ที่มีจำนวนเต็มแปลงยศน้อยกว่าหรือเท่ากับยศและintunsigned int
  • บิตสนามประเภท_Bool, int ,int ลงนาม, orint` ไม่ได้ลงนาม

หาก a intสามารถแทนค่าทั้งหมดของชนิดดั้งเดิม (ตามที่ จำกัด โดยความกว้างสำหรับบิตฟิลด์) ค่าจะถูกแปลงเป็นint; unsigned intมิฉะนั้นก็จะถูกแปลงเป็น เหล่านี้จะถูกเรียกว่าโปรโมชั่นจำนวนเต็ม ประเภทอื่น ๆ ทั้งหมดไม่เปลี่ยนแปลงโดยการส่งเสริมการขายจำนวนเต็ม

ดังนั้นหากตัวแปรมีประเภทuint8_tและค่า 255 การใช้โอเปอเรเตอร์อื่นนอกเหนือจากการร่ายหรือการมอบหมายให้ดำเนินการก่อนจะแปลงเป็นประเภทที่intมีค่า 255 ก่อนดำเนินการ นี่คือเหตุผลที่sizeof(~i)ให้ 4 แทน 1

ส่วนที่ 6.5.3.3 อธิบายว่าการส่งเสริมจำนวนเต็มนำไปใช้กับ~ผู้ปฏิบัติงาน

ผลลัพธ์ของ~โอเปอเรเตอร์คือส่วนประกอบ bitwise ของตัวถูกดำเนินการ (เลื่อนระดับ) (นั่นคือแต่ละบิตในผลลัพธ์จะถูกตั้งค่าถ้าหากบิตที่สอดคล้องกันในตัวถูกดำเนินการแปลงไม่ถูกตั้งค่า) การส่งเสริมการขายจำนวนเต็มดำเนินการกับตัวถูกดำเนินการและผลลัพธ์ที่ได้คือประเภทที่ได้รับการส่งเสริม หากประเภทที่ได้รับการส่งเสริมเป็นประเภทที่ไม่ได้ลงนามนิพจน์~Eจะเทียบเท่ากับค่าสูงสุดที่สามารถแทนได้ในประเภทEนั้น

ดังนั้นสมมติว่า 32 บิตintถ้าcounterมีค่า 8 บิต0xffจะถูกแปลงเป็นมูลค่า 32 บิต0x000000ffและการประยุกต์ใช้เพื่อช่วยให้คุณ~0xffffff00

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

if (!++counter) counter--;

การสรุปจำนวนเต็มที่ไม่ได้ลงนามนั้นทำงานได้ทั้งสองทิศทางดังนั้นการลดค่าเป็น 0 จะให้ค่าบวกที่ใหญ่ที่สุดแก่คุณ


1
if (!++counter) --counter;อาจเป็นเรื่องแปลกสำหรับโปรแกรมเมอร์บางคนน้อยกว่าการใช้เครื่องหมายคอมมา
Eric Postpischil

1
++counter; counter -= !counter;ทางเลือกหนึ่งคือ
Eric Postpischil

@EricPostpischil ที่จริงแล้วฉันชอบตัวเลือกแรกของคุณดีกว่า แก้ไข
dbush

15
สิ่งนี้น่าเกลียดและอ่านไม่ได้ไม่ว่าคุณจะเขียนมันอย่างไร หากคุณมีการใช้สำนวนเช่นนี้ทำทุกโปรแกรมเมอร์บำรุงรักษาชอบและห่อมันขึ้นเป็นฟังก์ชั่นแบบอินไลน์ : สิ่งที่ต้องการหรือincrement_unsigned_without_wraparound increment_with_saturationส่วนตัวผมใช้clampฟังก์ชั่นtri-operand ทั่วไป
Cody Gray

5
นอกจากนี้คุณไม่สามารถทำให้ฟังก์ชั่นนี้ได้เพราะมันจะต้องมีพฤติกรรมที่แตกต่างกันสำหรับประเภทอาร์กิวเมนต์ที่แตกต่างกัน คุณจะต้องใช้แมโครประเภททั่วไป
user2357112 รองรับ Monica

7

ในsizeof (i); คุณร้องขอขนาดของตัวแปรiดังนั้น 1

ในsizeof (~ i); คุณร้องขอขนาดของชนิดของนิพจน์ซึ่งเป็นintในกรณีของคุณ 4


ใช้

ถ้า (~ i)

ที่จะรู้ว่าถ้าฉันไม่ได้มูลค่า 255 (ในกรณีของคุณด้วย uint8_t) ไม่สามารถอ่านได้มากเพียงแค่ทำ

if (i != 255)

และคุณจะมีรหัสแบบพกพาและสามารถอ่านได้


มีตัวแปรหลายขนาด (เช่น. uint16_t และถ่านที่ไม่ได้ลงชื่อ)

ในการจัดการขนาดที่ไม่ได้ลงชื่อใด ๆ :

if (i != (((uintmax_t) 2 << (sizeof(i)*CHAR_BIT-1)) - 1))

นิพจน์เป็นค่าคงที่ดังนั้นคำนวณ ณ เวลารวบรวม

#include <จำกัด.h>สำหรับCHAR_BITและ#include <stdint.h>สำหรับuintmax_t


3
คำถามระบุอย่างชัดเจนว่าพวกเขามีหลายขนาดที่จะจัดการกับดังนั้นจึง!= 255ไม่เพียงพอ
Eric Postpischil

@EricPostpischil อ่าใช่ฉันลืมแล้วดังนั้น "ถ้า (i! = ((1u << sizeof (i) * 8) - 1))" สมมติว่าไม่ได้ลงชื่อเสมอ?
bruno

1
ที่จะไม่ได้กำหนดสำหรับunsignedวัตถุตั้งแต่การเปลี่ยนแปลงของวัตถุกว้างเต็มยังไม่ได้กำหนดโดยมาตรฐาน C (2u << sizeof(i)*CHAR_BIT-1) - 1แต่ก็สามารถแก้ไขด้วย
Eric Postpischil

โอ้ใช่ ofc, CHAR_BIT, bad ของฉัน
bruno

2
((uintmax_t) 2 << sizeof(i)*CHAR_BIT-1) - 1เพื่อความปลอดภัยกับชนิดกว้างหนึ่งอาจใช้
Eric Postpischil

5

ต่อไปนี้เป็นตัวเลือกหลายตัวสำหรับการนำ“ เพิ่ม 1 ไปยังxตัวยึดที่ค่าที่สามารถแทนค่าได้สูงสุด” เนื่องจากxเป็นประเภทจำนวนเต็มที่ไม่ได้ลงนาม:

  1. เพิ่มหนึ่งถ้าหากxน้อยกว่าค่าสูงสุดที่แสดงได้ในประเภท:

    x += x < Maximum(x);

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

  2. เปรียบเทียบกับค่าที่มากที่สุดของประเภท:

    if (x < ((uintmax_t) 2u << sizeof x * CHAR_BIT - 1) - 1) ++x

    (สิ่งนี้คำนวณ 2 Nโดยที่Nคือจำนวนบิตในxโดยเลื่อน 2 โดยN −1 บิตเราทำเช่นนี้แทนที่จะขยับ 1 Nบิตเนื่องจากการเปลี่ยนตามจำนวนบิตในประเภทไม่ได้ถูกกำหนดโดย C มาตรฐานCHAR_BITแมโครอาจไม่คุ้นเคยสำหรับบางคนมันคือจำนวนบิตในไบต์ดังนั้นจึงsizeof x * CHAR_BITเป็นจำนวนบิตในประเภทของx)

    สิ่งนี้สามารถห่อหุ้มในมาโครได้ตามต้องการเพื่อความสวยงามและชัดเจน:

    #define Maximum(x) (((uintmax_t) 2u << sizeof (x) * CHAR_BIT - 1) - 1)
    if (x < Maximum(x)) ++x;
  3. เพิ่มxและแก้ไขให้ถูกต้องหากมันครอบคลุมถึงศูนย์โดยใช้if:

    if (!++x) --x; // !++x is true if ++x wraps to zero.
  4. เพิ่มxและแก้ไขให้ถูกต้องถ้ามันล้อมรอบศูนย์โดยใช้นิพจน์:

    ++x; x -= !x;

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

  5. ตัวเลือกไร้สาขาซึ่งใช้มาโครด้านบนคือ:

    x += 1 - x/Maximum(x);

    ถ้าเป็นจำนวนสูงสุดของชนิดประเมินนี้ไปx มิฉะนั้นจะเป็นx += 1-1 x += 1-0อย่างไรก็ตามการแบ่งค่อนข้างช้าในสถาปัตยกรรมจำนวนมาก คอมไพเลอร์อาจออปติไมซ์สิ่งนี้กับคำแนะนำโดยไม่มีการแบ่งขึ้นอยู่กับคอมไพเลอร์และสถาปัตยกรรมเป้าหมาย


1
ฉันไม่สามารถพาตัวเองขึ้นมาโหวตคำตอบที่แนะนำให้ใช้มาโครได้ C มีฟังก์ชั่นแบบอินไลน์ คุณไม่ได้ทำอะไรในนิยามแมโครที่ไม่สามารถทำได้อย่างง่ายดายภายในฟังก์ชันอินไลน์ และถ้าคุณจะใช้มาโครตรวจสอบให้แน่ใจว่าคุณใช้วงเล็บอย่างมีกลยุทธ์เพื่อความชัดเจน: โอเปอเรเตอร์ << มีลำดับความสำคัญต่ำมาก -Wshift-op-parenthesesเสียงดังกราวเตือนเกี่ยวกับเรื่องนี้ด้วย ข่าวดีก็คือคอมไพเลอร์ที่ได้รับการเพิ่มประสิทธิภาพจะไม่สร้างส่วนที่นี่ดังนั้นคุณไม่ต้องกังวลว่ามันจะช้า
Cody Gray

1
@CodyGray ถ้าคุณคิดว่าคุณสามารถทำได้ด้วยฟังก์ชั่นเขียนคำตอบ
Carsten S

2
@CodyGray: sizeof xไม่สามารถนำมาใช้ในฟังก์ชั่น C เพราะxจะต้องเป็นพารามิเตอร์ (หรือการแสดงออกอื่น ๆ ) กับประเภทคงที่บางอย่าง ไม่สามารถสร้างขนาดของประเภทอาร์กิวเมนต์ใด ๆ ที่ผู้โทรใช้ แมโครสามารถ
Eric Postpischil

2

ก่อน stdint.h ขนาดตัวแปรสามารถแปรผันจากคอมไพเลอร์ถึงคอมไพเลอร์และชนิดตัวแปรที่แท้จริงใน C ยังคงเป็น int, long เป็นต้นและยังคงถูกกำหนดโดยผู้เขียนคอมไพเลอร์ตามขนาดของมัน ไม่ใช่สมมติฐานมาตรฐานหรือเป้าหมายเฉพาะบางอย่าง ผู้เขียนจึงต้องสร้าง stdint.h เพื่อทำแผนที่โลกทั้งสองนั่นคือจุดประสงค์ของ stdint.h เพื่อทำแผนที่ uint_ ซึ่งเป็น int, long, short

หากคุณกำลังย้ายรหัสจากคอมไพเลอร์อื่นและใช้อักขระ char, short, int, long คุณต้องผ่านแต่ละประเภทและทำพอร์ตด้วยตัวคุณเองไม่มีวิธีแก้ไข และเมื่อคุณจบลงด้วยขนาดที่เหมาะสมสำหรับตัวแปรการประกาศเปลี่ยนแปลง แต่รหัสเป็นงานเขียน ....

if(~counter) counter++;

หรือ ... จัดหามาสก์หรือ typecast โดยตรง

if((~counter)&0xFF) counter++;
if((uint_8)(~counter)) counter++;

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

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

if(sizeof(uint_8)!=1) return(FAIL);

และในขณะที่รูปแบบการเขียนรหัสนั้นทำงานได้ (หาก (~ ตัวนับ) ตัวนับ ++;) สำหรับความต้องการในการพกพาในตอนนี้และในอนาคตควรใช้หน้ากากเพื่อ จำกัด ขนาดโดยเฉพาะ (และไม่ต้องพึ่งพาการประกาศ) ทำเช่นนี้เมื่อ โค้ดจะถูกเขียนในตอนแรกหรือเพียงแค่จบพอร์ตและคุณไม่จำเป็นต้องทำการพอร์ตอีกครั้งในวันอื่น ๆ หรือเพื่อให้โค้ดอ่านง่ายขึ้นจากนั้นทำ if if x <0xFF แล้วหรือ x! = 0xFF หรืออะไรทำนองนั้นคอมไพเลอร์สามารถปรับมันให้เป็นโค๊ดเดียวกันกับที่มันต้องการสำหรับโซลูชันเหล่านี้เพียงแค่ทำให้อ่านง่ายขึ้นและมีความเสี่ยงน้อยลง ...

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


0
6.5.3.3 ตัวดำเนินการทางคณิตศาสตร์ Unary
...
4 ผลลัพธ์ของ~โอเปอเรเตอร์คือส่วนเสริม bitwise ของตัวถูกดำเนินการ (เลื่อนระดับ) (นั่นคือแต่ละบิตของผลลัพธ์จะถูกตั้งค่าถ้าหากบิตที่สอดคล้องกันในตัวถูกดำเนินการแปลงไม่ถูกตั้งค่า ) โปรโมชั่นจำนวนเต็มจะมีขึ้นในตัวถูกดำเนินการและผลที่มีประเภทการส่งเสริมการลงทุน หากประเภทที่ได้รับการส่งเสริมเป็นประเภทที่ไม่ได้ลงนามนิพจน์~Eจะเทียบเท่ากับค่าสูงสุดที่สามารถแทนได้ในประเภทEนั้น

C 2011 Online Draft

ปัญหาคือ~มีการเลื่อนตัวถูกดำเนินการของintก่อนที่จะใช้ตัวดำเนินการ

น่าเสียดายที่ฉันไม่คิดว่าจะมีวิธีที่ง่ายในการทำสิ่งนี้ การเขียน

if ( counter + 1 ) counter++;

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

#define MAX_COUNTER 255
...
if ( counter < MAX_COUNTER-1 ) counter++;

ฉันขอขอบคุณประเด็นเกี่ยวกับการส่งเสริมการขายจำนวนเต็ม - ดูเหมือนว่านี่เป็นปัญหาที่เรากำลังเผชิญอยู่ อย่างไรก็ตามมีค่าที่ชี้ให้เห็นว่าในตัวอย่างโค้ดที่สองของคุณ-1ไม่จำเป็นเนื่องจากจะทำให้ตัวนับชำระที่ 254 (0xFE) ไม่ว่าในกรณีใดวิธีการนี้ตามที่กล่าวถึงในคำถามของฉันไม่เหมาะเนื่องจากขนาดตัวแปรที่แตกต่างกันใน codebase ที่เข้าร่วมในสำนวนนี้
Charlie Salts
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.