ความสับสนเกี่ยวกับการเริ่มต้นอาร์เรย์ใน C


102

ในภาษา C หากเริ่มต้นอาร์เรย์เช่นนี้:

int a[5] = {1,2};

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

แต่ถ้าฉันเริ่มต้นอาร์เรย์เช่นนี้:

int a[5]={a[2]=1};

printf("%d %d %d %d %d\n", a[0], a[1],a[2], a[3], a[4]);

เอาต์พุต:

1 0 1 0 0

ฉันไม่เข้าใจทำไมa[0]พิมพ์1แทนที่จะเป็น0? เป็นพฤติกรรมที่ไม่ได้กำหนดหรือไม่?

หมายเหตุ:คำถามนี้ถูกถามในการสัมภาษณ์


35
การแสดงออกประเมินa[2]=1 1
tkausl

14
คำถามที่ลึกซึ้งมาก ฉันสงสัยว่าผู้สัมภาษณ์รู้คำตอบเองหรือไม่ ฉันไม่. แน่นอนว่าค่าของนิพจน์a[2] = 1คือ1แต่ฉันไม่แน่ใจว่าคุณได้รับอนุญาตให้ใช้ผลลัพธ์ของนิพจน์ initialiser ที่กำหนดเป็นค่าขององค์ประกอบแรกหรือไม่ การที่คุณเพิ่มแท็กทนายความหมายความว่าฉันคิดว่าเราต้องการคำตอบที่อ้างถึงมาตรฐาน
Bathsheba

15
ถ้านั่นเป็นคำถามโปรดของพวกเขาคุณอาจจะหลบกระสุนได้ โดยส่วนตัวแล้วฉันชอบแบบฝึกหัดการเขียนโปรแกรม (ด้วยการเข้าถึงคอมไพเลอร์และดีบักเกอร์) ที่ต้องใช้เวลาไม่กี่ชั่วโมงมากกว่าคำถามสไตล์ "เอซ" เช่นข้างต้น ฉันสามารถคาดเดาคำตอบได้ แต่ฉันไม่คิดว่ามันจะมีพื้นฐานที่เป็นข้อเท็จจริงจริงๆ
Bathsheba

1
@ บา ธ เชบาฉันจะทำตรงกันข้ามเพราะตอนนี้คำตอบที่นี่ตอบคำถามทั้งสองข้อ
Goodbye SE

1
@ บา ธ เชบาจะดีที่สุด ฉันยังคงให้เครดิตสำหรับคำถามแก่ OP ในขณะที่เขามากับหัวข้อ แต่นี่ไม่ใช่สำหรับฉันที่จะตัดสินว่าสิ่งที่ฉันรู้สึกว่าเป็น "สิ่งที่ถูกต้อง"
Goodbye SE

คำตอบ:


95

TL; DR: ฉันไม่คิดว่าพฤติกรรมของ int a[5]={a[2]=1};ถูกกำหนดไว้อย่างดีอย่างน้อยก็ใน C99

ส่วนที่ตลกคือสิ่งเดียวที่สมเหตุสมผลสำหรับฉันคือส่วนที่คุณถามถึง: a[0]ถูกตั้งค่าเป็น1เพราะตัวดำเนินการกำหนดส่งคืนค่าที่กำหนดให้ มันเป็นอย่างอื่นที่ไม่ชัดเจน

ถ้ารหัสที่ได้รับint a[5] = { [2] = 1 }ทุกอย่างที่จะได้รับง่าย: นั่นคือกำหนดค่าเริ่มต้นการตั้งค่าa[2]ไปและทุกอย่างอื่นไป1 0แต่ด้วย{ a[2] = 1 }เรามีตัวเริ่มต้นที่ไม่ได้กำหนดไว้ซึ่งมีนิพจน์มอบหมายและเราก็ตกลงไปในโพรงกระต่าย


