ทำไม auto a = 1; รวบรวมใน C?


125

รหัส:

int main(void)
{
    auto a=1;
    return 0;
}

รวบรวมโดยไม่มีข้อผิดพลาดโดยคอมไพเลอร์ MS Visual Studio 2012 เมื่อไฟล์มีนามสกุล. c ฉันคิดมาตลอดว่าเมื่อคุณใช้นามสกุล. c การคอมไพล์ควรเป็นไปตามไวยากรณ์ C ไม่ใช่ C ++ ยิ่งไปกว่านั้นเท่าที่ฉันรู้ว่าอัตโนมัติที่ไม่มีประเภทนั้นอนุญาตให้ใช้เฉพาะใน C ++ ตั้งแต่ C ++ 11 ซึ่งหมายความว่าประเภทนั้นอนุมานจากตัวเริ่มต้น

นั่นหมายความว่าคอมไพเลอร์ของฉันไม่ยึดติดกับ C หรือรหัสถูกต้องในภาษาซีจริงหรือไม่?


8
ไม่ว่าคุณจะคอมไพล์ด้วยโหมด C ++ (เป็นไปได้) หรือ MS ยังคงติดอยู่ในพันปีที่แล้ว Implicit intถูกลบออกจากมาตรฐาน C ในปี 2542
Jens Gustedt

16
@JensGustedt MSVC ++ รองรับเฉพาะ C89 (และคุณสมบัติบางอย่างจาก C99) เป็นคอมไพเลอร์ C ++ มากกว่า
ntoskrnl

3
@Brandin: ด้วยตัวเลือก / Wall จะให้คำเตือน C4431 โดยบอกว่าไม่มีตัวระบุประเภทและ int เริ่มต้นนั้นไม่ได้รับการสนับสนุนใน C อีกต่อไป (ดูความคิดเห็นของ Jens) มันขัดแย้งกันเล็กน้อยเนื่องจากเห็นได้ชัดว่าคอมไพเลอร์นี้สนับสนุน ...
lee77

4
@JensGustedt จากการวัดนั้น GCC 4.7 ซึ่งเปิดตัวในปี 2012 (และรุ่นที่ใหม่กว่าด้วยฉันสงสัยว่า - ฉันไม่มีสิ่งเหล่านั้นอยู่ในมือ) ก็ "ติดอยู่ในพันปีที่แล้ว" เช่นกัน รวบรวมรหัสของ OP โดยไม่ต้องแจ้งให้ทราบล่วงหน้าเมื่อไม่ได้รับแฟล็กใด ๆ

3
@ เดลแนนอย่างน้อยฉันก็คิดว่า OP ได้เปิดระดับการเตือนแล้ว เห็นได้ชัดว่าฉันคิดผิด และในแง่ที่ว่านี่เป็นความจริง gcc ก็ยังคงติดอยู่ที่นั่นเนื่องจากยังไม่มี C99 (หรือตัวแปร) เป็นค่าเริ่มต้น เสียงดังเตือนเกี่ยวกับโครงสร้างแม้ว่าจะไม่มีแฟล็กก็ตาม
Jens Gustedt

คำตอบ:


240

autoคือคีย์เวิร์ด C แบบเก่าซึ่งหมายถึง "ขอบเขตเฉพาะที่" auto aเหมือนกับauto int aและเนื่องจากขอบเขตภายในเป็นค่าเริ่มต้นสำหรับตัวแปรที่ประกาศภายในฟังก์ชันจึงเหมือนกับint aในตัวอย่างนี้

คีย์เวิร์ดนี้เป็นคีย์เวิร์ดที่เหลือจาก B รุ่นก่อนหน้าซึ่งไม่มีประเภทพื้นฐาน: ทุกอย่างคือintตัวชี้ไปintอาร์เรย์ของint(*) การประกาศจะเป็นอย่างใดอย่างหนึ่งautoหรือextrn[sic] C รับค่า "ทุกอย่างเป็นint" มาเป็นกฎเริ่มต้นดังนั้นคุณสามารถประกาศจำนวนเต็มด้วย

auto a;
extern b;
static c;

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

unsigned d;  // actually unsigned int

ซึ่งยังคงมีอยู่ทั่วไปในโค้ดสมัยใหม่

