博文

C语言高效编程的的四招技巧(2008-05-06 20:23:00)

摘要:引言:   编写高效简洁的c语言代码,是许多软件工程师追求的目标。本文就工作中的一些体会和经验做相关的阐述,不对的地方请各位指教。   第一招:以空间换时间   计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招--以空间换时间。   例如:字符串的赋值。   方法a:通常的办法: #define len 32char 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 ;   使用的时候可以直接用指针来操作。   从上面的例子可以看出,a和b的效率是不能比的。在同样的存储空间下,b直接使用指针就可以操作了,而a需要调用两个字符函数才能完成。b的缺点在于灵活性没有a好。在需要频繁更改一个字符串内容的时候,a具有更好的灵活性;如果采用方法b,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。 如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。该招数的变招--使用宏函数而不是函数。举例如下:   方法c: #define bwmcdr2_address 4#define bsmcdr2_address 17int 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#de......

阅读全文(1606) | 评论:0

C++中const总结(2008-04-17 21:44:00)

摘要:一:对于基本声明 1.const int r=100; //标准const变量声明加初始化,因为默认内部连接所以必须被初始化,其作用域 为此文件,编译器经过类型检查后直接用100在编译时替换. 2.extend const int r=100; //将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行 初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义. 3.const int r[]={1,2,3,4}; struct S {int a,b;}; const S s[]={(1,2),(3.4)}; //以上两种都是常量集合,编译器会为其分配内存,所以不能在编译期间使用其中 的值,例如:int temp[r[2]];这样的编译器会报告不能找到常量表达式 二:对于指针 1.const int *r=&x; //声明r为一个指向常量的x的指针,r指向的对象不能被修改,但他可以指向任何 地址的常量. 2.int const *r=&x;//与用法1完全等价,没有任何区别。 3.int * const r=&x; //声明r为一个常量指针,他指向x,r这个指针的指向不能被修改,但他指向的地址 的内容可以修改. 4.const int * const r=&x; //综合1,3用法,r是一个指向常量的常量型指针. 三:对于类型检查 可以把一个非const对象赋给一个指向const的指针,因为有时候我们不想从这个 指针来修改其对象的值,但是不可以把一个const对象赋值给一个非const指针, 因为这样可能会通过这个指针改变指向对象的值,但也存在使这种操作通过的合 法化写法,使用类型强制转换可以通过指针改变const对象: const int r=100; int *ptr=const_cast <int*> (&r);//C++标准,C语言使用:int* ptr =(int*)&r; 四:对于字符数组 如char * name = "china "; 这样的语句,在编译时是能够通过的,但是 "china "是常量字符数组,任何想修改 他的操作也能通过编译但会引起运行时错误,如果我们想修改字符数组的话就要 使用char name[]= "china ";这种形式. 五:对于函数 1.vo......

阅读全文(3083) | 评论:0

CONST(2008-04-17 21:40:00)

摘要:C中CONST的使用:const是一个C语言的关键字,它限定一个变量不允许被改变。使用const在一定程度上可以提高程序的健壮性,另外,在观看别人代码的时候,清晰理解const所起的作用,对理解对方的程序也有一些帮助。  虽然这听起来很简单,但实际上,const的使用也是c语言中一个比较微妙的地方,微妙在何处呢?请看下面几个问题。      问题:const变量 & 常量      为什么我象下面的例子一样用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?        const int n = 5;  int a[n];          答案与分析:      1)、这个问题讨论的是“常量”与“只读变量”的区别。常量肯定是只读的,例如5, “abc”,等,肯定是只读的,因为程序中根本没有地方存放它的值,当然也就不能够去修改它。而“只读变量”则是在内存中开辟一个地方来存放它的值,只不过这个值由编译器限定不允许被修改。C语言关键字const就是用来限定一个变量不允许被改变的修饰符(Qualifier)。上述代码中变量n被修饰为只读变量,可惜再怎么修饰也不是常量。而ANSI C规定数组定义时维度必须是“常量”,“只读变量”也是不可以的。      2)、注意:在ANSI C中,这种写法是错误的,因为数组的大小应该是个常量,而const int n,n只是一个变量(常量 != 不可变的变量,但在标准C++中,这样定义的是一个常量,这种写法是对的),实际上,根据编译过程及内存分配来看,这种用法本来就应该是合理的,只是 ANSI C对数组的规定限制了它。      3)、那么,在ANSI C 语言中用什么来定义常量呢?答案是enum类型和#define宏,这两个都可以用来定义常量。      问题:const变量 & const 限定的内容      下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?        typedef char * pStr;  char string[4] = "abc";  const char *p1 = string;  const pStr p2 = string;  p1++;  p2++;      答案与分析:      问题出在p2++上。      1)、const使用的基本形......

