[英]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 );
i
設置為0
i < 10
; 如果為假,則轉到 6printf
語句i
現在,如果你把它寫成
int i = 0;
for ( int i = 0; i < 10; i++ )
printf( "i = %d\n", i );
這將創建一個名為i
的新object ,它“隱藏” 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.