C语言学习笔记

发布时间 : 星期二 文章C语言学习笔记更新完毕开始阅读

编程规则

函数变量定义

数据的传递尽量通过函数的参数来实现,函数前面的函数返回值类型最好是表示函数执行的状态。

比如:我们通过串口接收一个字节数据,我们可以定义函数如下: BOOL

xSmsPortSerialGetByte( CHAR * pucByte ) {

*pucByte = UDR; return TRUE; }

还可以定义如下:

Char xSmsPortSerialGetByte( void) {

return UDR; }

第一种方法,调用函数后需要传递出一个接收到的数据,这个是通过函数的参数来传递的。第二种方法,是通过函数的返回值来传递的。 编译

代码编译后不能存在warning。

以后一定注意不能忽略编译器的警告 发表于 2008-7-13 22:54:25

1,为什么容易忽略编译器的警告信息?

1)编程时处理各种error已经很让人恼火的了,error自然被放在次要位置; 2)绝大多数下,程序在存在warnings的情况下,在短期内可以正常运行,我们根本看不到错误,导致warning近一步被轻视;

3)没有养成好的工作习惯,带有warning的代码是不能交付的,而我们判断交付的标准中往往没有对代码提出更为细致的要求。 2,忽视编译警告往往会给我们造成重大损失

最近的一个项目中,我的程序在实验室测试了1个星期没有出现问题。当设备部署到现场3天后,坏事情终于发生了:设备依次出现死机问题。

再从头分析代码, 百思不得其解,痛不欲生的时候注意到了一个编译警告,说一个表达式一直为真。原来发生了下面的错误: unsigned char i;

for( i = 0; i < 1000; i++) {.....}

变量类型的定义导致了表达式一直为真。为什么开始的测试中不会出现问题哪?原来,这段代码是必须程序运行一段时间后才会执行的一段代码,在短时间的测试中根本不会进入。

看来编程真不能偷懒,偷懒必遭惩罚,你还要费比当时偷懒省的劲多好多倍的劲来弥补,并且这个弥补的过程中你还要承受巨大的压力。跟平时多流汗战时少流血的道理一样的。 3)解决办法

给自己的编程增加一个规则,带有编译警告的代码不能交付。

引用没有声明的外部函数可能会导致严重问题

using a function without a valid prototype is VERY dangeours. 在一个函数中调用另外一个文件中定义的函数,但是这个函数没有进行声明,会出什么问题哪?

大多数的情况下可能会出现问题,有些情况下,可能是幸运不会出现问题。 看下面这段代码:

unsigned long ulWater_Max_Value=0xABCDEFUL; Puthexbyte(ulWater_Max_Value>>24); Puthexbyte(ulWater_Max_Value>>16); Puthexbyte(ulWater_Max_Value>>8); Puthexbyte(ulWater_Max_Value);

其中Puthexbyte函数在另外一个c文件中进行了定义,但是在该文件对应的.h文件中,我没有对这个函数进行声明。

这样,当运行上面的代码的时候,会导致错误的结果,输出的数据全部为0。 改正的方法是,为每个外部函数在.h文件做一个相应的声明。每个需要调用这个函数的文件都要包含这个.h头文件。

仍然是上面的代码,如果把变量定义为unsigned int 形式,却可以输出正确结果。这有点奇怪,绝对是侥幸,但是,为什么会对哪?即: unsigned int ulWater_Max_Value=0xABCD; //Puthexbyte(ulWater_Max_Value>>24); //Puthexbyte(ulWater_Max_Value>>16); Puthexbyte(ulWater_Max_Value>>8); Puthexbyte(ulWater_Max_Value);

在不声明Puthexbyte函数的情况下,仍然能正确输出,为什么哪?

assert(断言)的使用

编辑: 程序设计 发表日期: 2007-05-13 21:56 原创作者:Qdieyou,转载请加注。

程序一般分为Debug 版本和Release 版本,Debug 版本用于内部调试,Release 版本发行给用户使用。(这个概念可能不大好理解,想想VC下,调试时有个选项,一个是debug,一个是release)

assert(表达式); 的意思是:当表达式为真时,程序继续运行,如果表达市为假,那程序就会停止运行,并提示错误信息。

注意:assert是一个宏,只在debug版本中起作用,在release版本中,该语句是不起任何作用的。

先简单的看一个例子吧!

以下为一个使用了断言的C源程序: #include #include

void test(int *p) {

assert(p != NULL); printf(\}

int main(void) {

test(NULL); }

编译及运行结果:

Qdieyou@qdieyou /cygdrive/e/gcc $ gcc -o assert assert.c

Qdieyou@qdieyou /cygdrive/e/gcc $ ./assert

assertion \

19331 [sig] assert 3288 e:\\gcc\\assert.exe: *** fatal error - called with threa dlist_ix -1 Hangup

程序说明:由于我们在main函数中传了NULL指针值给test函数,在test函数执行到assert(p != NULL);发现表达式不为真,就终止了程序的运行,并提示错误的行数信息。

注意:由于assert宏只在debug版本中起作用,所以assert一般只用于内部函数对参数有效性进行检查,如果该函数作为一个外部接口来使用时,一般需要利用if,else语句进行防错设计。——Qdieyou

以下摘自《C高效编程》

【规则6-5-1】使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。

【规则6-5-2】在函数的入口处,使用断言检查参数的有效性(合法性)。

【建议6-5-1】在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的假定,就要使用断言对假定进行检查。

【建议6-5-2】一般教科书都鼓励程序员们进行防错设计,但要记住这种编程风格可能会隐瞒错误。当进行防错设计时,如果“不可能发生”的事情的确发生了,则要使用断言进行报警。

编程的时候,看到下面的一个函数: eMBErrorCode

eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength ) {

eMBErrorCode eStatus = MB_ENOERR; ENTER_CRITICAL_SECTION( );

assert( usRcvBufferPos < MB_SER_PDU_SIZE_MAX );

/* Length and CRC check */

if( ( usRcvBufferPos >= MB_SER_PDU_SIZE_MIN )

&& ( prvucMBLRC( ( UCHAR * ) ucASCIIBuf, usRcvBufferPos ) == 0 ) ) {

/* Save the address field. All frames are passed to the upper layed * and the decision if a frame is used is done there. */

*pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF]; /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus * size of address field and CRC checksum. */

*pusLength = ( USHORT )( usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC );

/* Return the start of the Modbus PDU to the caller. */

*pucFrame = ( UCHAR * ) & ucASCIIBuf[MB_SER_PDU_PDU_OFF]; } else {

eStatus = MB_EIO; }

EXIT_CRITICAL_SECTION( ); return eStatus; }

当看到UCHAR ** pucFrame的时候就优点晕了。不就是传出一个地址吗?干吗还要用指向指针的指针?于是,顺手修改了程序,将那个形参变成了UCHAR * pucFrame,具体的函数也变成了下面的样子: eMBErrorCode

eMBASCIIReceive( UCHAR * pucRcvAddress, UCHAR * pucFrame, USHORT * pusLength ) {

eMBErrorCode eStatus = MB_ENOERR;

联系合同范文客服:xxxxx#qq.com(#替换为@)