สลับคำสั่ง: ค่าเริ่มต้นจะต้องเป็นกรณีสุดท้ายหรือไม่


178

พิจารณาข้อความต่อไปนี้switch:

switch( value )
{
  case 1:
    return 1;
  default:
    value++;
    // fall-through
  case 2:
    return value * 2;
}

รหัสนี้จะรวบรวม แต่มันถูกต้อง (= พฤติกรรมที่กำหนด) สำหรับ C90 / C99? ฉันไม่เคยเห็นรหัสที่กรณีเริ่มต้นไม่ใช่กรณีสุดท้าย

แก้ไข:
ตามที่Jon CageและKillianDSเขียน: นี่เป็นโค้ดที่น่าเกลียดและสับสนและฉันก็รู้ดี ฉันแค่สนใจในไวยากรณ์ทั่วไป (มันถูกกำหนดหรือไม่) และผลลัพธ์ที่คาดหวัง


19
+1 ไม่เคยคิดแม้แต่พฤติกรรมนั้น
Jamie Wong

@ PéterTörök: คุณหมายถึงถ้า value == 2 มันจะคืนค่า 6 หรือไม่?
Alexandre C.

4
@ PéterTörök no คำสั่งไม่สำคัญ - ถ้าค่าตรงกับค่าคงที่ในเลเบลเคสใด ๆ จากนั้นคอนโทรลจะข้ามไปที่คำสั่งถัดจากเลเบลมิฉะนั้นการควบคุมจะข้ามไปยังคำสั่งตามเลเบลเริ่มต้นหากมี
Pete Kirkham

11
@ จอนเคจgotoไม่ได้ชั่วร้าย ผู้ติดตามลัทธิขนส่งสินค้าคือ! คุณไม่สามารถจินตนาการถึงสิ่งที่คนส่วนใหญ่สามารถหลีกเลี่ยงได้gotoเพราะมันเป็นสิ่งที่ชั่วร้ายมากจนทำให้รหัสของพวกเขาอ่านไม่ออก
Patrick Schlüter

3
ฉันใช้gotoเป็นหลักในการจำลองบางสิ่งบางอย่างเช่นfinallyประโยคในฟังก์ชั่นที่ซึ่ง ressources (ไฟล์, หน่วยความจำ) จะต้องได้รับการปล่อยตัวเมื่อหยุดและทำซ้ำสำหรับทุกกรณีข้อผิดพลาดรายการfreeและcloseไม่ได้ช่วยในการอ่าน มีหลายวิธีgotoที่ฉันต้องการหลีกเลี่ยง แต่ทำไม่ได้คือเมื่อฉันต้องการแยกวงออกจากกันและฉันอยู่switchในวงวนนั้น
Patrick Schlüter

คำตอบ:


83

มาตรฐาน C99 ไม่ชัดเจนเกี่ยวกับเรื่องนี้ แต่เมื่อนำข้อเท็จจริงทั้งหมดมารวมกันมันเป็นสิ่งที่ถูกต้องสมบูรณ์

A caseและdefaultฉลากเทียบเท่ากับgotoฉลาก ดู 6.8.1 ข้อความที่มีข้อความกำกับ ที่น่าสนใจเป็นพิเศษคือ 6.8.1.4 ซึ่งเปิดใช้งานอุปกรณ์ของ Duff ดังกล่าวแล้ว:

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

แก้ไข : โค้ดภายในสวิตช์ไม่มีอะไรพิเศษ มันเป็นบล็อคของรหัสปกติเช่นเดียวกับใน - สถานะifด้วยป้ายกระโดดเพิ่มเติม สิ่งนี้อธิบายถึงพฤติกรรมการพลัดตกและสาเหตุที่breakจำเป็น

6.8.4.2.7 ยังให้ตัวอย่าง:

switch (expr) 
{ 
    int i = 4; 
    f(i); 
case 0: 
    i=17; 
    /*falls through into default code */ 
default: 
    printf("%d\n", i); 
} 

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

