[c]C语言提高效率和减少代码size的方法

C语言提高代码效率的几种方法

一段完美的代码不仅在于找到一个给定的问题的解决方案,但在它的简单性,有效性,紧凑性和效率(内存)。设计的代码比实际执行更难。因此,每一个程序员当用C语言开发时,都应该保持这些基本的东西在头脑中。本文向你介绍规范你的C代码的5种方法。


1、在可能的情况下使用typedef替代macro.当然有时候你无法避免macro,但是typedef更好。

typedefint* INT_PTR;INT_PTR a ,b;# define INT_PTR int*;INT_PTR a ,b;
在这个宏定义中,a是一个指向整数的指针,而b是只有一个整数声明。使用typedef ab都是整数的指针。

2
、在一个逻辑条件语句中常数项永远在左侧。

int x = 4;if
x = 1{ x = x + 2;printf"%d",x);// Output is 3 }

int x = 4;if
1 = x{ x = x + 2;printf"%d",x);
// Compilation error }

使用"="赋值运算符,替代"=="相等运算符,这是个常见的输入错误。常数项放在左侧,将产生一个编译时错误,让你轻松捕获你的错误。注:"="是赋值运算符。b = 1会设置变量b等于值1. "=="相等运算符。如果左侧等于右侧,返回true,否则返回false.


3
、确保声明和定义是静态的,除非您希望从不同的文件中调用该函数。

在同一文件函数对其他函数可见,才称之为静态函数。它限制其他访问内部函数,如果我们希望从外界隐藏该函数。现在我们并不需要为内部函数创建头文件,其他看不到该函数。

静态声明一个函数的优点包括:

A
)两个或两个以上具有相同名称的静态函数,可用于在不同的文件。

B
)编译消耗减少,因为没有外部符号处理。

4
、节约内存(内存对齐和填充的概念)

struct { char c;inti;short s;}str_1;struct { char c;shorts;inti;}str_2;
假设一个字符需要1个字节,short占用2个字节和int需要4字节的内存。起初,我们会认为上面定义的结构是相同的,因此占据相同数量的内存。然而,而str_1占用12个字节,第二个结构只需要8个字节?这怎么可能呢?

请注意,在第一个结构,3个不同的4个字节被分配到三种数据类型,而在第二个结构的前4个自己charshort可以被采用,int可以采纳在第二个的4个字节边界(一共8个字节)。

5
、使用无符号整数,而不是整数的,如果你知道的值将永远是否定的。

有些处理器可以处理无符号的整数比有符号整数的运算速度要快。(这也是很好的实践,帮助self-documenting代码)。

 

嵌入开发中C可以提高效率和减少代码size的方法

  1 switch-case语句。在程序中经常会使用switch-case语句,每一个由机器语言实现的测试和跳转仅仅是为了决定下一步要做什么,就浪费了处理器时间。为了提高速度,可以把具体的情况按照它们发生的相对频率排序。即把最可能发生的情况放在第一,最不可能发生的情况放在最后,这样会减少平均的代码执行时间。
  2全局变量。使用全局变量比向函数传递参数更加有效率,这样做去除了函数调用前参数入栈和函数完成后参数出栈的需要。当然,使用全局变量会对程序有一些负作用。
  3嵌入式系统编程应避免使用标准库例程,因为很多大的库例程设法处理所有可能的情况,所以占用了庞大的内存空间,因而应尽可能地减少使用标准库例程。
    (1) Inline
函数

   C++中,关键字Inline可以被加入到任何函数的声明中。这个关键字请求编译器用函数内部的代码替换所有对于指出的函数的调用。这样做在两个方面快于函数调用。这样做在两个方面快于函数调用:第一,省去了调用指令需要的执行时间;第二,省去了传递变元和传递过程需要的时间。但是使用这种方法在优化程序速度的同时,程序长度变大了,因此需要更多的ROM。使用这种优化在Inline函数频繁调用并且只包含几行代码的时候是最有效的。 

    (2)用指针代替数组

   在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。
   
数组索引               指针运算
    For(;;){                p=array
    A=array[t++];           for(;;){
                                 a=*(p++);
    ......                    ......
    }                       }

   指针方法的优点是,array的地址每次装入地址p后,在每次循环中只需对p增量操作。在数组索引方法中,每次循环中都必须进行基于t值求数组下标的复杂运算。
    (3)
不定义不使用的返回值

    function函数定义并不知道函数返回值是否被使用,假如返回值从来不会被用到,应该使用void来明确声明函数不返回任何值。

    (4)手动编写汇编

   在嵌入式软件开发中,一些软件模块最好用汇编语言来写,这可以使程序更加有效。虽然C/C++编译器对代码进行了优化,但是适当的使用内联汇编指令可以有效的提高整个系统运行的效率。
    (5)
