[英]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.