ทำไมvolatile
ต้องใช้ C ใช้ทำอะไร? มันจะทำอะไร?
ทำไมvolatile
ต้องใช้ C ใช้ทำอะไร? มันจะทำอะไร?
คำตอบ:
Volatile บอกคอมไพเลอร์ว่าอย่าปรับแต่งสิ่งใดที่เกี่ยวข้องกับตัวแปร volatile
มีเหตุผลทั่วไปอย่างน้อยสามประการในการใช้งานทั้งหมดเกี่ยวข้องกับสถานการณ์ที่ค่าของตัวแปรสามารถเปลี่ยนแปลงได้โดยไม่ต้องดำเนินการใด ๆ จากโค้ดที่มองเห็นได้: เมื่อคุณเชื่อมต่อกับฮาร์ดแวร์ที่เปลี่ยนค่าตัวเอง เมื่อมีเธรดอื่นกำลังทำงานซึ่งใช้ตัวแปร หรือเมื่อมีตัวจัดการสัญญาณที่อาจเปลี่ยนค่าของตัวแปร
สมมติว่าคุณมีฮาร์ดแวร์ชิ้นเล็ก ๆ ที่ถูกแมปเข้ากับ RAM ที่ไหนสักแห่งและมีสองที่อยู่: พอร์ตคำสั่งและพอร์ตข้อมูล:
typedef struct
{
int command;
int data;
int isbusy;
} MyHardwareGadget;
ตอนนี้คุณต้องการส่งคำสั่ง:
void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isbusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
ดูง่าย แต่อาจล้มเหลวได้เนื่องจากคอมไพเลอร์มีอิสระในการเปลี่ยนลำดับการเขียนข้อมูลและคำสั่ง สิ่งนี้จะทำให้แกดเจ็ตเล็ก ๆ ของเราออกคำสั่งด้วยค่าข้อมูลก่อนหน้า ยังดูที่การรอในขณะที่ไม่ว่าง อันนั้นจะถูกปรับให้เหมาะสม คอมไพเลอร์จะพยายามฉลาดอ่านค่าของ isbusy เพียงครั้งเดียวแล้วเข้าสู่วงวนไม่สิ้นสุด นั่นไม่ใช่สิ่งที่คุณต้องการ
วิธีที่จะหลีกเลี่ยงปัญหานี้ก็คือการประกาศให้อุปกรณ์ตัวชี้มีความผันผวน วิธีนี้คอมไพเลอร์ถูกบังคับให้ทำสิ่งที่คุณเขียน ไม่สามารถลบการมอบหมายหน่วยความจำไม่สามารถแคชตัวแปรในการลงทะเบียนและไม่สามารถเปลี่ยนลำดับของการมอบหมาย:
นี่เป็นรุ่นที่ถูกต้อง:
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isbusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
volatile
ใน C จริง ๆ แล้วมีจุดประสงค์เพื่อไม่ให้แคชค่าของตัวแปรโดยอัตโนมัติ มันจะบอกคอมไพเลอร์ไม่ให้แคชค่าของตัวแปรนี้ ดังนั้นมันจะสร้างรหัสเพื่อรับค่าของvolatile
ตัวแปรที่กำหนดจากหน่วยความจำหลักทุกครั้งที่พบมัน กลไกนี้ใช้เพราะเมื่อใดก็ตามที่ค่าสามารถแก้ไขได้โดยระบบปฏิบัติการหรือขัดจังหวะใด ๆ ดังนั้นการใช้volatile
จะช่วยให้เราเข้าถึงคุณค่าใหม่ทุกครั้ง
volatile
การทำให้มันเป็นไปได้สำหรับคอมไพเลอร์เพื่อเพิ่มประสิทธิภาพรหัสในขณะที่ยังช่วยให้โปรแกรมเมอร์เพื่อให้บรรลุความหมายที่จะประสบความสำเร็จโดยไม่ต้องเพิ่มประสิทธิภาพดังกล่าว ผู้เขียนมาตรฐานคาดว่าการใช้งานที่มีคุณภาพจะรองรับความหมายอะไรก็ตามที่มีประโยชน์เนื่องจากแพลตฟอร์มเป้าหมายและฟิลด์แอปพลิเคชันและไม่คาดหวังว่าผู้เขียนคอมไพเลอร์จะพยายามเสนอความหมายคุณภาพต่ำสุดที่เป็นไปตามมาตรฐานและไม่ 100% โง่ (โปรดทราบว่าผู้เขียนมาตรฐานรู้จักชัดเจนในเหตุผล ...
การใช้งานอีกอย่างvolatile
คือตัวจัดการสัญญาณ หากคุณมีรหัสเช่นนี้:
int quit = 0;
while (!quit)
{
/* very small loop which is completely visible to the compiler */
}
คอมไพเลอร์ได้รับอนุญาตให้สังเกตเห็นร่างกายของลูปไม่ได้สัมผัสquit
ตัวแปรและแปลงลูปเป็นwhile (true)
ลูป แม้ว่าquit
ตัวแปรจะถูกตั้งค่าบนตัวจัดการสัญญาณสำหรับSIGINT
และSIGTERM
; คอมไพเลอร์ไม่มีทางรู้ว่า
อย่างไรก็ตามหากquit
มีการประกาศตัวแปรvolatile
คอมไพเลอร์จะถูกบังคับให้โหลดทุกครั้งเพราะสามารถแก้ไขได้ที่อื่น นี่คือสิ่งที่คุณต้องการในสถานการณ์นี้
quit
คอมไพเลอร์สามารถปรับให้เป็นวงคงที่โดยสมมติว่า ที่ไม่มีทางquit
เปลี่ยนระหว่างการวนซ้ำ หมายเหตุ: นี่ไม่จำเป็นต้องเป็นการทดแทนที่ดีสำหรับการเขียนโปรแกรม threadsafe จริง
volatile
หรือเครื่องหมายอื่น ๆ มันจะคิดว่าไม่มีอะไรนอกลูปแก้ไขตัวแปรนั้นเมื่อมันเข้าสู่วงแม้ว่ามันจะเป็นตัวแปรทั่วโลก
extern int global; void fn(void) { while (global != 0) { } }
ด้วยgcc -O3 -S
และดูที่ไฟล์การชุมนุมที่เกิดขึ้นในเครื่องของฉันมันไม่movl global(%rip), %eax
; testl %eax, %eax
; je .L1
; .L4: jmp .L4
นั่นคือการวนซ้ำไม่สิ้นสุดหากโกลบอลไม่ใช่ศูนย์ จากนั้นลองเพิ่มvolatile
และดูความแตกต่าง
volatile
บอกคอมไพเลอร์ว่าตัวแปรของคุณอาจถูกเปลี่ยนโดยวิธีอื่นนอกเหนือจากรหัสที่เข้าถึงได้ เช่นอาจเป็นตำแหน่งหน่วยความจำ I / O ที่แมป หากไม่ได้ระบุไว้ในกรณีเช่นนี้การเข้าถึงตัวแปรบางอย่างสามารถปรับให้เหมาะสมเช่นเนื้อหาสามารถเก็บไว้ในรีจิสเตอร์และตำแหน่งหน่วยความจำไม่ได้อ่านอีกครั้ง
ดูบทความนี้โดย Andrei Alexandrescu, " volatile - เพื่อนที่ดีที่สุดของโปรแกรมเมอร์แบบมัลติเธรด "
ระเหยคำหลักได้วางแผนเพื่อป้องกันการเพิ่มประสิทธิภาพคอมไพเลอร์ที่อาจทำให้รหัสไม่ถูกต้องในการปรากฏตัวของเหตุการณ์บางอย่างไม่ตรงกัน ตัวอย่างเช่นถ้าคุณกำหนดตัวแปรดั้งเดิมเป็น สารระเหย คอมไพเลอร์ไม่ได้รับอนุญาตให้แคชในการลงทะเบียนซึ่งเป็นการเพิ่มประสิทธิภาพทั่วไปที่จะเกิดความเสียหายหากตัวแปรนั้นถูกใช้ร่วมกันในหลายเธรด ดังนั้นกฎทั่วไปคือถ้าคุณมีตัวแปรประเภทดั้งเดิมที่ต้องใช้ร่วมกันในหลายกระทู้ประกาศตัวแปรเหล่านั้นผันผวนแต่จริง ๆ แล้วคุณสามารถทำได้มากขึ้นด้วยคำหลักนี้: คุณสามารถใช้มันเพื่อจับรหัสที่ไม่ได้เป็นเธรดที่ปลอดภัยและคุณสามารถทำได้ในเวลารวบรวม บทความนี้แสดงวิธีการทำ วิธีการแก้ปัญหาเกี่ยวข้องกับตัวชี้สมาร์ทที่เรียบง่ายที่ยังทำให้มันง่ายต่อการอนุกรมซีเรียลส่วนที่สำคัญของรหัส
บทความนำไปใช้กับทั้งสองและC
C++
โปรดดูบทความ " C ++ และภัยคุกคามจากการล็อคซ้ำซ้อน " โดย Scott Meyers และ Andrei Alexandrescu:
ดังนั้นเมื่อจัดการกับตำแหน่งหน่วยความจำบางส่วน (เช่นพอร์ตที่แมปหน่วยความจำหรือหน่วยความจำที่อ้างอิงโดย ISRs [Interrupt Service Routines]) การปรับแต่งบางอย่างจะต้องถูกระงับ มีความผันผวนสำหรับการระบุการดูแลเป็นพิเศษสำหรับสถานที่ดังกล่าวโดยเฉพาะ: (1) เนื้อหาของตัวแปรระเหยคือ "ไม่เสถียร" (สามารถเปลี่ยนแปลงได้โดยวิธีการที่ไม่รู้จักคอมไพเลอร์), (2) ทั้งหมดเขียนไปยังข้อมูลระเหย จะต้องดำเนินการทางศาสนาและ (3) การดำเนินการทั้งหมดเกี่ยวกับข้อมูลที่ระเหยได้ดำเนินการตามลำดับที่ปรากฏในซอร์สโค้ด กฎสองข้อแรกเพื่อให้แน่ใจว่าการอ่านและการเขียนที่เหมาะสม อันสุดท้ายให้ใช้งานโพรโทคอล I / O ที่ผสมอินพุตและเอาต์พุต นี่คือสิ่งที่ไม่แน่นอนว่า C และ C ++ รับประกันความผันผวนได้อย่างไร
volatile
ไม่รับประกันอะตอมมิก
คำอธิบายง่ายๆของฉันคือ:
ในบางสถานการณ์ตามตรรกะหรือรหัสคอมไพเลอร์จะทำการปรับให้เหมาะสมของตัวแปรที่คิดว่าจะไม่เปลี่ยนแปลง volatile
ป้องกันคำหลักตัวแปรถูกปรับ
ตัวอย่างเช่น:
bool usb_interface_flag = 0;
while(usb_interface_flag == 0)
{
// execute logic for the scenario where the USB isn't connected
}
จากโค้ดด้านบนคอมไพเลอร์อาจคิดว่าusb_interface_flag
เป็น 0 และในขณะที่ลูปจะเป็นศูนย์ตลอดไป หลังจากการปรับให้เหมาะสมคอมไพเลอร์จะถือว่าเป็นwhile(true)
ตลอดเวลาทำให้เกิดการวนซ้ำไม่สิ้นสุด
เพื่อหลีกเลี่ยงสถานการณ์ประเภทนี้เราขอประกาศว่าค่าสถานะเป็นความผันผวนเรากำลังบอกให้คอมไพเลอร์ทราบว่าค่านี้อาจเปลี่ยนแปลงได้โดยอินเทอร์เฟซภายนอกหรือโมดูลของโปรแกรมอื่น ๆ เช่นโปรดอย่าปรับให้เหมาะสม นั่นคือกรณีการใช้งานสำหรับการระเหย
การใช้งานเล็กน้อยสำหรับการระเหยมีดังต่อไปนี้ สมมติว่าคุณต้องการคำนวณอนุพันธ์เชิงตัวเลขของฟังก์ชันf
:
double der_f(double x)
{
static const double h = 1e-3;
return (f(x + h) - f(x)) / h;
}
ปัญหาคือว่าx+h-x
โดยทั่วไปไม่เท่ากับh
เนื่องจากข้อผิดพลาดในการ roundoff คิดเกี่ยวกับมัน: เมื่อคุณลบล้างตัวเลขที่ใกล้ชิดมากคุณจะสูญเสียตัวเลขจำนวนมากที่สามารถทำลายการคำนวณอนุพันธ์ (คิด 1.00001 - 1) วิธีแก้ปัญหาที่เป็นไปได้อาจเป็น
double der_f2(double x)
{
static const double h = 1e-3;
double hh = x + h - x;
return (f(x + hh) - f(x)) / hh;
}
แต่ขึ้นอยู่กับแพลตฟอร์มและสวิตช์คอมไพเลอร์ของคุณบรรทัดที่สองของฟังก์ชันนั้นอาจถูกลบออกโดยคอมไพเลอร์ที่ปรับให้เหมาะสมที่สุด ดังนั้นคุณเขียนแทน
volatile double hh = x + h;
hh -= x;
เพื่อบังคับให้คอมไพเลอร์อ่านตำแหน่งหน่วยความจำที่มี hh โดยไม่เสียโอกาสในการปรับให้เหมาะสมที่สุด
h
หรือhh
ในสูตรอนุพันธ์? เมื่อทำการhh
คำนวณแล้วสูตรสุดท้ายจะใช้เช่นเดียวกับสูตรแรกโดยไม่มีความแตกต่าง บางทีมันควรจะเป็น(f(x+h) - f(x))/hh
?
h
และhh
เป็นที่ถูกตัดทอนบางส่วนอำนาจเชิงลบของทั้งสองโดยการดำเนินการhh
x + h - x
ในกรณีนี้x + hh
และแตกต่างกันตรงโดยx
hh
นอกจากนี้คุณยังสามารถใช้สูตรของคุณมันจะให้ผลลัพธ์ที่เหมือนกันเนื่องจากx + h
และx + hh
เท่ากัน (เป็นตัวส่วนที่สำคัญที่นี่)
x1=x+h; d = (f(x1)-f(x))/(x1-x)
ใช่ไหม โดยไม่ต้องใช้สารระเหย
-ffast-math
หรือเทียบเท่า
มีอยู่สองวิธี สิ่งเหล่านี้ถูกใช้เป็นพิเศษในการพัฒนาแบบฝังตัว
คอมไพเลอร์จะไม่ปรับฟังก์ชั่นที่ใช้ตัวแปรที่กำหนดด้วยคำสำคัญระเหย
Volatile ใช้ในการเข้าถึงตำแหน่งหน่วยความจำที่แน่นอนใน RAM, ROM, ฯลฯ ... นี่คือการใช้บ่อยขึ้นเพื่อควบคุมอุปกรณ์ที่แมปหน่วยความจำเข้าถึง CPU ลงทะเบียนและค้นหาตำแหน่งหน่วยความจำที่เฉพาะเจาะจง
ดูตัวอย่างที่มีรายชื่อประกอบ เรื่องการใช้คำสำคัญ "ระเหย" C ในการพัฒนาแบบฝัง
สารระเหยยังมีประโยชน์เมื่อคุณต้องการบังคับให้คอมไพเลอร์ไม่ปรับลำดับรหัสเฉพาะ (เช่นสำหรับการเขียนเกณฑ์มาตรฐานขนาดเล็ก)
ฉันจะพูดถึงสถานการณ์อื่นที่ความสำคัญของสารระเหย
สมมติว่าคุณแมปหน่วยความจำ - ไฟล์สำหรับ I / O ที่เร็วขึ้นและไฟล์นั้นสามารถเปลี่ยนแปลงได้เบื้องหลัง (เช่นไฟล์ไม่ได้อยู่ในฮาร์ดไดรฟ์ในเครื่องของคุณ แต่ให้บริการผ่านเครือข่ายโดยคอมพิวเตอร์เครื่องอื่น)
หากคุณเข้าถึงข้อมูลของไฟล์หน่วยความจำที่แมปผ่านตัวชี้ไปยังวัตถุที่ไม่ลบเลือน (ที่ระดับซอร์สโค้ด) รหัสที่สร้างโดยคอมไพเลอร์สามารถดึงข้อมูลเดียวกันหลาย ๆ ครั้งโดยที่คุณไม่รู้ตัว
หากข้อมูลนั้นมีการเปลี่ยนแปลงโปรแกรมของคุณอาจใช้ข้อมูลสองเวอร์ชันขึ้นไปและเข้าสู่สถานะที่ไม่สอดคล้องกัน สิ่งนี้สามารถนำไปสู่พฤติกรรมที่ไม่ถูกต้องตามหลักเหตุผลของโปรแกรม แต่ยังรวมถึงช่องโหว่ด้านความปลอดภัยที่สามารถนำไปใช้ประโยชน์ได้หากโปรแกรมนั้นประมวลผลไฟล์หรือไฟล์ที่ไม่น่าเชื่อถือจากตำแหน่งที่ไม่น่าเชื่อถือ
หากคุณสนใจเรื่องความปลอดภัยและควรทำนี่เป็นสถานการณ์สำคัญที่ต้องพิจารณา
volatile หมายถึงที่เก็บข้อมูลมีแนวโน้มที่จะเปลี่ยนแปลงได้ตลอดเวลาและเปลี่ยนแปลงได้ แต่มีบางสิ่งที่อยู่นอกเหนือการควบคุมของโปรแกรมผู้ใช้ ซึ่งหมายความว่าหากคุณอ้างอิงตัวแปรโปรแกรมควรตรวจสอบที่อยู่ทางกายภาพเสมอ (เช่นการป้อนข้อมูลที่แมปแมป) และไม่ใช้ในทางที่แคช
Wiki พูดทุกอย่างเกี่ยวกับvolatile
:
และ doc ของเคอร์เนลของ Linux ยังให้ข้อมูลที่ดีเยี่ยมเกี่ยวกับvolatile
:
volatile
ในความคิดของคุณไม่ควรคาดหวังมากเกินไปจาก เพื่อแสดงให้เห็นดูตัวอย่างในNils Pipenbrinck เป็นอย่างมากโหวตคำตอบ
volatile
ผมจะบอกว่าตัวอย่างของเขาไม่เหมาะสำหรับ volatile
ใช้เพื่อ:
ป้องกันคอมไพเลอร์ไม่ให้ทำการปรับให้เป็นประโยชน์และเป็นที่ต้องการเท่านั้น ไม่มีอะไรเกี่ยวกับเธรดที่ปลอดภัยการเข้าถึงของอะตอมหรือแม้แต่ลำดับของหน่วยความจำ
ในตัวอย่างนั้น:
void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
// wait while the gadget is busy:
while (gadget->isbusy)
{
// do nothing here.
}
// set data first:
gadget->data = data;
// writing the command starts the action:
gadget->command = command;
}
gadget->data = data
ก่อนgadget->command = command
เพียง แต่มีการประกันเฉพาะในรหัสเรียบเรียงโดยรวบรวม ณ รันไทม์ตัวประมวลผลยังอาจเรียงลำดับข้อมูลและการกำหนดคำสั่งอีกครั้งเกี่ยวกับสถาปัตยกรรมตัวประมวลผล ฮาร์ดแวร์อาจได้รับข้อมูลที่ไม่ถูกต้อง (สมมติว่ามีการจับคู่แกดเจ็ตกับฮาร์ดแวร์ I / O) จำเป็นต้องมีกำแพงกั้นหน่วยความจำระหว่างข้อมูลและการกำหนดคำสั่ง
volatile
จะลดประสิทธิภาพลงโดยไม่มีเหตุผล ไม่ว่าจะเพียงพอหรือไม่นั้นจะขึ้นอยู่กับแง่มุมอื่น ๆ ของระบบที่โปรแกรมเมอร์อาจรู้มากกว่าคอมไพเลอร์ ในทางตรงกันข้ามหากหน่วยประมวลผลรับประกันว่าคำสั่งในการเขียนไปยังที่อยู่บางแห่งจะล้างแคช CPU แต่คอมไพเลอร์ไม่สามารถล้างตัวแปร register-cached ที่ CPU ไม่รู้เรื่องการล้างแคชจะไร้ประโยชน์
ในภาษาที่ออกแบบโดยเดนนิสริตชี่ทุก ๆ การเข้าถึงวัตถุใด ๆ นอกเหนือจากวัตถุอัตโนมัติที่ไม่ได้รับที่อยู่จะทำตัวราวกับว่ามันคำนวณที่อยู่ของวัตถุแล้วอ่านหรือเขียนที่เก็บข้อมูลที่อยู่นั้น สิ่งนี้ทำให้ภาษามีประสิทธิภาพมาก แต่มีโอกาสในการเพิ่มประสิทธิภาพที่ จำกัด อย่างรุนแรง
ในขณะที่มันอาจเป็นไปได้ที่จะเพิ่ม qualifier ที่จะเชิญคอมไพเลอร์ให้สมมติว่าวัตถุเฉพาะจะไม่ถูกเปลี่ยนในรูปแบบแปลก ๆ ข้อสันนิษฐานดังกล่าวเหมาะสมสำหรับวัตถุส่วนใหญ่ในโปรแกรม C และมันจะมี ถูกทำไม่ได้เพื่อเพิ่มคุณสมบัติให้กับวัตถุทั้งหมดที่สมมติฐานดังกล่าวจะเหมาะสม ในทางกลับกันบางโปรแกรมจำเป็นต้องใช้วัตถุบางอย่างซึ่งการสันนิษฐานดังกล่าวจะไม่ถือ เมื่อต้องการแก้ไขปัญหานี้มาตรฐานบอกว่าคอมไพเลอร์อาจสันนิษฐานว่าวัตถุที่ไม่ได้ประกาศvolatile
จะไม่มีค่าสังเกตหรือเปลี่ยนแปลงในลักษณะที่อยู่นอกการควบคุมของคอมไพเลอร์หรือจะอยู่นอกความเข้าใจที่สมเหตุสมผลของคอมไพเลอร์
เพราะแพลตฟอร์มต่างๆอาจมีวิธีการที่แตกต่างกันในการที่วัตถุที่อาจจะมีการตั้งข้อสังเกตหรือการปรับเปลี่ยนอยู่นอกเหนือการควบคุมของคอมไพเลอร์ก็เป็นสิ่งที่เหมาะสมที่คอมไพเลอร์ที่มีคุณภาพสำหรับแพลตฟอร์มเหล่านั้นควรจะแตกต่างกันในการจัดการแน่นอนของพวกเขาของvolatile
ความหมาย น่าเสียดายเนื่องจาก Standard ล้มเหลวในการแนะนำว่าคอมไพเลอร์คุณภาพสำหรับการเขียนโปรแกรมระดับต่ำบนแพลตฟอร์มควรจัดการvolatile
ในลักษณะที่จะรับรู้ถึงผลกระทบที่เกี่ยวข้องใด ๆ และทั้งหมดของการดำเนินการอ่าน / เขียนเฉพาะบนแพลตฟอร์มนั้นคอมไพเลอร์หลายตัว ดังนั้นในวิธีที่ทำให้ยากขึ้นในการประมวลผลสิ่งต่าง ๆ เช่นพื้นหลัง I / O ในวิธีที่มีประสิทธิภาพ แต่ไม่สามารถถูกทำลายโดย "การเพิ่มประสิทธิภาพ" ของคอมไพเลอร์
กล่าวง่ายๆว่าคอมไพเลอร์ไม่ทำการปรับให้เหมาะสมกับตัวแปรเฉพาะ ตัวแปรที่แมปกับการลงทะเบียนอุปกรณ์จะถูกปรับเปลี่ยนโดยอ้อมโดยอุปกรณ์ ในกรณีนี้ต้องใช้สารระเหย
สารระเหยสามารถเปลี่ยนแปลงได้จากภายนอกโค้ดที่คอมไพล์ (ตัวอย่างเช่นโปรแกรมอาจแม็พตัวแปรระเหยกับหน่วยความจำที่แม็พลงทะเบียน) คอมไพเลอร์จะไม่ใช้การปรับให้เหมาะสมกับโค้ดที่จัดการกับตัวแปรระเหย - ตัวอย่างเช่น ไม่โหลดลงในทะเบียนโดยไม่ต้องเขียนลงในหน่วยความจำ นี่เป็นสิ่งสำคัญเมื่อจัดการกับการลงทะเบียนฮาร์ดแวร์
ตามคำแนะนำที่ถูกต้องจากที่นี่การใช้คำหลักที่มีความผันผวนได้รับความนิยมคือการข้ามการเพิ่มประสิทธิภาพของตัวแปรที่ผันผวน
ข้อได้เปรียบที่ดีที่สุดที่อยู่ในใจและมูลค่าการพูดถึงหลังจากอ่านเกี่ยวกับความผันผวนคือ - เพื่อป้องกันไม่ให้ย้อนกลับของตัวแปรในกรณีของlongjmp
ของตัวแปรในกรณีของการกระโดดที่ไม่ใช่ท้องถิ่น
สิ่งนี้หมายความว่า?
หมายความว่าค่าสุดท้ายจะคงอยู่หลังจากที่คุณคลายสแต็กเพื่อกลับไปยังเฟรมสแต็กก่อนหน้า โดยทั่วไปในกรณีที่มีสถานการณ์ที่ผิดพลาด
เนื่องจากมันอยู่นอกขอบเขตของคำถามนี้ฉันจะไม่ลงรายละเอียดsetjmp/longjmp
ที่นี่ แต่คุ้มค่าที่จะอ่าน และคุณลักษณะความผันผวนสามารถใช้เพื่อรักษาค่าสุดท้ายได้อย่างไร
ไม่อนุญาตให้คอมไพเลอร์เปลี่ยนค่าตัวแปรโดยอัตโนมัติ ตัวแปรระเหยสำหรับการใช้งานแบบไดนามิก