使用寄存器变量

   在声明局部变量的时候可以使用register关键字。这就使得编译器把变量放入一个多用途的寄存器中,而不是在堆栈中,合理使用这种方法可以提高执行速度。函数调用越是频繁,越是可能提高代码的速度。
    (6)
使用增量和减量操作符

   在使用到加一和减一操作时尽量使用增量和减量操作符,因为增量符语句比赋值语句更快,原因在于对大多数CPU来说,对内存字的增、减量操作不必明显地使用取内存和写内存的指令,比如下面这条语句:
    x=x+1;
   
模仿大多数微机汇编语言为例,产生的代码类似于:
    move A,x      ;
x从内存取出存入累加器A
    add A,1       ;
累加器A
1
    store x       ;
把新值存回x

   如果使用增量操作符,生成的代码如下:
    incr x        ;x
1
   
显然,不用取指令和存指令,增、减量操作执行的速度加快,同时长度也缩短了。

    (7)减少函数调用参数

   使用全局变量比函数传递参数更加有效率。这样做去除了函数调用参数入栈和函数完成后参数出栈所需要的时间。然而决定使用全局变量会影响程序的模块化和重入,故要慎重使用。
    (8)Switch
语句中根据发生频率来进行case排序

    switch语句是一个普通的编程技术,编译器会产生if-else-if的嵌套代码,并按照顺序进行比较,发现匹配时,就跳转到满足条件的语句执行。使用时需要注意。每一个由机器语言实现的测试和跳转仅仅是为了决定下一步要做什么,就把宝贵的处理器时间耗尽。为了提高速度,没法把具体的情况按照它们发生的相对频率排序。换句话说,把最可能发生的情况放在第一位,最不可能的情况放在最后。
    (9)
将大的switch语句转为嵌套switch语句

   switch语句中的case标号很多时,为了减少比较的次数,明智的做法是把大switch语句转为嵌套switch语句。把发生频率高的case标号放在一个switch语句中,并且是嵌套switch语句的最外层,发生相对频率相对低的case标号放在另一个switch语句中。比如,下面的程序段把相对发生频率低的情况放在缺省的case标号内。         pMsg=ReceiveMessage();
        switch (pMsg->type)
        {
        case FREQUENT_MSG1:
        handleFrequentMsg();
        break;
        case FREQUENT_MSG2:
        handleFrequentMsg2();
        break;
        ......
        case FREQUENT_MSGn:
        handleFrequentMsgn();
        break;
        default:                      //
嵌套部分用来处理不经常发生的消息

        switch (pMsg->type)
        {
        case INFREQUENT_MSG1:
        handleInfrequentMsg1();
        break;
        case INFREQUENT_MSG2:
        handleInfrequentMsg2();
        break;
        ......
        case INFREQUENT_MSGm:
        handleInfrequentMsgm();
        break;
        }
        }
    
