收藏官网首页
查看: 10673|回复: 0

转载-编写高效的C程序与C代码优化(二)

65

主题

123

帖子

787

积分

高级会员

Rank: 4

积分
787
跳转到指定楼层
楼主
发表于 2015-9-29 10:38:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
汉枫LPB120模块
指针 / Pointers

如果可能,我们应该使用结构体的引用作为参数,也就是结构体的指针,否则,整个结构体就会被压入堆栈,然后传递,这会降低速度。程序适用值传递可能需要几K字节,而一个简单的指针也可以达到同样的目的,只需要几个字节就可以了。

如果在函数内部不会改变结构体的内容,那么就应该将参数**为const型的指针。举个例子:
void print_data_of_a_structure (const Thestruct  *data_pointer)
{
...printf contents of the structure...
}
这个例子代码告知编译器在函数内部不会改变外部结构体的内容,访问他们的时候,不需要重读。还可以确保编译器捕捉任何修改这个只读结构体的代码,给结构体以额外的保护。

指针链 / Pointer chains

指针链经常被用来访问结构体的信息,比如,下面的这段常见的代码:
typedef struct { int x, y, z; } Point3;
typedef struct { Point3 *pos, *direction; } Object;
void InitPos1(Object *p)
{
p->pos->x = 0;
p->pos->y = 0;
p->pos->z = 0;
}
代码中,处理器在每次赋值操作的时候都要重新装载p->pos,因为编译器不知道p->pos->x不是p->pos的别名。更好的办法是将p->pos缓存成一个局部变量,如下:
void InitPos2(Object *p)
{
Point3 *pos = p->pos;
pos->x = 0;
pos->y = 0;
pos->z = 0;
}
另一个可能的方法是将Point3结构体包含在Object结构体中,完全避免指针的使用。

条件的执行 / Conditional Execution

条件执行主要用在if语句中,同时也会用到由关系运算(<,==,>等)或bool运算(&&, !等)组成的复杂的表达式。尽可能的保持if和else语句的简单是有好处的,这样才能很好的条件化。关系表达式应该被分成包含相似条件的若干块。

下面的例子演示了编译器如何使用条件执行:
int g(int a, int b, int c, int d)
{
if(a > 0 && b > 0 && c < 0 && d < 0)  //分组化的条件被捆绑在一起
return a + b + c + d;
return -1;
}
条件被分组,便以其能够条件化他们。

Boolean表达式和范围检查 / Boolean Expressions & Range checking

有一种常见的boolean表达式被用来检查是否一个变量取值在某个特定的范围内,比方说,检查一个点是否在一个窗口内。
bool PointInRectangelArea (Point p, Rectangle *r)
{
return (p.x >= r->xmin && p.x < r->xmax && p.y >= r->ymin && p.y < r->ymax);
}
这里还有一个更快的方法:把(x >= min && x < max) 转换成 (unsigned)(x-min) < (max-min). 尤其是min为0时,更为有效。下面是优化后的代码:
bool PointInRectangelArea (Point p, Rectangle *r)
{
return ((unsigned) (p.x - r->xmin) < r->xmax && (unsigned) (p.y - r->ymin) < r->ymax);
}
Boolean表达式&与零的比较 / Boolean Expressions & Compares with zero

在比较(CMP)指令后,相应的处理器标志位就会被设置。这些标志位也可以被其他的指令设置,诸如MOV, ADD, AND, MUL, 也就是基本的数学和逻辑运算指令(数据处理指令)。假如一条数据处理指令要设置这些标志位,那么N和Z标志位的设置方法跟把数字和零比较的设置方法是一样的。N标志位表示结果是不是负数,Z标志位表示结果是不是零。

在C语言中,处理器中的N和Z标志位对应的有符号数的关系运算符是x < 0, x >= 0, x == 0, x != 0,无符号数对应的是x == 0, x != 0 (or x > 0)。

C语言中,每用到一个关系运算符,编译器就会产生一个比较指令。如果关系运算符是上面的其中一个,在数据处理指令紧跟比较指令的情况下,编译器就会将比较指令优化掉。比如:
int aFunction(int x, int y)
{
if (x + y < 0)
return 1;
else
return 0;
}
这样做,会在关键循环中节省比较指令,使代码长度减少,效率增加。C语言中没有借位(carry)标志位和溢出(overflow)标志位的概念,所以如果不使用内嵌汇编语言,要访问C和V标志位是不可能的。尽管如此,编译器支持借位标志位(无符号数溢出),比方说:
int sum(int x, int y)
{
int res;
res = x + y;
if ((unsigned) res < (unsigned) x) // carry set?  //
res++;
return res;
}   
惰性评估计算 / Lazy Evaluation Exploitation

在类似与这样的 if(a>10 && b=4) 语句中, 确保AND表达式的第一部分最有可能为false, 结果第二部分极有可能不被执行.

用switch() 代替if...else...,在条件选择比较多的情况下,可以用if…else…else…,像这样:
if( val == 1)
dostuff1();
else if (val == 2)
dostuff2();
else if (val == 3)
dostuff3();
使用switch可以更快:
switch( val )
{
case 1: dostuff1(); break;
case 2: dostuff2(); break;
case 3: dostuff3(); break;
}
在if语句中,即使是最后一个条件成立,也要先判断所有前面的条件是否成立。Switch语句能够去除这些额外的工作。如果你不得不使用if…else,那就把最可能的成立的条件放在前面。
switch语句和查找表 / Switch statement vs. lookup tables

switch语句通常用于以下情况:

●调用几个函数中的一个

●设置一个变量或返回值

●执行几个代码片断中的一个

如果case表示是密集的,在使用switch语句的前两种情况中,可以使用效率更高的查找表。比如下面的两个实现汇编代码转换成字符串的例程:
char * Condition_String1(int condition) {
switch(condition) {
  case 0: return "EQ";
  case 1: return "NE";
  case 2: return "CS";
  case 3: return "CC";
  case 4: return "MI";
  case 5: return "PL";
  case 6: return "VS";
  case 7: return "VC";
  case 8: return "HI";
  case 9: return "LS";
  case 10: return "GE";
  case 11: return "LT";
  case 12: return "GT";
  case 13: return "LE";
  case 14: return "";
  default: return 0;
}
}
char * Condition_String2(int condition) {
if((unsigned) condition >= 15) return 0;
return
   "EQ\0NE\0CS\0CC\0MI\0PL\0VS\0VC\0HI\0LS\0GE\0LT\0GT\0LE\0\0" +
    3 * condition;
}
第一个例程需要240个字节,第二个只需要72个。

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

加入Q群 返回顶部

版权与免责声明 © 2006-2024 Gizwits IoT Technology Co., Ltd. ( 粤ICP备11090211号 )

快速回复 返回顶部 返回列表