ประกาศตำแหน่งตัวแปรใน C


129

ฉันคิดมานานแล้วว่าใน C ตัวแปรทั้งหมดจะต้องถูกประกาศในตอนเริ่มต้นของฟังก์ชัน ฉันรู้ว่าใน C99 กฎเหมือนกันใน C ++ แต่อะไรคือกฎการวางประกาศตัวแปรสำหรับ C89 / ANSI C

โค้ดต่อไปนี้คอมไพล์ด้วยgcc -std=c89และgcc -ansi:

#include <stdio.h>
int main() {
    int i;
    for (i = 0; i < 10; i++) {
        char c = (i % 95) + 32;
        printf("%i: %c\n", i, c);
        char *s;
        s = "some string";
        puts(s);
    }
    return 0;
}

ไม่ควรประกาศcและsทำให้เกิดข้อผิดพลาดในโหมด C89 / ANSI


54
เพียงแค่ทราบ: ตัวแปรใน ansi C ไม่จำเป็นต้องประกาศ ณ จุดเริ่มต้นของฟังก์ชั่น แต่ในช่วงเริ่มต้นของบล็อก ดังนั้นถ่าน c = ... ที่ด้านบนสุดของลูปของคุณนั้นถูกต้องตามกฎหมายใน ansi C อย่างไรก็ตาม char * s จะไม่เป็นเช่นนั้น
Jason Coco

คำตอบ:


149

สามารถรวบรวมได้สำเร็จเนื่องจาก GCC อนุญาตให้ประกาศsเป็นส่วนขยาย GNU แม้ว่าจะไม่ได้เป็นส่วนหนึ่งของมาตรฐาน C89 หรือ ANSI หากคุณต้องการปฏิบัติตามมาตรฐานเหล่านั้นอย่างเคร่งครัดคุณจะต้องผ่าน-pedanticธง

การประกาศcที่จุดเริ่มต้นของ{ }บล็อกเป็นส่วนหนึ่งของมาตรฐาน C89 บล็อกไม่จำเป็นต้องเป็นฟังก์ชัน


41
น่าจะเป็นที่น่าสังเกตว่าเฉพาะการประกาศของsเป็นส่วนขยาย (จากมุมมอง C89) การประกาศของcถูกกฎหมายอย่างสมบูรณ์ใน C89 ไม่จำเป็นต้องมีส่วนขยาย
AnT

7
@AndreyT: ใช่ใน C การประกาศตัวแปรควรเป็น @ จุดเริ่มต้นของบล็อกและไม่ใช่ฟังก์ชันต่อ se แต่ผู้คนสับสนกับฟังก์ชั่นบล็อกเนื่องจากมันเป็นตัวอย่างหลักของบล็อก
ตำนาน 2k

1
ฉันย้ายความคิดเห็นที่มี +39 คะแนนโหวตเป็นคำตอบ
MarcH

78

สำหรับ C89 คุณต้องประกาศตัวแปรทั้งหมดของคุณที่จุดเริ่มต้นของการเป็นบล็อกขอบเขต

ดังนั้นchar cการประกาศของคุณจะถูกต้องตามที่อยู่ด้านบนของบล็อกขอบเขตลูป แต่การchar *sประกาศควรเป็นข้อผิดพลาด


2
ค่อนข้างถูกต้อง คุณสามารถประกาศตัวแปรที่จุดเริ่มต้นของ {... } ใด ๆ
Artelius

5
@Artelius ค่อนข้างไม่ถูกต้อง เฉพาะในกรณีที่ curlies เป็นส่วนหนึ่งของบล็อก (ไม่ได้ว่าพวกเขาเป็นส่วนหนึ่งของ struct หรือสหภาพแรงงานประกาศหรือการเริ่มต้นยัน.)
Jens

การประกาศอย่างผิดพลาดควรได้รับแจ้งอย่างน้อยตามมาตรฐาน C ดังนั้นควรเป็นข้อผิดพลาดหรือคำเตือนgccค่ะ นั่นคืออย่าวางใจว่าโปรแกรมสามารถคอมไพล์ได้เพื่อหมายความว่ามันเป็นไปตามมาตรฐาน
jinawee

35

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

