จะใช้ตัวแปร goto รั่วหรือไม่?


94

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

เช่น

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

จะไม่xรั่วไหล?


ที่เกี่ยวข้อง: stackoverflow.com/questions/1258201/… (แต่ฉันอยากทำตั้งแต่เริ่มต้นอย่างหมดจด!)
Lightness Races in Orbit

15
อะไร"Won't x be leaked"หมายถึง? ประเภทของxเป็นชนิดข้อมูลในตัว ทำไมคุณไม่เลือกตัวอย่างที่ดีกว่านี้
Nawaz

2
@Nawaz: ตัวอย่างสมบูรณ์แบบในแบบที่เป็นอยู่ เกือบทุกครั้งที่ฉันพูดคุยกับใครบางคนgotoพวกเขาคิดว่าแม้แต่ตัวแปรระยะเวลาการจัดเก็บอัตโนมัติก็ยัง "รั่วไหล" ที่คุณและฉันรู้ว่าเป็นอย่างอื่นนอกเหนือจากประเด็น
Lightness Races ใน Orbit

1
@David: ฉันยอมรับว่าคำถามนี้มีความหมายมากกว่าเมื่อตัวแปรมีตัวทำลายที่ไม่สำคัญ ... และฉันดูคำตอบของ Tomalak และพบตัวอย่างดังกล่าว นอกจากนี้ในขณะที่intไม่สามารถรั่วไหลได้ แต่ก็สามารถรั่วได้ ตัวอย่างเช่น: การvoid f(void) { new int(5); }รั่วไหลของintไฟล์.
Ben Voigt

ทำไมไม่เปลี่ยนคำถามเป็นบางอย่างเช่น "ในตัวอย่างที่ระบุเส้นทางการดำเนินการโค้ดจะโอนจาก f () ไปยัง main () โดยไม่ล้างสแต็กและฟังก์ชันการส่งคืนจากฟังก์ชันอื่น ๆ หรือไม่มันจะสำคัญหรือไม่หากผู้ทำลายจำเป็นต้องเป็น เรียกว่า C เหมือนกันไหม " ทั้งสองจะรักษาเจตนาของคำถามในขณะที่หลีกเลี่ยงความเข้าใจผิดที่อาจเกิดขึ้นได้หรือไม่?
Jack V.

คำตอบ:


210

คำเตือน:คำตอบนี้เกี่ยวข้องกับ C ++ เท่านั้น ; กฎค่อนข้างแตกต่างกันใน C.


จะไม่xรั่วไหล?

ไม่ไม่อย่างแน่นอน

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

พิจารณากลไกต่อไปนี้ที่ป้องกันไม่ให้คุณทำ "สิ่งที่ไม่ดี" กับป้ายกำกับ (ซึ่งรวมถึงcaseป้ายกำกับ)


1. ขอบเขตฉลาก

คุณไม่สามารถข้ามฟังก์ชันต่างๆได้:

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

// error: label 'lol' used but not defined

[n3290: 6.1/1]:[.. ] ขอบเขตของป้ายชื่อคือฟังก์ชันที่ปรากฏ [.. ]


2. การเริ่มต้นวัตถุ

คุณไม่สามารถข้ามการเริ่มต้นวัตถุ:

int main() {
   goto lol;
   int x = 0;
lol:
   return 0;
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘int x’

หากคุณข้ามกลับไปที่การเริ่มต้นอ็อบเจ็กต์ "อินสแตนซ์" ก่อนหน้าของออบเจ็กต์จะถูกทำลาย :

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   int x = 0;

  lol:
   T t;
   if (x++ < 5)
     goto lol;
}

// Output: *T~T*T~T*T~T*T~T*T~T*T~T

[n3290: 6.6/2]:[.. ] การถ่ายโอนออกจากลูปออกจากบล็อกหรือย้อนกลับผ่านตัวแปรเริ่มต้นที่มีระยะเวลาการจัดเก็บอัตโนมัติเกี่ยวข้องกับการทำลายวัตถุที่มีระยะเวลาการจัดเก็บอัตโนมัติที่อยู่ในขอบเขตที่จุดที่ถ่ายโอนจาก แต่ไม่ได้ถ่ายโอนไปที่จุด . [.. ]

