เหตุใดจึงประกาศตัวแปรในหนึ่งบรรทัดและกำหนดให้กับตัวแปรในบรรทัดถัดไป


101

ฉันมักจะเห็นในรหัส C และ C ++ การประชุมต่อไปนี้:

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;

แทน

some_type val = something;
some_type *ptr = &something_else;

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


12
+1 สำหรับ "ฉันเรียนรู้ที่จะไม่เลิกนิสัยนักพัฒนามืออาชีพอย่างรวดเร็ว" นั่นเป็นบทเรียนที่ฉลาดในการเรียนรู้
Wildcard

คำตอบ:


92

C

ใน C89 การประกาศทั้งหมดจะต้องอยู่ที่จุดเริ่มต้นของขอบเขต ( { ... }) แต่ข้อกำหนดนี้จะลดลงอย่างรวดเร็ว (ก่อนด้วยส่วนขยายคอมไพเลอร์และต่อมาด้วยมาตรฐาน)

C ++

ตัวอย่างเหล่านี้ไม่เหมือนกัน some_type val = something;เรียกตัวสร้างสำเนาในขณะที่val = something;เรียกตัวสร้างเริ่มต้นแล้วoperator=ฟังก์ชั่น ความแตกต่างนี้มักจะสำคัญ

นิสัย

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

เกี่ยวกับพอยน์เตอร์บางคนมีนิสัยเริ่มต้นตัวชี้ทุกตัวNULLหรือnullptrไม่ว่าพวกเขาจะทำอะไรกับตัวชี้นั้น


1
ความแตกต่างที่ยอดเยี่ยมสำหรับ C ++ ขอบคุณ แล้วใน C ธรรมดาล่ะ
Jonathan Sterling

13
ความจริงที่ว่า MSVC ยังไม่รองรับการประกาศยกเว้นที่จุดเริ่มต้นของบล็อกเมื่อมันรวบรวมในโหมด C เป็นแหล่งที่มาของการระคายเคืองไม่รู้จบสำหรับฉัน
Michael Burr

5
@Michael Burr: นี่เป็นเพราะ MSVC ไม่รองรับ C99 เลย
orlp

3
"some_type val = something; เรียกตัวสร้างการคัดลอก": มันอาจเรียกตัวสร้างการคัดลอก แต่มาตรฐานอนุญาตให้คอมไพเลอร์สามารถตัดการเริ่มต้นของ tempory, การสร้างสำเนาของ val และการทำลายชั่วคราวและสร้าง val โดยตรงโดยใช้some_typeคอนสตรัคที่ใช้somethingเป็นอาร์กิวเมนต์เพียงอย่างเดียว นี่คือกรณีขอบที่น่าสนใจและผิดปกติใน C ++ ... หมายความว่ามีข้อสันนิษฐานเกี่ยวกับความหมายเชิงความหมายของการดำเนินการเหล่านี้

2
@Aerovistae: สำหรับประเภทในตัวพวกเขาเหมือนกัน แต่ไม่สามารถพูดได้เหมือนกันสำหรับประเภทที่ผู้ใช้กำหนด
orlp

27

คุณติดแท็กคำถาม C และ C ++ พร้อมกันในขณะที่คำตอบนั้นแตกต่างกันอย่างมากในภาษาเหล่านี้

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

ประการที่สองในภาษา C ++ ตามที่ @nightcracker บันทึกไว้ในคำตอบของเขาทั้งสองโครงสร้างนี้มีความแตกต่างทางความหมาย คนแรกอาศัยการเริ่มต้นในขณะที่คนที่สอง - ตามที่ได้รับมอบหมาย ใน C ++ การดำเนินการเหล่านี้สามารถโหลดได้มากเกินไปและอาจนำไปสู่ผลลัพธ์ที่แตกต่างกันได้ (แม้ว่าเราจะสามารถทราบได้ว่าการสร้างการเริ่มต้นและการกำหนดค่าเกินพิกัดที่ไม่เทียบเท่ากันนั้นไม่ใช่ความคิดที่ดี)

