中易网

c语言中如何定义位变量

答案:2  悬赏:10  
解决时间 2021-01-24 01:16
c语言中如何定义位变量
最佳答案
使用宏定义

  在C语言中,宏是产生内嵌代码的唯一方法。对于嵌入式系统而言,为了能达到性能要求,宏是一种很好的代替函数的方法。

  写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个:

  错误做法:

#define MIN(A,B)  ( A <= B ? A : B )

  正确做法:

#define MIN(A,B) ((A)<= (B) ? (A) : (B) )

  对于宏,我们需要知道三点:

  (1)宏定义"像"函数;

  (2)宏定义不是函数,因而需要括上所有"参数";

  (3)宏定义可能产生副作用。

  下面的代码:

least = MIN(*p++, b);

  将被替换为:

( (*p++) <= (b) ?(*p++):(b) )

  发生的事情无法预料。

  因而不要给宏定义传入有副作用的"参数"。

  使用寄存器变量

  当对一个变量频繁被读写时,需要反复访问内存,从而花费大量的存取时间。为此,C语言提供了一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写,从而提高效率。寄存器变量的说明符是register。对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量,而循环计数是应用寄存器变量的最好候选者。

  (1) 只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量,包括:模块间全局变量、模块内全局变量、局部static变量;

  (2) register是一个"建议"型关键字,意指程序建议该变量放在寄存器中,但最终该变量可能因为条件不满足并未成为寄存器变量,而是被放在了存储器中,但编译器中并不报错(在C++语言中有另一个"建议"型关键字:inline)。

  下面是一个采用寄存器变量的例子:


WORD Addition(BYTE n)
{
 register i,s=0;
 for(i=1;i<=n;i++)
 {
  s=s+i;
 }
 return s;
}

  本程序循环n次,i和s都被频繁使用,因此可定义为寄存器变量。

  内嵌汇编

  程序中对时间要求苛刻的部分可以用内嵌汇编来重写,以带来速度上的显著提高。但是,开发和测试汇编代码是一件辛苦的工作,它将花费更长的时间,因而要慎重选择要用汇编的部分。

  在程序中,存在一个80-20原则,即20%的程序消耗了80%的运行时间,因而我们要改进效率,最主要是考虑改进那20%的代码。

  嵌入式C程序中主要使用在线汇编,即在C程序中直接插入_asm{ }内嵌汇编语句:


int result;
void Add(long a, long *b)
{
 _asm
 {
  MOV AX, a
  MOV BX, b
  ADD AX, [BX]
  MOV result, AX
 }
}  

  利用硬件特性

  首先要明白CPU对各种存储器的访问速度,基本上是:

CPU内部RAM > 外部同步RAM > 外部异步RAM > FLASH/ROM

  对于程序代码,已经被烧录在FLASH或ROM中,我们可以让CPU直接从其中读取代码执行,但通常这不是一个好办法,我们最好在系统启动后将FLASH或ROM中的目标代码拷贝入RAM中后再执行以提高取指令速度;

  对于UART等设备,其内部有一定容量的接收BUFFER,我们应尽量在BUFFER被占满后再向CPU提出中断。例如计算机终端在向目标机通过RS-232传递数据时,不宜设置UART只接收到一个BYTE就向CPU提中断,从而无谓浪费中断处理时间;

  如果对某设备能采取DMA方式读取,就采用DMA读取,DMA读取方式在读取目标中包含的存储信息较大时效率较高,其数据传输的基本单位是块,而所传输的数据是从设备直接送入内存的(或者相反)。DMA方式较之中断驱动方式,减少了CPU 对外设的干预,进一步提高了CPU与外设的并行操作程度。

  活用位操作

  使用C语言的位操作可以减少除法和取模的运算。在计算机程序中数据的位是可以操作的最小数据单位,理论上可以用"位运算"来完成所有的运算和操作,因而,灵活的位操作可以有效地提高程序运行的效率。举例如下:


int i,j;
i = 879 / 16;
j = 562 % 32;

int i,j;
i = 879 >> 4;
j = 562 - (562 >> 5 << 5);

  对于以2的指数次方为"*"、"/"或"%"因子的数学运算,转化为移位运算"<< >>"通常可以提高算法效率。因为乘除运算指令周期通常比移位运算大。

  C语言位运算除了可以提高运算效率外,在嵌入式系统的编程中,它的另一个最典型的应用,而且十分广泛地正在被使用着的是位间的与(&)、或(|)、非(~)操作,这跟嵌入式系统的编程特点有很大关系。我们通常要对硬件寄存器进行位设置,譬如,我们通过将AM186ER型80186处理器的中断屏蔽控制寄存器的第低6位设置为0(开中断2),最通用的做法是:

#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp &~INT_I2_MASK);

  而将该位设置为1的做法是:

#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp | INT_I2_MASK);

  判断该位是否为1的做法是:

#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
if(wTemp & INT_I2_MASK)
{

}

  上述方法在嵌入式系统的编程中是非常常见的,我们需要牢固掌握。

  总结

  在性能优化方面永远注意80-20准备,不要优化程序中开销不大的那80%,这是劳而无功的。

  宏定义是C语言中实现类似函数功能而又不具函数调用和返回开销的较好方法,但宏在本质上不是函数,因而要防止宏展开后出现不可预料的结果,对宏的定义和使用要慎而处之。很遗憾,标准C至今没有包括C++中inline函数的功能,inline函数兼具无调用开销和安全的优点。

  使用寄存器变量、内嵌汇编和活用位操作也是提高程序效率的有效方法。

  除了编程上的技巧外,为提高系统的运行效率,我们通常也需要最大可能地利用各种硬件设备自身的特点来减小其运转开销,例如减小中断次数、利用DMA传输方式等。
全部回答
给你个求阶乘的小程序,只要最大结果位数不超过20000就行。这种超大规模的阶乘是不能用简单的long保存的。位数太大了。 #define M 20000   #define N (M+5) main() { int Num; register int i,j,k,flag; register unsigned int n,m,pc; unsigned char str_n[5],result_0[N],result_1[N]; void fun_print_result(char *result,int flag); int fun_mul(char *a,char *b,char *c,int flag); printf("输入计算其阶乘的数:Num = "); scanf("%d",&Num); if(Num >= 100) {  printf("\n正在进行阶乘运算. . .\n");  printf("已完成 00000 的阶乘,请等待. . ."); } for (j=0;j<N;j++) {  result_1[j] = 0; result_0[j] = 0; } result_1[N-1] = 1; for(i=0;i<5;i++) {  str_n[i] = 0; } n = 1; pc = 0; flag = N-1; while (n <= Num) {  m = n;  k = 4;  while(m > 0)  {   str_n[k] = m%10; m /= 10; k--;  }  if (pc%2 == 0)  {   if(fun_mul (str_n,result_1,result_0,flag) != 1)   {    printf("\n\n结果溢出!当前所设置的最大结果为 %d 位 已完成 %d 的阶乘",M,pc);    exit(0);   }  }  else  {   if(fun_mul (str_n,result_0,result_1,flag) != 1)   {    printf("\n\n结果溢出!当前所设置的最大结果为 %d 位 已完成 %d 的阶乘",M,pc);    exit(0);   }  }  flag = flag-(4-k);  if(pc%2 == 0)  {   if(result_0[flag] == 0) flag++;  }  else  {   if(result_1[flag] == 0) flag++;  }  n++; pc++;  if((pc)%25 == 0 && Num >= 100 && pc >= 100)  {   printf("\r已完成 %5d",pc);  } } if(Num >= 100) printf("\n"); if ((pc-1)%2 == 0) {  fun_print_result (result_0,flag); } else {  fun_print_result (result_1,flag); } } int fun_mul(char *a,char *b,char *c,int flag) { register int i,j; for(j=flag;j<N;j++) c[j]=0; for(i=4;i>=0;i--) {  if(a)  {   for (j=N-1;j>=flag;j--)   {    if(b[j])    {     c[i+j-4] += a[i]*b[j];    }    if(c[i+j-4] > 9)    {     c[i+j-5] += c[i+j-4]/10; c[i+j-4] %= 10;    }   }  } } if(flag < 10) {  if (c[0] || c[1] || c[2] || c[3] || c[4])    {   return 0;  } } return 1; } void fun_print_result(char *result,int flag) { int p; printf("\n本次计算结果共有 %d 位,如下:\n\n",N-flag); for(p=flag;p<N-1;p++) {  printf("%d",result[p]);  if((p+1-flag)%(21*80) == 0)  {   printf("\n计算结果太长,请按任一键看下一屏,Esc 退出");   if(getch() == 27)   {    clrscr(); exit(0);   }   clrscr();  } } printf("%d",result[N-1]); printf("\n\n[OK]"); }
我要举报
如以上问答内容为低俗、色情、不良、暴力、侵权、涉及违法等信息,可以点下面链接进行举报!
大家都在看
矫正龅牙牙齿
游戏我们军团标准名是4个字,晚上我改名, 我
沙扒湾能不能自己去市场买海鲜再去大排档加工
SAP 怎么把单独的年和月合成年月
zara的鞋子在哪里买划算?
老凤祥千足金与千足金老凤祥区别
有时候可以在电影或小说里找到自己喜欢那种类
电脑开机不显示诊断卡显示8怎么解决啊
如何使用御装油呀
如何跳出多重嵌套循环
温州美克大酒店地址在什么地方,想过去办事
川K的车牌是哪个地方
紫云公馆停车场(出入口)(胜利路西50米紫云公
谁了解中式壁纸品牌哪个好?
面膜,爽肤水,美肌水使用程序?
推荐资讯
碗装方便面的直径和高度精确点
吃保列治缓解脱发,是不是有很大的副作用。
五菱汽车(怀玉山大道)地址好找么,我有些事要
锡组词有哪些
大家都有自己的人生目标吗?是什么啊,
欣然居茶楼地址有知道的么?有点事想过去
发信息用您称呼合适吗?
英致727是什么发动机
手机充电过久的危害
探究串并联电路中电流表测量个电路电流的大小
djmax打完一首歌全连后说的那句话
填了学校服从后还能退档不?
手机登qq时,显示手机磁盘不足,清理后重新登
刺客的套装怎么选啊?