อะไรคือความแตกต่างระหว่างการประกาศตัวแปรภายนอกลูปและการประกาศสแตติกภายในลูป


9

นี่คือสองวิธีที่ฉันสามารถเก็บตัวแปรนอกลูป (หรือฟังก์ชันใด ๆ )

ก่อนอื่นฉันสามารถประกาศด้วยขอบเขตโกลบอลนอกลูป:

void setup()
{
    Serial.begin(9600);
}

int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    delay(250);
}

ฉันยังสามารถประกาศว่ามันคงอยู่ในวง:

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static int count = 0;

    Serial.println(count);
    count++;

    delay(250);
}

สิ่งนี้จะมีความแตกต่างอะไรบ้าง?

คำตอบ:


10

ความแตกต่างพื้นฐานที่สุดคือขอบเขต

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

void setup()
{
    Serial.begin(9600);
}

void inc();
int count = 0;

void loop()
{
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

void inc() //Can edit the value of count
{
  count=count+1;
};

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

void inc();

void loop()
{
    static int count = 0;
    Serial.println(count);
    count++;

    inc();

    delay(500);
}

สิ่งนี้จะไม่คอมไพล์เนื่องจากฟังก์ชั่นinc()ไม่สามารถเข้าถึงcountได้

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

void setup()
{
    Serial.begin(9600);
}
void another_function();
int state=0;

void loop()
{
    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Inadvertently changes state
  state=1;

}

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

void setup()
{
    Serial.begin(9600);
}
void another_function();

void loop()
{
    static int state=0;

    //Keep toggling the state
    Serial.println(state);
    delay(250);
    state=state?0:1;

    //Some unrelated function call
    another_function();
}

void another_function()
{
  //Results in a compile time error. Saves time.
  state=1;

}

5

จากเปอร์สเปคทีฟการทำงานทั้งสองเวอร์ชันจะสร้างผลลัพธ์เดียวกันเนื่องจากในทั้งสองกรณีค่าของcountจะถูกเก็บไว้ระหว่างการประมวลผลของloop()(อย่างใดอย่างหนึ่งเนื่องจากมันเป็นตัวแปรทั่วโลกหรือเพราะมันถูกทำเครื่องหมายเป็นstaticและเก็บค่าไว้)

ดังนั้นการตัดสินใจที่จะเลือกข้อโต้แย้งต่อไปนี้:

  1. โดยทั่วไปในสาขาวิทยาศาสตร์คอมพิวเตอร์จะได้รับการสนับสนุนเพื่อให้ตัวแปรของคุณเป็นท้องถิ่นที่เป็นไปได้ในแง่ของขอบเขต ซึ่งมักส่งผลให้โค้ดที่ชัดเจนมากขึ้นมีผลข้างเคียงน้อยลงและลดโอกาสของผู้อื่นที่ใช้ตัวแปรส่วนกลางที่ทำให้ตรรกะของคุณผิดพลาด) เช่นในตัวอย่างแรกของคุณพื้นที่ตรรกะอื่น ๆ อาจเปลี่ยนcountค่าในขณะที่ในที่สองเฉพาะฟังก์ชันที่loop()สามารถทำได้)
  2. ตัวแปรโกลบอลและสแตติกใช้หน่วยความจำเสมอโดยที่คนในท้องถิ่นจะทำเมื่ออยู่ในขอบเขตเท่านั้น ในตัวอย่างข้างต้นของคุณที่ไม่สร้างความแตกต่าง (เนื่องจากคุณใช้ตัวแปรโกลบอลในตัวแปรคงที่) แต่ในโปรแกรมที่ใหญ่กว่าและซับซ้อนกว่ามันอาจและคุณสามารถบันทึกหน่วยความจำโดยใช้ไอเท็มที่ไม่คงที่ อย่างไรก็ตาม : หากคุณมีตัวแปรในพื้นที่ลอจิกที่ถูกดำเนินการบ่อยครั้งให้พิจารณาว่าเป็นแบบคงที่หรือแบบโกลบอลมิฉะนั้นคุณจะสูญเสียประสิทธิภาพเล็กน้อยในแต่ละครั้งที่ป้อนพื้นที่แบบลอจิกเนื่องจากใช้เวลาสักครู่ในการ จัดสรรหน่วยความจำสำหรับอินสแตนซ์ตัวแปรใหม่นั้น คุณต้องหาสมดุลระหว่างโหลดหน่วยความจำและประสิทธิภาพ
  3. ประเด็นอื่น ๆ เช่นเค้าโครงที่ดีขึ้นสำหรับการวิเคราะห์แบบคงที่หรือการเพิ่มประสิทธิภาพโดยคอมไพเลอร์อาจเข้ามาเล่น
  4. ในบางสถานการณ์พิเศษอาจมีปัญหากับลำดับการเริ่มต้นที่คาดเดาไม่ได้ขององค์ประกอบแบบคงที่ (ไม่แน่ใจเกี่ยวกับจุดนั้นให้เปรียบเทียบลิงค์นี้)