C + + โชคไม่ดีที่จะยอมรับวิธีการประกาศแบบเก่าที่เป็นที่นิยมมากที่สุดสำหรับความเข้ากันได้แบบย้อนหลังกับ C (ความเข้ากันได้ของ C หนึ่งรายการดึงออกมาจากหลาย ๆ เรื่อง ... ) แต่ C ++ พยายามที่จะย้ายออกไป

  • การออกแบบการอ้างอิง C ++ นั้นไม่อนุญาตให้มีการจัดกลุ่มด้านบนสุดดังกล่าว
  • หากคุณแยกการประกาศและการเริ่มต้นของวัตถุในท้องถิ่นของ C ++ คุณต้องจ่ายเงินค่า Constructor พิเศษโดยไม่ต้องเสียค่าใช้จ่ายใด ๆ ถ้าไม่มีคอนสตรัคเตอร์หาเรื่องไม่มีอีกแล้วคุณจะไม่ได้รับอนุญาตให้แยกทั้งสองอย่าง!

C99 เริ่มเคลื่อนที่ C ในทิศทางเดียวกันนี้

หากคุณกังวลว่าจะไม่พบตำแหน่งที่มีการประกาศตัวแปรท้องถิ่นนั่นหมายความว่าคุณมีปัญหาที่ใหญ่กว่า: บล็อกที่ล้อมรอบยาวเกินไปและควรแยกออก

https://wiki.sei.cmu.edu/confluence/display/c/DCL19-C.+Minimize+the+scope+of+variables+and+functions



ดูวิธีการบังคับให้ประกาศตัวแปรที่ด้านบนของบล็อกสามารถสร้างช่องโหว่: lwn.net/Articles/443037
มีนาคม

"น่าเสียดายที่ C ++ ยังคงยอมรับวิธีการประกาศที่เก่าแก่ที่สุดเพื่อความเข้ากันได้ย้อนหลังกับ C": IMHO เป็นเพียงวิธีที่สะอาดในการทำ ภาษาอื่น "แก้ปัญหา" โดยเริ่มต้นด้วย 0 Bzzt เสมอซึ่งจะปกปิดข้อผิดพลาดทางตรรกะหากคุณถามฉันเท่านั้น และมีบางกรณีที่คุณต้องประกาศโดยไม่ต้องเริ่มต้นเนื่องจากมีสถานที่ที่เป็นไปได้หลายแห่งสำหรับการเริ่มต้น และนั่นเป็นสาเหตุว่าทำไม RAII ของ C ++ จึงเป็นความเจ็บปวดครั้งใหญ่ในก้นบึ้ง - ตอนนี้คุณต้องรวมสถานะที่ไม่มีการกำหนด "ที่ถูกต้อง" ไว้ในแต่ละวัตถุเพื่อให้เกิดกรณีเหล่านี้
โจดังนั้น

1
@ โจโซ: ฉันสับสนว่าทำไมคุณคิดว่าการอ่านตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นจะทำให้เอฟเฟ็กต์ตามอำเภอใจจะทำให้การเขียนโปรแกรมผิดพลาดง่ายต่อการตรวจจับมากกว่าการให้พวกมันให้ค่าที่สอดคล้องกัน โปรดทราบว่าไม่มีการรับประกันว่าการอ่านที่จัดเก็บข้อมูลแบบไม่ตั้งใจจะทำงานในรูปแบบที่สอดคล้องกับรูปแบบบิตใด ๆ ที่ตัวแปรอาจมีอยู่หรือแม้แต่โปรแกรมดังกล่าวจะทำงานในรูปแบบที่สอดคล้องกับกฎหมายตามกาลเวลาและเวรกรรม บางสิ่งบางอย่างที่กำหนดเช่นint y; ... if (x) { printf("X was true"); y=23;} return y;...
SuperCat

1
@JoSo: สำหรับพอยน์เตอร์โดยเฉพาะอย่างยิ่งในการนำไปใช้งานที่การดำเนินการกับดักnullall-bits-zero มักจะเป็นค่ากับดักที่มีประโยชน์ นอกจากนี้ในภาษาที่ระบุอย่างชัดเจนว่าตัวแปรเริ่มต้นทุกบิตศูนย์การพึ่งพาค่าที่ไม่ได้เป็นข้อผิดพลาด คอมไพเลอร์ยังไม่ได้มีแนวโน้มที่จะแปลกประหลาดเกินไปกับ "การเพิ่มประสิทธิภาพ" ของพวกเขา แต่นักเขียนคอมไพเลอร์พยายามที่จะได้รับความฉลาดมากขึ้นเรื่อย ๆ ตัวเลือกคอมไพเลอร์เพื่อเริ่มต้นตัวแปรด้วยตัวแปรหลอกแบบจงใจอาจมีประโยชน์สำหรับการระบุความผิดพลาด แต่การทิ้งที่เก็บข้อมูลที่ถือค่าสุดท้ายไว้บางครั้งก็สามารถปกปิดความผิดพลาดได้
supercat

