ลูป 'for' ภายในลูป 'for' สามารถใช้ชื่อตัวแปรตัวนับเดียวกันได้หรือไม่?


107

ฉันสามารถใช้ตัวแปรตัวนับเดียวกันสำหรับforลูปภายในforลูปได้หรือไม่

หรือตัวแปรจะมีผลต่อกัน? โค้ดต่อไปนี้ควรใช้ตัวแปรอื่นสำหรับลูปที่สองเช่นjหรือiไม่เป็นไร

for(int i = 0; i < 10; i++)
{
  for(int i = 0; i < 10; i++)
  {
  }
}

72
มันสับสน - มันจะไม่ผ่านฉันไปในการตรวจสอบโค้ด แต่มันถูกต้องตามกฎหมาย มีการเรียกตัวแปรสองตัวแปรที่แตกต่างกันiโดยมีขอบเขตที่แตกต่างกัน ใช้-Wshadowกับ GCC เพื่อรายงานปัญหาดังกล่าวโดยอัตโนมัติ
Jonathan Leffler

15
ฉันแปลกใจที่-Wshadowไม่รวมอยู่ใน-Wall.
leftaround ประมาณ

5
@leftaroundabout -Wshadowเตือนเกี่ยวกับการแรเงาของตัวแปรส่วนกลางเช่นกันซึ่งอาจทำให้เกิดความรำคาญในโครงการขนาดใหญ่
คิวบิ

9
@leftaroundabout มากยิ่งขึ้นน่าแปลกใจที่แม้ไม่รวมถึง-Wextra -Wshadowฉันเดาว่ามันเป็นเรื่องปกติมากพอในบางโปรเจ็กต์หรือนักพัฒนาgccบางคนชอบการแชโดว์เป็นรูปแบบการเข้ารหัสเพื่อรับประกันว่าจะถูกทิ้งไว้เช่นนี้
hyde

5
@leftaround เกี่ยวกับการสะท้อนสิ่งที่ Cubic พูด-Wshadowมีอัตราผลบวกที่ผิดพลาดอย่างน่ากลัวทำให้มันไร้ประโยชน์อย่างสิ้นเชิง ขอบเขตมีอยู่ด้วยเหตุผลและการทำเงาเป็นสิ่งสำคัญที่ไม่ก่อให้เกิดปัญหา ตอนนี้-Wshadow-local(หมายเหตุ: ไม่ -Wshadow=local ) แตกต่างกันมาก แต่น่าเสียดายที่ GCC ปฏิเสธที่จะรวมไว้ในหีบ (แม้ว่าจะมีส้อมของ GCC ซึ่งรวมอยู่ด้วยก็ตาม)
Konrad Rudolph

คำตอบ:


140

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

โดยทั่วไปแล้วลักษณะนี้ไม่ดีมีแนวโน้มที่จะสับสนและควรหลีกเลี่ยง

วัตถุจะแตกต่างกันก็ต่อเมื่อมีการกำหนดวัตถุภายในแยกจากกันดังที่int iคุณแสดง หากใช้ชื่อเดียวกันโดยไม่ได้กำหนดวัตถุใหม่ลูปจะใช้วัตถุเดียวกันและจะรบกวนซึ่งกันและกัน


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

3
@BloodGain:“ Object” เป็นคำศัพท์ทางเทคนิคที่ใช้ในมาตรฐาน C ฉันใช้มันโดยเจตนาที่นี่
Eric Postpischil

1
@EricPostpischil: อ่าฉันเห็นใช่ ฉันไม่ทราบถึงคำจำกัดความดังกล่าวในมาตรฐานและกลัวว่าโปรแกรมเมอร์มือใหม่จะเข้าใจผิด (เนื่องจากเป็นคำถามเริ่มต้นอย่างชัดเจน) เนื่องจาก C ไม่มี "วัตถุ" ในความหมายที่เราใช้โดยทั่วไป ฉันเห็นมันในมาตรฐาน C11 และตอนนี้ฉันสงสัยว่ามันถูกกำหนดไว้ก่อนหน้า C11 หรือไม่
Bloodgain