ค่าคงที่ของเคสต้องไม่ซ้ำกันในคำสั่ง switch:

6.8.4.2.3 การแสดงออกของแต่ละกรณีฉลากจะต้องมีการแสดงออกอย่างต่อเนื่องจำนวนเต็มและไม่มีการแสดงออกอย่างต่อเนื่องกรณีที่สองในงบ Switch เดียวกันจะต้องมีค่าเดียวกันหลังจากการแปลง อาจมีฉลากเริ่มต้นอย่างน้อยหนึ่งรายการในคำสั่ง switch

ทุกกรณีได้รับการประเมินจากนั้นจะข้ามไปยังป้ายกำกับเริ่มต้นหากได้รับ:

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


6
@HeathHunnicutt ชัดเจนว่าคุณไม่เข้าใจวัตถุประสงค์ของตัวอย่าง รหัสนี้ไม่ได้ถูกสร้างขึ้นโดยผู้โพสต์นี้ แต่นำมาจากมาตรฐาน C เนื่องจากภาพประกอบของคำสั่งสวิตช์แปลก ๆ และการปฏิบัติที่ไม่ดีจะนำไปสู่ข้อบกพร่องอย่างไร หากคุณใส่ใจที่จะอ่านข้อความด้านล่างรหัสคุณจะตระหนักได้มาก
Lundin

2
+1 เพื่อชดเชย downvote การดูถูกใครบางคนจากการอ้างอิงมาตรฐาน C ดูเหมือนจะค่อนข้างรุนแรง
Lundin

2
@Lundin ฉันไม่ได้ลงคะแนนมาตรฐาน C และฉันไม่ได้มองข้ามสิ่งที่คุณแนะนำ ฉันลงคะแนนให้ตัวอย่างการสอนที่ไม่ดีของการใช้ตัวอย่างที่ไม่ดีและไม่จำเป็น โดยเฉพาะอย่างยิ่งตัวอย่างนั้นเกี่ยวข้องกับสถานการณ์ที่แตกต่างอย่างสิ้นเชิงมากกว่าที่ถูกถาม ฉันสามารถไปต่อได้ แต่ "ขอบคุณสำหรับความคิดเห็นของคุณ"
Heath Hunnicutt

12
Intel บอกให้คุณวางรหัสที่พบบ่อยที่สุดเป็นอันดับแรกในคำสั่ง switch ที่Branch and Loop Reorganization เพื่อป้องกันการเข้าใจผิด ฉันมาที่นี่เพราะฉันมีdefaultคดีที่ครอบงำกรณีอื่น ๆ ประมาณ 100: 1 และฉันไม่รู้ว่ามันถูกต้องหรือไม่ได้กำหนดให้defaultเป็นกรณีแรก
jww

@jww ฉันไม่แน่ใจว่าคุณหมายถึงอะไรโดย Intel ถ้าคุณหมายถึงความฉลาดฉันจะเรียกมันว่าสมมติฐาน ฉันมีความคิดเหมือนกัน แต่ต่อมาการอ่านระบุว่าไม่เหมือนคำสั่ง, คำสั่งสลับเป็นการเข้าถึงแบบสุ่ม ดังนั้นคดีสุดท้ายจะไม่ช้ากว่าคดีแรก นี่คือความสำเร็จโดย hashing ค่ากรณีคงที่ นั่นคือเหตุผลที่งบเปลี่ยนเร็วกว่าถ้างบเมื่อสาขามีจำนวนมาก

91

คำสั่ง case และคำสั่งเริ่มต้นสามารถเกิดขึ้นได้ในลำดับใด ๆ ในคำสั่ง switch ส่วนคำสั่งเริ่มต้นคือส่วนเพิ่มเติมที่จับคู่ถ้าไม่มีค่าคงที่ในคำสั่ง case สามารถจับคู่ได้

ตัวอย่างที่ดี: -

switch(5) {
  case 1:
    echo "1";
    break;
  case 2:
  default:
    echo "2, default";
    break;
  case 3;
    echo "3";
    break;
}