22

จากความสามารถในการบำรุงรักษามากกว่าความเห็นเชิงวากยสัมพันธ์มีอย่างน้อยสามขบวนความคิด:

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

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

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

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

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


27
ฉันใช้ตัวเลือก 2 หรือ 3 เพื่อให้ง่ายต่อการค้นหาตัวแปร - เพราะฟังก์ชั่นไม่ควรใหญ่จนคุณไม่เห็นการประกาศตัวแปร
Jonathan Leffler

8
ตัวเลือก 3 ไม่ใช่ปัญหายกเว้นว่าคุณใช้คอมไพเลอร์จากยุค 70
edgar.holleis

15
หากคุณใช้ IDE ที่เหมาะสมคุณไม่จำเป็นต้องไปหารหัสเพราะควรมีคำสั่ง IDE เพื่อค้นหาการประกาศสำหรับคุณ (F3 ใน Eclipse)
edgar.holleis

4
ฉันไม่เข้าใจว่าคุณจะมั่นใจได้อย่างไรว่าการเริ่มต้นในตัวเลือก 1 อาจถึงเวลาที่คุณจะได้รับค่าเริ่มต้นในบล็อกในภายหลังโดยการเรียกฟังก์ชั่นอื่นหรือดำเนินการ caclulation
Plumenator

4
@Plumenator: ตัวเลือก 1 ไม่รับประกันการเริ่มต้น ฉันเลือกที่จะเริ่มต้นพวกเขาเมื่อประกาศทั้งค่า "ถูกต้อง" ของพวกเขาหรือสิ่งที่จะรับประกันรหัสที่ตามมาจะแตกถ้าพวกเขาไม่ได้ตั้งค่าอย่างเหมาะสม ฉันพูดว่า "เลือก" เพราะความชอบของฉันเปลี่ยนไปเป็น # 2 ตั้งแต่ฉันเขียนสิ่งนี้บางทีอาจเป็นเพราะฉันใช้ Java มากกว่า C ตอนนี้และเพราะฉันมีเครื่องมือ dev ที่ดีกว่า
Adam Liss

6

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

C89 ต้องการให้มีการประกาศตัวแปรก่อนคำสั่งอื่น ๆ ภายในแต่ละขอบเขตมาตรฐานภายหลังอนุญาตให้มีการประกาศให้ใกล้เคียงกับการใช้มากขึ้น (ซึ่งอาจเป็นได้ทั้งที่เข้าใจได้ง่ายและมีประสิทธิภาพมากกว่า) โดยเฉพาะการประกาศพร้อมกัน


0

ตามที่ได้รับการกล่าวมีโรงเรียนคิดสองแห่งในเรื่องนี้

1) ประกาศทุกอย่างที่อยู่ด้านบนสุดของฟังก์ชั่นเพราะปีคือ 1987

2) ประกาศใกล้เคียงกับการใช้ครั้งแรกและอยู่ในขอบเขตที่เล็กที่สุดเท่าที่จะเป็นไปได้

คำตอบของฉันคือทำทั้ง! ให้ฉันอธิบาย:

สำหรับฟังก์ชั่นที่ยาวนาน 1) ทำให้การปรับโครงสร้างยาก หากคุณทำงานใน codebase ที่นักพัฒนาไม่เห็นด้วยกับแนวคิดของ subroutines คุณจะมีการประกาศตัวแปร 50 ตัวที่จุดเริ่มต้นของฟังก์ชันและบางอันอาจเป็น "i" สำหรับ for-loop ที่อยู่ในระดับต่ำมาก ด้านล่างของฟังก์ชั่น

ฉันจึงพัฒนาคำประกาศ - at - the - the - top - PTSD จากนี้และพยายามที่จะทำตัวเลือก 2) อย่างเคร่งครัด

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

นอกจากนี้รูปแบบการต่อต้านของ "ประกาศและตั้งค่าเป็น NULL" เมื่อคุณต้องการประกาศที่ด้านบน แต่คุณยังไม่ได้ทำการคำนวณที่จำเป็นสำหรับการเริ่มต้นจะได้รับการแก้ไขเพราะสิ่งที่คุณต้องเริ่มต้นจะได้รับเป็นอาร์กิวเมนต์

ตอนนี้ความคิดของฉันคือคุณควรประกาศที่ด้านบนสุดของฟังก์ชั่นและใกล้เคียงกับการใช้งานครั้งแรก ทั้งคู่! และวิธีที่จะทำคือมีรูทีนย่อยที่แบ่งออกเป็นอย่างดี

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

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

