基本编程语句的效率特性

基本编程语句的效率特性

一、选择语句的效率特性

1、  充分考虑if语句中逻辑表达式的执行效率

if中的逻辑表达式的运算遵循一下两个原则:

1、表达式从左到右求值。

2、当确定结果后,将不再进行计算。对于逻辑(&&)与而言,当出现表达式为假时停止运算;对于逻辑或(||)而言,当出现表达式为真时停止以后的运算。

例子:

//不充分逻辑与

       char str[] = "hello world!";

       int b = 0;

       if((strlen(str)) > 10 && b > 10)

       {

              k++;

       }

//充分逻辑与

       char str[] = "hello world!";

       int b = 0;

       if(b > 10 && (strlen(str)) > 10)

       {

              k++;

       }

//不充分逻辑或

       char str[] = "hello world!";

       int b = 11;

       if((strlen(str)) > 10 || b > 10)

       {

              k++;

       }

//充分逻辑或

       char str[] = "hello world!";

       int b = 11;

       if(b > 10 || (strlen(str)) > 10)

       {

              k++;

       }

2、  多层次判断下的效率特性

在多层次if语句的执行中,首先判断是否满足第一个条件,若满足则停止后面的比较,若不满足则进行第二个比较,依次进行。这必然导致越是后面的比较需要执行的语句越多,最后面的比较需要执行的语句最多。这必然要求我们将最有可能执行的语句尽可能的放在if…else结构的前部。

例子:若a的值小于10的概率比较大,介于10与100之间次之,大于100小于1000的概率最小则,如此安排比较顺序,则有助于提高效率。

       if(a < 10)

       {

              Function1();

       }

       else if(a < 100)

       {

              Function2();

       }

       else if(a < 1000)

       {

              Function3();

       }

3、  转移表的效率特性

当选项的范围可以转化为连续的数字范围(0、1、2、3、4)时,可以考虑运用转移表。其原理为:将待调用的函数指针插入表中,用选择项作为表的索引直接调用相应函数。

例子:

       typedef long (*Functs)(char c);

       Functs JumpTable[] = {Fun1, Fun2, Fun3, Fun4};

       …

       long result = JumpTable[selector](c);

注:转移表解决方案的实现对每个可能的选择拥有同样的执行效率。

4、  switch语句的效率特性

switch语句均可以转化为if…else语句实现,但是switch语句拥有较高的效率,因为编译器自动对其进行了优化,对于连续的数字将自动转化为转移表实现,对于随机的常数表达式将通过二分优化等方法进行优化。

在switch语句中,default中的语句最先执。故我们尽可能的把最经常执行的选择设置为默认情况。

二、循环语句的效率特性

循环语句可以一次编码重复执行,使得代码显得紧凑。但是循环可以在很大程度上影响程序的性能,如果循环体内存在低效的部分,循环会将其成倍的放大,此倍数即为循环体重复的次数。除了尽量提高循环体内语句的执行效率之外,我们还应该尽量的减少不必要的循环来提高程序的效率。

1、  退出循环

通过设置退出标志作为循环判断的条件,并且当可以完成查找后设置循环标志为真。从而退出循环。

void FindId(Student *stu, int index, int searchId)

{

       int flag = 0;

       for(int i = 0; i < index && !flag; i++)

       {

              if(stu.GetId() == searchId)

              {

                     flag = 1;

              }

              ......

       }

}

break语句可以使得我们在达到我们的目的后,直接退出循环,从而省去不必要的循环执行。

void FindId(Student *stu, int index, int searchId)

{

       for(int i = 0; i < index; i++)

       {

              if(stu.GetId() == searchId)

              {

                     break;

              }

              ......

       }

}

       在达到目的后直接返回也可以达到直接退出循环的目的。

int FindId(Student *stu, int index, int searchId)

{

       for(int i = 0; i < index; i++)

       {

              if(stu.GetId() == searchId)

              {

                     return stu.GetAge();

              }

              ......

       }

}

2、  跳过部分循环

continue语句可以跳过循环体内不必要的语句来加速循环。

void FindId(Student *stu, int index, int searchId)

{

       for(int i = 0; i < index; i++)

       {

              if(stu.GetId() < searchId)

              {

                     continue;

              }

              ......

       }

}




 

高效使用C/C++变量

1、  由于变量类型而引效率问题

在加法、减法以及乘法运算中,对整型的操作效率明显高于针对浮点型和双精度类型的操作效率,因此,在循环中运用整型运算代替浮点型和双精度类型,将会提高程序的效率。

例子:

双精度类型运算:

double s = 2.5;

for(int i = 0; i < 100; i++)

{

s *= 3;

}

转化为整型后的运算:

       int s1 = 25;

       double s2;

       for(int i = 0; i < 100; i++)

       {

              s1 *= 3;

       }

       s2 = s1/10.0;

2、  由于重复定义和释放变量而引起的效率问题

定义和释放变量同样需要引起开销,尤其在循环中重复的定义和释放变量将影响程序的内存和性能。因此,将循环中重复定义和释放的变量移到循环的外面,将会提高程序的效率。

变量定义在循环体内部:

for(int i = 0; i < 100; i++)

{

        int k = 0;

        if(a[i] > a[i+1])

        {

               k = a[i];

               a[i] = a[i+1];

               a[i+1] = k;

        }

}

变量定义在循环体外部:

int k = 0;

for(int i = 0; i < 100; i++)

{

        if(a[i] > a[i+1])

        {

               k = a[i];

               a[i] = a[i+1];

               a[i+1] = k;

        }

}

3、  由于重复调用函数而引起的效率问题

调用函数将造成较大的开销,在循环中重复调用同一个函数将造成较大的浪费,因此,创建局部变量,在循环体外一次调用函数并赋值给局部变量,在循环体内重复使用局部变量,将提高程序效率.

