C语言循环的实现

在C语言中采用3中语法来实现循环,它们分别是while、for、do while,本文将分别说明这三种循环的实现,并对它们的运行效率进行比较。

do while

首先来看do while的实现:下面是简单的代码:
int nCount = 0;
int nMax = 10;
do 
{
    nCount++;
} while (nCount < nMax);
return 0;   

下面对应的是它的汇编代码:

9:        int nCount = 0;
00401268   mov         dword ptr [ebp-4],0
10:       int nMax = 10;
0040126F   mov         dword ptr [ebp-8],0Ah
11:       do
12:       {
13:           nCount++;
00401276   mov         eax,dword ptr [ebp-4]
00401279   add         eax,1
0040127C   mov         dword ptr [ebp-4],eax
14:       } while (nCount < nMax);
0040127F   mov         ecx,dword ptr [ebp-4];exc = nCount
00401282   cmp         ecx,dword ptr [ebp-8];比较nCount 和 nMax的值
00401285   jl          main+26h (00401276);跳转到循环体中
15:       return 0;
00401287   xor         eax,eax

在汇编代码中首先执行了一次循环体中的操作,然后判断,当条件满足时会跳转回循环体,然后再次执行,当条件不满足时会接着执行后面的语句。 这个过程可以用goto来模拟:

    int nCount = 0;
    int nMax = 10;
__WHILE:
    nCount++;
    if(nCount < nMax)
        goto __WHILE;

while循环

不同于do while的先执行再比较,while采取的是先比较再循环的方式,下面是一个while的例子:

    int nCount = 0;
    int nMax = 10;
    while (nCount < nMax)
    {
        nCount++;
    }
00401268   mov         dword ptr [ebp-4],0
10:       int nMax = 10;
0040126F   mov         dword ptr [ebp-8],0Ah
11:       while (nCount < nMax)
00401276   mov         eax,dword ptr [ebp-4]
00401279   cmp         eax,dword ptr [ebp-8]
0040127C   jge         main+39h (00401289)
12:       {
13:           nCount++;
0040127E   mov         ecx,dword ptr [ebp-4]
00401281   add         ecx,1
00401284   mov         dword ptr [ebp-4],ecx
14:       }
00401287   jmp         main+26h (00401276)
15:       return 0;
00401289   xor         eax,eax

从汇编代码上可以看出,执行while循环时会有两次跳转,当条件不满足时会执行一次跳转,跳转到循环体外,而条件满足,执行完一次循环后,会再次跳转到循环体中,再次进行比较。相比于do while来说,while执行了两次跳转,效率相对较低。

for 循环

for循环是首先进行初始化操作然后进行比较,条件满足时执行循环,再将循环变量递增,最后再次比较,执行循环或者跳出。下面是for的简单例子:

    int nMax = 10;
    for (int i = 0; i < nMax; i++)
    {
        printf("%d\n", i);
    }

下面是它对应的汇编代码:

9:        int nMax = 10;
00401268   mov         dword ptr [ebp-4],0Ah
10:       for (int i = 0; i < nMax; i++)
0040126F   mov         dword ptr [ebp-8],0 ;初始化循环变量
00401276   jmp         main+31h (00401281);跳转到比较操作处
00401278   mov         eax,dword ptr [ebp-8]
0040127B   add         eax,1
0040127E   mov         dword ptr [ebp-8],eax;这三句话实现的是循环变量自增操作
00401281   mov         ecx,dword ptr [ebp-8];ecx = i
00401284   cmp         ecx,dword ptr [ebp-4];比较ecx与i
00401287   jge         main+4Ch (0040129c);跳转到循环体外
11:       {
12:           printf("%d\n", i);
00401289   mov         edx,dword ptr [ebp-8]
0040128C   push        edx
0040128D   push        offset string "%d\n" (0042e01c)
00401292   call        printf (00401540)
00401297   add         esp,8
13:       }
0040129A   jmp         main+28h (00401278);跳转到i++位置
14:       return 0;
0040129C   xor         eax,eax

从上面的汇编代码可以看出for循环的效率最低,它经过了3次跳转,生成对应的汇编代码上,初始化操作后面紧接着是循环变量自增操作,所以首先在完成初始化后会进行一次跳转,跳转到判断,然后根据判断条件再次跳转或者接着执行循环体,最后当循环完成后会再次跳转到循环变量自增的位置,同样采用goto语句来模拟这个操作:

    int nMax = 10;
    int i = 0;
    goto __CMP;
