จะเกิดอะไรขึ้นกับตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นใน C มันมีค่าหรือไม่?


139

ถ้าใน CI เขียน:

int num;

ก่อนที่ฉันจะมอบหมายอะไรให้numคุณค่าของการnumไม่ได้กำหนดคืออะไร?


4
อืม, นั่นไม่ใช่ตัวแปรที่กำหนด , ไม่ใช่ตัวแปรที่ประกาศ ? (ฉันขอโทษถ้านั่นคือ C ++ ของฉันส่องประกาย ... )
sbi

6
ไม่ฉันสามารถประกาศตัวแปรได้โดยไม่ต้องกำหนด: extern int x;อย่างไรก็ตามการกำหนดหมายถึงการประกาศอยู่เสมอ สิ่งนี้ไม่เป็นความจริงใน C ++ โดยที่ตัวแปรสมาชิกคลาสแบบสแตติกหนึ่งสามารถกำหนดได้โดยไม่ต้องประกาศเนื่องจากการประกาศจะต้องอยู่ในคำจำกัดความของคลาส (ไม่ใช่การประกาศ!) และคำจำกัดความต้องอยู่นอกนิยามของคลาส
bdonlan

ee.hawaii.edu/~tep/EE160/Book/chap14/subsection2.1.1.4.html ดูเหมือนว่ากำหนดไว้หมายความว่าคุณต้องเริ่มต้นด้วย
atp

คำตอบ:


188

ตัวแปรสแตติก (ขอบเขตไฟล์และฟังก์ชันสแตติก) ถูกกำหนดค่าเริ่มต้นเป็นศูนย์:

int x; // zero
int y = 0; // also zero

void foo() {
    static int x; // also zero
}

ตัวแปรที่ไม่คงที่ (ตัวแปรท้องถิ่น) มีความไม่แน่นอน อ่านพวกเขาก่อนที่จะกำหนดค่าผลลัพธ์ในพฤติกรรมที่ไม่ได้กำหนด

void foo() {
    int x;
    printf("%d", x); // the compiler is free to crash here
}

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

สำหรับสาเหตุที่มันเป็นพฤติกรรมที่ไม่ได้กำหนดแทนที่จะเป็น "ค่าไม่ได้กำหนด / โดยพลการ" มีจำนวนของสถาปัตยกรรมซีพียูที่มีค่าสถานะบิตเพิ่มเติมในการเป็นตัวแทนของพวกเขาสำหรับประเภทต่างๆ ตัวอย่างที่ทันสมัยจะItanium ซึ่งมี "ไม่ได้เป็นสิ่ง" บิตในรีจิส ; แน่นอนว่าผู้ร่างมาตรฐาน C กำลังพิจารณาสถาปัตยกรรมที่เก่ากว่า

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


2
โอ้ไม่พวกเขาไม่ได้ พวกเขาอาจจะอยู่ในโหมดการแก้ปัญหาเมื่อคุณไม่ได้อยู่ในด้านหน้าของลูกค้าในเดือนที่มีการวิจัยในถ้าคุณโชคดี
มาร์ติน Beckett

8
อะไรนะ มาตรฐานกำหนดให้มีการเริ่มต้นคงที่ ดู ISO / IEC 9899: 1999 6.7.8 # 10
bdonlan

2
ตัวอย่างแรกนั้นใช้ได้ดีอย่างที่ฉันบอกได้ ฉันน้อยว่าทำไมคอมไพเลอร์อาจผิดพลาดในหนึ่งวินาทีแม้ว่า :)

6
@ Stuart: มีสิ่งที่เรียกว่า "การเป็นตัวแทนกับดัก" ซึ่งเป็นรูปแบบบิตที่ไม่แสดงถึงค่าที่ถูกต้องและอาจทำให้เกิดข้อยกเว้นฮาร์ดแวร์เช่นที่รันไทม์ ชนิด C เท่านั้นที่มีการรับประกันว่ารูปแบบบิตใด ๆ เป็นค่าที่ถูกต้องคือchar; อื่น ๆ ทั้งหมดสามารถมีการเป็นตัวแทนกับดัก อีกทางเลือกหนึ่ง - เนื่องจากการเข้าถึงตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นคือ UB อย่างไรก็ตามคอมไพเลอร์ที่สอดคล้องอาจทำการตรวจสอบและตัดสินใจที่จะส่งสัญญาณปัญหา
Pavel Minaev

