如何高效使用C语言

引言:

编写高效简洁的C语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。

1招:以空间换时间

计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1——以空间换时间。

例如:字符串的赋值。

方法A,通常的办法:

#define LEN 32

char string1 [LEN];

memset (string1,0,LEN);

strcpy (string1,“This is a example!!”
;



方法B


const char string2[LEN] =“This is a example!”;

char * cp;

cp = string2 ;

(
使用的时候可以直接用指针来操作。)



从上面的例子可以看出,AB的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。


如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。

该招数的变招——使用宏函数而不是函数。举例如下:

方法C

#define bwMCDR2_ADDRESS 4

#define bsMCDR2_ADDRESS 17

int BIT_MASK(int __bf)

{

return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);

}

void SET_BITS(int __dst, int __bf, int __val)

{

__dst = ((__dst) & ~(BIT_MASK(__bf))) | (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

}

SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);



方法D

#define bwMCDR2_ADDRESS 4

#define bsMCDR2_ADDRESS 17

#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)

#define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))

#define SET_BITS(__dst, __bf, __val)

((__dst) = ((__dst) & ~(BIT_MASK(__bf))) |

(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);



函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。 D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。

2招:数学方法解决问题

现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。

数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。

举例如下,求 1~100的和。

方法E

int I , j;

for (I = 1 ;I<=100; I ++
{

j += I;

}



方法
F

int I;

I = (100 * (1+100)) / 2



这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式N+1/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(Ij);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大



3招:使用位操作,减少除法和取模的运算

在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用位运算来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:

方法G

int I,J;

I = 257 /8;

J = 456 % 32;



方法
H

int I,J;

I = 257 >>3;

J = 456 - (456 >> 4 << 4);



在字面上好像HG麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。


运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。

4招:汇编嵌入

高效C语言编程的必杀技,第四招——嵌入汇编。

在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。

举例如下,将数组一赋值给数组二,要求每一字节都相符。

char string1[1024],string2[1024];


方法I

int I;

for (I =0 ;I<1024;I++)

*(string2 + I) = *(string1 + I)



方法
J

#ifdef _PC_

int I;

for (I =0 ;I<1024;I++)

*(string2 + I) = *(string1 + I);

#else

#ifdef _ARM_

__asm

{

MOV R0,string1

MOV R1,string2

MOV R2,#0

loop:

LDMIA R0!, [R3-R11]

STMIA R1!, [R3-R11]

ADD R2,R2,#8

CMP R2, #400

BNE loop

}

#endif



方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。


虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。

限度地发挥数学的威力来提高程序运行的效率。

 

 

 

 

 

 

C语言程序设计技巧之命令行参数

在许多应用软件运行时都带有命令参数,其实这些命令行参数在C语言编写的程序中也可以实现,灵活地运用命令行参数进行处理可以有效地提高程序的运行效率,收到事半功倍的效果。

  C语言中有关命令行参数涉及到程序的主函数main(int argc,char *argv[]这样两个参数,其中,int argc表示命令行参数的个数(包括可执行程序名本身)char *argv[]表示每个参数的具体内容,argv[0]为命令行中可执行程序名本身,argv[1]为命令行中第二个参数的内容,依次类推。如下例输出命令行参数的个数及参数的内容:

main (int argc,char *argv[]

{int I;
 printf(\n命令行中可执行文件名为:%s,argv[0]);
 printf(\n总共有%d个参数:
,argc);
 
I=0;
 
while(argc>=1)
 {printf(″s  ,argv[I++
]);
  argc--
;}
}

  命令行参数用的最多还是在诸如DIR A:等之类带有盘符、路径或文件名这样的命令行中,所以说灵活处理这一类参数才能有效地提高程序的运行效果。譬如DIR命令,其后可以是盘符,可以是路径,也可以是文件名,如何区分这一参数呢?请看下例(此程序模拟DIR命令,程序要求在命令行输入一个参数:盘符或路径或文件名,若无参数或参数多于一个都将取默认的参数“*.*”)


\*
--------------------
功能:模拟DIR命令进行处理命令行参数
--------------------*/
include
include
include
inchlude
int j,num=0;
char ss[20],path[50],path2[50];
void main (int argc,char *argv[])
{
 
struct ffblk f;
 
int done;
 if(argc==2)  /*取命令行参数到数组
*/
  
strcpy(ss,argv[1]);
 
else
  strcpy(ss,″*.*″); /*给数组赋值缺省参数
*/
  if (((ss[strlen(ss)1]==′\\′||((ss[strlen(ss)
1]==':'))
   strcat(ss,″*.*″); /*若参数为路径或盘符,则加上″*.*″ 
*/
  getcwd(path1,50); /*取当前路径
*/
  if (chdir(ss)==0) /*判断参数是否为路径
*/
    strcat(ss,\\*.*); /*若路径末没有带\,则加上
*.* */

 chdir(path1); /*恢复原来路径
*/
 
strcpy(path2,ss);
 for(j=strlen(path2);j>0;j--)/*提取参数中的路径到
path2 */
  
{if((path2[j]=='\\'))||(path2[j]==':')){
   path2[j
1]='\0';
   
goto senull;}
  
}
 
path2[0]='\0';
 
senull:
 if(strlen(path2)==0)  /* 若给出的参数中没带路径,则取当前路径
*/
  
strcpy(path2,path1);
 printf(\n**模拟DIR**\n 命令目录路径%
s,path2);
 done=findfirst(ss,f,55); /*查找第一个配匹的文件
*/
 
j=1;
 
while(!done)
 {if (f.ff_attrib!=0x10) /* 若文件属性不是目录
*/
   printf(\n 15s
20ld,f.ff_name,f.ff_fsize);
  
else
   printf(\n
11s ,f.ff_name);
  num++
;
  j++
;
  
if(j==23)
   printf(\n --------More (按任意键继续)----
);
  
getch();
  
j=0;
  printf(″\n (目录路径%
s)″,path2);}
  done=findnext(f); /*查找下一个配匹的文件
*/
 
}
printf(″\n
当前目录中总共有%d个文件.\n″,num);

 

 

 

 

 

 

 

C语言的32个关键字

一、数据类型关键字(12个):
1
char [tʃɑ:]:声明字符型变量或函数
1)主要内容字符:容纳单字符的一种基本数据类型;(2n.炭;女清洁工 vt.烧焦;
3)字符类型:字符型(Char) c字符串型(String) s 、二进制型(Binary) bn布尔型(Boolean) b 、日期时间型(DateTime) d 数组(Array) a、象型(Object) o 、循环控制变量通常使用单一的字符
2
double [ˈdʌbəl] :声明双精度变量或函数
1n. 两倍;(2a. 两倍的,双重的;(3v. 加倍的,快步走,加倍努力
3
enum :声明枚举类型
1)枚举:枚举是一个被命名的整型常数的;(2)枚举类型;(3)列举型;
4)列举enumerate [iˈnju:məreit]
4
float [fləut] :声明浮点型变量或函数

1)浮点数、(2)浮点型、(3)漂浮、(4)浮动
5
int[int]声明整型变量或函数
1)符号整数、(2)取整、(3Int integer ['intidʒə] 的简写
6
long [lɔŋ] :声明长整型变量或函数
1)长整型2a./ ad.()()3 n.长时间4vi.渴望
7
short [ʃɔ:t] :声明短整型变量或函数
1a. 短的,矮的、(2n. 短裤、(3adv. 短暂地;突然地,急地
8
signed:声明有符号类型变量或函数
1)有符号的、(2)带正负号、(3sign [sain] n.标记,符号;招牌;迹象 v.()
9
struct:声明结构体变量或函数

1n.结构(2)结构体(4)创建构架数组(3structural[ˈstrʌktʃərəl]a.结构的
10
union [ˈju:niən]:声明共用体(联合)数据类型
1)联合、(2n.工会,联盟、(3)合并、(4)团结
11
unsigned [ʌn'saind]:声明无符号类型变量或函数
1)无符号的
12
void [vɔid] :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)
1a.无效的、(2)没有的、(3vt.使无效、(4n.空虚感
二、控制语句关键字(12个):
A
循环语句
1
for [fə, fɔ:]:一种循环语句(可意会不可言传)
2
do [du, du:] :循环语句的循环体
3
while [wail] :循环语句的循环条件
1conj.的时;(2)而;(3)虽然 n.一会儿 vt.消磨
4
break [breik]:跳出当前循环
1)中断、(2)断开、(3n.休息 vt.打破
5
continue[kənˈtinju:]:结束当前循环,开始下一轮循环
1v.继续,延续,延伸
B
条件语句
1
if [if]: 条件语句
1)条件函数、(2conj.如果,假如、(3)是否、(4)即使、(5)无论何时
2
else [els] :条件语句否定分支(与 if 连用)
1a. 别的(2ad. 其他,另外
3
goto:无条件跳转语句
1)跳转、(2)转向((3)跳转到
C
开关语句
1
switch [switʃ]:用于开关语句
1n. 开关,转换,接通或切断电流,转动、(2v. 转变,切换,摆动
2
case [keis]:开关语句分支
1n.事例、(2)情况、(3)手提箱(4)盒(5)案例
3
default [diˈfɔ:lt]:开关语句中的其他分支
1)预设、(2n. 假设值,默认(),不履行责任,缺席(3v. 默认,不履行义务,缺席,拖欠(4 [计算机] 缺省
D
返回语句
1
return [riˈtə:n]:子程序返回语句(可以带参数,也看不带参数)
1v.返回、(2)恢复、(3)归还、(4)盈利
 三、存储类型关键字(4个)
1
auto [ˈɔ:təu] :声明自动变量(一般不使用)
1)自动的、(2)汽车automobile [ˈɔ:təməubi:l]
2
extern:声明变量是在其他文件正声明(也可以看做是引用变量)

1)外部(的)、(2external [ikˈstə:nəl]a.外部的,外面的,外表的
3
register [ˈredʒistə]:声明积存器变量
1)寄存器、(2)注册(表)(3)登记(表)
4
static[ˈstætik]:声明静态变量
1a. 静态的,静电的、(2n. 静电,静电干扰
四、其它关键字(4个):
1
const :声明只读变量
1)常量、(2)常数、(3)编译时常量
2
sizeof:计算数据类型长度
1n. …的大小、(2)占的字节数(3size [saiz]n.大小,尺寸 vt.按大小排列(或分类)
3
typedef:用以给数据类型取别名(当然还有其他作用)

 (1n. 类型定义、(2)数据类型说明(3type [taip]n.类型,种类,品种;铅字 v.()
4
volatile [ˈvɔlətail]:说明变量在程序执行中可被隐含地改变

1a.动荡不定的、(2)反复无常的、(3)易挥发的

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值