ซอร์สโค้ดนี้กำลังเปิดใช้งานสตริงใน C มันทำได้อย่างไร?


106

ฉันกำลังอ่านโค้ดอีมูเลเตอร์บางอย่างและฉันได้ตอบโต้สิ่งที่แปลกอย่างแท้จริง:

switch (reg){
    case 'eax':
    /* and so on*/
}

เป็นไปได้อย่างไร? ฉันคิดว่าคุณทำได้เฉพาะswitchในประเภทอินทิกรัล มีกลอุบายมาโครบางอย่างเกิดขึ้นหรือไม่?


29
มันไม่ใช่สตริง 'eax'และแจกแจงค่าจำนวนเต็มคงที่
P__J__

12
คำพูดเดี่ยวไม่ใช่คู่ ค่าคงที่ของอักขระจะได้รับการเลื่อนขั้นเป็นintดังนั้นจึงถูกต้อง อย่างไรก็ตามค่าของค่าคงที่หลายอักขระถูกกำหนดให้ใช้งานได้ดังนั้นโค้ดอาจไม่ทำงานตามที่คาดไว้ในคอมไพเลอร์อื่น ยกตัวอย่างเช่นeaxอาจจะมี0x65, 0x656178, 0x65617800, 0x786165, 0x6165หรือสิ่งอื่น
Davislor

2
@Davislor: ตั้งชื่อตัวแปรว่า "reg" และความจริงที่ว่า eax เป็นรีจิสเตอร์ x86 ฉันเดาว่าพฤติกรรมที่กำหนดในการนำไปใช้นั้นมีจุดประสงค์เพื่อให้ใช้ได้เพราะมันเหมือนกันทุกที่ที่ใช้ในโค้ด ตราบเท่าที่'eax' != 'ebx'แน่นอนดังนั้นมันจึงล้มเหลวเพียงหนึ่งหรือสองตัวอย่างของคุณ แม้ว่าอาจมีโค้ดบางส่วนที่มีผลบังคับใช้*(int*)("eax") == 'eax'และทำให้ตัวอย่างส่วนใหญ่ของคุณล้มเหลว
Steve Jessop

2
@SteveJessop ฉันไม่เห็นด้วยกับสิ่งที่คุณพูด แต่มีอันตรายอย่างแท้จริงที่ใครบางคนอาจพยายามรวบรวมโค้ดในคอมไพเลอร์อื่นแม้จะใช้สถาปัตยกรรมเดียวกันและมีพฤติกรรมที่แตกต่างกัน ตัวอย่างเช่น'eax'อาจเปรียบเทียบเท่ากับ'ebx'หรือถึง'ax'และคำสั่ง switch จะไม่ทำงานตามที่ตั้งใจไว้
Davislor

1
ความลึกลับทั้งหมดนั้นจะถูกกำจัดไปอย่างรวดเร็วหากคุณค้นหา / แสดงประเภทข้อมูลของ reg
ths

คำตอบ:


146

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

เพื่อให้บรรลุการอ่านโปรแกรมพัฒนาไหวพริบที่มีการใช้ประโยชน์จากพฤติกรรมการดำเนินงานที่กำหนดไว้ 'eax'คือไม่สตริง แต่คงหลายตัว eaxหมายเหตุอย่างระมัดระวังตัวละครคำพูดเดียวรอบ เป็นไปได้มากว่าคุณจะได้รับintในกรณีของคุณซึ่งเป็นเอกลักษณ์เฉพาะของตัวละครที่ผสม (บ่อยครั้งที่อักขระแต่ละตัวใช้พื้นที่ 8 บิตใน 32 บิตint) และทุกคนรู้ว่าคุณสามารถswitchบนint!

สุดท้ายการอ้างอิงมาตรฐาน:

มาตรฐาน C99 กล่าวว่า:

6.4.4.4p10: "ค่าของค่าคงที่ของอักขระจำนวนเต็มที่มีอักขระมากกว่าหนึ่งตัว (เช่น 'ab') หรือมีอักขระหรือลำดับการหลีกเลี่ยงที่ไม่ได้จับคู่กับอักขระการดำเนินการแบบไบต์เดียวถูกกำหนดให้นำไปใช้งาน "


55
ในกรณีที่ใครเห็นสิ่งนั้นและตื่นตระหนก "การกำหนดการนำไปใช้งาน" เป็นสิ่งจำเป็นในการทำงานและต้องจัดทำเอกสารโดยคอมไพเลอร์ของคุณในรูปแบบที่เหมาะสม (มาตรฐานไม่ได้กำหนดให้พฤติกรรมนั้นใช้งานง่ายหรือเอกสารประกอบนั้นดี แต่ ... ). สิ่งนี้ "ปลอดภัย" ที่จะใช้สำหรับผู้เขียนโค้ดที่เข้าใจสิ่งที่กำลังเขียนอย่างเต็มที่ซึ่งต่างจาก "ไม่ได้กำหนด"
Leushenko

