ฉันจะใส่คำสั่งที่เพิ่มขึ้นสองคำสั่งใน C ++ 'for' loop ได้อย่างไร


93

ฉันต้องการเพิ่มตัวแปรสองตัวในforเงื่อนไข -loop แทนที่จะเป็นตัวแปรเดียว

สิ่งที่ชอบ:

for (int i = 0; i != 5; ++i and ++j) 
    do_something(i, j);

ไวยากรณ์ของสิ่งนี้คืออะไร?

คำตอบ:


155

สำนวนทั่วไปคือการใช้ตัวดำเนินการลูกน้ำซึ่งประเมินทั้งตัวถูกดำเนินการและส่งกลับตัวถูกดำเนินการที่สอง ดังนั้น:

for(int i = 0; i != 5; ++i,++j) 
    do_something(i,j);

แต่มันเป็นตัวดำเนินการลูกน้ำจริงหรือ?

ตอนนี้ได้เขียนไปแล้วผู้แสดงความคิดเห็นแนะนำว่าจริง ๆ แล้วมันเป็นน้ำตาลวากยสัมพันธ์พิเศษบางอย่างในคำสั่ง for ไม่ใช่ตัวดำเนินการลูกน้ำเลย ฉันตรวจสอบใน GCC ดังนี้:

int i=0;
int a=5;
int x=0;

for(i; i<5; x=i++,a++){
    printf("i=%d a=%d x=%d\n",i,a,x);
}

ฉันคาดหวังว่า x จะรับค่าดั้งเดิมของ a ดังนั้นมันควรจะแสดง 5,6,7 .. สำหรับ x สิ่งที่ฉันได้คือสิ่งนี้

i=0 a=5 x=0
i=1 a=6 x=0
i=2 a=7 x=1
i=3 a=8 x=2
i=4 a=9 x=3

อย่างไรก็ตามถ้าฉันยึดนิพจน์เพื่อบังคับให้ parser เห็นตัวดำเนินการลูกน้ำจริงๆฉันจะได้สิ่งนี้

int main(){
    int i=0;
    int a=5;
    int x=0;

    for(i=0; i<5; x=(i++,a++)){
        printf("i=%d a=%d x=%d\n",i,a,x);
    }
}

i=0 a=5 x=0
i=1 a=6 x=5
i=2 a=7 x=6
i=3 a=8 x=7
i=4 a=9 x=8

ในตอนแรกฉันคิดว่าสิ่งนี้แสดงให้เห็นว่ามันไม่ได้ทำงานเป็นตัวดำเนินการลูกน้ำเลย แต่ปรากฎว่านี่เป็นเพียงปัญหาลำดับความสำคัญ - ตัวดำเนินการลูกน้ำมีลำดับความสำคัญต่ำที่สุดเท่าที่จะเป็นไปได้ดังนั้นนิพจน์ x = i ++, a ++ จึงมีประสิทธิภาพ แยกวิเคราะห์เป็น (x = i ++), a ++

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


1
ฉันอ่านมาหลายครั้งแล้วว่าเครื่องหมายจุลภาคในส่วนแรกหรือส่วนที่สามของ for loop ไม่ใช่ตัวดำเนินการลูกน้ำ แต่เป็นเพียงตัวคั่นด้วยลูกน้ำ (อย่างไรก็ตามฉันไม่พบแหล่งข้อมูลอย่างเป็นทางการสำหรับเรื่องนี้เนื่องจากฉันไม่สามารถแยกวิเคราะห์มาตรฐานภาษา C ++ ได้)
Daniel Daranas

ตอนแรกฉันคิดว่าคุณไม่ถูกต้อง แต่ฉันเขียนรหัสทดสอบบางอย่างและคุณถูกต้อง - มันไม่ทำงานเหมือนตัวดำเนินการลูกน้ำ จะแก้ไขคำตอบของฉัน!
Paul Dixon

