知识屋:更实用的电脑技术知识网站
所在位置:首页 > 科技

笨鸟先飞学编程系列之二 基础代码的编写

发表时间:2022-03-24来源:网络

 

大家明白了怎么定义变量,怎么给变量赋值了,自然的就应该进一步了解一下一些详细的代码怎么编写了。

 

今天我们的任务比较简单,就讲一下如何编写代码及实现代码的流程控制。应该说这个是非常简单的东西了,本来我没打算讲它,可是本着一步一个脚印的原则,我还是简要的说一下。在本次课题之后,我会紧跟着出一个函数的专题,来作为代码篇的完善,至此大家应该能独立的写自己的程序了。

 

本次课题知识点不是很多(也不少,但是都很简单。),主要是在于多写,多练,知道自己能把一些现实的问题转换成代码来解决问题。

 

       不多废话,先说下本次课题要写的主要内容:

程序的运算和逻辑判断三种程序流程控制结构详解。养成良好的代码编写风格。结束语

 

下面开始进入正题。

一、           程序的运算和逻辑判断。

说计算是电脑最基本的功能,相必没有人会反对的。做程序,自然最基础的就是这些运算了。

我知道,看这个破烂文章的朋友对数据应该都是不感冒的。不过大家放心,这里牵扯的运算都很简单,就是小学的加、减、成、除、取余数,是、非、与或和位移,没别的了。先让我们来了解下算术运算。

1.         算术运算。

加减乘除相比大家都应该很了解的。这里我不想费太多的篇幅讲述这些大家都明白的知识,我把要说的知识列出来,大家自己了解就好。

 

用于算术运算的运算符有:

 

我想不用我说,加、减、成、除这些运算符大家都接触过的,关于“%”运算符,其实就是取余数;用这些运算符可以将一些数字,变量等连接起来,进行运算,这样的式子叫做表达式。例如:

int i =5;

i +7;     // 这里就是一个表达式。

 

“=”这个运算符其实不属于算术运算,它也并非是我们数学课上讲的“等于”,而是一个“赋值”运算符,它用来将一个常量(数字、字母等)赋值给一个变量的赋值运算符,由于我们进行算术运算以后,通常都会将运算结果保存到一个变量中,所以,我把这个赋值运算符归类到这里。而我们日常的“等于”运算符是:“= =”,它属于逻辑运算符,我们将在下一节中讲到它。

 

关于“++ 、 — —”这两个运算符,上图中已经说明了,它是对变量的对自己的自加或者自减运算,等同于:变量 = 变量+1; 或者 变量 = 变量 — 1;

 

现在让我们来举几个例子,来说明下这些算术运算符的用法。

void  main()

{

     int x = 1;

     int y = 2;

     int z = 20;

 

     x ++;         // 等同于x = x + 1;

     y --;         // 等同于y = y - 1;

 

     z /= x;       // 等同于z = z / 1;

     printf("%d, %d, %d\n", x, y, z);    // 打印 x,y,z 的结果。

 

     z %= x;  // 计算得出 z / x 以后的余数

 

     x = x+y;

     z -= x;

     printf("%d, %d, %d\n", x, y, z);   // 打印 x,y,z 的结果。

}

 

请先不要看下面的截图,先看下这个程序,分析一下,这两次的输出结果会是多少,然后对比下图:

看下就算的是不是正确,如果正确了,那恭喜你,基础的算术运算就算通过了,接下来,我们需要调试这段代码,来熟悉一些相关的汇编命令,具体操作如下图所示:

在代码上右击鼠标,选择如下命令:

 

我们来详细分析一下我们现在看到的代码:

1:    #include "stdio.h"

2:

3:    void main()

4:    {

  00401010   push        ebp

  00401011   mov         ebp,esp   ;// 将现有的堆栈给了EBP寄存器。

  00401013   sub         esp,4Ch   ;// 分配x4C大小的堆栈空间用来运算。

  00401016   push        ebx

  00401017   push        esi

  00401018   push        edi       ;// 保存寄存器环境。

  00401019   lea         edi,[ebp-4Ch] ;// 这里用ebp寄存器减去一个偏移来定位我们定义的变量,

  0040101C   mov         ecx,13h   ;// 这里-4Ch是用来定位到堆栈头,把堆栈内容改成int3中断以免内存泄露。

  00401021   mov         eax,0CCCCCCCCh     ;// 以上操作是保存堆栈环境,分配堆栈空间。

  00401026   rep stos    dword ptr [edi]    ;// 在下一次课题讲述函数时,我们会讲到,这里大家可以略过。

5:        int x = 1;

  00401028   mov    dword ptr [ebp-4],1;// [ebp-4]是我们的变量x,dword ptr是用来修饰这个变量是DWORD类型(也就是整型)。

6:        int y = 2;

  0040102F   mov         dword ptr [ebp-8],2;// MOV就是汇编指令,相当于我们C语言中"=" 赋值运算符,它的具体用法自己百度。

7:        int z = 20;

  00401036   mov         dword ptr [ebp-0Ch],14h

8:

9:        x ++;       // 等同于x = x + 1;

  0040103D   mov         eax,dword ptr [ebp-4]

  00401040   add         eax,1; // add指令就是我们C语言的"+"运算符,还有一个INC指令相当于我们的"++"运算符

  00401043   mov         dword ptr [ebp-4],eax

10:       y --;       // 等同于y = y - 1;

  00401046   mov         ecx,dword ptr [ebp-8]

  00401049   sub         ecx,1

  0040104C   mov         dword ptr [ebp-8],ecx

11:

12:       z /= x;     // 等同于z = z / 1;

  0040104F   mov         eax,dword ptr [ebp-0Ch]

  00401052   cdq

  00401053   idiv        eax,dword ptr [ebp-4]

  00401056   mov         dword ptr [ebp-0Ch],eax

13:       printf("%d, %d, %d\n", x, y, z);

  00401059   mov         edx,dword ptr [ebp-0Ch]

  0040105C   push        edx

  0040105D   mov         eax,dword ptr [ebp-8]

  00401060   push        eax

  00401061   mov         ecx,dword ptr [ebp-4]

  00401064   push        ecx

  00401065   push        offset string "%d, %d, %d\n" (0042001c)   // 传递参数,具体规则将在下次课题“函数”中讲述。

  0040106A   call        printf (004010f0)       ; // 调用printf函数打印结果

  0040106F   add         esp,10h       ; // 这里是C条用的对战平衡方式。(具体将在下次课题“函数”中讲述)

14:

15:       z %= x;

  00401072   mov         eax,dword ptr [ebp-0Ch]

  00401075   cdq

  00401076   idiv        eax,dword ptr [ebp-4]

  00401079   mov         dword ptr [ebp-0Ch],edx

16:

17:       x = x+y;

  0040107C   mov         edx,dword ptr [ebp-4]

  0040107F   add         edx,dword ptr [ebp-8]

  00401082   mov         dword ptr [ebp-4],edx

18:       z -= x;

  00401085   mov         eax,dword ptr [ebp-0Ch]

  00401088   sub         eax,dword ptr [ebp-4]

  0040108B   mov         dword ptr [ebp-0Ch],eax

19:       printf("%d, %d, %d\n", x, y, z);

  0040108E   mov         ecx,dword ptr [ebp-0Ch]

  00401091   push        ecx

  00401092   mov         edx,dword ptr [ebp-8]

  00401095   push        edx

  00401096   mov         eax,dword ptr [ebp-4]

  00401099   push        eax

  0040109A   push        offset string "%d, %d, %d\n" (0042001c)

  0040109F   call        printf (004010f0)

  004010A4   add         esp,10h

20:   }

  004010A7   pop         edi                     ; // 恢复寄存器环境

  004010A8   pop         esi

  004010A9   pop         ebx

  004010AA   add         esp,4Ch                 ; // 平衡堆栈

  004010AD   cmp         ebp,esp

  004010AF   call        __chkesp (00401170)     ; // DEBUG 模式程序专用的堆栈检查函数。

  004010B4   mov         esp,ebp

  004010B6   pop         ebp

004010B7   ret

 

相信你根据上述代码中的提示,应该能将这个汇编代码看的差不多,当然,看不明白也没有关系,我们需要掌握的汇编指令及其用法很少,就下面几个:

       mov/lea:         赋值/取地址。

       add:               加法指令。

       sub:               减法指令。

       div/idiv:          除法指令。

       mul/imul:        乘法指令。

这些汇编指令的具体用法大家自己百度或者参考相关资料,这里不做详细说明, 下面开始我们的逻辑、关系运算。

2.         逻辑、关系运算。

提起什么逻辑运算,或者什么关系运算,看名字貌似很复杂的。不过这里可能让大家放心的是,这些运算我们日常生活中经常用到,无非就是 真的,假的,是,不是,并且,或者之类的操作。

 

用于逻辑运算的运算符有如下几个:

运算符

含义

&&

 与(并且)

||

或(或者)

!

非(不是)

             

                     用于关系运算的运算符有如下几个:

                     

这些运算无非就是为了判断一个表达式成立不成立,在C语言中,只要表达式的值不为零并且符合关系运算符的要求,那这个表达式就成立的,就可以用上述的两种运算符进行比较运算,一般情况下,这些运算符会配合下节我们要讲述的流程控制语句来使用,所以这里我就不给出具体用法的例子了,有情趣的朋友,可以继续看下面章节中的例子

 

到现在我想主要的运算我都讲完了,虽然不是很详细,但是我想,只要大家坚持努力,多多百度,这些知识一定会掌握好的。

 

小学的时候,我们学过四则运算,在运算的时候,遵循先乘除,后加减,有括弧的先算括弧里面的,这个规则在这里一样使用,只不过在编程环境中,运算符很多,所以需要有个更为详细的运算符优先级表。这里我把它贴出来,但是还希望大家能够尽量的使用括弧来让人看的容易,以免出错,具体优先级表转载如下:

更详细的用法可以参考如下链接:http://www.xxlinux.com/linux/article/development/soft/20060909/4128.html

二、           三种程序流程控制结构详解。

有了上述的运算,我想大家都可以写出一些很简单的代码了,但是我们在写代码的时候,肯定会遇到类似这样的问题:

Ø         有时候,我们写的代码必须要在达到某种条件之后才可以执行,否则不让运行。

Ø         有时候,我们写的代码很庞大,很罗嗦,因为它有几种可能需要我们来写出几个程序。

Ø         有时候,我们写的代码需要一直重复执行直到某种条件不成立了才不执行。

 

只要你遇到过上述的问题,那我们这节课的内容就正是你所需要的。不多废话,进入正题:

1.         顺序结构

到现在为止,我们写的所有的代码都是顺序结构的,所谓顺序结构,就是代码从第一条指令开始执行,直到执行完最后一条。中间不会落下任何一条指令。

 

想必大家现在应该能理解什么是顺序结构了,所以我不想再这里浪费太多的篇幅,直接进入下一小节。

2.         分支结构

所谓分支结构,就是代码在达到某种条件的时候,执行某些指令,否则就执行别的指令。

分支结构是改变代码执行顺序最简单的方式,所以大家一定可以很容易的掌握它的,下面让我们一个一个的来看。

a)        if … else结构

这个结构算是编程中最基础的结构了,它有三种格式,我在这里列出来,大家可以根据实际情况选择使用哪个:

第一种格式:

if (条件表达式)

{   

     //条件成立时执行这里的语句

     …

}

第二种格式:

if (条件表达式)

{   

     //条件成立时执行这里的语句

     …

}

else

{

     //条件不成立时执行这里的语句

     …

}

第三种格式:

if (条件表达式1)    

{

     //条件表达式1成立时执行这里的语句

     …

}                   

else if (条件表达式2)  // 这里的else if可以有无限多个(如果有很多个时可以参考使用switch语句)。

     //条件表达式2成立时执行这里的语句

     …

}                   

else                

{

     //条件表达式都不成立时执行这里的语句

     …

}   

 

                            为了更好的说明这个语句的用法,我举个例子:

int MaxNum(int num001,  int num002,  int num003)

{

     if (num001 >= num002)

     {

         if (num001 >= num003)

         {

              return num001;         // 将 num001 作为函数的结果返回出来。

         }

         else

         {

              return num003;

         }

     }

     else

     {

         if (num002 >= num003)

         {

              return num002;

         }

         else

         {

              return num003;

         }

     }

}

                     说明:上述代码中的功能是从提供的三个数:num001、num002、num003中选出最大的数来。

至于与这些if有关的汇编指令就是跳转,像sub,cmp,test,之类的比较指令来影响相应的标志寄存器还有JE,JNE,JB,JNB之类的跳转指令来跳转到指定的代码中执行,大家可以像我们分析算术运算的方式一样,去调试它,去分析它,去掌握这些比较和跳转指令的用法。

 

这里我就省下篇幅,继续我们的switch结构。

b)        switch … 结构

上小节中讲述的if语句,是用于少数分支时的处理语句,它写起来方便,代码简洁明了,但是如果一些表达式的结构可能有5种甚至更多种结果,需要我们分别作出不同的处理时,最好的选择就是用wsitch语句。      

先说明一下switch结构的语法格式:

switch(表达式结果或者存放结果的变量)

{

case  结果1:

     // 当switch后的括弧中的值是结果时,就执行这里的语句

     ...;

     break;   // break是用来跳出分支结构的关键字,如果这里没有它,只要结果是结果1,那从结果1开始向下的所有指令都会被执行(包括结果2,结果3……)。

 

case  结果2:

     // 当switch后的括弧中的值是结果时,就执行这里的语句

     ...;

     break;   // 如果这里没有这个关键字时,只要上面的结果是结果2,那从结果2开始向下的所有指令都会被执行(包括结果3,结果4……)。

 

case  结果3:

     // 当switch后的括弧中的值是结果时,就执行这里的语句

     ...;

     break;   // 同上

 

case  结果N:

     // 当switch后的括弧中的值是结果N时,就执行这里的语句

     ...;

     break;  

 

default:

     // 当switch后的括弧中的值不是上面列出的任何一个值时,就执行这里的语句

     ...;

}

 

                     这里写的可能有点模糊,我给出一个代码片段,我说明一下switch语句的用法:

PGAME_CHAR_INFO pGCI = GetCharInfoPoint();

switch (pGCI->dwZhiYe)

{

case 1:

     lstrcpyW(szTemp, L"灵剑\0");

     break;

case 2:

     lstrcpyW(szTemp, L"日羽\0");

     break;

case 3:

     lstrcpyW(szTemp, L"枪侠\0");

     break;

case 4:

     lstrcpyW(szTemp, L"萨满\0");

     break;

case 5:

     lstrcpyW(szTemp, L"法皇\0");

     break;

case 6:

     lstrcpyW(szTemp, L"药王\0");

     break;

default:

     lstrcpyW(szTemp, L"未知\0");

     break;

}

 

其实,这个switch的汇编形式跟if结构很像,唯一的区别就是每个分支后面都会有一个break跳转(JMP)指令,大家可以自己试着去调试这段代码,分析一下尽量掌握这些代码的汇编形式。

3.         循环结构

所谓循环结构,就是一直重复执行某段指定的语句,知道条件不满足了为止。

a)        for循环结构

好,按照我们的习惯,我先写出这个语句的基本语法结构:

for (初始值; 满足条件; 增量)

{

     要循环的语句;

}

                            例如下面的代码:

#include "stdio.h"

 

void main()

{

     int i = 10;

    

     // 循环打印输出0到之间的所有自然数。

     for (int x = 0; x 

     00401010   push        ebp

     00401011   mov         ebp,esp

     00401013   sub         esp,48h

     00401016   push        ebx

     00401017   push        esi

     00401018   push        edi

     00401019   lea         edi,[ebp-48h]

     0040101C   mov         ecx,12h

     00401021   mov         eax,0CCCCCCCCh

     00401026   rep stos    dword ptr [edi]

5:        int i = 10;

     00401028   mov         dword ptr [ebp-4],0Ah   ; // 初始化变量i

6:

7:        // 循环打印输出0到之间的所有自然数。

8:        for (int x = 0; x 

收藏
  • 人气文章
  • 最新文章
  • 下载排行榜
  • 热门排行榜