为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

《如何才能写出高效优美的C语言代码》

2017-12-26 14页 doc 36KB 8阅读

用户头像

is_083599

暂无简介

举报
《如何才能写出高效优美的C语言代码》《如何才能写出高效优美的C语言代码》 如何才能写 出高效优美 的C语言代 码 程序能跑起 来并不见得 你的代码就 是很好的; 代码了,衡量代码的 好坏应该从 以下几个方 面来看 1,代码稳定,没有隐患。 2,执行效率高 。 3,可读性高。 4,便于移植。 下面发一些 我在网上看 到的技巧和 自己的一些 经验来和大 家分享; 1、如果可以的 话少用库函 数,便于不同的 mcu和编 译器间的移 植 2、选择合适的 算法和数据 结构 应该熟悉算 法语言,知道各种算 法的优缺点 ,具体资料请 参见相应的 参考资料,...
《如何才能写出高效优美的C语言代码》
《如何才能写出高效优美的C语言代码》 如何才能写 出高效优美 的C语言代 码 程序能跑起 来并不见得 你的代码就 是很好的; 代码了,衡量代码的 好坏应该从 以下几个方 面来看 1,代码稳定,没有隐患。 2,执行效率高 。 3,可读性高。 4,便于移植。 下面发一些 我在网上看 到的技巧和 自己的一些 经验来和大 家分享; 1、如果可以的 话少用库函 数,便于不同的 mcu和编 译器间的移 植 2、选择合适的 算法和数据 结构 应该熟悉算 法语言,知道各种算 法的优缺点 ,具体资料请 参见相应的 参考资料,有很多计算 机籍上都 有介绍。将比较慢的 顺序查找法 用较快的二 分查找或乱 序查找法代 替,插入排序或 冒泡排序法 用快速排序 、合并排序或 根排序代替 ,都可以大大 提高程序执 行的效率。.选择一种合 适的数据结 构也很重要 ,比如你在一 堆随机存放 的数中使用 了大量的插 入和删除指 令,那使用链表 要快得多。 数组与指针 语句具有十 分密码的关 系,一般来说,指针比较灵 活简洁,而数组则比 较直观,容易理解。对于大部分 的编译器,使用指针比 使用数组生 成的代码更 短,执行效率更 高。但是在,, il中则相 反,使用数组比 使用的指针 生成的代码 更短。 3、使用尽量小 的数据类型 能够使用字 符型(char)定义的变量 ,就不要使用 整型(int)变量来定义 ;能够使用整 型变量定义 的变量就不 要用长整型 (long int),能不使用浮 点型:,,;,, )变量就不要 使用浮点型 变量。当然,在定义变量 后不要超过 变量的作用 范围,如果超过变 量的范围赋 值,C编译器并 不报错,但程序运行 结果却错了 ,而且这样的 错误很难发 现。 在,,,, VR中,可以在:, ,,;,, 中设定使用 ,,,,, f参数,尽量使用基 本型参数(%c、%d、%x、%X、%u和%s格式说明 符),少用长整型 参数(%ld、%lu、%lx和%lX格式说 明符),至于浮点型 的参数(%f)则尽量不要 使用,其它C编译 器也一样。在其它条件 不变的情况 下,使用%f参数,会使生成的 代码的数量 增加很多,执行速度降 低。 4、使用自加、自减指令 通常使用自 加、自减指令和 复合赋值表 达式(如a-=1及a+=1等)都能够生成 高质量的程 序代码,编译器通常 都能够生成 inc和, ec之类的 指令,而使用a=a+1或a=a-1之类的指 令,有很多C编 译器都会生 成二到三个 字节的指令 。在AVR单 片适用的, ,,,,, 、,,,,, R、IAR等, 编译器以上 几种书写方 式生成的代 码是一样的 ,也能够生成 高质量的, nc和,, c之类的的 代码。 5、减少运算的 强度 可以使用运 算量小但功 能相同的表 达式替换原 来复杂的的 表达式。如下: (1)、求余运算。 a=a%8; 可以改为: a=a&7; 说明:位操作只需 一个指令周 期即可完成 ,而大部分的 C编译器的 “%”运算均是调 用子程序来 完成,代码长、执行速度慢 。通常,只要求是求 2n方的余 数,均可使用位 操作的方法 来代替。 (2)、平方运算 a=pow(a,2.0); 可以改为: a=a*a; 1 说明:在有内置硬 件乘法器的 单片机中(如51系列 ),乘法运算比 求平方运算 快得多,因为浮点数 的求平方是 通过调用子 程序来实现 的,在自带硬件 乘法器的, VR单片机 中,如,,,, ,,,,, 中,乘法运算只 需2个时钟 周期就可以 完成。既使是在没 有内置硬件 乘法器的, VR单片机 中,乘法运算的 子程序比平 方运算的子 程序代码短 ,执行速度快 。 如果是求, 次方,如: a=pow(a,3.0); 更改为: a=a*a*a; 则效率的改 善更明显。 (3)、用移位实现 乘除法运算 a=a*4; b=b/4; 可以改为: a=a<<2; b=b>>2; 说明:通常如果需 要乘以或除 以2n,都可以用移 位的方法代 替。在,,,, VR中,如果乘以, n,都可以生成 左移的代码 ,而乘以其它 的整数或除 以任何数,均调用乘除 法子程序。用移位的方 法得到代码 比调用乘除 法子程序生 成的代码效 率高。实际上,只要是乘以 或除以一个 整数,均可以用移 位的方法得 到结果,如: a=a*9 可以改为: a=(a<<3)+a 6、循环 (1)、循环语 对于一些不 需要循环变 量参加运算 的任务可以 把它们放到 循环外面,这里的任务 包括表达式 、函数的调用 、指针运算、数组访问等 ,应该将没有 必要执行多 次的操作全 部集合在一 起,放到一个, nit的初 始化程序中 进行。 (2)、延时函数: 通常使用的 延时函数均 采用自加的 形式: ,;,, ,,,,, (void) { ,,,,, ned int i; for (i=0;i<1000;i++) ; } 将其改为自 减延时函数 : ,;,, ,,,,, (void) { ,,,,, ned int i; for (i=1000;i>0;i--) ; } 两个函数的 延时效果相 似,但几乎所有 的C编译对 后一种函数 生成的代码 均比前一种 代码 2 少1~3个字节,因为几乎所 有的,,, 均有为0转 移的指令,采用后一种 方式能够生 成这类指令 。在使用,, ile循环 时也一样,使用自减指 令控制循环 会比使用自 加指令控制 循环生成的 代码更少, ~3个字母。但是在循环 中有通过循 环变量“,”读写数组的 指令时,使用预减循 环时有可能 使 数组超界,要引起注意 。 :,:,,,,, 循环和,; ?,,,,, 循环 用,,,, e循环时有 以下两种循 环形式: ,,,,, ned int i; i=0; ,,,,, (i<1000) { i++; //用户程序 } 或: ,,,,, ned int i; i=1000; do i--; //用户程序 ,,,,, (i>0); 在这两种循 环中,使用,;?,,,,, 循环编译后 生成的代码 的长度短于 ,,,,, 循环。 7、查表 在程序中一 般不进行非 常复杂的运 算,如浮点数的 乘除及开方 等,以及一些复 杂的数学模 型的插补运 算,对这些即消 耗时间又消 费资源的运 算,应尽量使用 查表的方式 ,并且将数据 表置于程序 存储区。如果直接生 成所需的表 比较困难,也尽量在启 了,减少了程序 执行过程中 重复计算的 工作量。 8、其它 比如使用在 线汇编及将 字符串和一 些常量保存 在程序存储 器中,均有利于优 化 C语言宏定 义技巧(常用宏定义 ) 写好C语言 ,漂亮的宏定 义很重要,使用宏定义 可以防止出 错,提高可移植 性,可读性,方便性 等等。下面列举一 些成熟软件 中常用得宏 定义。 CODE: 1,防止一个头 文件被重复 包含 ,,,,,, , ,:,,, F_H ,,,,,, , ,:,,, F_H //头文件内容 ,,,,,, 2,重新定义一 些类型,防止由于各 种平台和编 译器的不同 ,而产生的类 型字节数差 异,方便移植。 ,,,,, ,, ,,,,, ,,, ;,,, ,;;,, ,,, ,, ,;;,, ,, ,,,,, type. */ ,,,,, ,, ,,,,, ,,, ,;,, ,,, ,,,,, ,, ,, ,,,,, ,,, ,, ,,, ,,,,, */ 3 ,,,,, ,, ,,,,, ,,, ,,;,, ,,,,, ,, ,, ,,,,, ,,, ,, ,,, ,,,,, */ ty,,, ,, ,,,,, ,,, ;,,, ,,,,, , ,, ,,,,, ,,, , ,,, ,,,,, */ ,,,,, ,, ,,,,, , ,;,, ,,, ,,,,, , ,, ,,,,, , ,, ,,, ,,,,, */ ,,,,, ,, ,,,,, , ,,;,, ,,,,, , ,, ,,,,, , ,, ,,, ,,,,, */ ,,,,, ,, ,,,,, , ;,,, ,,,,, ,, ,,,,, d 8 b,, ,,,,, */ //下面的不建 议使用 ,,,,, ,, ,,,,, ,,, ;,,, ,,,,, ,, ,,,,, ,,, , ,,, ,,,,, type. */ ,,,,, ,, ,,,,, ,,, ,,;,, ,;,,, ,, ,,,,, ,,, ,, ,,, ,,,,, type. */ ,,,,, ,, ,,,,, ,,, ,;,, ,,;,, , ,, ,,,,, ,,, ,, ,,, ,,,,, type. */ typ,, ,, ,,,,, ,,, ;,,, ,,,,, , ,, ,,,,, ,,, , ,,, ,,,,, type. */ ,,,,, ,, ,,,,, ,,, ,,;,, ,,,,, , ,, ,,,,, ,,, ,, ,,, ,,,,, type. */ ,,,,, ,, ,,,,, ,,, ,;,, ,,,,, , ,, ,,,,, ,,, ,, ,,, ,,,,, type. */ ,,,,, ,, ,,,,, d char int1; ,, ,,,,, , , ,,, ,,,,, type. */ ,,,,, ,, ,,,,, , ,,;,, ,,,,, ,, ,,,,, , ,, ,,, ,,,,, type. */ ,,,,, ,, ,;,, ,,, ,,,,, ,, ,,,,, , ,, ,,, ,,,,, type. */ ,,,,, ,, ,,,,, , ,;,, ,,,,, ,, ,, ,,,,, , ,, ,,, ,,,,, */ typ,, ,, ,,,,, , ,,;,, ,,,,, ,, ,, ,,,,, , ,, ,,, ,,,,, */ ,,,,, ,, ,,,,, , ;,,, ,,,,, , ,, ,,,,, , , ,,, ,,,,, */ 3,得到指定地 址上的一个 字节或字 ,,,,,, , ,,,,, ( x ) ( *( (byte *) (x) ) ) ,,,,,, , ,,,,, ( x ) ( *( (word *) (x) ) ) 4,求最大值和 最小值 ,,,,,, e MAX( x, y ) ( ((x) > (y)) ? (x) : (y) ) ,,,,,, e MIN( x, y ) ( ((x) < (y)) ? (x) : (y) ) 5,得到一个, ield在 结构体:,,,,; t)中的偏移量 ,,,,,, , ,,:,: ,,,,~ ,,,,, ) \ ,,,,,, -,,,, ,, : :,,;,, : ,:: ,,,, ,: ,:-, ,,,,, ) /*lint +e545 */ 6,得到一个结 构体中,, eld所占 用的字节数 ,,,,,, , ,,,,: ,,,,~ ,,,,, : ,,,,; ,: ::,,,, ,: ,:-,,,,,, ) 7,按照,,, 格式把两个 字节转化为 一个,;, d ,,,,,, , ,,,,, ( ray ) ( (((word) (ray)[0]) * 256) + (ray)[1] ) 8,按照,,, 格式把一个 Word转 化为两个字 节 ,,,,,, , ,,:,, ( ray, val ) \ (ray)[0] = ((val) / 256); \ (ray)[1] = ((val) & 0xFF) 9,得到一个变 量的地址(word宽 度) ,,,,,, , ,,,,, ( var ) ( (byte *) (void *) &(var) ) ,,,,,, , ,,,,, ( var ) ( (word *) (void *) &(var) ) 10,得到一个字 的高位和低 位字节 ,,,,,, , ,:,,, LO(xxx) ((byte) ((word)(xxx) & 255)) ,,,,,, , ,:,,, HI(xxx) ((byte) ((word)(xxx) >> 8)) 11,返回一个比 X大的最接 近的8的倍 数 ,,,,,, e RND8( x ) ((((x) + 7) / 8 ) * 8 ) 12,将一个字母 转换为大写 4 ,,,,,, , ,,,,, E( c ) ( ((c) >= 'a' && (c) <= 'z') ? ((c) - 0x20) : (c) ) 13,判断字符是 不是10进 值的数字 ,,,,,, , ,,,,, K( c ) ((c) >= '0' && (c) <= '9') 14,判断字符是 不是16进 值的数字 ,,,,,, , ,,,,, K( c ) ( ((c) >= '0' && (c) <= '9') ||\ ((c) >= 'A' && (c) <= 'F') ||\ ((c) >= 'a' && (c) <= 'f') ) 15,防止溢出的 一个方法 ,,,,,, , ,,,,, AT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val)) 16,返回数组元 素的个数 ,,,,,, , ,,,,, ,,,: , : : ,,,,; ,: :,: : , ,,,,; f( (a[0]) ) ) 17,返回一个无 符号数n尾 的值,:, ,,,,, :,,,, :,,,, O(X,n)=X%(2^n) ,,,,,, , ,:,,, ,,,:, ,,,:, ,,,:: ,,,~ ,;,,, y ) \ : :,,;,, ::,,,: , :,,;,, :::,;,,, y)-1) ) 18,对于IO空 间映射在存 储空间的结 构,输入输出处 理 #de,,, , ,,,:,;,,: :,::,;,,, ile byte *) (port))) ,,,,,, , ,,,,:,;,,: :,::,;,,, ile word *) (port))) ,,,,,, , ,,,,, :,;,,: :,::,;,,, ,,, ,,;,, *)(port))) ,,,,,, , ;,,,:,;,,~ ,,,: :,::,;,,, ile byte *) (port)) = ((byte) (val))) ,,,,,, e o,,,, :,;,,~ ,,,: :,::,;,,, ile word *) (port)) = ((word) (val))) ,,,,,, , ;,,,, ,:,;,,~ ,,,: :,::,;,,, ,,, ,,;,, ,: :,;,,:: , ::,,;,, ) (val))) 19,使用一些宏 跟踪调试 A N S I标准说明 了五个预定 义的宏名。它们是: _ L I N E _ _ F I L E _ _ D A T E _ _ T I M E _ _ S T D C _ 如果编译不 是标准的,则可能仅支 持以上宏名 中的几个,或根本不支 持。记住编译程 序 也许还提供 其它预定义 的宏名。 _ L I N E _及_ F I L E _宏指令在 有关# l i n e的部分中 已讨论,这里讨论其 余的宏名。 _ D AT E _宏指令含 有形式为月 /日/年的串,表示源文件 被翻译到代 码时的日期 。 源代码翻译 到目标代码 的时间作为 串包含在, T I M E _中。串形式为时 :分:秒。 如果实现是 标准的,则宏_ S T D C _含有十进 制常量1。如果它含有 任何其它数 ,则实现是 非标准的。 可以定义宏 ,例如: 当定义了, ,,,,, ,输出数据信 息和所在文 件所在行 ,,,,,, ,,,,, G ,,,,,, , ,,,,, ,,,:,,,~,,,,: ,,,,, ,:,,,:,,,,,, ,:“%,%,%,”~,,,,~,,,,, ,~,,,,, _) #else ,,,,,, , ,,,,, MSG(msg,date) ,,,,,, 20,宏定义防止 使用是错误 用小括号包 含。 例如:,,,,,, e ADD(a,b) (a+b) 用,;,,,,,,, (0)语句包含多 语句防止错 误 例如:,,,,,, DO(a,b) a+b;\ 5 a++; 应用时:,,:?.: DO(a,b); //产生错误 else 解决方法: ,,,,,, DO(a,b) do{a+b;\ ,,,,,,,,,, (0) 宏中"#"和"##"的用法 一、一般用法 我们使用#把宏参数变 为一个字符 串,用##把两个宏参 数贴合在一 起. 用法: ,,,;,, ,,,;,,,, o> ,,,;,, ,,,;,,,, ts> ,,,,, ,,,,, pace std; ,,,,,, e STR(s) #s ,,,,,, e CONS(a,b) int(a##e##b) int main() { ,,,,, f(STR(vck)); // 输出字符串 "vck" ,,,,, f("%d\n", CONS(2,3)); // 2e3 输出:2000 ,,,,, n 0; } 二、当宏参数是 另一个宏的 时候 需要注意的 是凡宏定义 里有用'#'或'##'的地方宏参 数是不会再 展开. 1, 非'#'和'##'的情况 ,,,,,, e TOW (2) ,,,,,, e MUL(a,b) (a*b) ,,,,, f("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW)); 这行的宏会 被展开为: ,,,,, f("%d*%d=%d\n", (2), (2), ((2)*(2))); MUL里的 参数,:, 会被展开为 (2). 2, 当有'#'或'##'的时候 ,,,,,, e A (2) ,,,,,, e STR(s) #s ,,,,,, e CONS(a,b) int(a##e##b) ,,,,, ,:,,,, ,,,: %,,,,~ ,,,:,,,,, ,,::, ,, ,,,,, ,, ,,,;,, ,,,;,,,, ts> 这行会被展 开为: ,,,,, ,:,,,, ,,,: %,,,,~ ,,,,,, AX"); ,,,,, f("%s\n", CONS(A, A)); /, ;;,,, ,, ,,,;, 这一行则是 : ,,,,, f("%s\n", int(AeA)); ,,,,, AX和A都 不会再被展 开, 然而解决这 个问的方 法很简单. 加多一层中 间转换宏. 加这层宏的 用意是把所 有宏的参数 在这层里全 部展开, 那么在转换 宏里的那一 个宏(_STR) 就能得到正 确的宏参数 . ,,,,,, e A (2) ,,,,,, e _STR(s) #s 6 ,,,,,, e STR(s) _STR(s) // 转换宏 ,,,,,, , ,,:,, (a,b) int(a##e##b) ,,,,,, , ,:,,:,~,: ,,:,, (a,b) // 转换宏 ,,,,, ,:,,,, ,,,: %,,,,~ ,,,:,,,,, ,,::, ,, ,,,,, AX,int型的 最大值,为一个变量 ,,,;,, ,,,;,,,, ts> 输出为: ,,, ,,,: ,,,,, ,,,,, ,,,:,,,,, ,,: --, ,,,,:,,,,, ,,,,, ) 然后再转换 成字符串; ,,,,, ("%d\n", CONS(A, A)); 输出为:200 ,:,,:,~ ,: --, ,,:,, ((2), (2)) --> int((2)e(2)) 三、'#'和'##'的一些应用 特例 1、合并匿名变 量名 ,,,,,, , ,,,,, :,,,: US1(type, var, line) type var##line ,,,,,, , ,,,,: ,,,:, ,,:,,,,~ ,,,,: ,,,,, :,,,: ,,,:,,,,~ ,,,;, ,,;,, , line) ,,,,,, , ,,:,, ,:,,:,,,,: ,,,,: ,,,:, ,,:,,,,~ ,,,,, E__) 例:,,:,, ,:,,:,,,,, c int); 即: ,,,,, ; ,,, ,,,;, ,,;,, 70; 70表示该 行行号; 第一层:,,:,, ,:,,:,,,,, ; ,,,:, --, ,,,,: ,,,:, ,,:,,,,, ; ,,,~ ,,,,, E__); 第二层: --, ,,,,, :,,,: ,,,:,,,,, c i,,~ ,,,;, ,,;,, , 70); 第三层: --, ,,,,, ; ,,, ,,,;, ,,;,, 70; 即每次只能 解开当前层 的宏,所以,,, ,,,,, 在第二层才 能被解开; 2、填充结构 ,,,,,, e FILL(a) {a, #a} ,,,, ,,,,:,,,~ ,,:,, }; ,,,,, ,, ,,,,; t MSG{ IDD id; ;;,,, char * msg; }MSG; MSG _msg[] = {FI,,::,,,:~ ,,,,:,,:,, )}; 相当于: MSG _msg[] = {{OPEN, "OPEN"}, ,,,:,, ~ ,,,:,, "}}; 3、记录文件名 ,,,,,, , ,,,,, ,,,,, NAME(f) #f ,,,,,, , ,,,,, ,,,,, ,,,:,: ,,,,, ,,,,, NAME(f) ,,,,, ; ;,,, ,,,,, ,,,,,, , ,,,,, ,,,,, ,,,:,,,,, E__); 4、得到一个数 值类型所对 应的字符串 缓冲大小 ,,,,,, , ,,,,, ,,,,, ,,,,:,,,,: ,,,,; f #type ,,,,,, , ,,,,, ,,,,, ,,,:,,,,: ,,,,, ,,,,, SIZE(type) ;,,, ,,,,,,,,, ,,,,, ,,,:,,,,, AX)]; --, ;,,, ,,,,,,,,, ,,,,, ,,,,:,,,,, ,,,,, )]; --, ;,,, ,,,,,,,,; , ,,,,,, ,,,,, "]; 这里相当于 : char buf[11]; 7
/
本文档为【《如何才能写出高效优美的C语言代码》】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索