如果switch中每一种情况下都有很多的工作要做,那么把整个switch语句用一个指向函数指针的表来替换会更加有效,比如下面的switch语句,有三种情况:
        enum MsgType{Msg1, Msg2, Msg3}
        switch (ReceiveMessage()
        {
        case Msg1;
        ......
        case Msg2;
        .....
        case Msg3;
        .....
        }

    为了提高执行速度,用下面这段代码来替换这个上面的switch语句。

        /*准备工作*/
        int handleMsg1(void);
        int handleMsg2(void);
        int handleMsg3(void);
        /*
创建一个函数指针数组
*/
        int (*MsgFunction [])()={handleMsg1, handleMsg2, handleMsg3};
        /*
用下面这行更有效的代码来替换switch语句
*/
        status=MsgFunction[ReceiveMessage()]();

     (10)避免使用C++的昂贵特性

     C++在支持现代软件工程、OOP、结构化等方面对C进行了卓有成效的改进,但在程序代码容量、执行速度、程序复杂程度等方面比C语言程序性能差一些。并不是所有的C++特性都是昂贵的。比如,类的定义是完全有益的。公有和私有成员数据及函数的列表与一个 struct 及函数原形的列表并没有多大的差别。单纯的加入类,既不会影响代码的大小,也不会影响程序的效率。但C++的多重继承、虚拟基类、模板、异常处理及运行类型识别等特性对代码的大小和效率有负面的影响,因此对于C++的一些特性要慎重使用,可做些实验看看它们对应用程序的影响。

 
如何提高代码效率

代码效率包括两个方面内容:代码的大小和代码执行速度。如果代码精简和执行速度快,我们就说这个代码效率高。一般情况下,代码精简了速度也相应提上来了。单片机的ROM和RAM的空间都很有限,当您编程时遇到单片机的ROM和RAM的不够用的时候,或者您的程序要求较高的执行速度时,我们就得面对解决代码效率问题了。如何提高代码效率?现笔者以一个LED闪烁的程序为例与您探讨。

 #i nclude<reg52.h>//包含头文件

 sbit led=P2^0;//定义位变量led,使其关联单片机管脚P2.0

 void Delayms(unsigned int t);//声明延时函数

 int main(void)//主函数(C语言程序入口函数)

 {

       while(1)

       {

             led=0;//P2.0拉低,点亮LED

             Delayms(500);//调用延时函数,延时500毫秒

             led=1;//P2.0拉高,熄灭LED

             Delayms(500);//调用延时函数,延时500毫秒

       }

       return 0;

 }

void Delayms(unsigned int t)//定义延时函数

{

        unsigned int i,j;

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

               for(j=0;j<120;j++);//大约延时1毫秒

}

这是指示灯LED闪烁的C源码,这个源码在Keil uVision4 生成的程序代码是67个字节。下面我们就采用几个方法来提高这个程序的效率。

一.尽量定义局部变量

单片机程序的全局变量一般是放在通用数据存储器(RAM)中,而局部变量一般是放在特殊功能寄存器当中。处理寄存器数据的速度比处理RAM数据要快,如果在一个局部函数里调用一个全局变量将会多生成好几个代码出来。所以,少定义全局变量,多定义局部变量。如上例中,如果把延时函数里的i和j定义为全局变量,编译后程序代码会增加到79个字节,多了12个字节。

二.省略函数声明

在一个单片机程序里我们习惯在main函数的前面先声明被调用函数,然后在mian函数的下面再定义被调用函数。这样的写法固然是一个好习惯,但每声明一个函数会增加几个代码,而且函数形参数据类型越大、形参越多增加的代码就越多,显然这不是什么好事。如果不声明编译器又报错,怎么办?C编译器的编译顺序是从上往下编译,只要被调用的函数在主调函数调用之前定义就没有问题了。所以,笔者的习惯写法是不用事先声明函数,但要按先后顺序(被调用函数一定要在主调函数之前写好)来写函数定义,到最后再写main函数。这样做编译器不但不会报错,而且代码得到精简了。如上例中,把延时函数的声明删除了,然后把延时函数的定义搬到main函数的上面,编译后程序代码变成63个字节,减少了4个字节。

三.省略函数形参

函数带形参,是为了在函数调用时传递实参,不但可以避免重复代码出现,还可以通过传递不同的实参值多次调用函数且实现不同的函数功能,总体代码也会得到精简。在实际编程的时候,我们只要注意,还可以进一步精简代码。对于不是多次调用或者多次调用但实参值不变的函数我们可以省略函数形参。如上例中的延时函数,我们把它改成不带形参的函数:

void Delayms(void)//定义延时函数

 {

     unsigned int i,j;

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

          for(j=0;j<120;j++);//大约延时1毫秒

 }

编译后,程序代码变成了56个字节,减少了11个字节。

四.改换运算符

也许您可能没有注意到C运算符的运用也会影响程序代码的数量。如上例中,把延时函数里的自加运算符改成自减运算符后,如:

void Delayms(unsigned int t)//定义延时函数

 {

     unsigned int i,j;

     for(i=t;i>0;i--)

          for(j=120;j>0;j--);//大约延时1毫秒

 }

编译后,程序代码变成了65个字节,减少了2个字节。

通过改换运算符能达到精简代码的例子还有:

1.把求余运算表达式改为位与运算表达式。如:b=a%8 可以改为:b=a&7。

2.把乘法运算表达式改为左移运算表达式。如:b=a*8 可以改为:b=a<<3。

3.把除法运算表达式改为右移运算表达式。如:b=a/8 可以改为:b=a>>3。

五.选择合适的数据类型

C语言里选择变量的数据类型很讲究,变量的数据类型过小满足不了程序的要求,变量的数据类型过大会占用太多的RAM资源。您可能还没有注意到数据类型定义也影响程序代码的大小,而且这个影响还不小。如上例中,延时函数里的局部变量j定义的数据类型明显偏大,如果把它由unsigned int改成unsigned char 。编译后,程序代码变成了59个字节,减少了8个字节。

六.直接嵌入代码

    在您的程序里如果某个函数只调用一次,而您又要求代码提高执行速度,建议您不要采用调用函数的形式,而应该将该函数里的代码直接嵌入主调函数里,代码执行效率会大大提高。

七.使用效率高的C语句

C语言里有一个三目运算符“?”,俗称“问号表达式”。很多程序员都很喜欢使用,因为它逻辑清晰表达简洁。

看这个问号表达式:c=(a>b) ? a+1 : b+1;实际上等效于以下的if…else结构:

if (a>b)  c=a+1;

else  c=b+1;

可以看到,使用问号表达式,语句相当简洁,但它的执行效率却很低,远没有if…else语句效率高。所以,当您的程序要求提高执行速度的话,建议您不要使用问号表达式了。

另外,do…while语句也比while语句的效率高。

代码的效率问题,不是我们编程中的主要问题,除了程序要求较高的执行速度或者单片机的ROM和RAM不够用的时候才会考虑。一般情况下,我们不用在乎。如果您一味追求高效率的代码,可能会影响代码的可读性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值