C ++ 11 ใช้คีย์เวิร์ดซ้ำซึ่งมีเพียงไม่กี่คำที่โปรแกรมเมอร์ C ++ ใช้กับความหมายดั้งเดิมสำหรับการอนุมานประเภท ส่วนใหญ่ปลอดภัยเนื่องจากกฎ "ทุกอย่างเป็นint" จาก C ได้ถูกทิ้งใน C ++ 98 แล้ว สิ่งเดียวที่หยุดพักคือauto T aซึ่งไม่มีใครใช้อยู่แล้ว (บางแห่งในเอกสารของเขาเกี่ยวกับประวัติศาสตร์ของภาษา Stroustrup แสดงความคิดเห็นเกี่ยวกับเรื่องนี้ แต่ตอนนี้ฉันไม่พบข้อมูลอ้างอิงที่แน่นอน)

(*) การจัดการสตริงใน B นั้นน่าสนใจ: คุณต้องใช้อาร์เรย์intและแพ็คอักขระหลายตัวในสมาชิกแต่ละตัว B คือBCPL ที่มีไวยากรณ์ต่างกัน


7
ไม่นี่ไม่ใช่กฎหมาย C ตั้งแต่ปี 2542 ไม่มีคอมไพเลอร์ C สมัยใหม่ที่เหมาะสมอนุญาตให้ทำเช่นนี้
Jens Gustedt

18
@JensGustedt VS ไม่ได้เรียกร้องให้มีคอมไพเลอร์ C ที่ทันสมัย จากที่ปรากฏทั้งหมดการทำงานในคอมไพเลอร์ C หยุดไปเมื่อหลายปีก่อน พวกเขาจัดหาเพียงเพื่อให้ผู้ใช้สามารถรวบรวมรหัสเดิมต่อไปได้ (และแน่นอนว่าคอมไพเลอร์ C สมัยใหม่ที่เหมาะสมจะมีตัวเลือกในการรองรับรหัสเดิมรวมถึงตัวเลือกสำหรับ K&R C. )
James Kanze

23
@JensGustedt: แน่ใจเหรอ? GCC และ Clang ทั้งคู่เตือนเกี่ยวกับเรื่องนี้ในโหมด C99 แต่พวกเขาไม่ถือว่าเป็นข้อผิดพลาดยกเว้นด้วย-Werror.
Fred Foo

2
@larsman ใช่ใน 6.7.2 มีข้อ จำกัด ที่ชัดเจนสำหรับสิ่งนั้น: ต้องระบุตัวระบุประเภทอย่างน้อยหนึ่งประเภทในตัวระบุการประกาศในแต่ละการประกาศ ...
Jens Gustedt

40
@JensGustedt - re ไม่นี่ไม่ใช่กฎหมาย C ตั้งแต่ปี 2542 ไม่มีคอมไพเลอร์ C สมัยใหม่ที่เหมาะสมอนุญาตให้ทำเช่นนี้ คำสั่งแรกถูกต้อง มันผิดกฎหมายตั้งแต่ปี 2542 IMHO คำสั่งที่สองไม่ถูกต้อง คอมไพเลอร์ C สมัยใหม่ที่เหมาะสมต้องอนุญาตให้ทำเช่นนี้ ดูรหัสเดิมทั้งหมดที่จะต้องเขียนใหม่หากไม่อนุญาต ฉันได้เขียนคำตอบที่ขยายความคิดเห็นนี้แล้ว
David Hammen

35

นี่เป็นทั้งคำตอบและความคิดเห็นเพิ่มเติมสำหรับไม่นี่ไม่ใช่กฎหมาย C ตั้งแต่ปี 2542 ไม่มีคอมไพเลอร์ C สมัยใหม่ที่เหมาะสมอนุญาตให้ทำเช่นนี้

ใช่auto a=1;เป็นสิ่งผิดกฎหมายใน C1999 (และ C2011 ด้วย) เพียงเพราะตอนนี้สิ่งนี้ผิดกฎหมายไม่ได้หมายความว่าคอมไพเลอร์ C สมัยใหม่ควรปฏิเสธโค้ดที่มีโครงสร้างดังกล่าว ฉันจะเถียงในทางตรงกันข้ามว่าคอมไพเลอร์ C สมัยใหม่ที่เหมาะสมยังต้องยอมให้ทำเช่นนี้