阅读全文(1935) | 评论:0

实时操作系统uC/0S II下TCP/IP协议栈的实现(2008-04-17 10:36:00)

摘要:结合ez80和ARM7两种系统上的具体实现,说明了如何在嵌入式实时操作系统uC/0SII上移植实现LwIP这套TCP/IP协议栈,使uC/0S II成为支持网络的RTOS。 关键词: uC/0S II,TCP/IP,LwIP,网络设备驱动 1 引言 随着嵌入式系统与网络的日益结合,在嵌入式实时操作系统中引入TCP/IP协议栈,以支持嵌入式设备接入网络,成为嵌入式领域重要的研究方向。uC/0S II是近年来发展迅速的一个开放源码实时操作系统,但它只是一个实时的任务调度及通信内核,缺少对外围设备和接口的支持,如没有文件系统、网络协议、图形界面。笔者在多个嵌入式项目的开发过程中,以开源TCP/IP协议栈LwIP为基础,给uC/0S II加上了网络支持。下面就以uC/0S II +LwIP分别在8位MCU ez80和32位MCU ARM7TDMI上的实现为例进行说明。需要说明的是,笔者使用的ez80系统是Zilog公司的ez80190开发板,自带网络芯片。而ARM7系统是使用笔者参与开发的Skyeye,一个基于GDB的ARM7TDMI指令级软件仿真器。Skyeye小组最近为Skyeye加上了软件模拟的Ne2k兼容网络芯片,可以运行带网络支持的μcLinux和uC/0S II。以下的全部相关程序和代码都可以在Skyeye网站(hpclab.cs.tsinghua.edu.cn/~skyeye/)下载。 2 基于uC/0S II的网络平台概述 嵌入式操作系统uC/0S II是一个公开源代码的占先式多任务的微内核RTOS,其性能和安全性可以与商业产品竞争。uC/0S II的特点可以概括为以下几个方面:公开源代码,代码结构清晰、明了,注释详尽,组织有条理,可移植性好。可裁剪,可固化。内核属于抢占式,最多可以管理60个任务。uC/0S II自1992年的第一版(uC/0S)以来已经有好几百个应用,是一个经实践证明好用且稳定可靠的内核。目前国内对uC/0S II的研究和应用都很多。TCP/IP是Internet的基本协议,以其实用性、高效性已经成为事实上的工业标准。嵌入式设备要与Internet网络直接交换信息,就必须支持TCP/IP协议。目前嵌入式设备上TCP/IP方案有很多种,但面向低端应用的开源嵌入式网络平台还很少见。uC/0S II是一个富有开放色彩的RT......

阅读全文(2640) | 评论:0

C语言编程规范(2008-03-31 21:07:00)