7
@ จัสตินในขณะที่ทำได้มันค่อนข้างจะวิปริต หากไม่ทำตามที่คำตอบแนะนำเป็นไปได้มากที่สุดความเป็นไปได้ต่อไปอาจเป็นไปได้ว่ามันใช้อักขระตัวแรกและไม่สนใจส่วนที่เหลือ
Barmar

5
@ZanLynx ฉันไม่คิดบวก แต่ฉันเชื่อว่าฟีเจอร์นี้มาก่อน Unicode และมาตรฐาน MBCS อื่น ๆ "ตัวเลขมหัศจรรย์" ที่ดูเหมือนข้อความในหน่วยความจำและ ID รูปแบบไฟล์รูปแบบ RIFF เป็นแอปพลิเคชันแรกที่ฉันรู้จัก
Russell Borogove

16
@ jpmc26 นี่ไม่ใช่พฤติกรรมที่ไม่ได้กำหนด แต่เป็นการกำหนดการนำไปใช้งาน ดังนั้นเว้นแต่เอกสารประกอบของคอมไพเลอร์จะกล่าวถึงปีศาจจมูกของคุณก็ปลอดภัย
Barmar

7
@ZanLynx: ฉันกลัวว่าเจตนาดั้งเดิมจะเกิดขึ้นก่อน Unicode, UTF-8 และการเข้ารหัสอักขระแบบหลายไบต์เป็นเวลาเกือบ 20 ปี ค่าคงที่หลายอักขระเป็นเพียงวิธีที่สะดวกในการแสดงจำนวนเต็มที่เป็นตัวแทนของกลุ่ม 2, 3 หรือ 4 ไบต์ (ขึ้นอยู่กับขนาดไบต์และ int) ไม่สอดคล้องกันในการใช้งานและสถาปัตยกรรมนำคณะกรรมการจะประกาศนี้เช่นการดำเนินงานที่กำหนดไว้ซึ่งหมายความว่าไม่มีวิธีแบบพกพาในการคำนวณมูลค่าของ'ab'จากและ'a' 'b'
chqrlie

45

ตามมาตรฐาน C (6.8.4.2 คำสั่งสวิตช์)

3 นิพจน์ของป้ายกำกับแต่ละกรณีต้องเป็นนิพจน์คงที่จำนวนเต็ม ...

และ (6.6 นิพจน์คงที่)

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

ตอนนี้คือ'eax'อะไร?

มาตรฐาน C (6.4.4.4 ค่าคงที่ของอักขระ)

2 ค่าคงที่ของอักขระจำนวนเต็มคือลำดับของอักขระหลายไบต์อย่างน้อยหนึ่งตัวที่อยู่ในเครื่องหมายคำพูดเดี่ยวเช่นเดียวกับใน 'x' ...

ดังนั้น'eax'คือค่าคงที่ของอักขระจำนวนเต็มตามวรรค 10 ของส่วนเดียวกัน

  1. ... ค่าของค่าคงที่ของอักขระจำนวนเต็มที่มีอักขระมากกว่าหนึ่งตัว (เช่น 'ab') หรือมีอักขระหรือลำดับการหลีกเลี่ยงที่ไม่ได้จับคู่กับอักขระการดำเนินการแบบไบต์เดียวถูกกำหนดให้ใช้งานได้

ดังนั้นตามคำพูดที่กล่าวถึงครั้งแรกจึงสามารถเป็นตัวถูกดำเนินการของนิพจน์คงที่จำนวนเต็มซึ่งอาจใช้เป็นเลเบลเคส

สังเกตว่าค่าคงที่ของอักขระ (อยู่ในเครื่องหมายคำพูดเดี่ยว) มีชนิดintและไม่เหมือนกับสตริงลิเทอรัล (ลำดับของอักขระที่อยู่ในเครื่องหมายคำพูดคู่) ที่มีชนิดของอาร์เรย์อักขระ


12

อย่างที่คนอื่น ๆ กล่าวไว้นี่คือintค่าคงที่และค่าที่แท้จริงของมันถูกกำหนดให้นำไปใช้งานได้

ฉันคิดว่าส่วนที่เหลือของรหัสดูเหมือนว่า

if (SOMETHING)
    reg='eax';
...
switch (reg){
    case 'eax':
    /* and so on*/
}

คุณสามารถมั่นใจได้ว่า 'eax' ในส่วนแรกมีค่าเท่ากับ 'eax' ในส่วนที่สองดังนั้นทุกอย่างจึงใช้ได้จริงไหม? ... ไม่ถูกต้อง.

ในความคิดเห็น @Davislor แสดงค่าที่เป็นไปได้สำหรับ 'eax':

... 0x65, 0x656178, 0x65617800, 0x786165, 0x6165หรือสิ่งอื่น