19
มันเป็นผู้ประกอบการจุลภาคในบริบทที่ เหตุผลที่คุณไม่ได้รับสิ่งที่คุณคาดหวังก็คือตัวดำเนินการคำสั่งมีลำดับความสำคัญต่ำกว่าตัวดำเนินการกำหนดดังนั้นหากไม่มีคำสั่งมันจะแยกวิเคราะห์เป็น (x = i ++), j ++
คาเฟ่

6
เป็นตัวดำเนินการลูกน้ำ การกำหนดมีความสัมพันธ์อย่างมากกับตัวดำเนินการลูกน้ำดังนั้น x = i ++, a ++ จะถูกแยกวิเคราะห์ (x = i ++), a ++ และไม่ใช่ x = (i ++, a ++) ลักษณะดังกล่าวถูกใช้ในทางที่ผิดโดยไลบรารีบางแห่งเพื่อให้ v = 1,2,3; ทำสิ่งที่ใช้งานง่าย แต่เพียงเพราะ v = 1 ส่งคืนวัตถุพร็อกซีซึ่งตัวดำเนินการลูกน้ำที่โอเวอร์โหลดจะทำการผนวก
AProgrammer

3
ตกลง. จากopen-std.org/jtc1/sc22/wg21/docs/papers/2009/n2857.pdfส่วน 6.5.3 ส่วนสุดท้ายคือ "นิพจน์" (แม้ว่า 1.6 # 2 จะกำหนด "รายการนิพจน์" เป็น "รายการของนิพจน์ที่คั่นด้วยเครื่องหมายจุลภาค" โครงสร้างนี้จะไม่ปรากฏใน 6.5.3) ซึ่งหมายความว่าเมื่อเราเขียน "++ i, ++ j" มันจะต้องเป็นนิพจน์ในตัวเองดังนั้น "," จะต้องเป็นตัวดำเนินการลูกน้ำ (5.18) (นี่ไม่ใช่ "รายการตัวเริ่มต้น" หรือ "รายการอาร์กิวเมนต์ของฟังก์ชัน" ซึ่งเป็นตัวอย่างที่ "จุลภาคให้ความหมายพิเศษ" ตามที่ 5.18 # 2 กล่าว) ฉันคิดว่ามันค่อนข้างสับสน
Daniel Daranas

56

ลองทำตามนี้

for(int i = 0; i != 5; ++i, ++j)
    do_something(i,j);

18
+1 คุณสามารถประกาศ j ในส่วนแรกได้ด้วย สำหรับ (int i = 0, j = 0; i! = 5; ++ i, ++ j) {... }
Daniel Daranas

1
+1 เป็นหมายเหตุด้านข้างไวยากรณ์เดียวกันนี้ใช้งานได้ใน C # (ฉันได้มาจากการค้นหาโดย Google สำหรับ "C # for loop incrament 2 counter" ดังนั้นคิดว่าฉันจะพูดถึงสิ่งนี้)
CodingWithSpike

@CodingWithSpike: ใน C # เครื่องหมายจุลภาคเป็นพิเศษจริง ๆ แล้วมันไม่ถูกกฎหมายสำหรับนิพจน์ตัวดำเนินการลูกน้ำที่จะปรากฏที่นั่น ตัวอย่างคือการใช้ตัวดำเนินการลูกน้ำใน C ++ ตามกฎหมาย แต่ C # ถูกปฏิเสธ:for( ; ; ((++i), (++j)) )
Ben Voigt

@BenVoigt ไม่มีอะไรเกี่ยวข้องกับลูกน้ำ นี่ไม่ใช่ C # ตามกฎหมายเช่นกัน: for(int i = 0; i != 5; (++i)) {วงเล็บเสริมหลอกให้คอมไพเลอร์คิดว่าไม่ใช่การดำเนินการ "เพิ่ม" อีกต่อไป
CodingWithSpike

@CodingWithSpike: นั่นเป็นความจริง แต่วงเล็บยังเปลี่ยนวิธีที่ C # เห็นเครื่องหมายจุลภาคและป้องกันความหมายพิเศษภายใน for action
Ben Voigt

6

พยายามอย่าทำ!

จากhttp://www.research.att.com/~bs/JSF-AV-rules.pdf :

กฎ AV 199
นิพจน์ส่วนเพิ่มใน for loop จะไม่ดำเนินการใด ๆ นอกจากเปลี่ยนพารามิเตอร์ single loop เป็นค่าถัดไปสำหรับลูป

เหตุผล: ความสามารถในการอ่าน


4
นั่นเป็นเรื่องจริง แต่เพื่อความยุติธรรมฉันค่อนข้างมั่นใจว่ามาตรฐานของกฎถูกเขียนขึ้นสำหรับซอฟต์แวร์ฝังตัวในเครื่องบินรบไม่ใช่โปรแกรม C (++) ในสวน ดังที่กล่าวมาอาจเป็นนิสัยการอ่านที่ดีที่จะเข้ามาและใครจะรู้บางทีคุณอาจกำลังออกแบบซอฟต์แวร์ F-35 และมันจะเป็นนิสัยน้อยลง
galois


2

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

วันนี้ฉันทำงานใน C # แต่ฉันรู้สึกมั่นใจว่าจะต้องปฏิบัติตามกฎเดียวกันในเรื่องนี้เนื่องจากคำสั่ง FOR เป็นโครงสร้างควบคุมที่เก่าแก่ที่สุดในการเขียนโปรแกรมทั้งหมด โชคดีที่ฉันเพิ่งใช้เวลาหลายวันในการบันทึกพฤติกรรมของ FOR loop ในโปรแกรม C รุ่นเก่าของฉันอย่างแม่นยำและฉันก็รู้ได้อย่างรวดเร็วว่าการศึกษาเหล่านั้นมีบทเรียนที่ใช้กับปัญหา C # ในปัจจุบันโดยเฉพาะกับพฤติกรรมของตัวแปรดัชนีที่สอง .

สำหรับผู้ที่ไม่ระมัดระวังต่อไปนี้เป็นบทสรุปของการสังเกตของฉัน ทุกสิ่งที่ฉันเห็นเกิดขึ้นในวันนี้โดยการสังเกตตัวแปรในหน้าต่าง Locals อย่างรอบคอบยืนยันความคาดหวังของฉันว่าคำสั่ง C # FOR ทำงานเหมือนกับคำสั่ง C หรือ C ++ FOR

  1. ในครั้งแรกที่ FOR ลูปดำเนินการส่วนคำสั่งเพิ่ม (3 ใน 3) จะถูกข้ามไป ใน Visual C และ C ++ การเพิ่มจะถูกสร้างขึ้นเป็นคำสั่งเครื่องสามคำสั่งที่อยู่ตรงกลางของบล็อกที่ใช้การวนซ้ำเพื่อให้รหัสเริ่มต้นเรียกใช้รหัสเริ่มต้นเพียงครั้งเดียวจากนั้นกระโดดข้ามบล็อกส่วนเพิ่มเพื่อดำเนินการทดสอบการสิ้นสุด สิ่งนี้ใช้คุณลักษณะที่ FOR loop ดำเนินการเป็นศูนย์หรือมากกว่าครั้งขึ้นอยู่กับสถานะของดัชนีและตัวแปร จำกัด
  2. หากร่างกายของลูปดำเนินการคำสั่งสุดท้ายคือการข้ามไปที่คำแนะนำการเพิ่มครั้งแรกจากสามคำสั่งที่ข้ามไปโดยการวนซ้ำครั้งแรก หลังจากดำเนินการแล้วการควบคุมจะตกอยู่ในโค้ดทดสอบขีด จำกัด ที่ใช้ประโยคกลางตามธรรมชาติ ผลลัพธ์ของการทดสอบนั้นจะเป็นตัวกำหนดว่าเนื้อหาของลูป FOR ดำเนินการหรือไม่หรือว่าการควบคุมโอนไปยังคำสั่งถัดไปหลังจากกระโดดที่ด้านล่างของขอบเขต
  3. เนื่องจากการควบคุมถ่ายโอนจากด้านล่างของบล็อกลูป FOR ไปยังบล็อกที่เพิ่มขึ้นตัวแปรดัชนีจะเพิ่มขึ้นก่อนที่จะดำเนินการทดสอบ พฤติกรรมนี้ไม่เพียง แต่อธิบายว่าเหตุใดคุณจึงต้องเขียนโค้ดขีด จำกัด ของคุณตามที่คุณได้เรียนรู้มาเท่านั้น แต่ยังส่งผลต่อส่วนเพิ่มรองที่คุณเพิ่มผ่านตัวดำเนินการลูกน้ำเนื่องจากมันกลายเป็นส่วนหนึ่งของอนุประโยคที่สาม ดังนั้นจึงไม่เปลี่ยนแปลงในการทำซ้ำครั้งแรก แต่เป็นการทำซ้ำครั้งสุดท้ายซึ่งไม่เคยเรียกใช้ร่างกาย

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


1

ฉันเห็นด้วยกับ squelart การเพิ่มตัวแปรสองตัวมีแนวโน้มที่จะเกิดข้อผิดพลาดโดยเฉพาะอย่างยิ่งหากคุณทดสอบเพียงตัวแปรเดียว

นี่เป็นวิธีที่อ่านได้ในการดำเนินการนี้:

int j = 0;
for(int i = 0; i < 5; ++i) {
    do_something(i, j);
    ++j;
}

Forลูปมีไว้สำหรับกรณีที่ลูปของคุณทำงานบนตัวแปรเพิ่ม / ลดหนึ่งตัวแปร สำหรับตัวแปรอื่น ๆ ให้เปลี่ยนในลูป

หากคุณต้องการjที่จะเชื่อมโยงไปiทำไมไม่ออกจากตัวแปรเดิมที่เป็นอยู่และเพิ่มi?

for(int i = 0; i < 5; ++i) {
    do_something(i,a+i);
}

ถ้าตรรกะของคุณซับซ้อนกว่านี้ (เช่นคุณต้องมอนิเตอร์มากกว่าหนึ่งตัวแปรจริงๆ) ฉันจะใช้whileลูป


2
ในตัวอย่างแรก j จะเพิ่มขึ้นหนึ่งครั้งมากกว่า i! แล้วตัววนซ้ำที่ต้องดำเนินการบางอย่างสำหรับขั้นตอน x แรกเป็นอย่างไร? (และคอลเลกชั่นก็ยาวพอเสมอ) คุณสามารถขึ้นตัววนซ้ำได้ทุกครั้ง แต่จะสะอาดกว่ามาก
Peter Smit

0
int main(){
    int i=0;
    int a=0;
    for(i;i<5;i++,a++){
        printf("%d %d\n",a,i);
    } 
}

1
อะไรคือจุดที่ไม่สร้างiและเชื่อมaต่อกับลูป?
sbi

2
ไม่มีเพียงแค่แสดงวิธีการเพิ่มทั้งสองอย่างใน for ซึ่งเป็นเพียงตัวอย่างของ sytnax
Arkaitz Jimenez

0

ใช้คณิตศาสตร์ หากการดำเนินการทั้งสองทางคณิตศาสตร์ขึ้นอยู่กับการวนซ้ำแบบวนซ้ำทำไมไม่คำนวณทางคณิตศาสตร์

int i, j;//That have some meaningful values in them?
for( int counter = 0; counter < count_max; ++counter )
    do_something (counter+i, counter+j);

หรือโดยเฉพาะอย่างยิ่งอ้างถึงตัวอย่างของ OP:

for(int i = 0; i != 5; ++i)
    do_something(i, j+i);

โดยเฉพาะอย่างยิ่งถ้าคุณส่งผ่านฟังก์ชันตามค่าคุณควรได้รับสิ่งที่ตรงตามที่คุณต้องการ

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