นี่คือสิ่งที่ฉันพบจนถึงตอนนี้:

  • a ต้องเป็นตัวแปรท้องถิ่น

    6.7.8 การเริ่มต้น

    1. นิพจน์ทั้งหมดในตัวเริ่มต้นสำหรับอ็อบเจ็กต์ที่มีระยะเวลาการจัดเก็บแบบคงที่จะต้องเป็นนิพจน์คงที่หรือสตริงลิเทอรัล

    a[2] = 1ไม่ใช่นิพจน์คงที่ดังนั้นaต้องมีการจัดเก็บอัตโนมัติ

  • a อยู่ในขอบเขตในการเริ่มต้นของตัวเอง

    6.2.1 ขอบเขตของตัวระบุ

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

    ผู้ประกาศคือa[5]ตัวแปรจึงอยู่ในขอบเขตในการเริ่มต้นของตนเอง

  • a มีชีวิตอยู่ในการเริ่มต้นของตัวเอง

    6.2.4 ระยะเวลาในการจัดเก็บวัตถุ

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

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

  • มีจุดลำดับตามหลังa[2]=1.

    6.8 งบและบล็อก

    1. แสดงออกเต็มรูปแบบคือการแสดงออกที่ไม่ได้เป็นส่วนหนึ่งของการแสดงออกอื่นหรือของ declarator แต่ละรายการต่อไปนี้เป็นนิพจน์แบบเต็ม: initializer ; นิพจน์ในคำสั่งนิพจน์ นิพจน์ควบคุมของคำสั่งการเลือก ( ifหรือswitch); นิพจน์ควบคุมของ a whileหรือdoคำสั่ง; แต่ละนิพจน์ (ทางเลือก) ของforคำสั่ง นิพจน์ (เป็นทางเลือก) ในreturnคำสั่ง จุดสิ้นสุดของนิพจน์เต็มคือจุดลำดับ

    โปรดทราบว่าเช่นในส่วนหนึ่งเป็นรายการรั้งปิดล้อมของ initializers ซึ่งแต่ละจุดมีลำดับหลังจากที่มันint foo[] = { 1, 2, 3 }{ 1, 2, 3 }

  • การเริ่มต้นจะดำเนินการตามลำดับรายการตัวเริ่มต้น

    6.7.8 การเริ่มต้น

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

     

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

    6.7.8 การเริ่มต้น

    1. ลำดับที่ผลข้างเคียงใด ๆ เกิดขึ้นระหว่างนิพจน์รายการเริ่มต้นไม่ได้ระบุไว้

อย่างไรก็ตามยังคงมีคำถามบางอย่างที่ยังไม่มีคำตอบ

  • จุดลำดับมีความเกี่ยวข้องกันหรือไม่? กฎพื้นฐานคือ:

    6.5 นิพจน์

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

    a[2] = 1 เป็นนิพจน์ แต่การเริ่มต้นไม่ใช่

    สิ่งนี้ขัดแย้งเล็กน้อยกับภาคผนวก J:

    J.2 พฤติกรรมที่ไม่ได้กำหนด

    • ระหว่างจุดลำดับสองจุดวัตถุจะถูกแก้ไขมากกว่าหนึ่งครั้งหรือถูกแก้ไขและค่าก่อนหน้าจะถูกอ่านนอกเหนือจากการกำหนดค่าที่จะจัดเก็บ (6.5)

    ภาคผนวก J กล่าวว่าการปรับเปลี่ยนใด ๆ มีค่าไม่ใช่แค่การปรับเปลี่ยนด้วยนิพจน์ แต่เนื่องจากภาคผนวกนั้นไม่ใช่กฎเกณฑ์เราจึงอาจเพิกเฉยต่อสิ่งนั้นได้

  • การเริ่มต้นอ็อบเจ็กต์ย่อยเรียงลำดับตามนิพจน์ initializer อย่างไร ตัวเริ่มต้นทั้งหมดได้รับการประเมินก่อน (ตามลำดับ) จากนั้นวัตถุย่อยจะเริ่มต้นด้วยผลลัพธ์ (ตามลำดับรายการตัวเริ่มต้น) หรือไม่ หรือสามารถสอดแทรกได้?


ฉันคิดว่าint a[5] = { a[2] = 1 }ดำเนินการดังนี้:

  1. ที่เก็บข้อมูลสำหรับaจะถูกจัดสรรเมื่อป้อนบล็อกที่มี เนื้อหายังไม่แน่นอน ณ จุดนี้
  2. initializer (เท่านั้น) ถูกเรียกใช้งาน ( a[2] = 1) ตามด้วยจุดลำดับ ร้านค้านี้1ในและผลตอบแทนa[2]1
  3. ที่1ใช้ในการเตรียมใช้งานa[0](ตัวเริ่มต้นตัวแรกเริ่มต้นวัตถุย่อยแรก)