Outputs '2,default'

มีประโยชน์มากถ้าคุณต้องการให้กรณีของคุณถูกนำเสนอในลำดับตรรกะในรหัส (เช่นในไม่พูดกรณี 1 กรณี 3 กรณี 2 / เริ่มต้น) และกรณีของคุณมีความยาวมากดังนั้นคุณไม่ต้องการที่จะทำซ้ำทั้งกรณี รหัสที่ด้านล่างสำหรับการเริ่มต้น


7
นี่เป็นสถานการณ์ที่ฉันมักจะเริ่มต้นที่อื่นนอกเหนือจากจุดสิ้นสุด ... มีคำสั่งที่เป็นตรรกะสำหรับกรณีที่ชัดเจน (1, 2, 3) และฉันต้องการให้ค่าเริ่มต้นทำงานเหมือนกันกับหนึ่งในกรณีที่ชัดเจน ไม่ใช่คนสุดท้าย
ArtOfWarfare

51

มันถูกต้องและมีประโยชน์มากในบางกรณี

พิจารณารหัสต่อไปนี้:

switch(poll(fds, 1, 1000000)){
   default:
    // here goes the normal case : some events occured
   break;
   case 0:
    // here goes the timeout case
   break;
   case -1:
     // some error occurred, you have to check errno
}

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

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

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

อีกเหตุผลหนึ่งก็คือลำดับของคดีอาจเปลี่ยนพฤติกรรมการคอมไพล์โค้ดและที่สำคัญสำหรับการแสดง คอมไพเลอร์ส่วนใหญ่จะสร้างรหัสแอสเซมบลีที่คอมไพล์ในลำดับเดียวกันกับที่รหัสปรากฏในสวิตช์ ซึ่งทำให้เคสแรกแตกต่างจากเคสอื่น ๆ : เคสทั้งหมดยกเว้นเคสแรกจะเกี่ยวข้องกับการกระโดดและนั่นจะทำให้ไพพ์ไลน์ของโปรเซสเซอร์ว่างเปล่า คุณอาจเข้าใจว่ามันเหมือนกับการคาดคะเนสาขาที่ตั้งค่าเริ่มต้นให้เรียกใช้เคสตัวแรกที่ปรากฏในสวิตช์ หากเป็นกรณีที่พบได้บ่อยกว่าคนอื่น ๆ คุณก็มีเหตุผลที่ดีที่จะนำมาเป็นกรณีแรก

การอ่านความคิดเห็นเป็นเหตุผลเฉพาะที่ผู้โพสต์ดั้งเดิมถามคำถามหลังจากอ่านการจัดโครงสร้างใหม่ของ Branch Compiler ของ Intelเกี่ยวกับการปรับโค้ดให้เหมาะสม

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


6
+1 สำหรับยกตัวอย่าง (ดี) โดยไม่มีพฤติกรรมผิดพลาด
KillianDS

1
... ฉันคิดว่าฉันไม่มั่นใจว่าการมีค่าเริ่มต้นที่ด้านบนนั้นดีเพราะมีคนเพียงไม่กี่คนที่กำลังมองหาที่นั่น มันอาจจะดีกว่าที่จะกำหนดผลตอบแทนให้กับตัวแปรและจัดการกับความสำเร็จในด้านหนึ่งของ if และข้อผิดพลาดในอีกด้านหนึ่งด้วยคำสั่ง case
Jon Cage

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

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

1
@ Kriss: ฉันอยากจะพูดว่าครึ่ง "ฉันไม่ได้เป็นโปรแกรมเมอร์หลามด้วย!" :)
Andrew Grimm

16

ใช่มันถูกต้องและในบางกรณีมันก็มีประโยชน์ โดยทั่วไปถ้าคุณไม่ต้องการมันอย่าทำ


-1: นี่เป็นกลิ่นของความชั่วร้ายสำหรับฉัน มันจะเป็นการดีกว่าที่จะแบ่งรหัสออกเป็นคู่ของคำสั่งสวิตช์
Jon Cage