5
bdonian ถูกต้อง C ถูกระบุอย่างแม่นยำอยู่เสมอ ก่อนถึง C89 และ C99 กระดาษโดย dmr ได้ระบุสิ่งเหล่านี้ทั้งหมดในต้นปี 1970 แม้แต่ในระบบฝังตัวที่หยาบคายมันใช้เวลาเพียงหนึ่ง memset () ในการทำสิ่งที่ถูกต้องดังนั้นจึงไม่มีข้อแก้ตัวสำหรับสภาพแวดล้อมที่ไม่เป็นไปตามข้อกำหนด ฉันอ้างถึงมาตรฐานในคำตอบของฉัน
DigitalRoss

57

0 ถ้าเป็นแบบคงที่หรือทั่วโลกโดยไม่ทราบว่าคลาสหน่วยเก็บข้อมูลเป็นแบบอัตโนมัติ

C มีความเฉพาะเจาะจงมากเกี่ยวกับค่าเริ่มต้นของวัตถุ หากทั่วโลกหรือstaticพวกเขาจะเป็นศูนย์ หากautoมีค่าเป็นไม่แน่นอน

นี่เป็นกรณีในคอมไพเลอร์ pre-C89 และถูกระบุโดย K&R และในรายงาน C ต้นฉบับของ DMR

นี่คือเหตุการณ์ใน C89 ให้ดูส่วน6.5.7 การเริ่มต้น

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

นี่คือเหตุการณ์ใน C99 ให้ดูส่วน6.7.8 การเริ่มต้น

หากวัตถุที่มีระยะเวลาการจัดเก็บอัตโนมัติไม่ได้เริ่มต้นอย่างชัดเจนค่าของมันจะไม่ได้กำหนด หากวัตถุที่มีระยะเวลาการจัดเก็บแบบคงที่ไม่ได้เริ่มต้นอย่างชัดเจนแล้ว:
- ถ้ามันมีประเภทตัวชี้ก็จะเริ่มต้นได้ที่ตัวชี้โมฆะ;
- ถ้ามันมีประเภทเลขคณิตมันจะเริ่มต้นได้ถึงศูนย์ (บวกหรือไม่ได้ลงนาม);
- ถ้าเป็นผลรวมสมาชิกทุกคนจะถูกกำหนดค่าเริ่มต้น (เรียกซ้ำ) ตามกฎเหล่านี้
- หากเป็นสหภาพสมาชิกที่มีชื่อแรกจะถูกกำหนดค่าเริ่มต้น (เรียกซ้ำ) ตามกฎเหล่านี้

สำหรับความหมายที่ไม่แน่นอนฉันไม่แน่ใจสำหรับ C89, C99 พูดว่า:

3.17.2
ค่าที่ไม่แน่นอน

ทั้งค่าที่ไม่ระบุหรือการเป็นตัวแทนของแทร็บ

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

คุณอาจสงสัยว่าทำไมเป็นอย่างนี้ คำตอบ SO อื่น ๆ เกี่ยวข้องกับคำถามนั้นโปรดดู: https://stackoverflow.com/a/2091505/140740


3
โดยปกติแล้วจะไม่สามารถทำได้ มันอาจจะเป็นศูนย์มันอาจเป็นค่าที่อยู่ในนั้นมันอาจผิดพลาดของโปรแกรมมันสามารถทำให้คอมพิวเตอร์ผลิตแพนเค้กบลูเบอร์รี่ออกมาจากช่องใส่ซีดี คุณไม่มีหลักประกันใด ๆ มันอาจทำให้เกิดการล่มสลายของโลก อย่างน้อยที่สุดเท่าที่เป็นสเปคไป ... ใครก็ตามที่ทำคอมไพเลอร์ที่ไม่จริงอะไรเช่นนั้นจะได้รับการขมวดคิ้วสูงเมื่อ B-)
ไบรอัน Postow

ใน C11 N1570 ฉบับร่างคำจำกัดความของindeterminate valueสามารถพบได้ที่ 3.19.2
user3528438

มันขึ้นอยู่กับคอมไพเลอร์หรือระบบปฏิบัติการที่กำหนดค่าสำหรับตัวแปรแบบคงที่เสมอหรือไม่? ตัวอย่างเช่นถ้ามีคนเขียนระบบปฏิบัติการหรือคอมไพเลอร์ของฉันเองและหากพวกเขาตั้งค่าเริ่มต้นโดยค่าเริ่มต้นสำหรับสถิตเป็นไม่แน่นอนมันเป็นไปได้หรือไม่
Aditya Singh เมื่อ

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

@BrianPostow ไม่นั่นไม่ถูกต้อง ดูstackoverflow.com/a/40674888/584518 การใช้ค่าที่ไม่แน่นอนทำให้เกิดการที่ไม่ระบุพฤติกรรมพฤติกรรมไม่ได้กำหนดบันทึกสำหรับกรณีของการเป็นตัวแทนกับดัก
Lundin

