是for for循环维护在堆栈中还是仅仅是一个语句?

时间:2015-01-27 12:39:28

标签: c++ loops for-loop

very basic codes :

        //Code#1
       int i;
       int counterONE=0;
       for(i=0;i<5;i++)
       {
           counterONE+=1;
       }
        cout<<counterONE;

Code#2
        int i,j;
        int CounterONE=0,counterTWO=0;
       for(i=0;i<5;i++)
       {            
          for(j=0;j<5;j++)             
          {
                    counterONE++;
          }
              counterTWO++;
       }
         cout<<counterONE;
         cout<<endl<<counterTWO;
  • 对于这两个代码,我的问题是这些循环是如何工作的?是 堆栈帧是否保持?

    内部内存如何维护是否有队列?

    为什么看起来像一个函数(){}体如何解析函数 体

  

请不要评论任何答案我需要完整   详细说明请帮助人

3 个答案:

答案 0 :(得分:2)

for是一个简单的循环,被转换为&#34; goto&#34; (或类似的)在机器代码中,以使一些命令重复自己

for (int i = 0; i < 5; i++) {
  some code
}
some more code

将翻译成类似(非常简化)

的内容
         R_x = 0 // R_x is some register
loop:    check if R_x >= 5 
         if so, go to "after"
         some code
         increase R_x
         go to loop
after:   some more code

此代码不涉及任何递归,此处堆栈的重要性可忽略不计(仅使用一个,仅用于存储自动变量)。

答案 1 :(得分:2)

真正的答案是for 不是一个函数。这是一个关键字, 引入一种特殊的陈述。

不要被括号愚弄。 C ++中的圆括号重载: 它们可能是一个运算符(函数调用运算符),或者它们可能是 标点符号,这是语法的一部分。当他们关注关键字时 for(或whileifswitch),它们是标点符号,而不是 函数调用操作符;很多人,像我一样,喜欢 通过不同地格式化它们来区分这两种用途,放一个 关键字和开括号之间的空格 语句级别的标点符号,但名称之间没有空格 函数和(运算符。 (我认识的每个人也是这样做的 将所有用于转换的格式视为一个函数, 虽然技术上......)

编辑:

对于它的价值:你可以重载()运算符。如果括号是运算符(并且在函数样式转换的上下文中),则会考虑重载,但不是在它们是标点符号时。

答案 2 :(得分:0)

  

对于这两个代码,我的问题是这些循环是如何工作的?堆栈帧是否被维护?

让我们看一下编译器为你的循环生成的内容。我拿了你的第一个片段并用 1

构建了以下程序
#include <stdio.h>

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

这里是由gcc(使用gcc -S 2 生成的等效汇编代码,由我注释:

        .file   "loops.c"
        .text
.globl main
        .type   main, @function
main:                         ;; function entry point
.LFB2:
        pushq   %rbp          ;; save current frame pointer
.LCFI0:
        movq    %rsp, %rbp    ;; make stack pointer new frame pointer
.LCFI1:
        movl    $0, -4(%rbp)  ;; initialize counterONE to 0
        movl    $0, -8(%rbp)  ;; initialize i to 0
        jmp     .L2           ;; jump to label L2 below
.L3:
        addl    $1, -4(%rbp)  ;; add 1 to counterONE
        addl    $1, -8(%rbp)  ;; add 1 to i
.L2:
        cmpl    $4, -8(%rbp)  ;; compare i to the value 4
        jle     .L3           ;; if i is <= 4, jump to L3
        movl    $0, %eax      ;; return 0
        leave
        ret

所涉及的唯一堆栈帧是为main函数创建的堆栈帧;在for循环本身内没有创建额外的堆栈帧。即使您在for循环中声明了一个本地变量,例如

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

for ( i = 0; i < 5; i++ )
{
  int j;
  ...
}

新的堆栈帧将(很可能)为循环创建 ;循环局部的任何变量都将在封闭函数的框架中创建,尽管变量对循环体外部的代码不可见。

  

内部存储器的维护方式是否有队列?

无需其他数据结构。唯一涉及的内存是i(控制循环执行)和counterONE的内存,两者都保存在堆栈 3 上。它们的偏移量从存储在帧指针中的地址引用(例如,如果%rbp包含地址0x8000,那么i的内存将存储在地址0x8000 - 8 == 0x7ff8counterONE的内存将存储在地址0x8000 - 4 == 0x7ffc)。

  

为什么看起来像函数(){}体如何解析函数体?

语言语法告诉编译器如何解释代码。

这里是迭代语句的语法(取自online C 2011 draft):


(6.8.5) iteration-statement:
    while ( expression ) statement
    do statement while ( expression ) ;
    for ( expressionopt ; expressionopt ; expressionopt ) statement
    for ( declaration expressionopt ; expressionopt ) statement

同样,这是函数调用的语法:

(6.5.2) postfix-expression:
    ...
    postfix-expression ( argument-expression-listopt )
    ...

和函数定义:


(6.9.1) function-definition:
     declaration-specifiers declarator declaration-listopt compound-statement

在解析过程中,编译器会将源代码分解为令牌 - 关键字,标识符,常量,字符串文字和标点符号。然后,编译器尝试将令牌序列与语法进行匹配。

因此,假设源文件包含

for ( i = 0; i < 5; i++ )

编译器会看到for关键字;基于语法,它知道将以下( i = 0; i < 5; i++ )解释为循环控制体,而不是函数调用或函数定义。

无论如何,这是50,000英尺的视野;解析是一个非常复杂的主题,它只是编译器的一部分。您可能需要启动here并按照链接进行操作。

要知道这不是你周末要去的东西。

<小时/> 1。注意 - 格式化非常重要。如果您希望我们为您提供帮助,您需要使代码尽可能可读。粗糙的格式,不一致的缩进等使得查找错误变得更加困难,并且使得某人不太可能抽出时间来帮助他们。

2.这不是完整的列表,但其余部分与您的问题无关。

3.此解释适用于x86等常用体系结构,但请注意,有些旧的和/或古怪的体系结构可能会以不同的方式执行。