ทั้ง clang และ gcc จะทำเช่นนั้นเมื่อรวบรวมโค้ดตัวอย่างในคำถามเทียบกับมาตรฐานเวอร์ชันปี 1999 หรือ 2011 auto int a=1;ทั้งคอมไพเลอร์ออกวินิจฉัยแล้วดำเนินการในการเป็นถ้างบไม่เหมาะสมได้รับ

ในความคิดของฉันนี่คือสิ่งที่คอมไพเลอร์ที่ดีควรทำ ด้วยการออกการวินิจฉัยเสียงดังและ gcc เป็นไปตามมาตรฐานอย่างสมบูรณ์ มาตรฐานไม่ได้บอกว่าคอมไพเลอร์ต้องปฏิเสธรหัสที่ผิดกฎหมาย มาตรฐานกล่าวเพียงว่าการใช้งานที่สอดคล้องต้องสร้างข้อความวินิจฉัยอย่างน้อยหนึ่งข้อความหากหน่วยการแปลมีการละเมิดกฎไวยากรณ์หรือข้อ จำกัด ใด ๆ (5.1.1.3)

ด้วยรหัสที่มีโครงสร้างที่ผิดกฎหมายคอมไพเลอร์ที่เหมาะสมจะพยายามทำความเข้าใจกับรหัสที่ผิดกฎหมายเพื่อให้คอมไพเลอร์สามารถค้นหาข้อผิดพลาดถัดไปในโค้ดได้ คอมไพเลอร์ที่หยุดเมื่อเกิดข้อผิดพลาดแรกไม่ใช่คอมไพเลอร์ที่ดีมาก มีวิธีที่จะทำให้เข้าใจauto a=1ได้ซึ่งก็คือใช้กฎ "implicit int" กฎนี้บังคับให้คอมไพลเลอร์ตีความauto a=1ราวกับว่าauto int a=1เมื่อคอมไพเลอร์ถูกใช้ในโหมด C90 หรือ K&R

โดยทั่วไปแล้วคอมไพเลอร์ส่วนใหญ่จะปฏิเสธรหัส (ปฏิเสธ: ปฏิเสธที่จะสร้างไฟล์อ็อบเจ็กต์หรือไฟล์ปฏิบัติการ) ที่มีไวยากรณ์ที่ผิดกฎหมาย นี่เป็นกรณีที่ผู้เขียนคอมไพเลอร์ตัดสินใจว่าการคอมไพล์ล้มเหลวไม่ใช่ทางเลือกที่ดีที่สุด สิ่งที่ดีที่สุดที่ต้องทำคือการวินิจฉัยแก้ไขรหัสและดำเนินการต่อ มีรหัสดั้งเดิมมากเกินไปที่สร้างขึ้นด้วยโครงสร้างเช่นregister a=1;. คอมไพเลอร์ควรจะคอมไพล์โค้ดนั้นในโหมด C99 หรือ C11 ได้ (แน่นอนว่าต้องมีการวินิจฉัย)


1
@larsmans - ฉันเห็นว่าคุณมาจากไหน คุณต้องการ-ffs-please-stop-allowing-constructs-from-some-previous-millenniumตัวเลือกคอมไพเลอร์หรือตัวเลือกที่รวบรัด-fstrict-complianceกว่านั้น บ่นในคอมไพเลอร์: "เมื่อฉันใช้ -std = c11 ฉันไม่ได้คาดหวังว่า K&R kruft โบราณจะรวบรวมอันที่จริงฉันต้องการให้คอมไพเลอร์ไม่ได้ !"
David Hammen

1
อันที่จริงไม่ฉันต้องการให้มีการเปิดในธงที่จะได้รับ cruft ที่เลวร้ายที่สุดในการรวบรวม แต่การ-std=c99เข้มงวดขึ้นจะเป็นการก้าวไปในทิศทางที่ถูกต้อง :)
Fred Foo

1
หากคุณใช้gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(ซึ่งเป็นสิ่งที่ฉันใช้เป็นประจำแม้กระทั่งในรหัสจากคำถามใน SO) คุณก็จะเข้าใกล้สิ่งที่คุณต้องการได้ดีพอสมควร ฉันต้องการให้ GCC ตั้งค่าเริ่มต้นเป็นอย่างน้อย-std=c99และดีกว่า-std=c11(หรือ-std=gnu11พวกเขามีแนวโน้มที่จะทำเช่นนั้นมากกว่า) แต่ถึงตอนนั้น ... คุณสามารถปรับแต่งตัวเลือกเหล่านั้นได้ -pedantic, -Wshadow, -Wold-style-declarationและคนอื่น ๆ จะมีประโยชน์ แต่นี่เป็นสิ่งที่ดีที่เริ่มต้นการตั้งค่าตัวเลือก
Jonathan Leffler