1
มันเป็น เป็น 3.14 ในมาตรฐาน C99 แทนที่จะเป็น 3.15 ดังนั้นไม่มีข้อแก้ตัวในส่วนของฉัน นั่นจะสอนให้ฉันตั้งคำถามกับคุณ <: - |
Bloodgain

1
โดยทั่วไป: ไม่มีอะไรที่จะป้องกันไม่ให้คุณใช้ชื่อตัวแปรซ้ำในขอบเขตที่ซ้อนกัน ยกเว้นแน่นอนความกลัวการลงโทษของพระเจ้าที่เขียนโค้ดที่ทำให้สับสน
Isaac Rabinovitch

56

ประการแรกนี่เป็นสิ่งที่ถูกกฎหมายอย่างแน่นอน: โค้ดจะคอมไพล์และรันโดยทำซ้ำเนื้อหาของลูปที่ซ้อนกัน 10 × 10 = 100 ครั้ง ตัวนับลูปiภายในลูปที่ซ้อนกันจะซ่อนตัวนับของวงนอกดังนั้นตัวนับทั้งสองจะเพิ่มขึ้นโดยอิสระจากกัน

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

หมายเหตุ:แม้ว่าตัวแปรตัวนับของทั้งสองลูปจะมีตัวระบุเหมือนกันiแต่ก็ยังคงเป็นตัวแปรอิสระสองตัวกล่าวคือคุณไม่ได้ใช้ตัวแปรเดียวกันในทั้งสองลูป การใช้ตัวแปรเดียวกันในทั้งสองลูปก็เป็นไปได้เช่นกัน แต่โค้ดจะอ่านยาก นี่คือตัวอย่าง:

for (int i = 1 ; i < 100 ; i++) {
    for ( ; i % 10 != 0 ; i++) {
        printf("%02d ", i);
    }
    printf("%d\n", i);
}

ตอนนี้ทั้งสองลูปใช้ตัวแปรเดียวกัน อย่างไรก็ตามต้องใช้เวลาสักพักในการค้นหาว่าโค้ดนี้ทำอะไรได้บ้างโดยไม่ต้องรวบรวม ( สาธิต )


4
เนื่องจากคำถามมีข้อความว่า "ใช้ตัวแปรตัวนับเดียวกัน" ฉันจึงต้องการชี้ให้เห็นว่าการสร้างเงาจะเกิดขึ้นเมื่อมีการกำหนดนิยามใหม่เท่านั้น ถนัดในด้านการห่วงคือจริงโดยใช้ตัวแปรเคาน์เตอร์เดียวกันจะทำให้เกิดห่วงด้านนอกเพื่อให้ทำงานได้เพียงครั้งเดียวเป็นห่วงด้านในจะออกจากint i == 10นี่เป็นเรื่องเล็กน้อย แต่คิดว่ามันให้ความกระจ่างเมื่อถามคำถาม
Easton Bornemeier

@EastonBornemeier คุณพูดถูกฉันคิดว่าฉันควรจะจัดการกับปัญหาของ "ตัวแปรเดียวกัน" ในเนื้อหาของคำตอบ ขอบคุณ!
dasblinkenlight

@EricPostpischil "แชโดว์ตัวแปร" เป็นคำที่อย่างเป็นทางการที่สมบูรณ์กับหน้าของตัวเองในวิกิพีเดีย ฉันได้อัปเดตคำตอบเพื่อให้สอดคล้องกับถ้อยคำของมาตรฐานแล้ว ขอบคุณ!
dasblinkenlight

