繁体   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