簡體   English   中英

for() 如何在 c 內部工作?

[英]How for() works internally in c?

我注意到如果你傳遞一個局部變量,它可以改變它的值,即使你沒有通過地址傳遞它。

我有兩個問題:for() 是 function 還是宏? 我想看看它是怎么寫的。

如果沒有地址,它如何改變價值?

int main()
{
    int i = 0;
    for(; i < 5; i++);
    /* i is 5 now. How? */
    return 0;
}

你混淆了兩件事,“如何for ”(這不是你的實際問題)和變量 scope (這是)。

考慮:

#include <stdio.h>

int main()
{
    int i = 0;

    printf( "Address of i outside the loop: %p\n", &i );

    for ( int i = 0; i < 5; ++i ) // this "shadows" i
    {
        printf( "Address of i inside the loop: %p\n", &i );
    }
}

您會看到循環i位於不同的地址 這里有兩個名為i的變量,在for循環內部,程序開頭聲明的變量是不可見的。

for作品的方式無關; 這里是一樣的:

#include <stdio.h>

int main()
{
    int i = 0;

    printf( "Address of i outside the block: %p\n", &i );

    {
        int i = 0; // this "shadows" i
        printf( "Address of i inside the block: %p\n", &i );
    }
}

您正在查看一個“陰影”變量:在for循環/代碼塊內部, i表示除外部之外的其他內容。

如果你這樣寫:

#include <stdio.h>

int main()
{
    int i = 0;

    printf( "Address of i outside the loop: %p\n", &i );

    for ( i = 0; i < 5; ++i ) // does NOT "shadow" i
    {
        printf( "Address of i inside the loop: %p\n", &i );
    }
}

...您沒有重新聲明i -- for語句中沒有int ,因此只有一個聲明,並且程序中只有一個名為i的變量。

for不是 function 或宏,而是語言的關鍵字。 沒有方便的代碼可以向您展示它的內部工作原理,因為它所做的將分散在各個編譯階段(詞法分析、解析、代碼生成)。 但正如我上面所展示的,讓您感到困惑的一開始與for無關,而是與變量 scope 有關。我希望示例片段可以幫助您更好地理解問題。

我注意到如果你傳遞一個局部變量,它可以改變它的值

那是因為如果我們不能改變局部變量的值,就不可能編寫功能程序。

for() 是 function 還是宏?

兩者都不是,它是一種迭代語句,是該語言的核心構建塊之一。

我想看看它是怎么寫的。

由於上述原因,這沒有任何意義。 for循環可以生成各種機器代碼,包括編譯器執行完整的循環展開和/或完全刪除循環。 例如,如果我在程序底部添加一個printf("%d\n", i) ,則在 x86 上編譯時的整個循環將替換為mov esi, 5 = 將值 5 移動到寄存器中(然后打印它). 不需要循環。

根據 C 標准,循環不作為書面代碼存在,而是作為編譯器以某種方式處理的詞法元素存在。

如果沒有地址,它如何改變價值?

這與循環本身無關,而是與i++表達式無關,它保證“按照分配”更新變量,類似於i=i+1 不需要地址,直接寫入變量。

您必須了解變量scope的概念。 Scope 將一個標識符與 object 相關聯。用不那么花哨的話來說:一個帶有一些 memory 的名稱。

在這段代碼中:

int main(void) {
    for (int i = 0; i < 5; ++i) {
      ...
    }
    return 0;
}

i的 scope 是for塊的塊(在第二個{之后)。 這有點不尋常,因為標識符的 scope 通常在其在塊內的聲明之后開始,並在封閉的}處結束。

在這段代碼中:

int main(void) {
    int i = 0;
    for (;i < 5; ++i) {
    }
    return 0;
}