__ADD:
    i++;
__CMP:
    if (i >= nMax)
    {
        goto __RETURN;
    }

__LOOP:
    printf("%d\n", i);
    goto __ADD;
__RETURN:
    return 0;

continue语句

continue用于结束这次循环进入下一次循环,下面采用最复杂的for循环来说明continue语句:
int nMax = 10;
    int i = 0;
    for(;i < nMax; i++)
    {
        if (i == 6)
        {
            continue;
        }
    }

下面是它对应的汇编代码:

00401268   mov         dword ptr [ebp-4],0Ah
10:       int i = 0;
0040126F   mov         dword ptr [ebp-8],0
11:       for(;i < nMax; i++)
00401276   jmp         main+31h (00401281)
00401278   mov         eax,dword ptr [ebp-8]
0040127B   add         eax,1
0040127E   mov         dword ptr [ebp-8],eax
00401281   mov         ecx,dword ptr [ebp-8]
00401284   cmp         ecx,dword ptr [ebp-4]
00401287   jge         main+43h (00401293)
12:       {
13:           if (i == 6)
00401289   cmp         dword ptr [ebp-8],6;
0040128D   jne         main+41h (00401291);条件不满足组跳转到循环结束处
14:           {
15:               continue;
0040128F   jmp         main+28h (00401278)
16:           }
17:       }
00401291   jmp         main+28h (00401278)
18:       return 0;
00401293   xor         eax,eax

从上面的汇编代码可以看到,continue语句也是一个跳转语句,它会直接跳转到循环体的开始位置。对于for来说相对特殊一些(我觉得循环变量自增并不属于循环体),由于第一次进入循环时并没有执行循环变量自增,所以它会跳转到循环变量自增的位置,其他则直接到循环开始处。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏欧阳大哥的轮子

C++的new和delete详解

C++中如果要在堆内存中创建和销毁对象需要借助关键字new和delete来完成。比如下面的代码

10340
来自专栏极客生活

macOS扫雷逆向破解

其中安全帽只有10个,用完了之后就需要在App Store进行购买,同时「高级」和「自定义」功能也需要在应用商店进行购买才可以玩。

17220
来自专栏欧阳大哥的轮子

深入iOS系统底层之汇编语言

要想完全的了解一个系统唯一的方法就是去阅读这个系统的源代码实现!这个原则对于一个iOS程序员也是如此。很幸运的是我们现在处于一个开源代码迸发的美好时代(这里要感...

19130
来自专栏用户画像

1.2.5 计算机系统的多级层次结构

第3级是操作系统层,它由操作系统程序实现。操作系统程序由机器指令和广义指令组成,这些广义指令是为了扩展机器功能而设置的,它是由操作系统定义和解释的软件指令,所以...

11040
来自专栏liuchengxu

编译器入门

编译器(compiler)就是一个翻译其他程序的程序而已。传统的编译器将源代码翻译为计算机能够理解的可执行机器代码(有一些编译器将源代码翻译为另一种编程语言。这...

22010
来自专栏黑白安全

追寻特征码轻松免杀灰鸽子

随着杀毒软件病毒库的日益庞大,病毒被杀的的概率也越来越大。往往早上编写完成的病毒,到了下午就被列入病毒库。面对病毒被杀的尴尬,我们只能走一条路:免杀。

17830
来自专栏编程之旅

iOS开发 —— Runtime

因为Objc是一门动态语言,所以它总是想办法把一些决定工作从编译连接推迟到运行时。也就是说只有编译器是不够的,还需要一个运行时系统 (runtime syste...

12630
来自专栏coderhuo

gcc编译临时文件存放路径

仔细看了下错误信息,这个ccGjoKTF.s应该是编译过程的中间文件,其中文件名是随机值。然而makefile中并未要求保留汇编代码。

18220
来自专栏FD的专栏

unlink漏洞的原理和利用

网上关于unlink漏洞的文章已经非常多了,但是作为一个web狗,为了搞明白这个漏洞,还是花了好长时间,中间踩了几个坑,写这篇文章是希望跟我一样啃二进制的web...

22420
来自专栏喔家ArchiSelf

MCU上的代码执行时间

在许多实时应用程序中,二八原则并不生效,CPU 可以花费95%(或更多)的时间在不到5% 的代码上。电动机控制、引擎控制、无线通信以及其他许多对时间敏感的应用程...

13820

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励