ในภาษา C มาตรฐานดั้งเดิม (C89 / 90) มันผิดกฎหมายในการประกาศตัวแปรที่อยู่ตรงกลางของบล็อกซึ่งเป็นสาเหตุที่คุณอาจเห็นตัวแปรที่ประกาศไว้โดยไม่กำหนดค่าเริ่มต้น (หรือเริ่มต้นด้วยค่าดัมมี่) ที่จุดเริ่มต้นของบล็อกแล้วกำหนดความหมาย ค่าในภายหลังเมื่อค่าที่มีความหมายเหล่านั้นพร้อมใช้งาน

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


2
@Jonathan Sterling: ฉันอ่านตัวอย่างของคุณ คุณอาจจำเป็นต้องเข้าใจถึงคำศัพท์มาตรฐานของภาษา C และ C ++ โดยเฉพาะอย่างยิ่งในการประกาศเงื่อนไขและคำนิยามซึ่งมีความหมายเฉพาะในภาษาเหล่านี้ ฉันจะทำซ้ำอีกครั้ง: ในตัวอย่างของคุณทั้งสองตัวแปรจะถูกประกาศและกำหนดไว้ในหนึ่งบรรทัด ใน C / C ++ บรรทัดsome_type val;จะประกาศและกำหนดตัวแปรvalทันที นี่คือสิ่งที่ฉันต้องการในคำตอบของฉัน

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

1
ดังนั้นหากฉันทามติคือ "ประกาศ" เป็นคำที่ผิดฉันขอแนะนำให้คนที่มีความรู้มาตรฐานดีกว่าฉันแก้ไขหน้า Wikibooks
Jonathan Sterling

2
ในบริบทอื่น ๆ การประกาศจะเป็นคำที่ถูกต้อง แต่เนื่องจากการประกาศเป็นแนวคิดที่กำหนดไว้อย่างดีด้วยผลที่ตามมาใน C และ C ++ คุณไม่สามารถใช้มันได้อย่างอิสระเท่าที่คุณจะทำได้ในบริบทอื่น ๆ
orlp

2
@ybungalobill: คุณผิด การประกาศและคำนิยามใน C / C ++ ไม่ใช่แนวคิดที่ไม่เกิดร่วมกัน อันที่จริงความหมายเป็นเพียงรูปแบบเฉพาะของการประกาศ ทุกคำนิยามเป็นการประกาศในเวลาเดียวกัน (มีข้อยกเว้นเล็กน้อย) มีการกำหนดประกาศ (เช่นคำจำกัดความ) และการประกาศที่ไม่ได้กำหนด ยิ่งไปกว่านั้นโดยปกติแล้วการประกาศอุณหภูมิจะใช้ตลอดเวลา (แม้ว่าจะเป็นคำจำกัดความ) ยกเว้นบริบทเมื่อความแตกต่างระหว่างทั้งสองมีความสำคัญ

13

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


4

ฉันพูดอะไรบางอย่างเกี่ยวกับว่าในคำตอบของฉันที่จะแก้ไขปัญหาโดย Helium3

โดยพื้นฐานแล้วฉันว่ามันช่วยให้มองเห็นได้ง่ายว่ามีอะไรเปลี่ยนแปลง

if (a == 0) {
    struct whatever *myobject = 0;
    /* did `myobject` (the pointer) get assigned?
    ** or was it `*myobject` (the struct)? */
}

และ

if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* `myobject` (the pointer) got assigned */
}

4

คำตอบอื่น ๆ ค่อนข้างดี มีประวัติบางอย่างเกี่ยวกับเรื่องนี้ใน C ใน C ++ มีความแตกต่างระหว่างตัวสร้างและผู้ดำเนินการที่ได้รับมอบหมาย

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

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

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

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


2