循环体内重复调用函数:

int ArraySum(Array myArray)

       {

              int s = 0;

              for(int i = 0; i < myArray.Size(); i++)

              {

                     s += myArray[i];

              }

              return s;

       }

循环体外调用函数:

       int ArraySum(Array myArray)

       {

              int s = 0;

              int k = myArray.Size();

              for(int i = 0; i < k; i++)

              {

                     s += myArray[i];

              }

              return s;

       }

4、  由于重复使用成员变量而引起的效率问题

因为要使用this指针获取成员变量的基地址,故存取成员变量的时间一般是存取局部变量的二倍;因而,可以创建局部变量,在循环体外一次赋值,循环体内重复使用,将提高程序效率。

class Array

{

        int size;

        int array[100];

};

重复使用成员变量:

int Array::Sum()

{

        int s = 0;

        for(int i = 0; i < size; i++)

        {

               s += array[i];

        }

        return s;

}

使用局部变量代替成员变量:

int Array::Sum()

{

        int s = 0;

        int k = size;

        for(int i = 0; i < k; i++)

        {

               s += array[i];

        }

        return s;

}


一、函数、宏、内联函数

1、  函数

调用函数的开销大致可分两个部分:传递参数的开销和保存当前程序上下文信息所花费的开销。对于传递参数的开销而言,传递的参数越多开销就越大;对于保存当前程序上下文所花费的开销而言,函数越复杂需要花费的开销就越大。

2、  

宏在某种程度上可以代替函数,避免函数调用带来的开销。定义完宏之后,在编译程序时,用替代字符串代替程序中的宏。

       对于宏而言,虽然避免了函数调用带来的开销,但是增加了内存的开销。因为在每个应用宏的地方宏都会展开。

       另外,宏易读性比较差,容易出现问题。例如:

#define MAX(a,b) ((a) < (b) ? (b):(a))

void main()

{

    int a = 10;

    int b = 11;

    int c = 0;

    c = MAX(a++,b++);

    cout << c << endl;

    cin >> c;

}

我们会发现c的结果为12,因为宏展开之后变为:((a++) < (b++) ? (b++) : (a++))并不是我们预想中的结果。故一般来讲最好不使用宏。

3、  内联函数

内联函数是避免函数调用开销的另一种方法,与宏类似,在程序编译是用内联函数替换函数调用。但只能运用与C++中,C中无法使用。与宏相比拥有参数类型检查的优点。

在C++类中,对于设置和读取私有变量的函数设置为内敛函数,有助于提高函数的效率。

内敛函数的实现有两种方式,第一种是在函数定义时使用关键字inline,第二种方式时在函数定义时与函数体一起定义。

4、  递归与迭代

递归可以使程序显得短小精悍,然而由于函数的多层调用,会严重影响程序的运行效率和内存使用效率。幸运的是在某种情况下,我们可以使用迭代来代替递归,避免函数调用开销。

例子:

递归版计算n的阶乘

long Factorial(int n)

{

     if(n == 0)

     {

         return 1;

     }

     else

     {

         return Factorial(n-1);

     }

}

迭代版计算n的阶乘

long Factorial(int n)

{

     int fac = 1;

     while(n != 0)

     {

         fac *= n;

         n--;

     }

     return fac;

}

二、函数的参数传递

函数参数的多少直接影响到函数调用的开销,故编写函数时需要选择恰当的参数传递方式。我们可以用三种方式来传递函数:按值传递、按引用传递和通过全局变量传递。

按值传递参数时,需要把所需传递的参数全部复制到堆栈中,当函数访问该参数时相当于访问局部变量,为直接寻址;按引用传递参数时,复制到堆栈中的时指针所包含的地址,当函数访问该参数时相当于间接寻址,增加了访问参数的时间。通过全局变量传递数据,避免了函数调用的开销,拥有较高的效率,但会影响程序的可维护性,故非在非常明确的情况下,慎用全局变量。

当需要通过函数修改参数值时,通过引用传递参数或通过全局变量是不二的选择。

当无需通过函数修改参数值时,对于较小的参数(占用字节少)采用按值传递较之按引用传递较好,对于占用较多字节的参数(结构体、类等)时,采用按引用传递的方式有助于减少函数参数传递的开销。

三、类对象的高效初始化

1、  类对象初始化

构造一个对象类并设置其私有数据成员,通常有两种方法,第一种是首先通过无参构造函数创建类对象,再通过私有数据设置函数设置成员数据的值;第二种是通过带有参数的构造函数,同时创建类对象并设置私有数据成员的值。较第一种方法而言,第二种方式显然拥有更高的效率,因为,调用私有数据成员设置函数同样需要花费函数调用的开销。故当我们能够一次性完成创建与初始化时,最好采用第二种方案。

低效类对象初始化:

People peo;

peo.SetName("John");

peo.SetAge(23);

peo.SetSex("man");

高效类对象初始化:

People peo("John", 23, "man");

2、  基类初始化

当创建类对象时,若该类存在基类,则先调用基类的构造函数,在不采用基类初始化方式的情况下,系统调用基类的默认构造函数(无参构造函数)。此时若需设置基类的私有数据成员,必须调用基类的私有成员设置函数,从而增加函数调用的开销。故在能够同时提供基类私有数据成员的情况下,尽量采用基类初始化的方式初始化基类。

低效基类初始化:

Student::Student(string myName, int myAge, int mySex, int myGrade)

{

       SetName(myName);

       SetAge(myAge);

       SetSex(mySex);

       grade = myGrade;

}

高效基类初始化:

Student::Student(string myName, int myAge, int mySex, int myGrade):

People(myName, myAge, mySex, myGrade)

{

       this->grade = myGrade;

}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值