int foo = 0;
<code that uses foo>

int bar = 1;
<code that uses bar>

<code that uses foo>

ตอนนี้กำหนดขอบเขตบล็อกที่เริ่มต้นก่อนการประกาศและย้ายจุดสิ้นสุดจนกว่าโปรแกรมจะคอมไพล์

{
    int foo = 0;
    <code that uses foo>
}

int bar = 1;
<code that uses bar>

>>> First compilation error here
<code that uses foo>

สิ่งนี้ไม่ได้รวบรวมเนื่องจากมีรหัสอีกจำนวนหนึ่งที่ใช้ foo เราสามารถสังเกตได้ว่าคอมไพเลอร์สามารถผ่านโค้ดที่ใช้แถบเพราะมันไม่ได้ใช้ foo ณ จุดนี้มีสองตัวเลือก กลไกหนึ่งคือเพียงเลื่อน "}" ลงไปจนคอมไพล์และตัวเลือกอื่นคือตรวจสอบโค้ดและพิจารณาว่าคำสั่งซื้อสามารถเปลี่ยนเป็น:

{
    int foo = 0;
    <code that uses foo>
}

<code that uses foo>

int bar = 1;
<code that uses bar>

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

สิ่งอื่นที่ควรทราบค่าของ foo จำเป็นต้องเก็บรักษาไว้ระหว่างบล็อกของรหัสที่ใช้หรืออาจเป็น foo ที่แตกต่างกันในทั้งสองอย่าง ตัวอย่างเช่น

int i;

for(i = 0; i < 8; ++i){
    ...
}

<some stuff>

for(i = 3; i < 32; ++i){
    ...
}

สถานการณ์เหล่านี้ต้องการมากกว่าขั้นตอนของฉัน ผู้พัฒนาจะต้องวิเคราะห์รหัสเพื่อกำหนดว่าต้องทำอย่างไร

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

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


0

คุณควรประกาศตัวแปรทั้งหมดที่ด้านบนหรือ "ภายใน" ในฟังก์ชั่น คำตอบคือ:

มันขึ้นอยู่กับว่าคุณใช้ระบบแบบไหน:

1 / ระบบสมองกลฝังตัว (โดยเฉพาะที่เกี่ยวข้องกับชีวิตเช่นเครื่องบินหรือรถยนต์): มันช่วยให้คุณใช้หน่วยความจำแบบไดนามิก (เช่น: calloc, malloc, ใหม่ ... ) ลองนึกภาพคุณกำลังทำงานในโครงการขนาดใหญ่มากมีวิศวกร 1,000 คน ถ้าพวกเขาจัดสรรหน่วยความจำแบบไดนามิกใหม่และลืมที่จะลบมัน (เมื่อมันไม่ได้ใช้อีกต่อไป)? หากระบบฝังตัวทำงานเป็นเวลานานระบบจะนำไปสู่การโอเวอร์โฟลว์และซอฟต์แวร์จะเสียหาย ไม่ใช่เรื่องง่ายที่จะตรวจสอบคุณภาพ (วิธีที่ดีที่สุดคือห้ามหน่วยความจำแบบไดนามิก)

หากเครื่องบินทำงานใน 30 วันและไม่ทำการปิดการทำงานจะเกิดอะไรขึ้นหากซอฟต์แวร์เสียหาย (เมื่อเครื่องบินยังคงอยู่ในอากาศ)

2 / ระบบอื่น ๆ เช่นเว็บ, พีซี (มีพื้นที่หน่วยความจำขนาดใหญ่):

คุณควรประกาศตัวแปร "ในพื้นที่" เพื่อเพิ่มประสิทธิภาพการใช้หน่วยความจำ หากระบบเหล่านี้ทำงานเป็นเวลานานและเกิดการล้นสแต็ก (เพราะมีคนลืมลบหน่วยความจำแบบไดนามิก) เพียงทำสิ่งง่าย ๆ เพื่อรีเซ็ตพีซี: P มันไม่มีผลกระทบต่อชีวิต


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

1 / ระบบฝังตัวบางครั้งไม่อนุญาตหน่วยความจำแบบไดนามิกดังนั้นหากคุณประกาศตัวแปรทั้งหมดที่ด้านบนของฟังก์ชั่น เมื่อสร้างซอร์สโค้ดมันสามารถคำนวณจำนวนไบต์ที่ต้องการในสแต็กเพื่อเรียกใช้โปรแกรม แต่ด้วยหน่วยความจำแบบไดนามิกคอมไพเลอร์ไม่สามารถทำเช่นเดียวกัน
Dang_Ho