25
@ John Cage: วางฉัน -1 ที่นี่เป็นที่น่ารังเกียจ ไม่ใช่ความผิดของฉันที่เป็นรหัสที่ถูกต้อง
Jens Gustedt

แค่อยากรู้อยากเห็นฉันอยากจะรู้ว่าภายใต้สถานการณ์ใดที่มีประโยชน์?
Salil

1
-1 มีวัตถุประสงค์เพื่อยืนยันว่ามันมีประโยชน์ ฉันจะเปลี่ยนเป็น +1 หากคุณสามารถให้ตัวอย่างที่ถูกต้องเพื่อสำรองการอ้างสิทธิ์ของคุณ
Jon Cage

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

8

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

send(to, from, count)
register short *to, *from;
register count;
{
  register n=(count+7)/8;
  switch(count%8){
    case 0: do{ *to = *from++;
    case 7:     *to = *from++;
    case 6:     *to = *from++;
    case 5:     *to = *from++;
    case 4:     *to = *from++;
    case 3:     *to = *from++;
    case 2:     *to = *from++;
    case 1:     *to = *from++;
            }while(--n>0);
  }
}

4
และสำหรับใครที่ไม่คุ้นเคยกับอุปกรณ์ของดัฟฟ์รหัสนี้ไม่สามารถอ่านได้อย่างสมบูรณ์ ...
KillianDS

7

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

สวิทช์ (widget_state)
{
  เริ่มต้น: / * ลดลงจากราง - รีเซ็ตและดำเนินการต่อ * /
    widget_state = WIDGET_START;
    /* เจ๊ง */
  กรณี WIDGET_START:
    ...
    หยุดพัก;
  กรณี WIDGET_WHATEVER:
    ...
    หยุดพัก;
}

การจัดการทางเลือกหากรัฐที่ไม่ถูกต้องไม่ควรรีเซ็ตเครื่อง แต่ควรระบุได้อย่างง่ายดายว่าเป็นสถานะที่ไม่ถูกต้อง:

สวิทช์ (widget_state) { กรณี WIDGET_IDLE: widget_ready = 0; widget_hardware_off (); หยุดพัก; กรณี WIDGET_START: ... หยุดพัก; กรณี WIDGET_WHATEVER: ... หยุดพัก; ค่าเริ่มต้น: widget_state = WIDGET_INVALID_STATE; /* เจ๊ง */ กรณี WIDGET_INVALID_STATE: widget_ready = 0; widget_hardware_off (); ... ทำอย่างอื่นที่จำเป็นเพื่อสร้างเงื่อนไข "ปลอดภัย" }

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


6

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

  switch (style)
  {
  default:
    MSPUB_DEBUG_MSG(("Couldn't match dash style, using solid line.\n"));
  case SOLID:
    return Dash(0, RECT_DOT);
  case DASH_SYS:
  {
    Dash ret(shapeLineWidth, dotStyle);
    ret.m_dots.push_back(Dot(1, 3 * shapeLineWidth));
    return ret;
  }
  // more cases follow
  }

5

มีหลายกรณีเมื่อคุณแปลง ENUM เป็นสตริงหรือแปลงสตริงเป็น enum ในกรณีที่คุณกำลังเขียน / อ่านไปยัง / จากไฟล์

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

switch(textureMode)
{
case ModeTiled:
default:
    // write to a file "tiled"
    break;

case ModeStretched:
    // write to a file "stretched"
    break;
}

2

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

จากการทดสอบฉันใส่โค้ดตัวอย่างลงในฟังก์ชั่นที่เรียกใช้test(int value){}และรัน:

  printf("0=%d\n", test(0));
  printf("1=%d\n", test(1));
  printf("2=%d\n", test(2));
  printf("3=%d\n", test(3));
  printf("4=%d\n", test(4));

ผลลัพธ์คือ:

0=2
1=1
2=4
3=8
4=10

1

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

เป็นการดีกว่าที่จะแบ่งกรณีเหล่านี้ออกเป็นงบสวิตช์หรือฟังก์ชันที่เล็กลง