i有主塊 function 的塊 scope(第一個{ )。

然后是在不同的嵌套塊中使用相同標識符的情況(在 C 行話中,這稱為shadowing ):

int main(void) {
    int i = 0;                    /* This i is in main's block scope. */
    for (int i = 0;i < 5; ++i) {  /* This i shadows main's i. */
       frob(i);                   /* Uses i of the inner scope. */
    }
    /* Here i is from main's scope again. */
    return 0;
}

從您對問題的評論來看,真正的問題似乎是您不了解變量 scope 和陰影是什么。 所以這是一個簡短的演示程序:

#include <stdio.h>

int main( void )
{
    int i = 1000;

    printf( "Test 1: i now has the value: %d\n", i );

    for ( int i = 0; i < 5; i++ )
    {
        printf( "Test 2: i now has the value: %d\n", i );
    }

    printf( "Test 3: i now has the value: %d\n", i );
}

該程序具有以下 output:

Test 1: i now has the value: 1000
Test 2: i now has the value: 0
Test 2: i now has the value: 1
Test 2: i now has the value: 2
Test 2: i now has the value: 3
Test 2: i now has the value: 4
Test 3: i now has the value: 1000

在這個程序中,我聲明了兩個不同的int變量,它們都具有相同的名稱i 第二個變量僅存在於循環內,當我在循環內使用標識符i時,它指的是第二個變量。 這意味着第二個變量i隱藏第一個變量,只要它存在。 一旦退出循環,第二個變量就不復存在,因此它不再影響第一個變量。 這就是為什么“測試 3”再次打印第一個變量的值,即1000

for() 是 function 還是宏?

它既不是 function 也不是宏。 它是一個關鍵字,這意味着它對編譯器有特殊的意義。 因此,它也沒有自己的源代碼。

您的循環等效於:

int main(void)
{
    int i = 0;

    loop:

    if(!(i < 5)) goto loop_exit;
    i++;
    goto loop;

    loop_exit:

    printf("%d\n", i);
}

for() 是 function 還是宏?

兩者都不是——這是一個聲明 語法如下:

for ( expr1 opt ; expr2 opt ; expr3 opt ) statement

不要讓括號誤以為它是 function 或宏,它們只是用來將控制表達式與循環體分開。

它是這樣工作的:

  • expr1 ,如果存在,被評估一次——它通常初始化expr2正在測試的任何條件;

  • expr2如果存在,則在每次循環迭代之前進行評估 - 如果為真,則執行語句(循環體),否則我們退出循環;

  • expr3 (如果存在)在每次循環迭代進行評估 - 它通常會更新expr2正在測試的任何條件。

讓我們通過一個例子來看看它是如何工作的;

int i = 0;
for ( i = 0; i < 10; i++ )
  printf( "i = %d\n", i );
  1. i設置為0
  2. 評估i < 10 如果為假,則轉到 6
  3. 執行printf語句
  4. 增量i
  5. 轉到 2
  6. 退出循環

現在,如果你把它寫成

int i = 0;
for ( int i = 0; i < 10; i++ )
  printf( "i = %d\n", i );

這將創建一個名為iobject ,它“隱藏” i在循環之前聲明的變量。 這個新的 object i是循環體的局部變量,對它的任何更改都不會影響在循環外聲明的變量i

每個控制表達式都是可選的 - 您可以將循環編寫為

for ( ;; )
  do_something();

這將“永遠”循環 - 在這種情況下expr2被假定為true

正如 John Bode 在他的回答中所說,這是一個聲明,但您可以將其視為執行此操作的宏:

#define for(A;B;C){STUFF} A; while(B){STUFF C;}

因為這:

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

與此相同:

int i = 0;
while (i < 10){
    puts("howareyou");
}

這與此相同:

int i = 0;
loopstart:

puts("howareyou");

if(i < 10) goto loopstart;

TLDR:for 循環是 while 循環的快捷方式,while 循環是“goto 循環”的快捷方式。 Switch-case 語句和函數在某種程度上也只是 goto 語句的快捷方式。

考慮一下:

int main() {
    int i = 1;  // function scope
    int j = 5;
    printf( "i = %d j = %d\n", i, j );

    {   // Begin local scope
        int i = 3; // local scope within "{}"
        printf( "i = %d j = %d\n", i, j );
        // There is no legitimate way to access the 'other' i in this scope.
        // The 'function scope' i stands in the shadows...

    }   // end local scope

    printf( "i = %d j = %d\n", i, j ); // function scope

    return 0;
}
i = 1 j = 5
i = 3 j = 5 // j is not being "shadowed"
i = 1 j = 5

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM