【汇编 C】循环语句goto、while、dowhile、for

发布于:2022-12-06 ⋅ 阅读:(698) ⋅ 点赞:(0)

目录

前言

一、什么是循环语句?

二、goto语句实现循环

        goto底层汇编

三、while循环语句 

        while底层汇编

四、dowhile语句 

        do while底层汇编 

五、for循环

        for语句的几种写法     

        for底层汇编 

总结

结语

封面


前言

        本教程内容可能会涉及到汇编,还是建议有一些汇编基础进行观看,当然我会尽力讲的通俗易懂,使用到的工具是vs2010、DTDEBUG。

一、什么是循环语句?

        在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。一组被重复执行的语句称之为循环体,能否继续重复,决定循环的终止条件。循环结构是在一定条件下反复执行某段程序的流程结构,被反复执行的程序被称为循环体。 循环语句是由循环体及循环的终止条件两部分组成的。

        例子:(这里就拿汇编举例吧)

        首先401210位置的jmp会跳到401230的位置,然后将1,2,3,4分别放到对应的寄存器中之后再一次使用jmp跳到401210的位置。这就是个简单的循环,不过它是一个死循环,按F8一直执行会发现程序会一直循环将1,2,3,4存入寄存器。

        下面就来介绍C语言中的几种循环方式。

二、goto语句实现循环

        C 语言中的 goto 语句允许把控制无条件转移到同一函数内的被标记的语句。

        注意:在任何编程语言中,都不建议使用 goto 语句。因为它使得程序的控制流难以跟踪,使程序难以理解和难以修改。任何使用 goto 语句的程序可以改写成不需要使用 goto 语句的写法。

        测试代码goto.c:

#include <stdio.h>
#include <Windows.h>

int main()
{
label1:								// 用于goto跳转的标签
	if(1)							// 1一直为真,所以程序死循环
	{
		printf("我爱你。\n");		// 每隔一秒会输出一次“我爱你”
		Sleep(1000);				// 延时1秒,程序会停下来一秒
		goto label1;				// goto语句跳到label1这个标签
	}
	system("pause");
	return 0;
}

        运行可以发现,程序每隔一秒会输出一个“我爱你”,并且是一直在输出,除非我们关闭程序,这就产生了个死循环。

        为什么是死循环?因为我们的条件是if(1),我们知道,如果if后面括号中的内容为真,就会执行if大括号中的内容,1一直都是真,所以会一直输出。

        如果想循环五次输出“我爱你”,改一下条件即可。

        代码如下:

#include <stdio.h>
#include <Windows.h>

int main()
{
    int ii = 5;
label1:								// 用于goto跳转的标签
	if(ii)							// 1一直为真,所以程序死循环
	{
        ii=ii-1;                    // 持续减1,直到条件变为0就不会执行循环
		printf("我爱你。\n");		// 每隔一秒会输出一次“我爱你”
		Sleep(1000);				// 延时1秒,程序会停下来一秒
		goto label1;				// goto语句跳到label1这个标签
	}
	system("pause");
	return 0;
}

        运行结果如下:

        这就不再是循环了,执行printf("我爱你。\n")五次之后就到了system("pause");这行代码 。

        那么标签能否放到goto语句下面呢?

        按照C语言从上往下的执行过程来讲,肯定是不可以的。但是goto语句可以。

        测试代码如下:

#include <stdio.h>
#include <Windows.h>

int main()
{
label1:								// 用于goto跳转的标签
	printf("label1\n");
	Sleep(1000);
	goto label2;

label2:
	printf("label2\n");
	Sleep(1000);
	goto label1;				// goto语句跳到label1这个标签
	system("pause");
	return 0;
}

        运行结果如下:

        label1和label2都有输出,所以是可以的。

        以如下代码为例,我们查看一下底层goto是如何实现的。

#include <stdio.h>
#include <Windows.h>

int ii = 5;

int main()
{
	__asm mov eax,eax;        // 此处设置断点

label1:
	if(ii)
	{
		ii--;
		printf("我爱你。\n");
		goto label1;
	}
	system("pause");
	return 0;
}

        goto底层汇编

        ctrl+alt+f7重新编译,f5调试,alt+8转到反汇编如下:

         

        通过这一行代码就可以知道,goto语句就是通过jmp来实现的。jmp是跳转到指定位置,goto也是。