[แก้ไข] @Tristopia: ตัวอย่างของคุณ:

Example from UCS-2 to UTF-8 conversion 

r is the destination array, 
wc is the input wchar_t  

switch(utf8_length) 
{ 
    /* Note: code falls through cases! */ 
    case 3: r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x800; 
    case 2: r[1] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x0c0; 
    case 1: r[0] = wc;
}

จะชัดเจนว่าเป็นความตั้งใจ (ฉันคิดว่า) ถ้ามันเขียนเช่นนี้:

if( utf8_length >= 1 )
{
    r[0] = wc;

    if( utf8_length >= 2 )
    {
        r[1] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x0c0; 

        if( utf8_length == 3 )
        {
            r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x800; 
        }
    }
}   

[edit2] @Tristopia: ตัวอย่างที่สองของคุณน่าจะเป็นตัวอย่างที่ดีที่สุดสำหรับการติดตามผล:

for(i=0; s[i]; i++)
{
    switch(s[i])
    {
    case '"': 
    case '\'': 
    case '\\': 
        d[dlen++] = '\\'; 
        /* fall through */ 
    default: 
        d[dlen++] = s[i]; 
    } 
}

.. แต่เป็นการส่วนตัวฉันจะแบ่งการรับรู้ความคิดเห็นออกเป็นฟังก์ชั่นของตัวเอง:

bool isComment(char charInQuestion)
{   
    bool charIsComment = false;
    switch(charInQuestion)
    {
    case '"': 
    case '\'': 
    case '\\': 
        charIsComment = true; 
    default: 
        charIsComment = false; 
    } 
    return charIsComment;
}

for(i=0; s[i]; i++)
{
    if( isComment(s[i]) )
    {
        d[dlen++] = '\\'; 
    }
    d[dlen++] = s[i]; 
}

2
มีหลายกรณีที่การล้มคือความคิดที่ดีจริงๆ
Patrick Schlüter

ตัวอย่างจากการแปลง UCS-2 ถึง UTF-8 rคืออาเรย์ปลายทางwcคือwchar_t สวิตช์อินพุต(utf8_length) {/ * หมายเหตุ: รหัสผิดพลาดทุกกรณี! * / กรณี 3: r [2] = 0x80 | (wc & 0x3f); ห้องสุขา >> = 6; wc | = 0x800 กรณีที่ 2: r [1] = 0x80 | (wc & 0x3f); ห้องสุขา >> = 6; wc | = 0xc0; กรณีที่ 1: r [0] = wc; }
Patrick Schlüter

ที่นี่อีกหนึ่งชุดคำสั่งคัดลอกสตริงที่มีการหลีกเลี่ยงอักขระ: for(i=0; s[i]; i++) { switch(s[i]) { case '"': case '\'': case '\\': d[dlen++] = '\\'; /* fall through */ default: d[dlen++] = s[i]; } }
Patrick Schlüter

ใช่ แต่รูทีนนี้เป็นหนึ่งในฮอตสปอตของเรานี่เป็นวิธีที่เร็วที่สุดพกพาได้ (เราจะไม่ทำชุดประกอบ) เพื่อนำไปใช้งาน มันมีการทดสอบเพียง 1 ครั้งสำหรับความยาว UTF คุณมี 2 หรือ 3 นอกจากนี้ฉันไม่ได้มาด้วยฉันเอามันจาก BSD
Patrick Schlüter

1
ใช่มีโดยเฉพาะอย่างยิ่งในการแปลงในบัลแกเรียและกรีก (บน Solaris SPARC) และข้อความด้วยมาร์กอัปภายในของเรา (ซึ่งคือ 3 ไบต์ UTF8) ยอมรับในโตโต้มันไม่มากและไม่เกี่ยวข้องกับการปรับปรุงฮาร์ดแวร์ครั้งล่าสุดของเรา แต่ในเวลานั้นมันเขียนมันสร้างความแตกต่าง
Patrick Schlüter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.