12

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

สำหรับตัวแปรอัตโนมัติ (ท้องถิ่น) ตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นจะมีค่าไม่แน่นอน ค่า Indeterminate เหนือสิ่งอื่นใดหมายความว่าสิ่งที่ "คุ้มค่า" คุณอาจจะ "เห็น" ในตัวแปรที่ไม่ได้เป็นที่คาดเดาไม่เพียง แต่จะไม่รับประกันแม้จะเป็นมีเสถียรภาพ ตัวอย่างเช่นในทางปฏิบัติ (เช่นไม่สนใจ UB สักวินาที) รหัสนี้

int num;
int a = num;
int b = num;

ไม่รับประกันว่าตัวแปรนั้นaและbจะได้รับค่าเหมือนกัน ที่น่าสนใจคือนี่ไม่ใช่แนวคิดเชิงทฤษฎีบางอย่างเกี่ยวกับเรื่องนี้สิ่งนี้เกิดขึ้นในทางปฏิบัติอันเป็นผลมาจากการปรับให้เหมาะสม

ดังนั้นโดยทั่วไปคำตอบที่ได้รับความนิยมว่า "การเริ่มต้นกับขยะใด ๆ ก็ตามที่อยู่ในหน่วยความจำ" ไม่ถูกต้องจากระยะไกล เตรียมพฤติกรรมของตัวแปรจะแตกต่างจากที่ของตัวแปรเริ่มต้นด้วยขยะ


ฉันไม่สามารถเข้าใจ (ดีฉันเป็นอย่างดีสามารถ ) ทำไมนี้มี upvotes มากน้อยกว่าหนึ่งจาก DigitalRoss แค่นาทีหลังจากที่: D
Antti Haapala

7

ตัวอย่าง Ubuntu 15.10, เคอร์เนล 4.2.0, x86-64, GCC 5.2.1

มาตรฐานที่เพียงพอมาดูการใช้งาน :-)

ตัวแปรท้องถิ่น

มาตรฐาน: พฤติกรรมที่ไม่ได้กำหนด

การติดตั้งใช้งาน: โปรแกรมจัดสรรพื้นที่สแต็กและไม่ย้ายสิ่งใดไปยังที่อยู่นั้น

#include <stdio.h>
int main() {
    int i;
    printf("%d\n", i);
}

รวบรวมกับ:

gcc -O0 -std=c99 a.c

เอาท์พุท:

0

และถอดรหัสกับ:

objdump -dr a.out

ถึง:

0000000000400536 <main>:
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
  40053a:       48 83 ec 10             sub    $0x10,%rsp
  40053e:       8b 45 fc                mov    -0x4(%rbp),%eax
  400541:       89 c6                   mov    %eax,%esi
  400543:       bf e4 05 40 00          mov    $0x4005e4,%edi
  400548:       b8 00 00 00 00          mov    $0x0,%eax
  40054d:       e8 be fe ff ff          callq  400410 <printf@plt>
  400552:       b8 00 00 00 00          mov    $0x0,%eax
  400557:       c9                      leaveq
  400558:       c3                      retq

จากความรู้ของเราในการเรียกประชุม x86-64:

  • %rdiเป็นอาร์กิวเมนต์ printf แรกดังนั้นสตริง"%d\n"ที่แอดเดรส0x4005e4

  • %rsiเป็นอาร์กิวเมนต์ printf iสองจึง

    มันมาจาก-0x4(%rbp)ซึ่งเป็นตัวแปรท้องถิ่น 4 ไบต์แรก

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

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

จากนั้นเราสามารถเล่นกับการดัดแปลงแบบกองซ้อนของเราเองและเขียนสิ่งที่สนุกสนานเช่น:

#include <assert.h>

int f() {
    int i = 13;
    return i;
}

int g() {
    int i;
    return i;
}

int main() {
    f();
    assert(g() == 13);
}

ตัวแปรท้องถิ่นใน -O3

การวิเคราะห์การใช้งานที่: <value Optimization out> หมายถึงอะไรใน gdb?

ตัวแปรทั่วโลก

มาตรฐาน: 0

การใช้งาน: .bssส่วน

#include <stdio.h>
int i;
int main() {
    printf("%d\n", i);
}

gcc -00 -std=c99 a.c

รวบรวมเพื่อ:

0000000000400536 <main>:
  400536:       55                      push   %rbp
  400537:       48 89 e5                mov    %rsp,%rbp
  40053a:       8b 05 04 0b 20 00       mov    0x200b04(%rip),%eax        # 601044 <i>
  400540:       89 c6                   mov    %eax,%esi
  400542:       bf e4 05 40 00          mov    $0x4005e4,%edi
  400547:       b8 00 00 00 00          mov    $0x0,%eax
  40054c:       e8 bf fe ff ff          callq  400410 <printf@plt>
  400551:       b8 00 00 00 00          mov    $0x0,%eax
  400556:       5d                      pop    %rbp
  400557:       c3                      retq
  400558:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  40055f:       00

# 601044 <i>บอกว่าiเป็นที่อยู่0x601044และ:

readelf -SW a.out

ประกอบด้วย:

[25] .bss              NOBITS          0000000000601040 001040 000008 00  WA  0   0  4

ซึ่งระบุว่า0x601044อยู่ตรงกลางของ.bssส่วนซึ่งเริ่มต้นที่0x601040และมีความยาว 8 ไบต์

มาตรฐานเอลฟ์แล้วรับประกันว่าส่วนที่มีชื่อ.bssจะสมบูรณ์เต็มไปด้วยของศูนย์:

.bssส่วนนี้เก็บข้อมูลที่ไม่ได้กำหนดค่าเริ่มต้นซึ่งนำไปสู่อิมเมจหน่วยความจำของโปรแกรม ตามคำนิยามระบบจะเริ่มต้นข้อมูลด้วยค่าศูนย์เมื่อโปรแกรมเริ่มทำงาน ส่วน occu- SHT_NOBITSพายพื้นที่ไม่มีแฟ้มตามที่ระบุไว้ตามประเภทส่วน

นอกจากนี้ชนิดSHT_NOBITSยังมีประสิทธิภาพและไม่มีที่ว่างบนไฟล์เรียกทำงาน:

sh_sizeสมาชิกนี้ให้ขนาดของส่วนเป็นไบต์ ยกเว้นว่าเป็นSHT_NOBITSส่วนประเภทส่วนที่ครอบครองsh_size ไบต์ในไฟล์ ส่วนของประเภทSHT_NOBITSอาจมีขนาดที่ไม่เป็นศูนย์ แต่จะไม่มีพื้นที่ในไฟล์

จากนั้นจะขึ้นอยู่กับเคอร์เนลของ Linux เพื่อให้ไม่มีพื้นที่หน่วยความจำนั้นเมื่อโหลดโปรแกรมลงในหน่วยความจำเมื่อเริ่มต้น


4

ขึ้นอยู่กับว่า หากคำจำกัดความนั้นเป็นสากล (นอกฟังก์ชั่นใด ๆ ) จากนั้นnumจะเริ่มต้นให้เป็นศูนย์ ถ้าเป็นของท้องถิ่น (ภายในฟังก์ชัน) ค่าของมันจะไม่แน่นอน ในทางทฤษฎีการพยายามอ่านค่ามีพฤติกรรมที่ไม่ได้กำหนดไว้ - C ช่วยให้ความเป็นไปได้ของบิตที่ไม่ได้มีส่วนร่วมกับค่า แต่ต้องตั้งค่าในวิธีเฉพาะเพื่อให้คุณได้รับผลลัพธ์ที่กำหนดจากการอ่านตัวแปร


1

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

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

uint16_t hey(uint32_t x, uint32_t mode)
{ uint16_t q; 
  if (mode==1) q=2; 
  if (mode==3) q=4; 
  return q; }

 uint32_t wow(uint32_t mode) {
   return hey(1234567, mode);
 }

อาจส่งผลให้การwow()จัดเก็บค่า 1234567 ในการลงทะเบียน 0 และ 1 ตามลำดับและการโทรfoo()ดีขึ้น เนื่องจากxไม่จำเป็นต้องอยู่ภายใน "foo" และตั้งแต่ฟังก์ชั่นที่ควรจะใส่ค่าตอบแทนของพวกเขาในการลงทะเบียน 0, คอมไพเลอร์อาจจัดสรรลงทะเบียน 0 qถึง ถ้าmodeเป็น 1 หรือ 3 การลงทะเบียน 0 จะถูกโหลดด้วย 2 หรือ 4 ตามลำดับ แต่ถ้าเป็นค่าอื่นฟังก์ชันอาจส่งคืนสิ่งที่อยู่ใน register 0 (เช่นค่า 1234567) แม้ว่าค่านั้นจะไม่อยู่ในช่วง จาก uint16_t