摘要:C语言编程规范  〖文章转载或出处〗≡中国电子技术信息网≡ 网址:www.CETINet.com C语言编程规范(仅供参考) 1. 基本要求1.1 程序结构清析,简单易懂,单个函数的程序行数不得超过100行。1.2 打算干什么,要简单,直接了当,代码精简,避免垃圾程序。1.3 尽量使用标准库函数和公共函数。1.4 不要随意定义全局变量,尽量使用局部变量。1.5 使用括号以避免二义性。 2.可读性要求2.1 可读性第一,效率第二。2.2 保持注释与代码完全一致。2.3 每个源程序文件,都有文件头说明,说明规格见规范。2.4 每个函数,都有函数头说明,说明规格见规范。2.5 主要变量(结构、联合、类或对象)定义或引用时,注释能反映其含义。2.7 常量定义(DEFINE)有相应说明。2.8 处理过程的每个阶段都有相关注释说明。2.9 在典型算法前都有注释。2.10 利用缩进来显示程序的逻辑结构,缩进量一致并以Tab键为单位,定义Tab为 6个字节。2.11 循环、分支层次不要超过五层。2.12 注释可以与语句在同一行,也可以在上行。2.13 空行和空白字符也是一种特殊注释。2.14 一目了然的语句不加注释。2.15 注释的作用范围可以为:定义、引用、条件分支以及一段代码。2.16 注释行数(不包括程序头和函数头说明部份)应占总行数的 1/5 到 1/3 。 3. 结构化要求3.1 禁止出现两条等价的支路。3.2 禁止GOTO语句。3.3 用 IF 语句来强调只执行两组语句中的一组。禁止 ELSE GOTO 和 ELSE RETURN。3.4 用 CASE 实现多路分支。3.5 避免从循环引出多个出口。3.6 函数只有一个出口。3.7 不使用条件赋值语句。3.8 避免不必要的分支。3.9 不要轻易用条件分支去替换逻辑表达式。 4. 正确性与容错性要求4.1 程序首先是正确,其次是优美4.2 无法证明你的程序没有错误,因此在编写完一段程序后,应先回头检查。4.3 改一个错误时可能产生新的错误,因此在修改前首先考虑对其它程序的影响。4.4 所有变量在调用前必须被初始化。4.5 对所有的用户输入,必须进行合法性检查。4.6 不要比较浮点数的相等,如: 10.0 * 0.1 == 1.0 , 不可靠4.7 程序与环境或状态发生关系时,必须主动去处理发生的意外事件,......

阅读全文(1778) | 评论:0

一些实用的单片机c程序(2008-03-31 20:54:00)

摘要:一些实用的单片机c程序  //16进制<->10进制互换程序  unsigned char d[10];    //用于显示的10位显示缓存  //========================================================     //16进制to10进制输出子程序:显示数据,起始位,结束位,有无小数点 //======================================================== void output(unsigned long dd,unsigned char s,unsigned char  e,unsigned char dip) {     unsigned long div;     unsigned char tm[8],i,j;     div=10000000;     for (i=0;i<8;i++) {         tm[i]=dd/div;         dd%=div;         div/=10;     }     for (i=0;i<6;i++) {         if (tm[i]!=0) break;         tm[i]=nul;     }     tm[5]|=d......

阅读全文(2688) | 评论:0

C 语言编程常见错误(2008-03-22 15:22:00)

摘要:1. 书写标识符时,忽略了大小写字母的区别。       int main( void )       {           int a = 5;           printf( "%d", A );            return 0;       }编译器认为 a 和 A 是两个不同的变量名,而显示出错信息。C 语言规定大写字母和小写字母是不同的字符;而有些编程语言是不分大小写的。习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。 2. 忽略了变量的类型,进行了不合法的运算。       int main( void )       {           float a, b;           printf( "%d", a % b );            return 0;       }% 是求余运算符,a % b 的结果是 a 除以 b 的余数。只有 % 左右两边的操作数都是整型时,才可以进行求余运算。故而上面的程序是错误的,因为 a 和 b 是浮点型变量。 3. 将字符常量与字符串常量混淆。       char c;......

阅读全文(1852) | 评论:0

位运算(2008-03-10 21:21:00)