2
@dasblinkenlight: อันที่จริงฉันมีอาการกระตุกของสมองเกี่ยวกับทิศทางและชื่อด้านในทำให้ชื่อภายนอกเป็นเงา ความคิดเห็นก่อนหน้านี้ของฉันไม่ถูกต้องในเรื่องนั้น ขอโทษด้วย. (อย่างไรก็ตามนั่นเป็นความหมายภาษาอังกฤษไม่ใช่ในความหมายอย่างเป็นทางการ - Wikipedia ไม่ใช่สิ่งพิมพ์อย่างเป็นทางการสำหรับภาษา C หรือการเขียนโปรแกรมโดยทั่วไปและฉันไม่ทราบว่าสำนักงานหรือหน่วยงานที่มีอำนาจใด ๆ ที่กำหนดคำนี้) มาตรฐาน C ใช้ "ซ่อน" ดังนั้นดีกว่า
Eric Postpischil

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

26

คุณสามารถ. แต่คุณควรตระหนักถึงขอบเขตของis ถ้าเราเรียกด้านนอกiด้วยi_1และด้านในiด้วยi_2ขอบเขตของis จะเป็นดังนี้:

for(int i = 0; i < 10; i++)
{
     // i means i_1
     for(int i = 0; i < 10; i++)
     {
        // i means i_2
     }
     // i means i_1
}

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


17

เป็นไปได้อย่างสมบูรณ์ แต่โปรดทราบว่าคุณจะไม่สามารถจัดการ กับ i ที่ประกาศครั้งแรกได้

for(int i = 0; i < 10; i++)//I MEAN THE ONE HERE
{

  for(int i = 0; i < 10; i++)
    {

    }
}

ในลูปที่สองภายในลูปลูกที่สอง

for(int i = 0; i < 10; i++)
{

  for(int i = 0; i < 10; i++)//the new i
    {
        // i cant see the i thats before this new i here
    }
}

หากคุณต้องการปรับหรือรับค่าของ i ตัวแรกให้ใช้ j ในลูปที่สอง

for(int i = 0; i < 10; i++)
{

  for(int j = 0; j < 10; j++)
    {

    }
}

และหากครีเอทีฟโฆษณาของคุณเพียงพอคุณสามารถทำทั้งสองอย่างในลูปเดียว

for(int i ,j= 0; i < 10; (j>9) ? (i++,j=0) : 0 ,j++)
{
    printf("%d %d\n",i,j);
}

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

มันเป็นหนึ่งวงมีเพียงวงเดียวสำหรับการวนซ้ำถ้าเป็น 2 ก็จะมีสองคำสำหรับคำหลักหรือสองคำในขณะที่คำหลักหรือคำหลักสำหรับและในขณะที่
Dodo

3
นั่นเป็นเหตุผลที่ฉันบอกว่าคุณทำให้วงสับสน คุณยังคงวนลูปอยู่คุณเพิ่งซ่อนมันด้วยไวยากรณ์ที่ไม่ค่อยชัดเจน และมันแย่ลงในทุกๆด้าน
Bloodgain

12

ใช่คุณสามารถใช้ชื่อตัวแปรที่เคาน์เตอร์เดียวกันภายในforห่วงสำหรับนอกforวง

จากสำหรับลูป :

for ( init_clause ; cond_expression ; iteration_expression ) loop_statement
คำสั่งการแสดงออกมาใช้เป็นloop_statementกำหนดของตัวเองขอบเขตบล็อกแตกต่างไปจากขอบเขตของinit_clause

for (int i = 0; ; ) {
    long i = 1;   // valid C, invalid C++
    // ...
}  

ขอบเขตของการloop_statementจะซ้อนกันอยู่ในขอบเขตของinit_clause

จาก C Standards # 6.8.5p5 คำสั่งการวนซ้ำ[เน้นของฉัน]

คำสั่งการวนซ้ำคือบล็อกที่มีขอบเขตเป็นส่วนย่อยที่เข้มงวดของขอบเขตของบล็อกที่ปิดล้อม ร่างกายห่วงยังเป็นบล็อกที่มีขอบเขตเป็นชุดย่อยที่เข้มงวดของขอบเขตของคำสั่งซ้ำที่

จากมาตรฐาน C # 6.2.1p4 ขอบเขตของตัวระบุ[เน้นเหมือง]