สังเกตเห็นค่าที่เป็นไปได้แรก? นั่นเป็นเพียงการ'e'เพิกเฉยต่อตัวละครอีกสองตัว ปัญหาคือโปรแกรมอาจใช้'eax', 'ebx'และอื่น ๆ หากค่าคงที่เหล่านี้มีค่าเท่ากับที่'e'คุณลงท้ายด้วย

switch (reg){
    case 'e':
       ...
    case 'e':
       ...
    ...
}

มันดูไม่ดีเกินไปใช่ไหม

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

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

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


1
นอกเหนือจากรูปแบบบางอย่าง assert('eax' != 'ebx'); //if this fails you can't compile the code because...แล้วยังมีอะไรที่ผู้เขียนต้นฉบับสามารถทำได้เพื่อป้องกันความล้มเหลวของคอมไพเลอร์อื่น ๆ โดยไม่ต้องเปลี่ยนโครงสร้างทั้งหมด>
Dan Is Fiddling By Firelight

6
ป้ายกำกับเคสสองตัวที่มีค่าเดียวกันเป็นการละเมิดข้อ จำกัด (6.8.4.2p3: "... ไม่มีนิพจน์ค่าคงที่ของเคสสองตัวในคำสั่งสวิตช์เดียวกันจะต้องมีค่าเหมือนกันหลังการแปลง") ตราบใดที่โค้ดทั้งหมด ถือว่าค่าของค่าคงที่เหล่านี้เป็นแบบทึบซึ่งรับประกันได้ว่าจะทำงานหรือไม่สามารถรวบรวมได้
zwol

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

1

ส่วนรหัสใช้เหตุการณ์ที่แปลกประหลาดในประวัติศาสตร์ที่เรียกว่าตัวอักษรหลายตัวละครอย่างต่อเนื่องนอกจากนี้ยังเรียกว่าหลายตัวอักษร

'eax' เป็นค่าคงที่จำนวนเต็มซึ่งมีการกำหนดค่าการนำไปใช้งาน

นี่คือหน้าที่น่าสนใจเกี่ยวกับหลายตัวอักษรและวิธีการใช้งาน แต่ไม่ควร:

http://www.zipcon.net/~swhite/docs/computers/languages/c_multi-char_const.html


เมื่อมองย้อนกลับไปในกระจกมองหลังนี่คือวิธีที่คู่มือ C ฉบับดั้งเดิมของ Dennis Ritchie จากสมัยก่อนที่ดี ( https://www.bell-labs.com/usr/dmr/www/cman.pdf ) ค่าคงที่ของอักขระที่ระบุ .

2.3.2 ค่าคงที่ของอักขระ

ค่าคงที่ของอักขระคือ 1 หรือ 2 อักขระที่อยู่ในเครื่องหมายคำพูดเดี่ยว '' ''' ภายในค่าคงที่ของอักขระต้องมีเครื่องหมายคำพูดเดียวนำหน้าด้วยเครื่องหมายทับ '' \'' อักขระที่ไม่ใช่กราฟิกบางตัวและ '' \'' เองอาจถูกหลีกเลี่ยงตามตารางต่อไปนี้:

    BS \b
    NL \n
    CR \r
    HT \t
    ddd \ddd
    \ \\

Escape '' \ddd'' ประกอบด้วยแบ็กสแลชตามด้วย 1, 2 หรือ 3 หลักฐานแปดซึ่งใช้เพื่อระบุค่าของอักขระที่ต้องการ กรณีพิเศษของโครงสร้างนี้คือ '' \0'' (ไม่ตามด้วยตัวเลข) ซึ่งระบุอักขระว่าง

ค่าคงที่ของอักขระจะทำงานเหมือนกับจำนวนเต็ม (ไม่ใช่โดยเฉพาะอย่างยิ่งเช่นอ็อบเจ็กต์ประเภทอักขระ) ตามโครงสร้างการกำหนดแอดเดรสของ PDP-11 ค่าคงที่ของอักขระที่มีความยาว 1 มีรหัสสำหรับอักขระที่กำหนดในไบต์ลำดับต่ำและ 0 ในไบต์ลำดับสูง ค่าคงที่ของอักขระที่มีความยาว 2 มีรหัสสำหรับอักขระตัวแรกในไบต์ต่ำและสำหรับอักขระตัวที่สองในไบต์ลำดับสูง ค่าคงที่ของอักขระที่มีมากกว่าหนึ่งอักขระขึ้นอยู่กับเครื่องโดยเนื้อแท้และควรหลีกเลี่ยง

วลีสุดท้ายคือสิ่งที่คุณต้องจำเกี่ยวกับโครงสร้างที่น่าสงสัยนี้: ค่าคงที่ของอักขระที่มีอักขระมากกว่าหนึ่งตัวขึ้นอยู่กับเครื่องจักรโดยเนื้อแท้และควรหลีกเลี่ยง

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