เพื่อหลีกเลี่ยงการให้คอมไพเลอร์ทำงานพิเศษเพื่อให้แน่ใจว่าตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นจะไม่เก็บค่าไว้นอกโดเมนของตนและหลีกเลี่ยงการระบุพฤติกรรมที่ไม่แน่นอนในรายละเอียดที่มากเกินไปมาตรฐานบอกว่าการใช้ตัวแปรอัตโนมัติ ในบางกรณีผลที่ตามมาของเหตุการณ์นี้อาจน่าแปลกใจมากกว่าค่าที่อยู่นอกช่วงของประเภท ตัวอย่างเช่นกำหนด:

void moo(int mode)
{
  if (mode < 5)
    launch_nukes();
  hey(0, mode);      
}

คอมไพเลอร์อาจอนุมานได้ว่าเนื่องจากการเรียกใช้moo()ด้วยโหมดที่มากกว่า 3 จะนำไปสู่โปรแกรมที่เรียกใช้พฤติกรรมที่ไม่ได้กำหนดอย่างหลีกเลี่ยงไม่ได้คอมไพเลอร์อาจละเว้นโค้ดใด ๆ ที่จะเกี่ยวข้องหากmodeมี 4 หรือมากกว่าเช่นรหัสที่ปกติ การเปิดตัวของนิวเคลียร์ในกรณีเช่นนี้ โปรดทราบว่าทั้งมาตรฐานและปรัชญาคอมไพเลอร์สมัยใหม่จะใส่ใจกับความจริงที่ว่าค่าที่ส่งคืนจาก "เฮ้" นั้นไม่ได้รับการสนใจการกระทำของการพยายามส่งคืนนั้นจะให้สิทธิ์การใช้งานแบบไม่ จำกัด สำหรับคอมไพเลอร์


0

คำตอบพื้นฐานคือใช่มันไม่ได้กำหนด

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


0

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

แต่คอมไพเลอร์บางตัวอาจมีกลไกในการหลีกเลี่ยงปัญหาดังกล่าว

ฉันทำงานกับ nec v850 series เมื่อฉันรู้ว่ามีการแสดงกับดักซึ่งมีรูปแบบบิตที่แสดงค่าที่ไม่ได้กำหนดสำหรับชนิดข้อมูลยกเว้นสำหรับถ่าน เมื่อฉันเอาถ่านไม่ได้เริ่มต้นฉันได้รับค่าเริ่มต้นเป็นศูนย์เนื่องจากการเป็นตัวแทนกับดัก สิ่งนี้อาจมีประโยชน์สำหรับ any1 ที่ใช้ necv850es


ระบบของคุณไม่เข้ากันหากคุณได้รับการเป็นตัวแทนกับดักเมื่อใช้ถ่านที่ไม่ได้ลงนาม พวกเขาไม่ได้รับอนุญาตอย่างชัดเจนให้มีการเป็นตัวแทนกับดัก, C17 6.2.6.1/5
Lundin

-2

ค่าของ NUM จะเป็นค่าขยะบางส่วนจากหน่วยความจำหลัก (RAM) มันจะดีกว่าถ้าคุณเริ่มต้นตัวแปรหลังจากสร้าง


-4

เท่าที่ฉันได้ไปมันส่วนใหญ่ขึ้นอยู่กับคอมไพเลอร์ แต่โดยทั่วไปกรณีส่วนใหญ่ค่าจะถือว่าเป็น 0 โดยคอมไพเลอร์
ฉันได้ค่าขยะในกรณี VC ++ ในขณะที่ TC ให้ค่าเป็น 0 ฉันพิมพ์ตามด้านล่าง

int i;
printf('%d',i);

หากคุณได้รับค่าที่กำหนดไว้เช่น0คอมไพเลอร์ของคุณส่วนใหญ่จะไปตามขั้นตอนพิเศษเพื่อให้แน่ใจว่าได้รับค่านั้น (โดยการเพิ่มรหัสเพื่อเริ่มต้นตัวแปรต่อไป) คอมไพเลอร์บางตัวทำสิ่งนี้เมื่อทำการคอมไพล์ "debug" แต่การเลือกค่า0สำหรับสิ่งเหล่านี้เป็นความคิดที่ไม่ดีเนื่องจากมันจะซ่อนความผิดพลาดในโค้ดของคุณ (สิ่งที่เหมาะสมกว่าคือการรับประกัน0xBAADF00Dว่า ฉันคิดว่าคอมไพเลอร์ส่วนใหญ่จะทิ้งขยะใด ๆ ก็ตามที่เกิดขึ้นเพื่อครอบครองหน่วยความจำเป็นค่าของตัวแปร (เช่นโดยทั่วไปแล้วไม่ถือว่าเป็น0)
skyking
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.