




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C 程序设计授课教师 孙向群fd_C 程序设计授课教师 孙向群C语言编程常见错误分析专业名称:物流工程C语言编程常见错误分析专业名称:物流工程C语言是目前世界上最通用的编程语言之一,也是目前研发使用最多的编程语言。同各种各样的bug作斗争,是每一个C程序员每天所面临的课题。本文将从微观角度出发,对一些常见出错类型的案例进行分析,希望大家今后能避免类似的错误。程序设计(Programming in C)C语言是目前世界上最通用的编程语言之一,也是目前研发使用最多数字和表达式错误变量的错误数组和指针的错误逻辑和流程的错误程序设计(Programming in C)数字和表达式错误程序设计(Prog
2、ramming in C)数字和表达式的错误运算符和优先级的错误字节序的错误魔鬼数字宏定义的错误sizeof的错误程序设计(Programming in C)数字和表达式的错误运算符和优先级的错误程序设计(Progra最常见的运算符错误就是“=”和“=” 的误用int main ()int ret;ret = GetVars();if (ret = VOS_OK).return 0;错误后果:1.变量被错误赋值。2.逻辑判断不正确。建议和结论:尽管是初级bug但是还是常有发生,建议写成“VOS_OK = ret”的形式,这样在编译的时候即可发现这种错误。程序设计(Programming in
3、C)最常见的运算符错误就是“=”和“=” 的误用int mai“+”和“-”在表达式中的应用#define mypower(a) (a)*(a)int main()int i = 1,j = 2;j = mypower(+i);printf(rn%i=d j=%d, i, j);return 0;错误后果:结果与期望的不一致建议与结论:1.对于“+”和“-”这种基本的知识还是应该掌握的2.自增和自减变量在本表达式中不要再引用,否则可能依赖编译器实现3.没有把握的用法千万不要用,否则可能导致意想不到的错误程序设计(Programming in C)“+”和“-”在表达式中的应用#define m
4、ypow优先级问题也是编码初期容易出现的问题C语言有众多的运算符号,它们之间的优先级关系非常复杂,即使是一个熟练的C程序员,要清楚地记住这些优先级关系也绝非易事。if (high if (high if (a | b) & (a & c) if (a | b = 1) = if (a | b) = 1) 程序设计(Programming in C)优先级问题也是编码初期容易出现的问题C语言有众多的运算符号,建议和结论:不要使用默认优先级,使用括号来保证自己的运算优先级,不要考验字节的记忆力。程序设计(Programming in C)建议和结论:程序设计(Programming in C)字节
5、序错误网络设备和网络协议的开发涉及到许多字节序问题。网络序:所有设备、系统都一样,表示字节在网络中的传输顺序,也就是设备接收、发送数据的顺序。数据总是按照从“高字节” =“低字节”的顺序发。程序设计(Programming in C)字节序错误网络设备和网络协议的开发涉及到许多字节序问题。程序主机序:依赖于CPU,表示的是字节在内存中的存放顺序。对于Intel系列CPU:高字节存放高位,低字节存放低位;和网络序相反;一般称为“小尾”或者“小端”( little endian )对于PPC系列CPU:高字节存放低位,低字节存放高位;和网络序相同;一般称为“大尾”或者“打端”( big endia
6、n )程序设计(Programming in C)主机序:程序设计(Programming in C)对于32位int型数0 x12345678,intel和ppc系列CPU的存放格式分别如下:程序设计(Programming in C)对于32位int型数0 x12345678,intel和ppc假定有一种协议报文,报文类型为2个字节,某一特定类型为0 xABCD程序设计(Programming in C)假定有一种协议报文,报文类型为2个字节,某一特定类型为0 xA两个因为字节序错误导致的真实案例案例一:某一种协议,其中一种报文类型是3,但是在代码中填写报文类型时没有进行字节序转换,导致对
7、端总是识别报文类型为0 x0300,于是对端认为是非法报文,直接作丢弃处理后果:当时使用的是Intel的CPU,这种错误类型的报文从来没有被处理过,当时也没有做单元测试,错误很久以后才被发现。因为这个报文是用来进行协议性能优化的,修改过后协议性能得到极大提升。程序设计(Programming in C)两个因为字节序错误导致的真实案例案例一:程序设计(Progr案例二:某一种协议,报文中有一个字段用DWORD表示报文长度,但是在填写报文时遗漏了字节序转换,因为使用的是Intel的CPU,导致对端从收到的报文中提取出的报文长度为类似0 x4800000000的巨大数字。我们字节的设备在接收时也遗
8、漏了字节序转换,所以从收到的报文中反而能正确提取出报文长度,该设备也一直没有与cisco的设备做互通测试,使该问题一直没有暴露出来。后果:该设备第一次开局时,与cisco设备互通,结果周边所有cisco的设备异常重启(cisco当年设备的鲁棒性也比较差劲!)。程序设计(Programming in C)案例二:程序设计(Programming in C)字节序错误不只在报文中存在一.联合域定义:union ULONG ulIP;UCHAR szIP4; stIPAddr;如果赋值:stIPAddr.szIP0 = 192;stIPAddr.szIP1 = 168;stIPAddr.szIP2
9、= 0;stIPAddr.szIP3 = 1;则大端和小端上stIPAddr.ulIP的值不同:大端:stIPAddr.ulIP = 0 xC0A80001;小端:stIPAddr.ulIP = 0 x0100A8C0;程序设计(Programming in C)字节序错误不只在报文中存在一.联合域定义:程序设计(Prog二.指针强转:定义如下变量:ULONG ulTool = 0 x12345678;USHORT *pusTool = (USHORT *)&ulTool;UCHAR *pucTool = (UCHAR *)&ulTool;则在大端和小端上, *pusTool 和*pucToo
10、l 的值是不同的。大端系统:*pusTool = 0 x1234*pucTool = 0 x12小端系统:*pusTool = 0 x5678*pucTool =0 x78程序设计(Programming in C)二.指针强转:程序设计(Programming in C)建议和结论:1.填写报文和解析报文时一定要注意字节序问题,使用相应的宏操作(htonl、ntohl等)2.当取变量的一部分值时,如联合体、强制指针转换等,需要考虑字节序问题3.互通测试很重要程序设计(Programming in C)建议和结论:程序设计(Programming in C)魔鬼数字问题魔鬼数字是指直接使用数字
11、,而不是使用预先定义好的宏、常量、枚举等,这是一种不好的编程习惯。如下:l = round / 3.14159;DeadInt = HelloInt * 4;pstPack = malloc(36);pTcp = pIp + 20;程序设计(Programming in C)魔鬼数字问题魔鬼数字是指直接使用数字,而不是使用预先定义好的const double PI = 3.14159;l = round / PI;#define DEADTIME 4DeadInt = HelloInt * DEADTIME;#define PACKSIZE 36pstPack = malloc(PACKSIZ
12、E);#define IPHEADLEN 20pTcp = pIp + IPHEADLEN;程序设计(Programming in C)const double PI = 3.14159;程序设计建议和结论:1.魔鬼数字是一种不好的编程习惯,一方面代码可读性差,另一方面在修改多出数字时容易造成遗漏,从而各处使用导致不一致。2.不是所有的数字都是魔鬼数字3.有明确意义的数字应该定义长宏、枚举或者常量,如申请的内存大小、函数的返回值、各种标记位等。程序设计(Programming in C)建议和结论:程序设计(Programming in C)宏定义错误宏定义最常见的问题就是没有使用足够的括号去
13、保证展开的正确性。示例1:#define mul1(a, b) (a * b)#define mul2(a, b) (a) * (b)int main()int x = 0;x = mul1(1+2, 5); /* x = 11 */x = mul2(1+2, 5); /* x = 15 */ 程序设计(Programming in C)宏定义错误宏定义最常见的问题就是没有使用足够的括号去保证展开示例2:#define add1(a, b) (a) + (b)#define add2(a, b) (a) + (b)int main()int x = 0;x = add1(1+2)*5; /*
14、x = 11 */x = add2(1+2)*5; /* x = 15 */ 程序设计(Programming in C)示例2:程序设计(Programming in C)建议和结论:1.宏定义会忠实地进行展开,这个展开过程忽略运算符、优先级和函数。2.宏定义里面的算术表达式里面的各个参数需要加括号,整个表达式本身也需要加括号。程序设计(Programming in C)建议和结论:程序设计(Programming in C)Sizeof问题Sizeof是一个编译时处理的操作符,sizeof最常见的问题就是混淆了结构的体积和结构指针的体积。struct theNodeint a;char b
15、20;Node;.int x = 0;struct Node *pstNode;x = sizeof(Node); / x = 24x = sizeof(pstNode);/ x = 4程序设计(Programming in C)Sizeof问题Sizeof是一个编译时处理的操作符,siz建议与结论:1.sizeof是编译器在编译时处理的,而不是在程序运行时处理的2.结构指针的体积与结构体的体积是两回事,在32位机上,指针一般都是32位长的(即4字节),而结构体的长度则依赖于结构体定义程序设计(Programming in C)建议与结论:程序设计(Programming in C)struc
16、t theNode int b5;short c;Node;sizeof(Node) = ?另一个常见的错误是某些结构体定义没有正确使用#pragma pack,导致结构体体积的计算与理想有偏差。#pragma pack(1) struct theNode int b5;short c;Node;sizeof(Node) = ?程序设计(Programming in C)struct theNode 另一个常见的错误是某些结构体定建议和结论:1.对齐有利于提高存储效率,常见的系统一般默认4字节或8字节对齐,编译时编译器将选取系统对齐和本结构中最常基础结构二者中的较小值作为该结构的实际对齐值。2
17、.这个错误经常发生在定义报文结构是时,报文结构一般都应该按pack(1)来定义。程序设计(Programming in C)建议和结论:程序设计(Programming in C)变量的错误变量的类型和存储全局变量局部变量程序设计(Programming in C)变量的错误变量的类型和存储程序设计(Programming 全局变量:定义在任何函数的外部,生命周期是在整个程序的周期内。-定义时没有初始化,或者初始化为0的全局变量存放在bss段(对于bss段,操作系统在加载时会自动全部清0)-定义时初始化为非0的全局变量存放在data段程序设计(Programming in C)全局变量:定义在
18、任何函数的外部,生命周期是在整个程序的周期内局部变量:定义在函数内,只能在所在函数内访问-静态局部变量存放在全局堆中,生命周期是在整个程序-普通局部变量存放在栈中,生命周期是在函数内程序设计(Programming in C)局部变量:定义在函数内,只能在所在函数内访问程序设计(Pro注意:不管什么变量都要注意初始化问题,变量不初始化而直接作为右值使用是一个常犯的错误。程序设计(Programming in C)程序设计(Programming in C)全局变量在定义是初始化,会使app文件增大。因此,对于全局的大数组,应该尽量避免在定义是初始化,可以在程序执行的初始化阶段进行初始化。int
19、 array10001000 = 1; int main(int argc, char* argv)return 0;用VC编出来的app大小大约为180Kint array10001000 ; int main(int argc, char* argv)return 0;用VC编出来的app大小大约为4.75M程序设计(Programming in C)全局变量在定义是初始化,会使app文件增大。因此,对于全局的建议和结论:1.尽量避免对大的全局变量在定义时进行初始化,这样可以减小app大小,节省存储空间2.无论初始化与否,全局变量总是要占用运行时的内存空间,因此要避免定义不必要的大型全局变
20、量程序设计(Programming in C)建议和结论:程序设计(Programming in C)普遍局部变量是存放在当前任务或系统栈中,因此避免定义过大的局部变量,从而使堆栈溢出。int main(int argc, char* argv)int array10001000 = 0;return 0;有什么问题?程序设计(Programming in C)普遍局部变量是存放在当前任务或系统栈中,因此避免定义过大的局示例:int func1()int a4000;func2(0);.int func1()int b4000;func3(0);.错误后果:1.在嵌入式设备上,每个任务的栈大小
21、是有限的,一般在任务创建是指定(一般是4-40k),一旦发生栈空间溢出,容易造成栈被写坏,系统死机2.变量超大等错误无法通过编译等手段发现3.一旦发生栈被写坏,则函数调用栈也已被破坏,使得问题难以定位。程序设计(Programming in C)示例:错误后果:程序设计(Programming in C)建议和结论:1.编写代码时不要定义大的局部变量,如果必须要使用大的内存,则可以通过malloc从堆中申请。2.使用局部数组时,要谨防写越界。3.如果发生调用栈损坏,可以从局部变量超大和局部数组写越界这个思路开始追查,看看问题出现时,可能发生的调用栈。程序设计(Programming in C)
22、建议和结论:程序设计(Programming in C)示例:char* func1()char arr200;strcpy(arr, abcd);return arr;错误后果:1.字符串的内存赋值到栈中,这个空间在func1返回后就不再有意义2.如果func1返回后继续向arr这个地址写入内容,则会造成栈写坏,可能导致系统崩溃程序设计(Programming in C)示例:错误后果:程序设计(Programming in C)建议与结论:1.局部变量一定不要超越其作用域的范围2.编码时,在函数返回指针时要特别注意,千万不要返回栈空间地址。程序设计(Programming in C)建议与
23、结论:程序设计(Programming in C)思考:下面程序有什么问题?struct Queue global_q; /* 定义一个队列 */.void ospf_routing_calc()struct QueueNode n;clean_queue(global_q); /* 清空队列 */.EnQueue(global_q, n); /* 入队列 */.if (err) /* 出错返回 */return clean_queue(global_q);/* 清空队列 */return; /* 计算成功返回 */程序设计(Programming in C)思考:下面程序有什么问题?程序设计
24、(Programming 建议与结论:1.局部变量一定不要超出其作用域的范围2.要特别关注函数中异常分支的处理,看看异常分支中有没有进行必要的资源回收和回退处理。程序设计(Programming in C)建议与结论:程序设计(Programming in C)数组和指针的错误访问越界指针释放错误指针移位错误数组和指针的混用程序设计(Programming in C)数组和指针的错误访问越界程序设计(Programming i访问越界数组和内存的访问越界是一类最常发生的错误,这类错误一旦发生,经常会引起内存链损坏、调用栈写坏等严重后果常见问题和建议:1.字符缓冲区:在进行字符串操作时(strc
25、py、strcat等),要确保目的缓冲区的大小足够大.(包括后面的0)2.报文缓冲区:需要考虑各种报文长度,如正常报文、畸形报文、非法报文等程序设计(Programming in C)访问越界数组和内存的访问越界是一类最常发生的错误,这类错误一3.数组定义:数组大小已变化,而引用的地方没有作相应修改,引起访问越界。(一般是魔鬼数字导致修改不全,建议用宏来标识数组大小)4.数组下标:检查数组下标的合法性,防止下标过大导致数组访问越界。5.字符串的0结尾:定义字符串数组时忘记后面的0,是一种常见错误。如char str3 = abc;程序设计(Programming in C)3.数组定义:数组大
26、小已变化,而引用的地方没有作相应修改,引指针释放错误指针释放内存错误是非常大的一类错误,一代代的C程序员绞尽脑汁地同这些错误作斗争,在消灭错误的同时,他们也在不断创造新的错误!最简单的一类错误就是遗漏指针释放,导致内存泄漏。主要原因:1.异常处理分支、多个处理分支中遗漏内存释放和相关的资源回收处理。建议:a:关注各个分支的资源释放处理,尤其是新增加一个分支时。b:将资源释放集中处理,整合成一个流程。2.责任主体不清,接口设计没有明确释放主体a:设计定义接口时,要明确定义资源的申请者和释放者。程序设计(Programming in C)指针释放错误指针释放内存错误是非常大的一类错误,一代代的C程
27、引用已释放的指针也是最常见的一类错误。如:free(pIntf);printf(free interface %s, pIntf-name);建议与结论:1.即使是刚释放的内存,也不能再访问,因为里面的内容已经没有意义了。2.养成释放内存后,立即将指针设成NULL的良好习惯。程序设计(Programming in C)引用已释放的指针也是最常见的一类错误。如:程序设计(Prog指针赋值除了容易造成内存释放后再访问的问题外,还容易导致内存重复释放。pRoute-pIntf = pIntf;.free(pIntf);.if(NULL != pRoute-pIntf).theID = pRoute-
28、pIntf-theID; /* 错误访问 */free(pRoute-pIntf) /* 重复释放 */.程序设计(Programming in C)指针赋值除了容易造成内存释放后再访问的问题外,还容易导致内存建议与结论:1.对于等价指针的情况,必须要严格明确申请者和释放者。2.在释放的时候要将所有的等价指针设成NULL程序设计(Programming in C)建议与结论:程序设计(Programming in C)指针移位错误指针可以通过加减运算进行推移。需要注意的是,指针的加、减运算都是基于它所指向的对象尺寸大小进行考虑的,常见的错误就是额外的计算了数据类型的体积。示例:int arr1
29、00;int *p = arr;p = p + sizeof(int); /* 错误 */p = p + 1; /* 正确 */程序设计(Programming in C)指针移位错误指针可以通过加减运算进行推移。程序设计(Prog建议和结论:1.指针偏移时,编译器已经考虑了对象的体积,编程者不需要再画蛇添足。2.void型指针由于编译器不知道其对象体积,所以不能进行加、减偏移运算程序设计(Programming in C)建议和结论:程序设计(Programming in C)指针和数组的混用指针和数组有相同之处,但是在绝大多数情况下二者含义是不同的,不可混淆。char *b = abc;。
30、char a4 = abc;程序设计(Programming in C)指针和数组的混用指针和数组有相同之处,但是在绝大多数情况下二1.给指针赋值为数组首地址,可以通过推移指针来访问数组各元素char a4;char *p = a;通过a1和*(p+1)都可以正确访问数组2.函数使用数组作为参数时,数组地址只能以指针的方式传入,此时通过数组或指针的方式定义都是可以的。char a4;int func(char *p).ret = func(a);char *p = a;程序设计(Programming in C)1.给指针赋值为数组首地址,可以通过推移指针来访问数组各元素流程和逻辑的错误统计和
31、计数的错误任务切换程序设计(Programming in C)流程和逻辑的错误统计和计数的错误程序设计(Programmi统计和计数错误统计包括很多,如报文数目统计、错误统计、各种表项统计等。统计计数的常见错误一般有以下几种情况:1.统计计数变量没有初始化。2.多个分支时,某些分支中遗漏统计。3.统计计数变量溢出程序设计(Programming in C)统计和计数错误统计包括很多,如报文数目统计、错误统计、各种表统计计数错误引起的问题大多不太严重,但有时也可能导致严重的问题。一个真实的案例:某一子系统采用一个LONG型计数器记录系统启动到当前的毫秒数,并以此进行定时器调度,不幸的是,这个计数
32、器没有进行溢出保护,也就是说0 x7FFFFFFF/1000/3600/24=24.8天后,这个计数器将发生溢出。后果:这个设备在实验室从来没有这么长时间运行过,问题一直没有被发现;结果,一次开局,路由器在网上运行了二十多天,计数器发生溢出,定时器系统崩溃,系统重启。由于没有调用栈,重启时没有任何操作,问题很难定位;直到又过了二十多天,问题再次出现,研发人员发现间隔的时间惊人的一致,才发现了这个问题。程序设计(Programming in C)统计计数错误引起的问题大多不太严重,但有时也可能导致严重的问建议与结论:1.统计计数虽然简单,但也不可以掉以轻心,分支流程容易遗漏,需要特别关注。2.对
33、于计数器一定要考虑什么时候会溢出,以及溢出时的保护处理。程序设计(Programming in C)建议与结论:程序设计(Programming in C)任务切换1.在多任务编程时,需要考虑任务切换时对全局资源的保护处理。2.如果多个任务间有严格的时序要求,则需要保证各个任务间的同步,防止乱序而导致意想不到的结果。程序设计(Programming in C)任务切换程序设计(Programming in C)C语言程序设计常见问题程序设计(Programming in C)C语言程序设计常见问题程序设计(Programming in1.下面代码有什么问题?当我试图访问p2是得到了错误,为什么
34、?char* p1,p2;p2 = (char *)0 x80000000;程序设计(Programming in C)1.下面代码有什么问题?当我试图访问p2是得到了错误,为什么作者的原因是定义两个char型的指针,但是上述代码实际上等价于:char *p1, p2; 或者char *p1;char p2;因此作者在后面的指针赋值语句时会产生错误正确的定义法是:char *p1, *p2;程序设计(Programming in C)作者的原因是定义两个char型的指针,但是上述代码实际上等价2.C语言的关键字extern在函数的声明中起到什么作用?如:extern int func(int)
35、;如果函数的声明中带有关键字extern,除了暗示这个函数可能在其他源文件里定义外,无其他作用。下面两个函数的声明没有明显区别:extern int func(int);int func(int);程序设计(Programming in C)2.C语言的关键字extern在函数的声明中起到什么作用?如3.下面两种对于定义string_t数据类型的方法,哪一种更好?typedef char * string_t#define string_t char *程序设计(Programming in C)3.下面两种对于定义string_t数据类型的方法,哪一种更通常讲,typedef要比#defin
36、e好,尤其在对指针的处理上。示例:typedef char * string_t1#define string_t2 char *string_t1 s1, s2;string_t2 s3, s4上述变量s1、s2、s3都被定义成了char*,而s4被定义成了char,而不是预期的char*。根本原因就是#define只是进行字符串的简单替换。除此之外,宏定义有#ifdef和#ifndef等用来进行逻辑判断,这是它的长处。程序设计(Programming in C)通常讲,typedef要比#define好,尤其在对指针的处4.typedef中的嵌套定义问题。下面定义有问题吗?typedef
37、struct mystructint a;MY_STRUCTURE *pnext;MY_STRUCTURE;程序设计(Programming in C)4.typedef中的嵌套定义问题。程序设计(Program规范的做法是:typedef struct mystructint a;struct mystruct *pnext;typedef struct mystruct MY_STRUCTURE程序设计(Programming in C)规范的做法是:程序设计(Programming in C)5.下面的方法定义数组有问题吗?const int a = 5;int arraya;程序设计(
38、Programming in C)5.下面的方法定义数组有问题吗?程序设计(Programmi这个问题讨论的是常量与只读变量的区别。常量,如5、“abc”等肯定是只读的,因为程序中根本没有地方存放它们的值,别说修改它了。而只读变量,则是在内存中开辟一个地方来存储它的值,只不过这个值不允许修改。C语言的const就是用来限定一个变量不允许修改的修饰符。上述代码的a被修饰为只读变量,但它本质上还是变量,而不是常量。C语言规定,数组的定义必须是常量,而不能是只读变量。程序设计(Programming in C)这个问题讨论的是常量与只读变量的区别。常量,如5、“abc”6.下面的代码中编译器会报一个
39、错误,为什么?typedef char * charptr;char str4 = abc;const char *p1 = str;const charptr p2 = str;p1+;p2+;程序设计(Programming in C)6.下面的代码中编译器会报一个错误,为什么?程序设计(Prop2+有问题,会报错。因为const charptr p2与#define不同,不是进行简单的字符串替换,它实际上定义的是一个常指针,即指针初始化后不允许改变。类似于const long xyz;只不过charptr是我们自己定义的类型。程序设计(Programming in C)p2+有问题,会报
40、错。因为const charptr p27.为什么结构体变量不能用“=”和“!=”进行比较?C语言是一种低级语言,没有一种简单有效的办法实现结构体变量的比较。具体原因有两个:1)结构体对齐问题,导致的一些填充域,这些填充域可能是随机值,因此按字节比较;2)如果按域比较,结构体中若含有指针域,则指针域所指内容的比较则无法实现。因此结构体的比较往往需要各应用模块根据自己的需要专门写一个比较函数程序设计(Programming in C)7.为什么结构体变量不能用“=”和“!=”进行比较?C语言8.如何初始化一个联合体的任意成员?typedef union myunionint x;short y;
41、UN;UN ux = 4, 1; /很遗憾,标准C不支持初始化union的任意成员,而只能初始化它的第一个成员。程序设计(Programming in C)8.如何初始化一个联合体的任意成员?程序设计(Program9.下面代码能告诉我们b在a和c之间吗?if (a b c).程序设计(Programming in C)9.下面代码能告诉我们b在a和c之间吗?程序设计(Progr上述代码实际等价于if (a b) c).意思是,取(a b) 判断的逻辑结果,然后再和c比大小。程序设计(Programming in C)上述代码实际等价于程序设计(Programming in C10.C语言的指
42、针很重要,也很灵活,不过它到底有哪些好处?请列举程序设计(Programming in C)10.C语言的指针很重要,也很灵活,不过它到底有哪些好处?请1.方便使用动态分配的数组2.对相同类型或相似类型的多个变量进行通用访问3.变相改变函数的只传递特性,如将变量地址作为参数传入函数,这样就可以修改该变量的值4.动态扩展数据结构,如链表、hash表5.遍历数组6.节省函数调用代价,将参数尤其是大个的参数,按指针传递,以减少开销。7.程序设计(Programming in C)1.方便使用动态分配的数组程序设计(Programming 11.下面的代码打印出来的结果是多少?int i, array
43、5, *ip;ip = array;for(i=0; i5; i+)arrayi= i;printf(rn%d, *(ip+3*sizeof(int);程序设计(Programming in C)11.下面的代码打印出来的结果是多少?程序设计(Progra呵呵。,具体打印什么我也不清楚。问题就出在ip+3*sizeof(int)上了。指针的相加实际上已经考虑了其类型的长度。示例:如果ip所指向的地址为0 x80000000,则ip+1所指的地址0 x80000004。因此本题中不需要再乘以sizeof(int)了。程序设计(Programming in C)呵呵。,具体打印什么我也不清楚。问题
44、就出在ip+3*siz12.*p+到底是给谁加1?是给指针p还是p所指的内容加1?程序设计(Programming in C)12.*p+到底是给谁加1?是给指针p还是p所指的内容加1单目操作符和操作的结合顺序是:从右到左一个表达式中单目运算符的执行顺序是:从左到右因此本题中实际上是对指针p加1,而不是p的内容加1,如果要对p的内容加1,则用下面的表达式: (*p)+程序设计(Programming in C)单目操作符和操作的结合顺序是:从右到左程序设计(Progra13.我有一个char *类型的指针,恰好指向一个int型,我想让指针跳过这个int,跳到下一个char,试问下面代码能否实现
45、? (int *)p+;程序设计(Programming in C)13.我有一个char *类型的指针,恰好指向一个int型,这种实现标准C不支持,但是主流编译器默认都会支持,如VC和gcc。虽然这么用一般不会有什么问题,不过还是建议大家不要这么用,上面问题可以直接用下面代码替代: p += sizeof(int);程序设计(Programming in C)这种实现标准C不支持,但是主流编译器默认都会支持,如VC和g14.为什么我不能对void*型的指针进行算术运算?如:void *p = array;p += 5;程序设计(Programming in C)14.为什么我不能对void*
46、型的指针进行算术运算?程序设计前面说过,对指针进行算术运算(指针加、减运算)时实际上已经考虑了该指针所指类型的大小了。同理,如果无法知道该指针类型的大小,则无法进行指针的算术运算。因为编译器根本不知道你这个指针所指的变量占几个字节,也就无法进行指针偏移。程序设计(Programming in C)前面说过,对指针进行算术运算(指针加、减运算)时实际上已经考15.有些头文件中将NULL定义为0,为什么?NULL用于表示指针为空指针;一般用于指针变量的初始化。NULL的具体的机器表示也随机器而定,不过一般都是0.程序设计(Programming in C)15.有些头文件中将NULL定义为0,为什
47、么?程序设计(Pr16.在源文件里定义了一个数组:int array5;我能否在另一个文件里声明一个指针,从而引用这个数组?extern int * array;程序设计(Programming in C)16.在源文件里定义了一个数组:程序设计(Programmi不可以,程序运行时会告诉你非法访问!指向类型T的指针并不等价于类型T的数组。正确的用法是: extern int array;程序设计(Programming in C)不可以,程序运行时会告诉你非法访问!指向类型T的指针并不等价17.有人说数组名无法赋值,但是下面程序确实可以工作,难道我记错了?int fun(char sz100
48、).if (0 = sz0)sz = NONE;.程序设计(Programming in C)17.有人说数组名无法赋值,但是下面程序确实可以工作,难道我在C语言中,数组无法真正传递给函数,因而在编译器内部这个函数就被解释为:int fun(char *sz)因此,C语言函数参数若是数组,则实际传递的是一个指针,也就是该数组的首地址。因此上述函数当然没有问题。程序设计(Programming in C)在C语言中,数组无法真正传递给函数,因而在编译器内部这个函数18.假定有一个整型数组a,则a和&a有什么区别?int a2;a 和 &a有什么区别?程序设计(Programming in C)1
49、8.假定有一个整型数组a,则a和&a有什么区别?程序设计(a是指向数组第一个元素的指针,而&a则是指向整个数组的指针。int x, a2;int *p = NULL;p = a;x = *p;p = &a;程序设计(Programming in C)a是指向数组第一个元素的指针,而&a则是指向整个数组的指针。19.什么时候需要定义指向数组的指针而不是数组元素的指针?如何定义?int array23 = 1,2,3, 4,5,6;程序设计(Programming in C)19.什么时候需要定义指向数组的指针而不是数组元素的指针?如如果需要对行进行遍历的话,就需要一个指向行的指针,定义如下:in
50、t array23 = 1,2,3,4,5,6;int (*p)3 = 0;/ int *p2 = 0;p = &array1;/ p2 = array1;程序设计(Programming in C)如果需要对行进行遍历的话,就需要一个指向行的指针,定义如下:20.有人写了一个将整数转换成字符串的函数,char* itoa(int n)char buf20;sprintf(buf, %d, n);return buf;如果按下面调用,有什么问题?char *p3;p3 = itoa(5);int c;c = 1;printf(%s,p3);程序设计(Programming in C)20.有人
51、写了一个将整数转换成字符串的函数,程序设计(Prochar* itoa(int n)函数返回了栈空间的地址,要知道,栈空间的内容在该函数返回后就不再受到保护,也就是说此时函数的栈空间的已经被系统回收。如果函数返回了栈空间的内存,则很容易出现错误的结果,因为该空间可能已经分配给了其他函数或任务。程序设计(Programming in C)char* itoa(int n)函数返回了栈空间的地址,要21.为什么有些代码中malloc申请内存时的返回值总是强制类型转换一下,不转行不行?char *p = (char *)malloc(100);程序设计(Programming in C)21.为什么
52、有些代码中malloc申请内存时的返回值总是强制在C语言引入void *指针类型之前,这种强制转换主要是为了消除编译告警。在C语言引入了void *指针类型之后,这种转换已经没有必要,但是这是一种良好的编程习惯,建议大家继续保持这种强制转换,增加代码可读性。程序设计(Programming in C)在C语言引入void *指针类型之前,这种强制转换主要是为了22.下面的程序有什么问题?#define PI 3.14int fit_size(double rount)if (rount/PI = 10) | (rount/PI = 20)return 1;return 0程序设计(Progra
53、mming in C)22.下面的程序有什么问题?程序设计(Programming此题同样是浮点数比较问题。浮点数不能直接与某一个整数进行比较,而是应该和一个范围比较。上面程序应该改成:#define PI 3.14int fit_size(double rount)if (fabs(rount/PI-10) 0.000001) | (fabs(rount/PI-20) 0.000001)return 1;return 0程序设计(Programming in C)此题同样是浮点数比较问题。浮点数不能直接与某一个整数进行比较23.下面这样的写法正确吗?int fun(int a)int k =
54、 0;switch(a)default:k = 1;break;case 1:k = 2;break;case 2:k = 3;break;return 0程序设计(Programming in C)23.下面这样的写法正确吗?程序设计(Programming这种写法正确,因为switchcase里的default语句可以放在任意的位置,当然可以放在开头。但是一般不建议这样做,建议把default语句放在switch的最后,这样代码更清晰。程序设计(Programming in C)这种写法正确,因为switchcase里的default24.下面程序有什么问题?for (i=start; iend, i+);printf(rn%d, i);程序设计(Programming in C)24.下面程序有什么问题?程序设计(Programming for循环后面的“;”实际上指示了fo
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年证件打印一体机项目合作计划书
- 2025年中石化:石油脑项目合作计划书
- 吧台设备转让合同范例
- 影片拍摄投标合同范本
- 农业技能培训合同范本
- 司机水泥合同范例
- 合同范例新版正版
- 单位绿化施工合同范例
- LED户外显示屏广告位租赁合同范本
- 个人购房合同范本简易
- 脾破裂保守治疗的护理
- 煤矿安全质量标准化培训课件
- 2024解析:第十七章欧姆定律-基础练(解析版)
- 【MOOC】电工电子学-浙江大学 中国大学慕课MOOC答案
- 新教材 人教版高中化学选择性必修2全册各章节学案(知识点考点精讲及配套习题)
- (一模)长春市2025届高三质量监测(一)生物试卷(含答案)
- DB35T 1036-2023 10kV及以下电力用户业扩工程技术规范
- 《现代家政导论》电子教案 1.1模块一项目一家政与家政学认知
- 《人工智能通识教程》(第2版)教学大纲
- 科研伦理与学术规范-期末考试答案
- 中国移动自智网络白皮书(2024) 强化自智网络价值引领加速迈进L4级新阶段
评论
0/150
提交评论