3
@DavidHammen: ความซับซ้อนของวัฏจักรผู้จัดการโครงการหรือนโยบายของ บริษัท ไม่ได้เป็นองค์ประกอบของภาษา
Jerry B

3
ธงที่จะได้รับพฤติกรรมที่คุณต้องการใน GCC คือ-pedantic-errors
τεκ

29

autoมีความหมายในCและC++ก่อนหน้ามาตรฐาน 2011 ก็หมายความว่าตัวแปรที่มีอายุการใช้งานโดยอัตโนมัติ, ที่อยู่, อายุการใช้งานที่กำหนดโดยขอบเขต ซึ่งตรงข้ามกับเช่นstaticอายุการใช้งานที่ตัวแปรคงอยู่ "ตลอดไป" โดยไม่คำนึงถึงขอบเขต autoคืออายุการใช้งานเริ่มต้นและแทบจะไม่เคยสะกดออกมาอย่างชัดเจน ด้วยเหตุนี้การเปลี่ยนความหมายจึงC++ปลอดภัย

ตอนนี้Cก่อนหน้า 99 Standard หากคุณไม่ระบุประเภทของตัวแปรค่าเริ่มต้นจะintเป็น

ดังนั้นauto a = 1;คุณกำลังประกาศ (และกำหนด) intตัวแปรโดยอายุการใช้งานจะถูกกำหนดโดยขอบเขต

("อายุการใช้งาน" เรียกถูกต้องมากกว่า "ระยะเวลาการจัดเก็บ" แต่ฉันคิดว่าอาจไม่ค่อยชัดเจน)


เอาล่ะจริงๆแล้วอนุญาตให้ a = 1 อัตโนมัติใน C และหมายถึงตัวแปร int ที่มีระยะเวลาการจัดเก็บอัตโนมัติ
lee77

1
อย่างถูกต้อง "ระยะเวลาการจัดเก็บ" จะใช้หนึ่งในรายการของค่า "อัตโนมัติ" "คงที่" "ไดนามิก" "เธรด" "อายุการใช้งาน" คือเวลาที่แท้จริงของชีวิตของวัตถุ ดังนั้นตัวแปรจึงมีระยะเวลาการจัดเก็บ "อัตโนมัติ" และอายุการใช้งาน "ระยะเวลาของขอบเขตของmainฟังก์ชัน"
Steve Jessop

@ สตีฟใช่ฉันไม่ได้ตั้งใจจะบอกเป็นนัยว่าautoและstaticเป็นไปได้สองทางเท่านั้น ฉันพยายามเขียนคำตอบในแบบที่กำหนดเป้าหมายไปที่ผู้ถามซึ่งดูเหมือนจะไม่ค่อยใหม่สำหรับC++(และC) ดังนั้นฉันจึงอธิบายรายละเอียดเล็กน้อย บางทีนั่นอาจเป็นความคิดที่ไม่ดี พวกเขาจะต้องได้รับการคุ้มครองไม่ช้าก็เร็ว
BoBTFish

1
@BoBTFish: โอ้ฉันไม่ได้บ่นเรื่องนั้น ฉันแค่ตั้งใจจะขยายความแตกต่างทางความหมายระหว่าง "อายุการใช้งาน" ซึ่งก็คือระยะเวลาและ "ระยะเวลาการจัดเก็บ" ซึ่งอาจเรียกว่า "ประเภทระยะเวลาการจัดเก็บ" ให้ถูกต้องมากกว่า
Steve Jessop

intสิ่งที่เป็นนัยนี้ถูกลบออกจาก C ตั้งแต่ปี 2542
Jens Gustedt

8

ในภาษา C และภาษาถิ่นในอดีตของ C ++ autoเป็นคำหลักที่aมีความหมายที่มีการจัดเก็บอัตโนมัติ เนื่องจากสามารถใช้ได้กับตัวแปรภายในเท่านั้นซึ่งเป็นค่าเริ่มต้นโดยอัตโนมัติจึงไม่มีใครใช้มัน ซึ่งเป็นเหตุผลว่าทำไม C ++ จึงเปลี่ยนคีย์เวิร์ดใหม่

