จะพิมพ์อะไรออกมา 6 6 หรือ 6 7? และทำไม?
void foo()
{
static int x = 5;
x++;
printf("%d", x);
}
int main()
{
foo();
foo();
return 0;
}
จะพิมพ์อะไรออกมา 6 6 หรือ 6 7? และทำไม?
void foo()
{
static int x = 5;
x++;
printf("%d", x);
}
int main()
{
foo();
foo();
return 0;
}
คำตอบ:
มีสองประเด็นที่นี่อายุการใช้งานและขอบเขต
ขอบเขตของตัวแปรคือที่ที่สามารถมองเห็นชื่อตัวแปรได้ ที่นี่ x จะมองเห็นได้เฉพาะภายในฟังก์ชัน foo ()
อายุการใช้งานของตัวแปรคือช่วงเวลาที่มีอยู่ ถ้า x ถูกกำหนดโดยไม่มีคีย์เวิร์ด static อายุการใช้งานจะมาจากการป้อน foo () ไปจนถึงผลตอบแทนจาก foo (); ดังนั้นจะเริ่มต้นใหม่เป็น 5 ทุกครั้งที่โทร
คำหลักคงทำหน้าที่ขยายอายุการใช้งานของตัวแปรไปจนถึงอายุการใช้งานของโปรแกรม เช่นการเริ่มต้นเกิดขึ้นครั้งเดียวและครั้งเดียวจากนั้นตัวแปรจะยังคงรักษาค่าไว้ไม่ว่าจะเป็นอะไรก็ตาม - ในอนาคตทั้งหมดที่เรียกไปที่ foo ()
เอาต์พุต : 6 7
เหตุผล : ตัวแปรคงถูกเริ่มต้นเพียงครั้งเดียว (ไม่เหมือนกับตัวแปรอัตโนมัติ) และคำจำกัดความเพิ่มเติมของตัวแปรคงที่จะถูกข้ามระหว่างรันไทม์ และหากไม่ได้เริ่มต้นด้วยตนเองระบบจะเริ่มต้นด้วยค่า 0 โดยอัตโนมัติ ดังนั้น,
void foo() {
static int x = 5; // assigns value of 5 only once
x++;
printf("%d", x);
}
int main() {
foo(); // x = 6
foo(); // x = 7
return 0;
}
6 7
คอมไพเลอร์จัดเรียงว่าการเริ่มต้นตัวแปรแบบคงที่จะไม่เกิดขึ้นทุกครั้งที่ป้อนฟังก์ชัน
เช่นเดียวกับการมีโปรแกรมต่อไปนี้:
static int x = 5;
void foo()
{
x++;
printf("%d", x);
}
int main()
{
foo();
foo();
return 0;
}
สิ่งที่คีย์เวิร์ดแบบคงที่ทำในโปรแกรมนั้นคือมันบอกคอมไพเลอร์ (โดยพื้นฐาน) ว่า 'เฮ้ฉันมีตัวแปรที่นี่ฉันไม่ต้องการให้ใครเข้าถึงอย่าบอกคนอื่นว่ามีอยู่'
ภายในเมธอดคีย์เวิร์ดแบบคงที่จะบอกคอมไพลเลอร์เช่นเดียวกับด้านบน แต่อย่าบอกใครว่าสิ่งนี้มีอยู่นอกฟังก์ชันนี้ควรเข้าถึงได้ภายในฟังก์ชันนี้เท่านั้น
ฉันหวังว่านี่จะช่วยได้
x
ใน main; เป็นระดับโลก ในตัวอย่างเดิมx
คือ foo ในพื้นที่ซึ่งมองเห็นได้เฉพาะในขณะที่อยู่ในบล็อกนั้นซึ่งโดยทั่วไปนิยมใช้: ถ้า foo มีอยู่เพื่อรักษาx
ในรูปแบบที่คาดเดาได้และมองเห็นได้การปล่อยให้คนอื่นแหย่โดยทั่วไปจะเป็นอันตราย เป็นประโยชน์อีกประการหนึ่งของการรักษาขอบเขตfoo()
นอกจากนี้ยังช่วยให้foo()
พกพา
c
ดังนั้นในบริบทนี้ตัวอย่างของคุณจะผิดกฎหมายในขอบเขตทั่วโลก (C ต้องการตัวเริ่มต้นคงที่สำหรับ globals, C ++ ไม่ได้)
ตัวแปรคงที่ภายในฟังก์ชันมีอายุการใช้งานตราบเท่าที่โปรแกรมของคุณทำงาน จะไม่ถูกจัดสรรทุกครั้งที่ฟังก์ชันของคุณถูกเรียกและยกเลิกการจัดสรรเมื่อฟังก์ชันของคุณกลับมา
การประกาศx
อยู่ภายในfoo
แต่การx=5
เริ่มต้นเกิดขึ้นนอกfoo
!
สิ่งที่เราต้องทำความเข้าใจก็คือ
static int x = 5;
ไม่เหมือนกับ
static int x;
x = 5;
คำตอบอื่น ๆ ได้ใช้คำสำคัญที่นี่ขอบเขตและอายุการใช้งานและชี้ให้เห็นว่าขอบเขตx
มาจากจุดที่ประกาศในฟังก์ชันfoo
foo
ไปยังจุดสิ้นสุดของการทำงาน ตัวอย่างเช่นฉันตรวจสอบโดยย้ายการประกาศไปที่จุดสิ้นสุดของฟังก์ชันและนั่นทำให้x
ไม่ได้ประกาศในx++;
คำสั่ง
ดังนั้น static int x
(ขอบเขต) เป็นส่วนหนึ่งของคำสั่งที่ใช้จริงที่คุณอ่านมันอยู่ที่ไหนสักแห่งภายในฟังก์ชั่นและมีเพียงจากนั้นเป็นต้นไปไม่ได้อยู่เหนือมันไว้ภายในฟังก์ชั่น
อย่างไรก็ตามส่วนx = 5
(อายุการใช้งาน) ของคำสั่งคือการเริ่มต้นของตัวแปรและเกิดขึ้นภายนอกของฟังก์ชันซึ่งเป็นส่วนหนึ่งของการโหลดโปรแกรม ตัวแปรx
เกิดพร้อมกับค่า5
เมื่อโปรแกรมโหลด
ฉันอ่านสิ่งนี้ในความคิดเห็นหนึ่ง: " นอกจากนี้สิ่งนี้ไม่ได้กล่าวถึงส่วนที่สับสนจริงๆซึ่งเป็นความจริงที่ว่าตัวเริ่มต้นถูกข้ามไปในการโทรครั้งต่อ ๆ ไป " มันถูกข้ามไปในทุกสาย การเริ่มต้นตัวแปรอยู่นอกรหัสฟังก์ชันที่เหมาะสม
ค่า 5 ถูกตั้งค่าตามทฤษฎีไม่ว่าจะเรียก foo หรือไม่ก็ตามแม้ว่าคอมไพเลอร์อาจปรับฟังก์ชันให้เหมาะสมที่สุดหากคุณไม่ได้เรียกใช้ที่ใดก็ตาม ค่า 5 ควรอยู่ในตัวแปรก่อนที่จะเรียก foo
ด้านในของ foo
แถลงการณ์static int x = 5;
ไม่น่าจะสร้างรหัสใด ๆ เลย
ฉันพบว่าที่อยู่นั้นx
ใช้เมื่อฉันใส่ฟังก์ชันfoo
ลงในโปรแกรมของฉันแล้ว (ถูกต้อง) เดาว่าจะใช้ตำแหน่งเดียวกันถ้าฉันเรียกใช้โปรแกรมอีกครั้ง จับภาพหน้าจอบางส่วนด้านล่างแสดงให้เห็นว่าx
มีความคุ้มค่าแม้กระทั่งก่อนที่สายแรกที่5
foo
6 7
การส่งออกจะเป็น ตัวแปรคงที่ (ไม่ว่าจะอยู่ในฟังก์ชันหรือไม่ก็ตาม) จะถูกเตรียมใช้งานครั้งเดียวก่อนที่ฟังก์ชันใด ๆ ในหน่วยการแปลจะดำเนินการ หลังจากนั้นจะคงค่าไว้จนกว่าจะมีการแก้ไข
Vadiklk,
ทำไม ... ? เหตุผลคือตัวแปรคงที่จะเริ่มต้นเพียงครั้งเดียวและคงค่าไว้ตลอดทั้งโปรแกรม หมายความว่าคุณสามารถใช้ตัวแปรคงระหว่างการเรียกใช้ฟังก์ชัน นอกจากนี้ยังสามารถใช้เพื่อนับ "กี่ครั้งที่เรียกใช้ฟังก์ชัน"
main()
{
static int var = 5;
printf("%d ",var--);
if(var)
main();
}
และคำตอบคือ 5 4 3 2 1 ไม่ใช่ 5 5 5 5 5 5 .... (ลูปไม่มีที่สิ้นสุด) อย่างที่คุณคาดหวัง อีกครั้งเหตุผลคือตัวแปรคงที่จะเริ่มต้นครั้งเดียวเมื่อครั้งต่อไป main () ถูกเรียกว่าจะไม่เริ่มต้นเป็น 5 เนื่องจากเริ่มต้นในโปรแกรมแล้วดังนั้นเราสามารถเปลี่ยนค่าได้ แต่ไม่สามารถเริ่มต้นใหม่ได้ นั่นคือวิธีการทำงานของตัวแปรคงที่
หรือคุณสามารถพิจารณาตามที่จัดเก็บ: ตัวแปรคงถูกเก็บไว้ในส่วนข้อมูลของโปรแกรมและตัวแปรที่เก็บไว้ในส่วนข้อมูลจะเริ่มต้นครั้งเดียว และก่อนการเริ่มต้นระบบจะเก็บไว้ในส่วน BSS
ในทางกลับกันตัวแปรอัตโนมัติ (ในเครื่อง) จะถูกเก็บไว้ใน Stack และตัวแปรทั้งหมดบนสแต็กจะเริ่มต้นใหม่ตลอดเวลาเมื่อฟังก์ชันถูกเรียกใช้เป็น FAR ใหม่ (บันทึกการเปิดใช้งานฟังก์ชัน) สำหรับสิ่งนั้น
โอเคเพื่อความเข้าใจมากขึ้นทำตามตัวอย่างด้านบนโดยไม่ต้อง "คงที่" และแจ้งให้คุณทราบว่าผลลัพธ์จะเป็นอย่างไร นั่นทำให้คุณเข้าใจความแตกต่างระหว่างสองสิ่งนี้
ขอบคุณ Javed
มาอ่านบทความ Wikipedia เรื่อง Static Variables ...
ตัวแปรโลคัลแบบคงที่: ตัวแปรที่ประกาศเป็นแบบคงที่ภายในฟังก์ชันจะถูกจัดสรรแบบคงที่ในขณะที่มีขอบเขตเดียวกันกับตัวแปรโลคัลอัตโนมัติ ดังนั้นค่าใดก็ตามที่ฟังก์ชันใส่ไว้ในตัวแปรโลคัลแบบคงที่ในระหว่างการเรียกหนึ่งครั้งจะยังคงปรากฏอยู่เมื่อฟังก์ชันถูกเรียกอีกครั้ง
คุณจะได้รับ 6 7 พิมพ์ตามที่ทดสอบได้ง่ายและนี่คือเหตุผล: เมื่อใด foo
ใดที่ถูกเรียกครั้งแรกตัวแปรคงที่ x จะเริ่มต้นเป็น 5 จากนั้นจะเพิ่มขึ้นเป็น 6 และพิมพ์
foo
ตอนนี้สำหรับการโทรไปเพื่อ โปรแกรมจะข้ามการเริ่มต้นตัวแปรแบบคงที่และใช้ค่า 6 ซึ่งกำหนดให้ x ในครั้งสุดท้ายแทน การดำเนินการดำเนินการตามปกติโดยให้ค่าเป็น 7
6 7
x เป็นตัวแปรส่วนกลางที่มองเห็นได้จาก foo () เท่านั้น 5 คือค่าเริ่มต้นตามที่เก็บไว้ในส่วน. data ของรหัส การแก้ไขในภายหลังจะเขียนทับค่าก่อนหน้า ไม่มีการสร้างรหัสการกำหนดในเนื้อหาฟังก์ชัน
6 และ 7 เนื่องจากตัวแปรคงที่เริ่มต้นเพียงครั้งเดียวดังนั้น 5 ++ จึงกลายเป็น 6 ในการโทรครั้งที่ 1 6 ++ กลายเป็น 7 ในการเรียกครั้งที่ 2 หมายเหตุ - เมื่อการเรียกครั้งที่ 2 เกิดขึ้นจะใช้ค่า x เป็น 6 แทน 5 เนื่องจาก x เป็นตัวแปรคงที่
ใน C ++ 11 เป็นอย่างน้อยเมื่อนิพจน์ที่ใช้เริ่มต้นตัวแปรแบบคงที่ในเครื่องไม่ใช่ 'constexpr' (ไม่สามารถประเมินโดยคอมไพลเลอร์) การกำหนดค่าเริ่มต้นจะต้องเกิดขึ้นระหว่างการเรียกใช้ฟังก์ชันครั้งแรก ตัวอย่างที่ง่ายที่สุดคือการใช้พารามิเตอร์เพื่อ intialize ตัวแปรคงที่ในเครื่องโดยตรง ดังนั้นคอมไพเลอร์ต้องปล่อยโค้ดเพื่อเดาว่าการเรียกนั้นเป็นครั้งแรกหรือไม่ซึ่งจะต้องใช้ตัวแปรบูลีนในเครื่อง ฉันได้รวบรวมตัวอย่างดังกล่าวและตรวจสอบว่าเป็นจริงโดยดูรหัสการประกอบ ตัวอย่างอาจเป็นดังนี้:
void f( int p )
{
static const int first_p = p ;
cout << "first p == " << p << endl ;
}
void main()
{
f(1); f(2); f(3);
}
แน่นอนว่าเมื่อ expresion เป็น 'constexpr' ก็ไม่จำเป็นต้องใช้และตัวแปรสามารถเริ่มต้นได้เมื่อโหลดโปรแกรมโดยใช้ค่าที่คอมไพเลอร์เก็บไว้ในรหัสแอสเซมบลีเอาต์พุต