.... ภายในขอบเขตภายในตัวระบุกำหนดเอนทิตีที่ประกาศในขอบเขตภายใน นิติบุคคลประกาศในขอบเขตด้านนอกถูกซ่อนอยู่ (และไม่ได้มองเห็นได้) ภายในขอบเขตภายใน


10

จากมุมมองของโค้ด / คอมไพเลอร์นี่เป็นสิ่งที่ถูกต้องและถูกกฎหมายที่ต้องทำ การint iประกาศในfor(int i = 0; i < 10; i++)วงในวงในอยู่ในขอบเขตใหม่และเล็กลงดังนั้นการประกาศจะทำให้เงาของการประกาศint iในวงนอก (หรือพูดอีกนัยหนึ่ง: ในขอบเขตภายในการเข้าถึงตัวแปรทั้งหมดiไปที่int iประกาศในขอบเขตด้านใน ปล่อยให้int iอยู่ในขอบเขตภายนอกโดยไม่ถูกแตะต้อง)

ที่กล่าวว่าจากมุมมองด้านคุณภาพของรหัสสิ่งนี้น่ากลัวอย่างที่สุด อ่านยากเข้าใจยากและเข้าใจผิดได้ง่าย อย่าทำ


8

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

int a = 5;
// scope of a that has value 5
int func(){
    int a = 10;
   // scope of a that has value 10
}
// scope of a that has value 5

ในทำนองเดียวกันกับกรณีที่มีลูปตัวแปรที่ประกาศภายในวงในมีขอบเขตที่แตกต่างกันและตัวแปรที่ประกาศวงนอกมีขอบเขตที่แตกต่างกัน

for(int i = 0; i < 10; i++){
    // In first iteration, value of i is 0

    for(int i = 1; i < 10; i++){
        // In first iteration, value of i is 1
    }
    // In first iteration, value of i is 0
}

แนวทางที่ดีกว่าคือการใช้ตัวแปรที่แตกต่างกันสำหรับลูปภายในและภายนอก

for(int i = 0; i < 10; i++){

    for(int j = 1; j < 10; j++){

    }

}

8

ใช่แน่นอนคุณสามารถใช้ตัวแปรชื่อเดียวกันได้

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

แต่ในกรณีของคุณ i scopeจะต้องคำนึงถึงสิ่งต่างๆ

for(int i = 0; i < 10; i++)
{
     // i means 1st for loop variable
     for(int i = 0; i < 10; i++)
     {
        // but here i means 2nd for loop  variable
     }
     //interesting thing here i means 1st for loop variable
}

หมายเหตุ: แนวทางปฏิบัติที่ดีที่สุดคือใช้ตัวแปรต่างๆสำหรับลูปภายในและภายนอก


6

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


3

กฎขอบเขต:ตัวแปรที่ประกาศในคำสั่ง for สามารถใช้ได้เฉพาะในคำสั่งนั้นและเนื้อหาของลูป

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

int main(void) {

    int i = 2; //defined with file global scope outside of a function and will remain 2
    if(1)
    {       //new scope, variables created here with same name are different
        int i = 5;//will remain == 5
        for(int i = 0; i < 10; i++)
        {   //new scope for "i"

            printf("i value in first loop: %d \n", i); // Will print 0 in first iteration
            for(int i = 8; i < 15; i++) 
            {   //new scope again for "i", variable with same name is not the same
                printf("i value in nested loop: %d \n", i); // Will print 8 in first iteration
            }
        }

    }

    return 0;
}

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


1

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

for(int i = 0; i < 10; i++) // This code will print "Test" 100 times
{
 for(int i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

โปรดทราบว่าโค้ดด้านบนรวมint iอยู่ในพารามิเตอร์วงในและโค้ดด้านล่างรวมiไว้เท่านั้น

for(int i = 0; i < 10; i++) // This code will print "Test" 10 times
{
 for(i = 0; i < 10; i++)
 {
  puts("Test");
 }
}

0

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

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