ที่มา: กระทู้ที่คล้ายกันใน arduino.cc


การเข้าใหม่ไม่ควรเป็นปัญหาสำหรับ Arduino เพราะไม่รองรับการทำงานพร้อมกัน
Peter Bloomfield

จริง นั่นเป็นจุดทั่วไปมากกว่า แต่จริงๆแล้วไม่เกี่ยวข้องกับ Arduino ฉันลบบิตนั้น
Philip Allgaier

1
ตัวแปรสแตติกที่ประกาศภายในขอบเขตจะมีอยู่เสมอและใช้พื้นที่เดียวกันกับตัวแปรโกลบอล! ในรหัส OP ความแตกต่างเพียงอย่างเดียวคือรหัสใดที่สามารถเข้าถึงตัวแปร ใน scipe static wil สามารถเข้าถึงได้ภายในขอบเขตเดียวกัน
jfpoilpret

1
@ jfpoilpret แน่นอนว่ามันเป็นเรื่องจริงและฉันเห็นว่าส่วนที่เกี่ยวข้องในคำตอบของฉันนั้นทำให้เข้าใจผิดเล็กน้อย แก้ไขที่
Philip Allgaier

2

ตัวแปรทั้งสองเป็นแบบสแตติก - คงอยู่ตลอดช่วงการดำเนินการทั้งหมด ฟังก์ชั่นทั่วโลกสามารถมองเห็นได้ถ้ามันประกาศ - ไม่ได้กำหนด - ทั่วโลกหรือถ้าฟังก์ชั่นดังต่อไปนี้คำนิยามในหน่วยการรวบรวมเดียวกัน (ไฟล์ + รวม)

การย้ายคำจำกัดความcountไปยังภายในของฟังก์ชันนั้น จำกัด ขอบเขตการมองเห็นของชุด{}es ที่อยู่ใกล้ที่สุดและให้อายุการใช้งานของฟังก์ชันการเรียกใช้ (มันถูกสร้างและทำลายเมื่อฟังก์ชั่นเข้าและออก) การประกาศstaticด้วยจะให้อายุการใช้งานเซสชันที่มีอยู่ตั้งแต่เริ่มต้นจนถึงสิ้นสุดเซสชันการดำเนินการซึ่งคงอยู่ระหว่างการเรียกใช้ฟังก์ชัน

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


ฉันไม่แน่ใจว่าฉันเข้าใจสิ่งที่คุณหมายถึงฟังก์ชั่นประกาศทั่วโลก คุณหมายถึงเป็นextern?
Peter Bloomfield

@ PeterR.Bloomfield: ฉันไม่แน่ใจว่าส่วนหนึ่งของการโพสต์ของคุณถามเกี่ยวกับ แต่ฉันหมายถึงสองตัวอย่างของ OP - ครั้งแรกที่คำจำกัดความทั่วโลกโดยกำเนิดและที่สองคงที่ในท้องถิ่น
JRobert

-3

ตามเอกสารของ Atmel: "หากมีการประกาศตัวแปรส่วนกลางที่อยู่เฉพาะใน SRAM จะถูกกำหนดให้กับตัวแปรนี้ในเวลาที่ลิงก์ของโปรแกรม"

documetation ที่สมบูรณ์อยู่ที่นี่ (เคล็ดลับ # 2 สำหรับตัวแปรทั่วโลก): http://www.atmel.com/images/doc8453.pdf


4
ทั้งสองตัวอย่างจะไม่ได้ลงท้ายด้วยที่อยู่เฉพาะใน SRAM หรือไม่ พวกเขาทั้งสองต้องยืนกราน
Cybergibbons

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