摘要:顾名思义,按位运算符允许按照位来操作整型变量。可以把按位运算符应用于任意signed和unsigned整型,包括char类型。但是,它们通常应用于不带符号的整型。 这些运算符的一个常见应用是在整型变量中使用单个的位存储信息。例如标记,它用于描述二进制状态指示符。可以使用一个位来描述有两个状态的值:开或关、男或女,真或假。 也可以使用按位运算符处理存储在一个变量中的几个信息项。例如,颜色值常常记录为三个八位值,分别存储颜色中红、绿和蓝的强度。这些常常保存到四字节变量中的三个字节。第四个字节也不会浪费,包含表示颜色透明度的值。显然,要处理各个颜色成分,需要从变量中分离出各个字节,按位运算符就可以做到这一点。 再看另外一个例子,假定需要记录字体的信息,那么,只要存储每种字体的样式和字号,以及字体是黑体还是斜体,就可以把这些信息都存储在一个二字节的整型变量中,如图3-1所示。 图3-1 把字体数据存储在2个字节中 可以使用一位来记录字体是否为斜体—— 1表示斜体,0表示一般。同样,用另一位来指定字体是否为黑体。使用一个字节可以从多达256种不同的样式中选择一个,再用另外5位记录最多32磅的字号。因此,在一个16位的字中,可以记录四个不同的数据项。按位运算符提供了访问和修改整数中单个位和一组位的便利方式,能方便地组合和分解一个16位的字。 3.3.1 移位运算符 移位运算符可以把整型变量中的内容向左或向右移动指定的位数。移位运算符和其他按位运算符一起使用,可以获得前面描述的结果。>>运算符把位向右移动,<<运算符把位向左移动,移出变量两端的位被舍弃。 所有的按位操作都可以处理任何类型的整数,但本章的例子使用16位的变量,使例子较为简单。用下面的语句声明并初始化一个变量number: unsigned short number=16387U; 如第2章所述,不带符号的字面量应在数字的最后添加字母U或u。 在下面的语句中,对这个变量的内容进行移位,并存储结果: unsigned short result = number <<2; //Shift left two bit positions 移位运算符的左操作数是要移位的值,右操作数指定要移动的位数。图3-2列出了该操作的过程。 图3-2 移位运算 从图3......

阅读全文(3278) | 评论:0

c++位运算(2008-03-10 21:13:00)

摘要:前言     看到有些人对位运算还存在问题,于是决定写这篇文章作个简要说明。         什么是位(bit)?         很简单,位(bit)就是单个的0或1,位是我们在计算机上所作一切的基础。计算机上的所有数据都是用位来存储的。一个字节(BYTE)由八个位组成,一个字(WORD)是二个字节或十六位,一个双字(DWORD)是二个字(WORDS)或三十二位。如下所示:             0   1   0   0   0   1   1   1   1   0   0   0   0   1   1   1   0   1   1   1   0   1   0   0   0   1   1   1   1   0   0   0     |   |                             |                               |                               |                             ......

阅读全文(1971) | 评论:0

LPC213X硬件SPI接口LCD19232显示C++代码(2008-02-22 22:31:00)

摘要:/*-------------------------------------------------------------------- LPC213X硬件SPI接口LCD19232显示C++代码 HotPower@126.com --------------------------------------------------------------------*/ //__inlineLcdObj::LcdObj(void){  LcdSpiInit();//SPI初始化   LcdInit();} //__inlinevoid LcdObj::LcdSpiInit(void){/* 设置MOSI 和SCK 及SS 为输出,其他为输入 */  LCDPORT->IODIR |= (1 << LCDCS);  LCDPORT->IOCLR = (1 << LCDCS);/*  LCDPORT->IODIR |= (1 << LCDSCK)| (1 << LCDSID);//设置输出方式  LCDPORT->IOSET = (1 << LCDSCK) | (1 << LCDSID);*/  POWER->P_CONP |= (1 << PCSPI0);  PINSEL->PIN_SEL0 |= ((P0_4_SCK0 << P0_4_PINSEL) | (P0_6_MOSI0 << P0_6_PINSEL));//  SPI->SPI_SPCCR = 0x168;              // 设置SPI时钟分频  SPI->SPI_SPCCR = 0x52;              // 设置SPI时钟分频  SPI->......

阅读全文(2482) | 评论:0