ใน C นี่คือมาตรฐานการปฏิบัติเพราะตัวแปรจะต้องมีการประกาศที่จุดเริ่มต้นของฟังก์ชั่นซึ่งแตกต่างจาก C ++ ซึ่งมันสามารถประกาศได้ทุกที่ในร่างกายของฟังก์ชั่นที่จะใช้หลังจากนั้น ตัวชี้ถูกตั้งค่าเป็น 0 หรือ NULL เนื่องจากเพิ่งทำให้แน่ใจว่าตัวชี้ชี้ไปที่ขยะ ไม่อย่างนั้นไม่มีข้อได้เปรียบที่สำคัญที่ฉันสามารถคิดได้ซึ่งบังคับให้ทุกคนทำอย่างนั้น


2

ข้อดีสำหรับการกำหนดตัวแปรท้องถิ่นและการเริ่มต้นมีความหมาย:

  • หากตัวแปรที่ได้รับมอบหมายเป็นปกติวิสัยค่ามีความหมายเมื่อพวกเขาปรากฏตัวครั้งแรกในรหัส (มุมมองอื่นในสิ่งเดียวกัน: คุณประวิงลักษณะของพวกเขาจนกว่าจะมีค่ามีความหมายไปอีก) แล้วมีโอกาสที่จะไม่ให้พวกเขาตั้งใจที่จะถูกใช้กับค่าความหมายหรือ uninitialised ( ซึ่งสามารถเกิดขึ้นได้ง่ายคือการเริ่มต้นบางอย่างจะถูกข้ามโดยไม่ตั้งใจเนื่องจากคำสั่งเงื่อนไขการประเมินผลการลัดวงจรข้อยกเว้น ฯลฯ )

  • สามารถมีประสิทธิภาพมากขึ้น

    • หลีกเลี่ยงค่าโสหุ้ยในการตั้งค่าเริ่มต้น (การสร้างเริ่มต้นหรือการตั้งค่าเริ่มต้นให้กับค่า Sentinel บางค่าเช่น NULL)
    • operator= บางครั้งอาจมีประสิทธิภาพน้อยลงและต้องการวัตถุชั่วคราว
    • บางครั้ง (โดยเฉพาะสำหรับฟังก์ชั่นอินไลน์) เครื่องมือเพิ่มประสิทธิภาพสามารถลบบางส่วน / ไร้ประสิทธิภาพทั้งหมด

  • การลดขอบเขตของตัวแปรให้น้อยที่สุดจะลดจำนวนตัวแปรโดยเฉลี่ยในขอบเขตพร้อมกัน : สิ่งนี้

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

  • จำเป็นสำหรับบางประเภทเช่นการอ้างอิงและเมื่อคุณต้องการให้วัตถุเป็น const

อาร์กิวเมนต์สำหรับการนิยามกลุ่มตัวแปร:

  • บางครั้งมันสะดวกและ / หรือสั้นกระชับในการแยกประเภทของตัวแปร:

    the_same_type v1, v2, v3;

    (หากเหตุผลเป็นเพียงชื่อประเภทยาวเกินไปหรือซับซ้อนtypedefบางครั้งอาจดีกว่า)

  • บางครั้งการจัดกลุ่มตัวแปรเป็นอิสระจากการใช้เพื่อเน้นชุดของตัวแปร (และประเภท) ที่เกี่ยวข้องในการดำเนินการบางอย่าง:

    type v1;
    type v2; type v3;

    สิ่งนี้เน้นความธรรมดาสามัญของประเภทและทำให้ง่ายต่อการเปลี่ยนแปลงเล็กน้อยในขณะที่ยังคงติดกับตัวแปรต่อบรรทัดซึ่งอำนวยความสะดวกในการคัดลอกวาง//แสดงความคิดเห็น ฯลฯ

มักจะเป็นกรณีของการเขียนโปรแกรมในขณะที่อาจมีผลประโยชน์เชิงประจักษ์ที่ชัดเจนกับการฝึกฝนหนึ่งในสถานการณ์ส่วนใหญ่การปฏิบัติอื่น ๆ อาจดีขึ้นอย่างท่วมท้นในบางกรณี


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