คุณไม่สามารถข้ามไปยังขอบเขตของวัตถุแม้ว่าจะไม่ได้เริ่มต้นอย่างชัดเจน:

int main() {
   goto lol;
   {
      std::string x;
lol:
      x = "";
   }
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘std::string x’

... ยกเว้นวัตถุบางประเภทซึ่งภาษาสามารถจัดการได้โดยไม่คำนึงถึงเพราะไม่ต้องการโครงสร้างที่ "ซับซ้อน":

int main() {
   goto lol;
   {
      int x;
lol:
      x = 0;
   }
}

// OK

[n3290: 6.7/3]:เป็นไปได้ที่จะถ่ายโอนไปยังบล็อก แต่ไม่ใช่ในลักษณะที่ข้ามการประกาศด้วยการเริ่มต้น โปรแกรมที่กระโดดจากจุดที่ตัวแปรที่มีระยะเวลาการจัดเก็บอัตโนมัติไม่อยู่ในขอบเขตไปยังจุดที่อยู่ในขอบเขตนั้นมีรูปแบบที่ไม่ถูกต้องเว้นแต่ตัวแปรจะมีประเภทสเกลาร์ประเภทคลาสที่มีตัวสร้างเริ่มต้นเล็กน้อยและตัวทำลายเล็กน้อย a เวอร์ชันที่ผ่านการรับรอง cv ของประเภทใดประเภทหนึ่งเหล่านี้หรืออาร์เรย์ของประเภทก่อนหน้าและประกาศโดยไม่มีตัวเริ่มต้น [.. ]


3. การกระโดดเป็นไปตามขอบเขตของวัตถุอื่น ๆ

ในทำนองเดียวกันวัตถุที่มีระยะเวลาการจัดเก็บอัตโนมัติจะไม่ "รั่วไหล" เมื่อคุณอยู่gotoนอกขอบเขต :

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   {
      T t;
      goto lol;
   }

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]:เมื่อออกจากขอบเขต (แต่ทำได้สำเร็จ) อ็อบเจ็กต์ที่มีระยะเวลาการจัดเก็บอัตโนมัติ (3.7.3) ที่สร้างขึ้นในขอบเขตนั้นจะถูกทำลายตามลำดับย้อนกลับของการสร้าง [.. ]


สรุป

กลไกข้างต้นช่วยให้มั่นใจได้ว่าคุณgotoจะไม่ทำลายภาษา

ของหลักสูตรนี้ไม่ได้หมายความว่าคุณ "ควรจะ" ใช้gotoสำหรับปัญหาใดก็ตาม แต่ก็ไม่หมายความว่ามันไม่ได้เกือบเป็น "ความชั่ว" เป็นตำนานคนทั่วไปนำไปสู่ความเชื่อ


8
คุณอาจสังเกตว่า C ไม่ได้ป้องกันไม่ให้สิ่งอันตรายเหล่านี้เกิดขึ้นทั้งหมด
Daniel

13
@ แดเนียล: คำถามและคำตอบเกี่ยวกับ C ++ โดยเฉพาะ แต่เป็นประเด็นที่ยุติธรรม บางทีเราอาจมีคำถามที่พบบ่อยอื่นเพื่อปัดเป่าตำนานที่ C และ C ++ เหมือนกัน;)
Lightness Races in Orbit

3
@Tomalak: ฉันไม่คิดว่าเราไม่เห็นด้วยที่นี่ คำตอบจำนวนมากที่ให้ไว้ใน SO นั้นได้รับการบันทึกไว้อย่างชัดเจนที่ใดที่หนึ่ง ฉันแค่สร้างประเด็นว่าโปรแกรมเมอร์ C อาจจะอยากเห็นคำตอบนี้และคิดว่าถ้ามันทำงานใน C ++ มันควรจะทำงานในลักษณะเดียวกันใน C.
Daniel

2
คุณอาจต้องการเพิ่มว่าสิ่งเหล่านี้กระโดดข้ามสิ่งที่ทำให้เป็นอินทิเรียลเหมือนกันสำหรับป้ายกำกับเคส
PlasmaHH

12
ว้าวฉันเพิ่งคิดว่าความหมายของ C ++ ถูกทำลายเพราะ goto แต่มันมีเหตุผลอย่างน่าประหลาดใจ! คำตอบที่ดี
Joseph Garvin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.