แต่ที่นี่สิ่งที่ได้รับเลือนเพราะองค์ประกอบที่เหลือ ( a[1], a[2], a[3], a[4]) ที่ควรจะเริ่มต้นได้0แต่มันไม่ชัดเจนเมื่อ: มันไม่ได้เกิดขึ้นก่อนที่a[2] = 1จะถูกประเมิน? ถ้าเป็นเช่นนั้นa[2] = 1จะ "ชนะ" และเขียนทับa[2]แต่การมอบหมายนั้นจะมีลักษณะการทำงานที่ไม่ได้กำหนดหรือไม่เนื่องจากไม่มีจุดลำดับระหว่างการเริ่มต้นเป็นศูนย์และนิพจน์การมอบหมาย จุดลำดับมีความเกี่ยวข้องกันหรือไม่ (ดูด้านบน) หรือการเริ่มต้นเป็นศูนย์เกิดขึ้นหลังจากประเมินค่าเริ่มต้นทั้งหมดแล้ว? ถ้าเป็นเช่นนั้นa[2]ควรจบลงด้วยการเป็น0ควรจะจบลงด้วยการ

เนื่องจากมาตรฐาน C ไม่ได้กำหนดอย่างชัดเจนว่าเกิดอะไรขึ้นที่นี่ฉันเชื่อว่าพฤติกรรมนั้นไม่ได้กำหนดไว้ (โดยการละเว้น)


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

1
"เราตกลงไปในโพรงกระต่าย" ฮ่า ๆ ! ไม่เคยได้ยินว่าสำหรับ UB หรือสิ่งที่ไม่ระบุ
BЈовић

2
@Someprogrammerdude ฉันไม่คิดว่าจะไม่สามารถระบุได้ (" พฤติกรรมที่มาตรฐานสากลนี้ให้ความเป็นไปได้สองอย่างขึ้นไปและไม่มีข้อกำหนดเพิ่มเติมว่าจะเลือกในกรณีใด ") เนื่องจากมาตรฐานไม่ได้ให้ความเป็นไปได้ใด ๆ เลือก. มันไม่ได้บอกว่าเกิดอะไรขึ้นซึ่งฉันเชื่อว่าอยู่ภายใต้ " พฤติกรรมที่ไม่ได้กำหนด [... ] ที่ระบุไว้ในมาตรฐานสากลนี้ [... ] โดยการละเว้นนิยามพฤติกรรมใด ๆ ที่ชัดเจน "
melpomene

2
@ BЈовићนอกจากนี้ยังเป็นคำอธิบายที่ดีมากไม่เพียง แต่สำหรับพฤติกรรมที่ไม่ได้กำหนดเท่านั้น แต่ยังรวมถึงพฤติกรรมที่กำหนดซึ่งต้องการเธรดเช่นนี้เพื่ออธิบาย
gnasher729

1
@JohnBollinger ความแตกต่างคือคุณไม่สามารถเริ่มต้นa[0]วัตถุย่อยได้จริงก่อนที่จะประเมินค่าเริ่มต้นและการประเมินค่าเริ่มต้นใด ๆ จะมีจุดลำดับด้วย (เนื่องจากเป็น "นิพจน์เต็ม") ดังนั้นฉันเชื่อว่าการปรับเปลี่ยนวัตถุย่อยที่เรากำลังเริ่มต้นนั้นเป็นเกมที่ยุติธรรม
melpomene

22

ฉันไม่เข้าใจทำไมa[0]พิมพ์1แทนที่จะเป็น0?

สันนิษฐานว่าa[2]=1เริ่มต้นa[2]ก่อนและผลลัพธ์ของนิพจน์ถูกใช้เพื่อเริ่มต้นa[0]ครั้งแรกและผลของการแสดงออกที่ใช้ในการเริ่มต้น

จาก N2176 (ร่าง C17):

6.7.9 การเริ่มต้น

  1. การประเมินนิพจน์รายการการเริ่มต้นจะถูกจัดลำดับอย่างไม่แน่นอนซึ่งเกี่ยวเนื่องกันดังนั้นจึงไม่ได้ระบุลำดับที่ผลข้างเคียงใด ๆ เกิดขึ้น 154)

ดังนั้นมันจะดูเหมือนว่าผลลัพธ์ 1 0 0 0 0จะเป็นไปได้เช่นกัน

สรุป: อย่าเขียน initializers ที่ปรับเปลี่ยนตัวแปรเริ่มต้นได้ทันที


1
ส่วนนั้นใช้ไม่ได้: มีนิพจน์ initializer เพียงตัวเดียวที่นี่ดังนั้นจึงไม่จำเป็นต้องจัดลำดับด้วยอะไรเลย
melpomene

@melpomene มีคือ{...}การแสดงออกซึ่งเริ่มต้นa[2]ไป0และa[2]=1ย่อยแสดงออกซึ่งเริ่มต้นในการa[2] 1
user694733

1
{...}คือรายการเริ่มต้นวงเล็บปีกกา มันไม่ใช่การแสดงออก
melpomene

@melpomene โอเคคุณอาจจะอยู่ที่นั่น แต่ฉันยังคงเถียงว่ายังมีผลข้างเคียงที่แข่งขันกัน 2 รายการเพื่อให้ย่อหน้านั้นยืนขึ้น
user694733

@melpomene มีสองสิ่งที่จะจัดลำดับ: ตัวเริ่มต้นตัวแรกและการตั้งค่าองค์ประกอบอื่น ๆ เป็น 0
MM

6

ฉันคิดว่ามาตรฐาน C11 ครอบคลุมพฤติกรรมนี้และบอกว่าผลลัพธ์นั้นไม่ได้ระบุไว้และฉันไม่คิดว่า C18 ทำการเปลี่ยนแปลงใด ๆ ที่เกี่ยวข้องในพื้นที่นี้

ภาษามาตรฐานไม่ใช่เรื่องง่ายที่จะแยกวิเคราะห์ ส่วนที่เกี่ยวข้องของมาตรฐานคือ §6.7.9การเริ่มต้น ไวยากรณ์จัดทำเป็นเอกสาร:

initializer:
                assignment-expression
                { initializer-list }
                { initializer-list , }
initializer-list:
                designationopt initializer
                initializer-list , designationopt initializer
designation:
                designator-list =
designator-list:
                designator
                designator-list designator
designator:
                [ constant-expression ]
                . identifier

โปรดทราบว่าหนึ่งในเงื่อนไขคือนิพจน์การกำหนดและเนื่องจากa[2] = 1เป็นนิพจน์การกำหนดโดยไม่ต้องสงสัยจึงอนุญาตให้อยู่ในตัวเริ่มต้นสำหรับอาร์เรย์ที่มีระยะเวลาไม่คงที่:

§4นิพจน์ทั้งหมดในตัวเริ่มต้นสำหรับอ็อบเจ็กต์ที่มีระยะเวลาการจัดเก็บแบบคงที่หรือเธรดต้องเป็นนิพจน์คงที่หรือตัวอักษรสตริง

หนึ่งในย่อหน้าสำคัญคือ:

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

151) ตัวเริ่มต้นใด ๆ สำหรับวัตถุย่อยที่ถูกแทนที่และไม่ได้ใช้เพื่อเริ่มต้นวัตถุย่อยนั้นอาจไม่ได้รับการประเมินเลย

และอีกย่อหน้าสำคัญคือ:

§23การประเมินนิพจน์รายการการเริ่มต้นจะถูกจัดลำดับอย่างไม่แน่นอนซึ่งเกี่ยวเนื่องกันดังนั้นจึงไม่ระบุลำดับที่ผลข้างเคียงใด ๆ เกิดขึ้น 152)

152)โดยเฉพาะอย่างยิ่งลำดับการประเมินไม่จำเป็นต้องเหมือนกับลำดับของการเริ่มต้นวัตถุย่อย

ฉันค่อนข้างแน่ใจว่าย่อหน้า§23ระบุว่าสัญกรณ์ในคำถาม:

int a[5] = { a[2] = 1 };

นำไปสู่พฤติกรรมที่ไม่ระบุรายละเอียด การมอบหมายให้a[2]เป็นผลข้างเคียงและลำดับการประเมินของนิพจน์จะเรียงตามลำดับอย่างไม่แน่นอนซึ่งเกี่ยวเนื่องกัน ดังนั้นฉันไม่คิดว่าจะมีวิธีอุทธรณ์มาตรฐานและอ้างว่าคอมไพเลอร์เฉพาะจัดการสิ่งนี้อย่างถูกต้องหรือไม่ถูกต้อง


มีนิพจน์รายการเริ่มต้นเพียงรายการเดียวดังนั้น§23จึงไม่เกี่ยวข้อง
melpomene

2

ความเข้าใจของฉัน a[2]=1ส่งคืนค่า1ดังนั้นรหัสจึงกลายเป็น

int a[5]={a[2]=1} --> int a[5]={1}

int a[5]={1}กำหนดค่าสำหรับ [0] = 1

ดังนั้นจึงพิมพ์1สำหรับ[0]

ตัวอย่างเช่น

char str[10]={‘H’,‘a’,‘i’};


char str[0] = H’;
char str[1] = a’;
char str[2] = i;

2
นี่เป็นคำถาม [ภาษา - ทนายความ] แต่นี่ไม่ใช่คำตอบที่ใช้ได้กับมาตรฐานจึงทำให้ไม่เกี่ยวข้อง นอกจากนี้ยังมีคำตอบเชิงลึกอีก 2 คำตอบและคำตอบของคุณดูเหมือนจะไม่เพิ่มอะไรเลย
Goodbye SE

ฉันมีข้อสงสัยเป็นแนวคิดที่ฉันโพสต์ผิดหรือไม่คุณช่วยชี้แจงฉันด้วยสิ่งนี้ได้ไหม
Karthika

1
คุณแค่คาดเดาด้วยเหตุผลในขณะที่มีคำตอบที่ดีมากอยู่แล้วในส่วนที่เกี่ยวข้องของมาตรฐาน แค่บอกว่ามันเกิดขึ้นได้อย่างไรไม่ใช่คำถามเกี่ยวกับ เป็นเรื่องที่มาตรฐานระบุว่าควรเกิดขึ้น
Goodbye SE

แต่คนที่โพสต์ข้างบนถามเหตุผลแล้วทำไมมันถึงเกิดขึ้น? มีเพียงฉันเท่านั้นที่ตอบคำตอบนี้ไป แต่แนวคิดถูกต้องใช่ไหม
Karthika

OP ถามว่า " เป็นพฤติกรรมที่ไม่ได้กำหนดหรือไม่ " คำตอบของคุณไม่ได้พูด
melpomene

1

ฉันพยายามให้คำตอบสั้น ๆ และง่ายสำหรับปริศนา: int a[5] = { a[2] = 1 };

  1. ขั้นแรกa[2] = 1ถูกตั้งค่า นั่นหมายความว่าอาร์เรย์กล่าวว่า:0 0 1 0 0
  2. แต่ดูเถิดกำหนดว่าคุณไม่ได้อยู่ใน{ }วงเล็บซึ่งจะใช้ในการเริ่มต้นอาร์เรย์ในการสั่งซื้อก็จะใช้เวลาค่าแรก (ซึ่งเป็น1) a[0]และชุดที่ มันเป็นถ้าจะยังคงอยู่ที่เรามีอยู่แล้วint a[5] = { a[2] }; a[2] = 1อาร์เรย์ผลลัพธ์ตอนนี้:1 0 1 0 0

อีกตัวอย่างหนึ่ง: int a[6] = { a[3] = 1, a[4] = 2, a[5] = 3 };- แม้ว่าคำสั่งจะค่อนข้างเป็นไปตามอำเภอใจ แต่สมมติว่าเรียงจากซ้ายไปขวา แต่จะดำเนินการใน 6 ขั้นตอนต่อไปนี้:

0 0 0 1 0 0
1 0 0 1 0 0
1 0 0 1 2 0
1 2 0 1 2 0
1 2 0 1 2 3
1 2 3 1 2 3