2 / หากคุณประกาศตัวแปรในเครื่องตัวแปรนั้นจะมีอยู่ในวงเล็บเปิด / ปิด "{}" เท่านั้น คอมไพเลอร์สามารถปล่อยพื้นที่ของตัวแปรถ้าตัวแปรนั้น "อยู่นอกขอบเขต" นั่นอาจดีกว่าที่จะประกาศทุกอย่างที่ด้านบนสุดของฟังก์ชั่น
Dang_Ho

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

1
ในขณะที่ฉันยอมรับว่านี่จะเป็นวิธีที่เหมาะสมในการดำเนินงาน แต่มันไม่ใช่สิ่งที่เกิดขึ้นจริงในทางปฏิบัติ นี่คือการชุมนุมที่เกิดขึ้นจริงสำหรับบางสิ่งบางอย่างมากเช่นตัวอย่างของคุณ: godbolt.org/z/mLhE9a อย่างที่คุณเห็นในบรรทัดที่ 11 sub rsp, 1008กำลังจัดสรรพื้นที่สำหรับทั้งอาร์เรย์นอกคำสั่ง if นี้เป็นจริงสำหรับclangและgccทุกครั้งที่ผมรุ่นและการเพิ่มประสิทธิภาพระดับพยายาม
QuinnFreedman

-1

ฉันจะอ้างอิงข้อความบางส่วนจากคู่มือสำหรับ gcc รุ่น 4.7.0 สำหรับคำอธิบายที่ชัดเจน

"คอมไพเลอร์สามารถยอมรับมาตรฐานพื้นฐานหลายอย่างเช่น 'c90' หรือ 'c ++ 98' และภาษา GNU ของมาตรฐานเหล่านั้นเช่น 'gnu90' หรือ 'gnu ++ 98' โดยการระบุมาตรฐานพื้นฐานคอมไพเลอร์ จะยอมรับโปรแกรมทั้งหมดตามมาตรฐานนั้นและผู้ใช้ส่วนขยาย GNU ที่ไม่ขัดแย้งกับมันตัวอย่างเช่น '-std = c90' จะปิดคุณสมบัติบางอย่างของ GCC ที่ไม่สามารถใช้งานร่วมกับ ISO C90 เช่นคำหลัก asm และ typeof แต่ไม่ ส่วนขยาย GNU อื่น ๆ ที่ไม่มีความหมายใน ISO C90 เช่นการเว้นระยะกลางของ?: นิพจน์

ฉันคิดว่าประเด็นสำคัญของคำถามของคุณคือทำไม gcc ไม่สอดคล้องกับ C89 แม้ว่าจะใช้ตัวเลือก "-std = c89" ฉันไม่รู้รุ่น gcc ของคุณ แต่ฉันคิดว่ามันจะไม่แตกต่างกันมากนัก ผู้พัฒนา gcc แจ้งให้เราทราบว่าตัวเลือก "-std = c89" นั้นหมายถึงการปิดนามสกุลที่ขัดแย้งกับ C89 ดังนั้นจึงไม่มีอะไรเกี่ยวข้องกับส่วนขยายบางอย่างที่ไม่มีความหมายใน C89 และส่วนขยายที่ไม่ จำกัด ตำแหน่งของการประกาศตัวแปรเป็นของส่วนขยายที่ไม่ขัดแย้งกับ C89

ตามจริงแล้วทุกคนจะคิดว่ามันควรเป็นไปตาม C89 ทั้งหมดตั้งแต่แรกเห็นของตัวเลือก "-std = c89" แต่มันก็ไม่ได้ สำหรับปัญหาที่ประกาศตัวแปรทั้งหมดที่จุดเริ่มต้นดีกว่าหรือแย่กว่านั้นเป็นเพียงเรื่องของนิสัย


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

@ Marc Lehmann ใช่คุณพูดถูกเมื่อคำว่า "conform" ถูกใช้เพื่อแยกความแตกต่างของคอมไพเลอร์ แต่เมื่อคำว่า "conform" ถูกใช้เพื่ออธิบายประเพณีบางอย่างคุณสามารถพูดว่า "การใช้งานไม่เป็นไปตามมาตรฐาน" และผู้เริ่มต้นทุกคนมีความคิดเห็นว่าประเพณีที่ไม่เป็นไปตามมาตรฐานควรทำให้เกิดข้อผิดพลาด
Junwanghe

@Marc Lehmann ยังไม่มีการวินิจฉัยเมื่อ gcc เห็นการใช้งานที่ไม่เป็นไปตามมาตรฐาน C89
Junwanghe

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