目录
1.简单就是美
2. sizeof
3.字节序
4.函数参数
5.返回值
6.强制类型转换
7.swith case
8.字符串
9. 资源释放
10. if规范
11. 临界资源保护
1.简单就是美
优先级搞不清楚用括号
复合语句太罗嗦,拆成几行来写
编码的三不原则
不要挑战自己的记性
不要挑战自己的耐心
不要挑战编译器的水平
编码的三用原则
能用简单句的,就不要用复杂的技巧
能用成熟代码的,就不要再来一套
能用上工具的,就一定要机械化
Struct xxx
{
char cA;
short sB;
long lC;
}
void main()
{
char *pchar;
pchar = (char *)malloc(7); /* 1—魔鬼的数字;2—申请失败后怎么办? */ mencpy (pchar, “abcdefgh”, sizefo(xxx)); /* 3—内存 */
printf(“%s\n”, pchar); /* 缺少字符串结束符必越界 */
return; /* 5—退出前没有释放内存 */
}
2. sizeof
数据结构是C语言的基础。C语言的灵活性很大,程度上在于其数据结构的灵活性。要用好的数据结构,首先要掌握数据结构的大小的计算,系统的每个数据机构,每个变量都会分配到一个对应的存储空间,这个存储空间的大小就是数据结构的尺寸。
sizeof 为编译时的一元运算符,可用来计算任一对象的大小
sizeof 的结果是编译时的常量
sizeof 不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型。如未知存储大小的数组类型,未知内容的结构或联合类型,void 类型等。
3. 字节序
X86系统
void QosConfigPolicy(xxx)
{
ulDestIP = 从命令行读取用户配置的参数;
pQosPoliscy->ulDestIP = ulDestIP;
return;
}
主机处理
void QosClassify(xxx)
{
Plp = (IP-S*)pData;
If(pQosPolicy->ulDestIP == plp->ulDestIP)
{
Vos_HTONL(pQosPolicy->ulDestIP;
DoSomething();
}
Return;
}
由于历史的原因,世界存在两种字节序标准——BigEndian和LittleEndian。Power PC是大头,X86是小头。有些CPU可以通过寄存器设置支持不同的字节序。如MIPS BigEndian——高位在低字节,地位在高字节
LittleEndian——低位在低字节,高位在高字节
e.g.0x345678 大头内存从低到高存放次序 00,34,56,78;小头内存从低到高存放次序78,56,34,00
字节序问题广泛存在于设备与设备之间,单板与单板之间,单板与底层之间,只要两个处理单元的字节序不同,这个问题就存在。为了解决不同字节序的处理单元之间的同信问题,世界上定义了主机序和网络序的概念,网络序主要用于信息传递,一般不用于计算,其字节顺序与大头一致。
除在编码时紧绷这根弦之外,我们在器件选择是主机序与网络序一致的芯片,同一设备的不同单板使用相同的字节序。并优先选择支持大头的芯片,这样即使不能彻底解决问题,也可以彻底规避问题。
4. 函数参数
C语言中,函数通过返回值和参数与调用者交换信息。函数参数自身占用的存储单元在堆栈中分配。入口参数指向的数组或地址,在函数入口处拷贝到堆栈区中,因此对函数参数所在存储单元的直接修改不会作用到函数之外,而对参数存储单元中存放的地址指向的存储空间的修改,则会在函数之外起作用。调用者在进行函数调用之前,必须事先申明被调用函数的原型,包括返回值类型和参数类型。
CHAR *GetMemory(CHAR *p)
{
/* 申请内存 */
P = (CHAR *)malloc(100);
Return p;
}
Malloc 申请的内存空间与操作系统有关,在PC中molloc申请空间以byte为单位,如申请100个int内存则 p=(INT *)malloc(400);
VOID Test(void)
{
CHAR *str = NULL;
If(NULL != GetMemory(&str))
{
Strcpy(str, ”hellworld”);
Print(str);
Free (str);
Str = NULL;
Return;
}
5. 返回值
C语言中,函数的调用者通过返回值了解函数的执行情况,函数缺省的返回值类型为int,编程规范要求必须显示定义函数的返回类型。对于反映了函数执行状态的返回值,调用者必须依据返回值进行相应的处理,尤其是对于函数执行异常的情形。函数的出口参数能够起到与返回值类似的作用,上一条同样适用于出口参数。
对于函数返回值为恒值得函数,建议使用void返回值
#include “stdio.h”
Void main()
{
Char *p;
P = (char *)malloc(100);
If(p != NULL)
{
Strcp(p,”hello world!\n”);
Printf(p);
Free(p);
}
Return;
}
Long A()
{
If (exp())
{
Return VOS_ERROR;
}
Ruturn VOS_OK;
}
Long B()
{
If (A())
{
Dosomething1();
}
else
{
Dosomething2();
}
Return;
}
6.强制类型转换
强制类型转换给C编程带来了极大灵活性,也正是这种灵活性,埋下了无数隐患。
当目地结构的空间大于源结构的空间时,要重点关注内存访问超过源结构范围的情况,可能越界。当目地结构的空间小于源结构空间时,要重点关注对目地结构赋值不能完全覆盖源结构范围的情形,能遗漏。
与结构体之间的强制类型转换相比,基本数据的强制类型转换更容易出现上述情况
1)目地结构小于源结构
Void B (char *p)
{
*p = 1;
Return;
}
Void A()
{
Ulong a;
B((char*)(&a));
Return;
}
A = ? 是1吗?答案:不可预知
2) 目的结构大于源结构
Void B(ulong *p)
{
*p = 1000;
Return;
}
Void A()
{
Uchar a = 10;
B((ulong *)(&a));
Return;
}
在函数B给*P赋值前,*P值时多少?答案:不可预知
*P赋值后,会出现什么情况?答案:越界
7. switch case
C语言使用switch case处理一个条件的多个取值有不同的处理分支的情况。
当所有的case都匹配不成功时,进入default分支。如果程序从逻辑上不可能走到这个分支,可以在该分支中使用断言。
结束case分支的执行最常用的办法是使用break/return,否则程序将自动进入下一个case 分支继续执行。编译器对switch…..case……可以做优化,用空间换取时间,default分支按照编程规范,要求放在switch case的末尾,C本身不做强制要求。
Void main()
{
Long ulcnt1=0,ulcnt2=0;
Char *ch = “aha!”;
While(*ch)
{
Switch (*ch)
{
Case ‘a’:
Case ‘h’:
ulcnt2++;
Default:
Ulcnt1++;
}
Ch++;
}
Printf (“%u, %u\n”,ulcnt1,ulcnt2);
Return;
}
Ulcnt1和ulcnt2分别是多少? Ulcnt1=4,ulcnt2=3
8.字符串
Ulong buildrun ((char **ppbuildrun)
{
Ulong ullen;
Char *pbuf;
Ullen = calculatebuildrunlen();
If ( 0 == ulen )
{
*ppbuildrun = NULL;
Return B_ZERO_LENGTH;
}
Pbuf = VOS_malloc ( 0,ullen);
If (NULL == pbuf)
{
*ppbuildrun = NULL;
Return B_MALLOC_FAILED;
}
VOS_Strcpy(pbuf, buildruninfo);
*ppbuildrun = pbuf;
Return VOS_OK;
}
案例点评:
为信息输出,字符串必不可少,字符串在动态申请时少分配一个字符是非常普遍的一个错误,strlen等计算字符串长度的函数都是不考虑字符串的\0结束符的,代码review时,字符串越界问题是一个大客户,要盯紧看严
Sizeof与strlen的区别
Char char[ ] = “abc”; sizeof (char) = 4, strlen(char) = 3
Char char[ ] = “ab\0c”; sizeof (char) = 5, strlen(char) = 2
Sizeof() 为编译时执行,strlen()为运行时执行
Long getxyhead ( char **pdata, char ** pbuf)
{
Ulong ulen;
Char *ptmpdata = *pdata, *ptmbuf = *pbuf;
Ullen = analysisheand(ptmpbuf);
// strcpy(ptmpdata, ptmpbuf);
// sprintf(ptmpdata, ptmpbuf, ullen);
// memcpy(ptmpdata, ptmpbuf, ullen);
Return ullen;
}
*pbuf 中存放的是xyz协议的peer发送过来的一段报文,这个函数负责将协议头拷贝到pdata指向的空间中,假定空间是够,那条语句最合适? Mencpy语句最合适
因为没人保证 *pbuf中不出现’\0’,infact,协议头中非常容易出现’\0’,此时它不再是字符串,字符串工具函数必须是专款专用,而 mencpy则要宽泛得多。
Void getdigitString (char *pdata, char *pbuf)
{
Char * ptmpdata = pdata, *ptmpbuf = pbuf;
While ( ‘\0’ != (*ptmpbuf))
{
If ((‘0’ <= *ptmpbuf) && (‘9’ >= *ptmpbuf))
{
*ptmpdata = *ptmpbuf;
Ptmpdata++;
}
Else
{
Break;
}
Ptmpbuf++;
}
*ptmpdata=’\0’; /* 没有’\0’,就不是字符串 */
Return ;