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;
对于这两个代码,我的问题是这些循环是如何工作的?是 堆栈帧是否保持?
内部内存如何维护是否有队列?
为什么看起来像一个函数(){}体如何解析函数 体
请不要评论任何答案我需要完整 详细说明请帮助人
答案 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
不是一个函数。这是一个关键字,
引入一种特殊的陈述。
for
(或while
或if
或switch
),它们是标点符号,而不是
函数调用操作符;很多人,像我一样,喜欢
通过不同地格式化它们来区分这两种用途,放一个
关键字和开括号之间的空格
语句级别的标点符号,但名称之间没有空格
函数和(
运算符。 (我认识的每个人也是这样做的
将所有用于转换的格式视为一个函数,
虽然技术上......)
编辑:
对于它的价值:你可以重载()
运算符。如果括号是运算符(并且在函数样式转换的上下文中),则会考虑重载,但不是在它们是标点符号时。
答案 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 == 0x7ff8
,counterONE
的内存将存储在地址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++ )
解释为循环控制体,而不是函数调用或函数定义。
要知道这不是你周末要去的东西。
<小时/> 1。注意 - 格式化非常重要。如果您希望我们为您提供帮助,您需要使代码尽可能可读。粗糙的格式,不一致的缩进等使得查找错误变得更加困难,并且使得某人不太可能抽出时间来帮助他们。