ในอดีต C อนุญาตให้มีการประกาศตัวแปรโดยไม่มีตัวระบุประเภท intเริ่มต้นชนิด ดังนั้นคำประกาศนี้จึงเทียบเท่ากับ

int a=1;

ฉันคิดว่าสิ่งนี้เลิกใช้แล้ว (และอาจเป็นสิ่งต้องห้าม) ใน C สมัยใหม่ แต่คอมไพเลอร์ยอดนิยมบางตัวเริ่มต้นเป็น C90 (ซึ่งฉันคิดว่าอนุญาต) และเปิดใช้งานคำเตือนเฉพาะเมื่อคุณขอโดยเฉพาะ รวบรวมด้วย GCC และระบุ C99 ด้วย-std=c99หรือเปิดใช้งานคำเตือนโดยมี-Wallหรือ-Wimplicit-intให้คำเตือน:

warning: type defaults to int in declaration of a

4
เป็นสิ่งต้องห้ามใน C ตั้งแต่ปี 2542
Jens Gustedt

5

ในภาษา C autoหมายถึงสิ่งเดียวกันregisterใน C ++ 11 นั่นหมายความว่าตัวแปรมีระยะเวลาการจัดเก็บอัตโนมัติ

และใน C ก่อนที่จะ C99 (และคอมไพเลอร์ไมโครซอฟท์ไม่สนับสนุนการอย่างใดอย่างหนึ่งหรือ C99 C11 แม้ว่ามันอาจสนับสนุนบางส่วนของมัน), ชนิดสามารถถูกมองข้ามในหลาย ๆ intกรณีที่มันจะเริ่มต้น

มันไม่ได้ใช้ประเภทจากตัวเริ่มต้นเลย คุณเพิ่งบังเอิญเลือกผู้เริ่มต้นที่เข้ากันได้


1
คีย์เวิร์ด register ไม่เลิกใช้งานใน C ++ 11 ใช่หรือไม่
เลวร้าย

@sordid ใช่แล้ว ก่อนหน้า C ++ 11 autoและregisterมีความหมายเหมือนกันทุกประการ (ก่อนหน้านี้ฉันให้ความเห็นว่ามีข้อ จำกัด ในการรับที่registerอยู่ของตัวแปรที่มีคุณสมบัติเหมาะสม แต่ C ++ นั้นไม่ถูกต้อง)registerแม้ว่าจะเลิกใช้แล้ว แต่ก็ยังคงความหมายเดิมไว้ในตอนนี้

5
@JensGustedt: คำตอบไม่ได้บอกว่าพวกเขาเป็น มันบอกว่าautoใน C หมายถึงเช่นเดียวกับregisterใน C ++ ซึ่งเป็นเช่นนั้น (ทั้งหมายถึงระยะเวลาการจัดเก็บอัตโนมัติและไม่มีอะไรอื่น)
Mike Seymour

3

right click on file -> Properties -> C/C++ -> Advanced -> Compile Asภาพสตูดิโอประเภทสะสมที่มีอยู่ใน เพื่อให้แน่ใจว่าคอมไพล์เป็นตัวเลือก C force แล้ว/TCในกรณีนี้คือสิ่งที่ larsmans พูด ( autoคีย์เวิร์ดC เก่า) อาจรวบรวมเป็น C ++ โดยที่คุณไม่รู้


3

คลาสหน่วยเก็บข้อมูลกำหนดขอบเขต (การมองเห็น) และอายุการใช้งานของตัวแปรและ / หรือฟังก์ชันภายในโปรแกรม C

มีคลาสพื้นที่เก็บข้อมูลต่อไปนี้ซึ่งสามารถใช้ในโปรแกรม C

auto
register
static
extern

auto เป็นคลาสพื้นที่จัดเก็บเริ่มต้นสำหรับตัวแปรโลคัลทั้งหมด

{
        int Count;
        auto int Month;
}

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

intเป็นประเภทเริ่มต้นสำหรับautoโค้ดด้านล่าง:

auto Month;
/* Equals to */
int Month;

ด้านล่างรหัสถูกกฎหมายเช่นกัน:

/* Default-int */
main()
{
    reurn 0;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.