您好,欢迎访问三七文档
当前位置:首页 > 临时分类 > C语言编程基础培训(2011)
C语言编程基础培训v0.1应用研究课题组目录•1.简单就是美•2.sizeof•3.字节序•4.函数参数•5.返回值•6.强制类型转换•7.swithcase•8.字符串•9.资源释放•10.if规范•11.临界资源保护1.简单就是美•优先级搞不清楚用括号•复合语句太罗嗦,拆成几行来写•编码的三不原则–不要挑战自己的记性–不要挑战自己的耐心–不要挑战编译器的水平•编码的三用原则–能用简单语句,就不要用复杂的技巧–能用成熟代码的,就不要再来一套–能用上工具的,就一定要机械化2.sizeof•数据结构是C语言的基础。C语言的灵活性很大,程度上在于其数据结构的灵活性。要用好的数据结构,首先要掌握数据结构的大小的计算,系统的每个数据机构,每个变量都会分配到一个对应的存储空间,这个存储空间的大小就是数据结构的尺寸。•sizeof为编译时的一元运算符,可用来计算任一对象的大小•sizeof的结果是编译时的常量•sizeof不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型。如未知存储大小的数组类型,未知内容的结构或联合类型,void类型等。3.字节序•X86系统voidQosConfigPolicy(xxx){ulDestIP=从命令行读取用户配置的参数;pQosPoliscy-ulDestIP=ulDestIP;return;}•主机处理voidQosClassify(xxx){plp=(IP_S*)pData;if(pQosPolicy-ulDestIP==plp-ulDestIP){VOS_HTONL(pQosPolicy-ulDestIP);DoSomething();}Return;}3.字节序•由于历史的原因,世界上存在两种字节序标准BigEndian和LittleEndian。PowerPC是大端,X86是小端。有些CPU可以通过寄存器设置支持不同的字节序。如MIPS。•BigEndian——高位在低字节,地位在高字节•LittleEndian——低位在低字节,高位在高字节–e.g.0x345678大端内存从低到高存放次序00,34,56,78;小端内存从低到高存放次序78,56,34,003.字节序•为了解决不同字节序的处理单元之间的通信问题,世界上定义了主机序和网络序的概念,网络序主要用于信息传递,一般不用于计算,其字节顺序与大头一致。•除在编码时紧绷这根弦之外,我们在器件选择时选择主机序与网络序一致的芯片,同一设备的不同单板使用相同的字节序。并优先选择支持大头的芯片,这样即使不能彻底解决问题,也可以彻底规避问题。4.函数参数•C语言中,函数通过返回值和参数与调用者交换信息。•函数参数自身占用的存储单元在堆栈中分配。•入口参数指向的数组或地址,在函数入口处拷贝到堆栈区中,因此对函数参数所在存储单元的直接修改不会作用到函数之外,•对参数存储单元中存放的地址指向的存储空间的修改,则会在函数之外起作用。•调用者在进行函数调用之前,必须事先声明被调用函数的原型,包括返回值类型和参数类型。4.函数参数CHAR*GetMemory(CHAR*p){/*申请内存*/p=(CHAR*)malloc(100);returnp;}/*malloc申请的内存空间与操作系统有关,在PC中malloc申请空间以byte为单位,如申请100个int内存则p=(INT*)malloc(400);*/VOIDTest(void){CHAR*str=NULL;if(NULL!=GetMemory(str)){strcpy(str,“hellworld”);printf(str);return;}}5.返回值•C语言中,函数的调用者通过返回值了解函数的执行情况,函数缺省的返回值类型为int,编程规范要求必须显示定义函数的返回类型。•对于反映了函数执行状态的返回值,调用者必须依据返回值进行相应的处理,尤其是对于函数执行异常的情形。•函数的出口参数能够起到与返回值类似的作用,上一条同样适用于出口参数。•对于函数返回值为恒值的函数,建议使用void返回值5.返回值•main函数的返回值是int,main函数有以下两种形式(C99):–intmain(void)intmain(intargc,char*argv[])(参考资料:ISO/IEC9899:1999(E)Programminglanguages—C5.1.2.2.1Programstartup)•Thedefinitionvoidmain(){/*...*/}isnotandneverhasbeenC++,norhasitevenbeenC.——C++之父BjarneStroustrup6.强制类型转换•强制类型转换给C编程带来了极大灵活性,也正是这种灵活性,埋下了无数隐患。•当目的结构的空间大于源结构的空间时,要重点关注内存访问超过源结构范围的情况,可能越界。•当目的结构的空间小于源结构空间时,要重点关注对目地结构赋值不能完全覆盖源结构范围的情形,可能遗漏。•与结构体之间的强制类型转换相比,基本数据的强制类型转换更容易出现上述情况。6.强制类型转换•目的结构小于源结构voidB(char*p){*p=1;return;}voidA(){ULONGa;B((char*)(&a));return;}•a=?是1吗?答案:不可预知6.强制类型转换目的结构大于源结构voidB(ULONG*p){*p=1000;return;}voidA(){UCHARa=10;B((ULONG*)(&a));return;}•在函数B给*p赋值前,*p值时多少?•*p赋值后,会出现什么情况?答案:不可预知答案:越界7.switchcase•C语言使用switchcase处理一个条件的多个取值有不同的处理分支的情况。•当所有的case都匹配不成功时,进入default分支。如果程序从逻辑上不可能走到这个分支,可以在该分支中使用断言。•结束case分支的执行最常用的办法是使用break/return,否则程序将自动进入下一个case分支继续执行。•编译器对switch…..case……可以做优化,用空间换取时间,default分支按照编程规范,要求放在switchcase的末尾,C本身不做强制要求。7.switchcasevoidmain(){LONGulcnt1=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=38.字符串•为信息输出,字符串必不可少,字符串在动态申请时少分配一个字符是非常普遍的一个错误,strlen等计算字符串长度的函数都是不考虑字符串的\0结束符的,代码review时,字符串越界问题是一个bigissue,要盯紧看严8.字符串•sizeof与strlen的区别–charChar[]=“abc”;–sizeof(Char)=4,strlen(Char)=3–charChar[]=“ab\0c”;–sizeof(Char)=5,strlen(Char)=2–sizeof()为编译时执行,strlen()为运行时执行8.字符串LONGgetxyhead(char**pdata,char**pbuf){ULONGullen;CHAR*ptmpdata=*pdata,*ptmbuf=*pbuf;ullen=analysishead(ptmpbuf);//strcpy(ptmpdata,ptmpbuf);//sprintf(ptmpdata,ptmpbuf,ullen);//memcpy(ptmpdata,ptmpbuf,ullen);returnullen;}•*pbuf中存放的是xyz协议的peer发送过来的一段报文,这个函数负责将协议头拷贝到pdata指向的空间中,假定空间是够的,那条语句最合适?•memcpy语句最合适–因为没人保证*pbuf中不出现’\0’,infact,协议头中非常容易出现’\0’,此时它不再是字符串,字符串工具函数必须是专款专用,而memcpy则要宽泛得多。8.字符串voidgetdigitString(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;}•该函数功能是将pbuf中的连续数字拷贝到pdata中,生成一个新字符串!8.字符串#defineBUFFER_SIZE250voidTest(void){charpszbuf[BUFFER_SIZE]=”\0”;sprintf(pszbuf,“File:%sLine:%d”,_FILE_,_LINE_);snprintf(pszbuf,sizeof(pszbuf)-1,”File:%sLine:%s”,_FILE_,_LINE_);pszbuf[sizeof(pszbuf)-1]=‘\0’;printf(“%s”,pszbuf);return;}#defineBUFFER_SIZE250voidTest(char*pszmsg){char*pszbuf=NULL;if(NULL==pszmsg)return;pszbuf=malloc(BUFFER_SIZE+1);if(pszbuf!=NULL){strncpy(pszbuf,pszmsg,BUFFER_SIZE);pszbuf[BUFFER_SIZE]=’\0’;free(pszbuf);}return;}案例点评C语言提供的函数库字符串函数sprintf/vsprintf/strcpy/strcat/gets等非常危险,很容易导致内存越界,应使用安全的字符串库函数snprintf/strncpy/strncat/fgets指定操作内存大小。Strncpy等安全函数,当拷贝字符串到达指定的长度时,不会在目标字符串结尾添加’\0’,必须手工添加’\0’,可以在调用strncpy后紧接着赋0,也可以在申请内存时将最后一字节置0。8.字符串•函数功能:把190个字节空间中能容纳的非负整数按小到大的顺序不间断打印出来。#defineBUFFER_SIZE190voidmain(void){charszbuf[BUFFER_SIZE]={0};inti=0;for(;i100;i++)/*190个字节空间最多能容纳多少个整数?100?*/{sprintf(szbuf+strlen(szbuf),“%d”,i);}printf(“%s\n”,szbuf);return;}•当i=10时,每个整数占用两字节,循环100次后,Buffer总长度(包括’\0’)会超过190,导致内存写越界。•上述例子属于人工计算字符串长度出错的典型例子,案例中100就是人工计算的魔数。魔数是指代码中出现的难以理解的数字。这里的代码不仅仅指*.c文件,也包含宏定义。简单的使用宏替换,魔数并没消失,正确的做法是,除使用宏表明数字的含义外,还应从若干基础数字自动运算出衍生数字,任何情况下都不要手工计算数字!计算数字的工作应由计算机完成。8.字符串#defineBUFFER_SIZE190voidmain(void){charszbuf[BUFFER_SIZE]={0};inti=0;intiposition=0;/*记录每次copy的起始位置*/intlength=0;/*记录每次copy的字符数(不算’\0’
本文标题:C语言编程基础培训(2011)
链接地址:https://www.777doc.com/doc-3970485 .html