三、while循环语句 

        C语言while循环的写法

        while(条件)

        {

                语句;

        }

        只要给定的条件为真,C 语言中的 while 循环语句会重复执行一个目标语句。

        while语句不像goto那样需要设置标签然后跳转。

        示例代码while.c:

#include <stdio.h>
#include <Windows.h>


int main()
{
	while(1)				// 条件1一直为真,所以会一直每隔1秒输出一个“我爱你”
	{
		printf("我爱你。\n");
		Sleep(1000);		
	}

	system("pause");
	return 0;
}

        自己运行一下看看结果。

        那么while语句能不能像goto那样设置条件只执行五次呢?

        可以。

        while后边的小括号就是用来设置条件的,他比goto+if更灵活。

        测试如下:

#include <stdio.h>
#include <Windows.h>


int main()
{
	int ii = 5;
	while(ii!=0)			// 等ii==0的时候就不会再循环了
	{
		ii--;				// 执行一次循环ii-1
		printf("我爱你。\n");
		Sleep(1000);		
	}

	system("pause");
	return 0;
}

        运行结果如下:

        转到反汇编看看while底层是如何实现的(这里可以把延时关闭,因为断点调试我们不需要延时,而且Sleep的汇编代码可能会干扰你的思路)。 

        代码:

#include <stdio.h>
#include <Windows.h>


int main()
{
	int ii = 5;
	__asm mov eax,eax;      // 此处设置断点

	while(ii!=0)			// 等ii==0的时候就不会再循环了
	{
		ii--;				// 执行一次循环ii-1
		printf("我爱你。\n");
	}

	system("pause");
	return 0;
}

        while底层汇编

        可以看到while语句底层也是先判断条件,然后执行完毕之后再jmp跳回原来的地址。

四、dowhile语句 

        dowhile和while语句的用法差不多,但是while语句是先执行再判断,相反do while是先循环再判断,这会导致即使do while的条件为假,也会执行一次。

        示例代码do_while.c:

#include <stdio.h>
#include <Windows.h>


int main()
{
	do
	{
		printf("我爱你。\n");
		Sleep(1000);
	}while(1);

	system("pause");
	return 0;
}

        自己运行一下看看结果,和上面一样,也是每隔1秒输出一个“我爱你”。

        那么如何做到只输出五次“我爱你”呢?

        和上面while的代码都差不多,这边建议自己先试着写写,然后再看带代码,如下:

#include <stdio.h>
#include <Windows.h>


int main()
{
	int ii = 5;
	do
	{
		ii--;
		printf("我爱你。\n");
		Sleep(1000);
	}while(ii);

	system("pause");
	return 0;
}

        运行结果如下:

        那么我们什么时候使用do while呢?

        我们知道do while与while最大的区别就是do while无论如何都会执行一次,所以当我们需要先输入再判断的时候就可以使用do while,如下:

#include <stdio.h>
#include <Windows.h>


int main()
{
	char getc;						// 定义一个变量用来接收到字符。
	do{
		getc = getchar();			// 接收往控制台输入的字符
		getchar();					// 接受字符'\n',因为我们输入字符后是用回车(\n)进行确认的,但是回车'\n'也算是个字符,也需要接收

		putchar(getc);				// 打印出接收到的字符。
		putchar('\n');				// 打印\n(回车)

	}while(getc != 'A');			// 如果我们输入的字符(getc)不是'A'就可以一直输入。

	system("pause");
	return 0;
}

        运行测试如下:

        如果不输入'A'就可以一直输入。

        do while底层汇编 

        测试代码:

#include <stdio.h>
#include <Windows.h>


int main()
{
	int ii = 5;
    __asm mov eax,eax;        // 此处设置断点
	do
	{
		ii--;
		printf("我爱你。\n");
	}while(ii);

	system("pause");
	return 0;
}

        汇编如下:

         dowhile底层是先处理事物代码,然后判断。

五、for循环

        for 循环允许您编写一个执行指定次数的循环控制结构。

        for语句的结构如下:

        for(表达式1;表达式2;表达式3)

        {

                // 需要执行的代码4

        }

        for语句的执行顺序是:

        1 2 4 3

        2 4 3

        2 4 3

        也就是说for循环执行完表达式1之后就会执行表达式2,然后再执行代码4,最后才执行表达式3,并且表达式1只会执行一次。并且表达式2必须包含bool类型,也就是说表达式2就相当于我们while循环中的判断真假的条件。

        当然也可以不写,就是说没有判断条件,直接循环。 

        验证for循环语句的执行顺序,代码如下,看不懂的不用看,看结果就行:

        下面for语句的第二种写法也可以验证这一观点。

        for.cpp:(这里使用cpp文件,因为T2()需要返回一个bool,这是C++的类型,当然也可以用int,因为bool也属于整型)

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>			// bool类型头文件