1
A = B = C = 5ไม่ใช่การประกาศ (หรือการเริ่มต้น) มันเป็นนิพจน์ปกติที่แยกวิเคราะห์A = (B = (C = 5))เนื่องจากตัว=ดำเนินการเชื่อมโยงได้ถูกต้อง นั่นไม่ได้ช่วยอธิบายว่าการเริ่มต้นทำงานอย่างไร อาร์เรย์เริ่มต้นที่มีอยู่จริงเมื่อมีการป้อนบล็อกซึ่งอาจใช้เวลานานก่อนที่จะเรียกใช้นิยามจริง
melpomene

1
" จากซ้ายไปขวาแต่ละรายการเริ่มต้นด้วยการประกาศภายใน " ไม่ถูกต้อง มาตรฐาน C กล่าวอย่างชัดเจนว่า " ลำดับที่ผลข้างเคียงใด ๆ เกิดขึ้นระหว่างนิพจน์รายการการเริ่มต้นไม่ได้ระบุไว้ "
melpomene

1
" คุณทดสอบโค้ดจากตัวอย่างของฉันหลายครั้งเพียงพอและดูว่าผลลัพธ์สอดคล้องกันหรือไม่ " นั่นไม่ใช่วิธีการทำงาน ดูเหมือนคุณจะไม่เข้าใจว่าพฤติกรรมที่ไม่ได้กำหนดคืออะไร ทุกอย่างใน C มีพฤติกรรมที่ไม่ได้กำหนดโดยค่าเริ่มต้น มีเพียงบางส่วนที่มีพฤติกรรมที่กำหนดโดยมาตรฐาน เพื่อพิสูจน์ว่ามีบางสิ่งที่กำหนดพฤติกรรมคุณต้องอ้างถึงมาตรฐานและแสดงให้เห็นว่ามาตรฐานกำหนดสิ่งที่ควรเกิดขึ้น ในกรณีที่ไม่มีคำจำกัดความพฤติกรรมดังกล่าวจะไม่ได้กำหนดไว้
melpomene

1
การยืนยันในจุด (1) เป็นการก้าวกระโดดอย่างมากสำหรับคำถามสำคัญที่นี่: การเริ่มต้นโดยปริยายขององค์ประกอบ a [2] ถึง 0 เกิดขึ้นก่อนที่จะใช้ผลข้างเคียงของa[2] = 1นิพจน์ initializer หรือไม่? ผลลัพธ์ที่สังเกตได้นั้นเหมือนกับว่ามันเป็น แต่มาตรฐานไม่ได้ระบุว่าควรเป็นเช่นนั้น นั่นคือศูนย์กลางของการโต้เถียงและคำตอบนี้มองข้ามมันไปโดยสิ้นเชิง
John Bollinger

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

0

การมอบหมายa[2]= 1คือนิพจน์ที่มีค่า1และคุณเขียนเป็นหลัก int a[5]= { 1 };(โดยมีผลข้างเคียงที่a[2]กำหนด1ด้วย)


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

@KamiKaze: แน่นอนค่า 1 ไปที่นั่นโดยบังเอิญ
Yves Daoust

0

ฉันเชื่อว่านั่นint a[5]={ a[2]=1 };เป็นตัวอย่างที่ดีสำหรับโปรแกรมเมอร์ที่ยิงเขา / ตัวเองเข้าที่เท้าของเขาเอง

ฉันอาจถูกล่อลวงให้คิดว่าสิ่งที่คุณหมายถึงคือint a[5]={ [2]=1 };สิ่งที่ C99 กำหนดองค์ประกอบการตั้งค่าเริ่มต้น 2 ถึง 1 และส่วนที่เหลือเป็นศูนย์

ในกรณีที่หายากที่คุณหมายถึงจริงๆนั่นint a[5]={ 1 }; a[2]=1;อาจเป็นวิธีที่ตลกในการเขียน อย่างไรก็ตามนี่คือสิ่งที่โค้ดของคุณเดือดถึงแม้ว่าบางส่วนในที่นี้จะชี้ให้เห็นว่ามันไม่ได้กำหนดไว้อย่างชัดเจนเมื่อดำเนินการเขียนถึงa[2]จริง ข้อผิดพลาดที่นี่a[2]=1ไม่ใช่เครื่องมือเริ่มต้นที่กำหนด แต่เป็นการกำหนดง่ายๆซึ่งตัวเองมีค่า 1


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