int x=1;					// 定义全局变量,作为判断条件

void T1()
{
	printf("T1\n");
}

bool T2()					// 这里bool也可以使用int,看自己喜欢而定
{
	printf("T2\n");
	return x--;             // 使用一次该函数的返回值之后,返回值会减1
}

void T3()
{
	printf("T3\n");
}

int main()
{
	for(T1();T2();T3())
	{
		printf("T4\n");
	}

	system("pause");
	return 0;
}

        这个代码的逻辑是:

        首先从main函数开始运行,然后for(T1();T2();T3) { T4 }。

        我们是为了验证1243的顺序,所以我们在T1、T2、T3函数中各自输出对应的数值。其中T2返回的是一个全局变量的值,我们将全局变量初始化为1。然后第一次循环的时候T2()的值就是1,会执行一次循环。第二次循环的时候判断T2的返回值时就已经是0了,不会再往下运行了,所以结果应该是T1,T2,T4,T3,T2(因为printf在return前面)

        运行结果如下:

         for语句执行顺序1243和表达式1只执行一次,这两个观点验证完毕。

        再次声明一次,上面的代码不需要看明白,只需要记住for执行顺序是1243即可,并且表达式1只执行1次,表达式2必须是bool类型作为判断条件。

        下面for语句的第二种写法也可以验证这一观点。

        for语句的几种写法     

1、

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>			// bool类型头文件


int main()
{
	for(;;)
	{
		printf("我爱你。\n");
	}
	system("pause");
	return 0;
}

        这样会一直循环,因为表达式2为空,他代表没有条件,那不就是无条件运行吗,无条件运行不就是无限循环了嘛。

2、

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>			// bool类型头文件


int main()
{
	int ii = 0;
	for(;ii<100;ii++)				// 0-99总共一百次。
	{
		printf("%d",ii);			// 第一次输出0,也证明了1243这个顺序,因为如果先执行3(i++),那么第一次输出的应该是1。
	}
	system("pause");
	return 0;
}

        运行结果如下:

 3、

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>			// bool类型头文件


int main()
{
	int ii;	
	for(ii=0;ii<100;ii++)			// 0-99总共一百次。
	{
		printf("%d\n",ii);			// 第一次输出0,也证明了1243这个顺序,因为如果先执行3(i++),那么第一次输出的应该是1。
	}
	system("pause");
	return 0;
}

        这就是最正规的C语言for循环写法,在外面声明ii,然后在循环里边表达式1进行初始化,表达式2进行判断,表达式3进行加减操作。

        运行结果和上面一样。

        可能有人会说,不是可以直接在for循环里进行int定义嘛

        那是C++的写法。

        如下:

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>			// bool类型头文件


int main()
{
	for(int ii=0;ii<100;ii++)			// 0-99总共一百次。
	{
		printf("%d\n",ii);			// 第一次输出0,也证明了1243这个顺序,因为如果先执行3(i++),那么第一次输出的应该是1。
	}
	system("pause");
	return 0;
}

        这样写在C++中是被允许的。但是C语言不允许,证明如下:

C++: 

C语言:

        以上便是for循环比较常用的写法。

        for底层汇编 

        测试代码:

#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>			// bool类型头文件


int main()
{
	int ii;
	__asm mov eax,eax;              // 此处设置断点
	for(ii=0;ii<100;ii++)			// 0-99总共一百次。
	{
		printf("%d\n",ii);			// 第一次输出0,也证明了1243这个顺序,因为如果先执行3(i++),那么第一次输出的应该是1。
	}
	system("pause");
	return 0;
}

        汇编如下:

        从汇编可以很容易的看出,for循环执行顺序的1243。 

总结

        无论是while、do while还是for,都各有应用的场景,当然goto也是,不过不建议使用goto,而且他也不那么好用是吧。虽然我们学了那么多循环,但是实际开发项目中你会发现还是for循环用的最多,所以掌握for循环是关键,当然while用的也不少。

结语

        还是感谢观看!另外有错误请指出,听不懂的也请问,谢谢大家。

封面

 

本文含有隐藏内容,请 开通VIP 后查看