C语言陷阱和缺陷
- 格式:pdf
- 大小:151.71 KB
- 文档页数:16
1.1&,| :按位运算&&,|| :逻辑运算= 不同于==来看这样一个例子,while (c=' ' || c== '\t' || c=='\n')c = getc(f);由于程序员在比较字符… ‟和变量c时,误将== 写成= ,那么while后的表达式恒为1,因为' '不等于零,它的ASCII码值为32。
为了避免这种情况,我们可以这样写while(''==c || '\t'== c || '\n'==c) ,这样即使不小心将== 写成= ,编译器也会报错。
★★在C中,单引号括起来表示一个整数,双引号括起来表示指针。
例如,字符和字符串。
1.3 词法分析中的“贪心法”C编译器读入一个符号的规则:每一个符号应该包含尽可能多的字符,方向为自左向右。
.大嘴法编译器在解析字符的时候会尽可能的解析成可能组成一个符号的最常的字符串。
例如,a---b <==> (a--) -b而y = x/*p; 中/* 被编译器理解为一段注释的开始,如果本意是用x除以p所指向的值,应该重写如下y = x/ *p; 或更加清楚一点,写作y = x/(*p);★★在用双引号括起来的字符串中,注释符/* 属于字符串的一部分,而在注释中出现的双引号""属于注释的一部分。
整数常量的第一个字符是数字0,那么该常量将被视为八进制数。
单引号所括起的字符代表一个整数,双引号括起的则代表一个指针(字符数组,自动加0)1.理解函数声明通过认识这个让人“不寒而栗”的式子:(*(void(*)())0)()1)把0强制转换成了类型void(*)(),这是一个指向函数的指针,所指向的这个函数返回值为voidps:a)float *g():这是一个函数,返回指针float*,因为()的优先级比*高。
编程初学者应该避免的常见错误和陷阱编程对于初学者来说是一项具有挑战性的技能。
不管是学习编程语言,还是解决问题的过程中,初学者都会遇到各种错误和困难。
在这篇文章中,将会介绍一些初学者常见的错误和陷阱,并给出一些建议来避免它们。
一、语法错误语法错误是初学者常见的错误之一。
编程语言有着严格的规则,一旦出现语法错误,程序将无法运行。
初学者容易犯的错误包括拼写错误、忘记使用分号、缩进错误等。
为了避免这些错误,初学者应该仔细阅读文档,学习和熟悉编程语言的语法规则,并养成良好的编码习惯。
二、逻辑错误逻辑错误指的是程序在运行时没有出现语法错误,但结果与预期不符合的问题。
初学者常见的逻辑错误包括变量使用错误、循环条件错误、算法错误等。
为了避免逻辑错误,初学者应该学会使用调试工具,通过逐步执行代码、观察变量的取值来找出问题所在,并进行适当的修改。
三、不理解错误信息当程序出现错误时,编程语言通常会提供一些错误信息。
初学者经常犯的错误是不理解错误信息的含义,从而难以找到问题所在。
为了充分利用错误信息,初学者应该学会阅读和理解错误信息的含义,根据错误信息提供的提示来解决问题。
四、过度依赖复制粘贴学习编程时,很多初学者倾向于直接复制粘贴他人的代码,而不去理解其中的原理和思路。
这种方法虽然能够解决问题,但对于初学者的成长并不利。
为了克服这个陷阱,初学者应该尝试自己编写代码,理解每一行代码的作用,并进行适当的实验和调试。
五、忽略细节编程是一项细致入微的工作,很多时候一个小的细节问题会导致程序无法正常运行。
初学者常常忽略一些细节,比如大小写敏感、变量命名规范、注释等。
为了避免这个错误,初学者应该注重细节,养成良好的代码风格和命名规范,并进行代码审查和测试。
六、不善于沟通和寻求帮助编程是一个社区活动,初学者应该善于与他人沟通、交流和分享经验。
当初学者遇到问题时,应该主动向他人请教、寻求帮助。
在各种在线社区和论坛上,有很多经验丰富的程序员愿意分享他们的知识和经验,并帮助初学者解决问题。
C语⾔之陷阱与缺陷详解⽬录⼀、前⾔⼆、字符指针三、边界计算与不对称边界1.经典错误①2.经典错误②3、⼩结四、求值顺序五、运算符&& ||和!总结⼀、前⾔⼆、字符指针结论⼀:复制指针并不会复制指针所指向的内容。
两个指针所指向位置相同,实际为同⼀个指针。
结论⽽:开辟两个数组,即使两个数组内容相同,地址也绝不相同。
三、边界计算与不对称边界1.经典错误①int main(){int i = 0; int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };for (i = 0; i < 13; i++){arr[i] = 0;printf("haha");}return 0;}计算的结果是程序陷⼊死循环分析:1.栈区默认先使⽤⾼地址,再使⽤低地址2.数组内元素随下标增长,地址由低到⾼变化调试后即可发现,i与arr[9]的地址相差3字节,所以i即为实际不存在的arr[12].[补充知识:ANSI c标准允许这种⽤法——数组中溢界元素的地址位于数组所占内存之后,这个地址可以进⾏赋值和⽐较,但是不能解引⽤(若是数组之前存在溢界则语法不允许)] 2.经典错误②⼗⽶长的围栏每⼀⽶就需要⼀根栏杆⽀撑,则共需要⼏根栏杆? 113、⼩结栏杆问题你若不假思索可能会回答为10。
栏杆问题的根源正是加减⼀带来的困惑对此我们坚持以下原则原则⼀:考虑最简单的特例(如考虑20到10间有⼏个数,20-10还要+1吗。
不妨考虑10到10有⼏个数)原则⼆:仔细计算边界⽽在实际编程中,⼀个编程技巧则可以"⼀⾔以蔽之",即不对称边界。
x>=0 && x<16 要优于 x>=0 && x<=15不对称边界上界-下界就是之间所包含的数。
四、求值顺序总结:c语⾔中只有四个运算符(&& ;|| ;?: ;,)明确规定了求值顺序&&和||先对左边求值,只在需要时对右边求值:if(y!=0 && x/y>a)如此避免除0错误。
c陷阱与缺陷学习笔记.txt求而不得,舍而不能,得而不惜,这是人最大的悲哀。
付出真心才能得到真心,却也可能伤得彻底。
保持距离也就能保护自己,却也注定永远寂寞。
Chapter 1 词法陷阱程序中的单个字符孤立起来看并没有什么意义,只有结合上下文才有意义,如p->s = "->";两处的-意义是不同的。
程序的基本单元是token ,相当于自然语言中的单词。
一个token的意义是不会变的。
而组成token 的字符序列则随上下文的不同而改变。
token之间的空格将被忽略。
1.1 = 不同于 ==1.2 &和|不同于&&和||1.3 词法分析中的贪心法token分为单字符token和多字符token,如/ 和 == ,当有岐义时,c语言的规则是:每一个token应包括尽可能多的字符。
另外token的中间不能有空白(空格,制表符,换行符)y = x /*p 应写为y = x / *p 或者y = x / (*p);老编译器允许用=+来代表现在+=的含义。
所以它们会将a=-1理解为a=- 1 即a = (a-1); 它们还会将复合赋值语句看成两个token,于是可以处理 a>> =1, 而现代的编译器会报错。
1.4 整型常量常量前加0代表是8进制。
1.5 字符与字符串用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针a+++++b的含义是什么?C不允许嵌套注释。
Chapter 2 语法陷阱2.1 构造函数声明构造函数声明的规则:按照使用的方式来声明。
任何C声明都由两部分组成:类型及类似表达式的声明符(declarator)。
float *g(), (*h)();g是一个函数,该函数的返回值类型为指向浮点数的指针。
h是一个函数指针, h所指向函数的返回值为浮点类型。
()的优先级高于*。
因为float (*g)();表示g是一个指向返回值为浮点类型的函数的指针。
C语言技术常见陷阱及避免方法C语言作为一门广泛应用于嵌入式系统和操作系统开发的高级编程语言,具有高效、灵活和可移植等优点。
然而,由于其语法灵活性和底层操作的特性,C语言也存在一些常见的陷阱,如果不注意避免,可能会导致程序的错误和不稳定性。
本文将介绍一些常见的C语言技术陷阱,并提供相应的避免方法。
1. 内存管理陷阱在C语言中,内存管理是程序员需要特别关注的一个重要方面。
常见的内存管理陷阱包括内存泄漏和野指针。
内存泄漏指的是程序在分配内存后没有正确释放,导致内存资源浪费。
而野指针则是指指向已经释放或未初始化的内存地址,会导致程序崩溃或产生未定义行为。
避免内存泄漏的方法是在每次分配内存后,及时使用free()函数释放已分配的内存。
另外,可以使用内存分析工具来检测内存泄漏问题。
避免野指针问题的方法是在指针使用前进行初始化,并在释放内存后将指针置为NULL,以避免误用。
2. 数组越界访问陷阱在C语言中,数组越界访问是一种常见的错误操作。
当程序试图访问数组的越界元素时,可能会导致数据损坏、程序崩溃或者安全漏洞。
为避免数组越界访问问题,可以使用循环结构和条件判断来控制数组的访问范围。
另外,可以使用辅助变量来记录数组的长度,以避免超出数组边界的访问。
3. 整数溢出陷阱在C语言中,整数溢出是一种常见的错误情况。
当一个整数变量的值超出了其数据类型所能表示的范围时,会导致溢出现象,结果可能会产生错误的计算结果。
为避免整数溢出问题,可以使用合适的数据类型来存储变量,并在计算过程中进行溢出检查。
此外,可以使用条件判断来避免溢出情况的发生。
4. 字符串处理陷阱在C语言中,字符串处理是一项常见的任务。
然而,由于C语言中字符串以null字符结尾,容易引发一些错误情况,如缓冲区溢出、内存泄漏和安全漏洞等。
为避免字符串处理问题,可以使用安全的字符串处理函数,如strncpy()、strncat()等,来限制字符串的长度。
此外,可以使用合适的缓冲区大小来存储字符串,并进行输入验证,以避免缓冲区溢出问题。
C陷阱与缺陷P6:当程序员本意是作比较运算时,却无意写成了赋值运算,例如:本意是想检查x是否等于y,if(x=y) break;而实际上市将y赋值给了x,然后检查该值是否为零,再例如:本例的循环语句是想跳过文件的空格符、制表符和换行符:while(c=’‘||c==’\t’||’\n’)由于程序员在比较字符’’和变量c时,误将比较运算符==写成赋值运算符=,因为赋值运算符的优先级低于逻辑运算符||,因此是这样的:’‘||==’\t’||’\n’因为’’不等于零(’’的ASCII码值是32),那无论变量c此前是何值,上述表达式求值都是1,循环一直到整个文件结束。
P9:y=x/*p;本意是用x除以p所指的值,再把商给了y,但是这里有/*是注释的开始,所以不会顾及到p,应该是y=x/(*p);P16:float ff();这个声明的含义是:表达式ff()求值是一个浮点数,也就是说ff是一个返回值为浮点类型的函数,类似地,flaot *fp;这个声明含义为*fp是一个浮点数,fp是一个指向浮点数的指针。
Float *g(),(*h)();表示*g()与(*h)()是浮点数表达式,因为()优先级高于*,即*g()也就是*(g()):即g是一个函数,该函数的返回值是指向浮点数的指针,同理,h 是一个函数指针,h所指函数的返回值为浮点类型。
P20:if(flags&FLAG!=0) 这有一个错误的语句,因为!=运算符的优先级要高于&运算符,所以上式被解释为:if(flags&(FLAG!=0)),因此除了FLAG恰好为1,FLAG为其他式子都是错误的。
P21:如果p是一个函数指针,要调用p所指向的函数,必须写成:(*p)(),如果写成*p(),编译器会解释成*(p())P23:while(c=getc(in)!=EOF) 这个的本意是想c首先被赋予函数gstc(in)的返回值,然后与EOF比较是否达到文件结尾以便决定是否终止循环,但是由于赋值运算要低于任何一个比较运算,所以实际上c的值是gstc(in)的返回值予与EOF比较的结果,此处getc(in)的返回值只是一个临时变量,在与EOF比较后被丢弃。
第1章词法“陷阱”1.1 =不同于====为比较运算符, =为赋值运算符例:while( c = ' ' || c == '\t' || c == '\n' )c = getc( f );本意是c和 ' ' 比较,但错用成赋值符。
这样的后果是将 ' ' || c == '\t' || c == '\n' 这个表达式的值给了c,而使c = 1。
同样: if ( ( filedesc == open( argv[i], 0 ) ) < 0 ) error();open的返回值和filedesc比较的结果只能是0或1,所以,error没有机会调用。
但是,此时filedesc的值于open返回值无关,编译器这里不会报错。
容易被忽视,达不到检查效果。
1.2 & 和 | 不同于 && 和 ||&和|均为按位运算符,而&& 和 || 均为逻辑运算符,不能混淆。
1.3 语法分析中的“贪心法”当C编译器读入一个字符后又跟了一个字符,那么编译器就必须做出判断:是将其作为两个分别的符号对待,还是合起来作为一个符号对待。
C语言对这个问题的解决方案可以归纳为一个很简单的规则:每一个符号应该包含尽可能多的字符。
a---b 与a -- - b 的含义相同,而与 a - -- b 的含义不同。
1.4 整型常量如果一个整形常量的第一个字符是数字0,那么该常量将被视作八进制数。
因此,10和010是完全不同的含义。
此外书中还介绍了一些ANSI C不允许的做法,比如将8和9也作为八进制数字处理。
1.5 字符和字符串C语言中的单引号和双引号含义迥异,在某些情况下如果把两者弄混,编译器并不会检测报错,从而在运行是产生难以预料的结果。
用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。
C语言中的缺陷与陷阱(博雅课期末作业:计算机网络安全与防护)刚进大学,学的第一门计算机语言就是C语言,这是我第一次接触到计算机语言,对它一无所知。
有些同学之前已经学过了一些类似的语言,例如VB、PASCAL等等。
因为从来没有学过计算机语言,所以在学习C语言的过程中,遇到了很多困难,许多问题都不理解。
因此虽然我已经学完了这本书,但是对书中的内容都不知甚解。
学过C语言的人都知道,指针是C语言中的一个重要概念,也是它的一个重要特色。
正确而灵活地运用指针,可以有效地表示复杂的数据结构;能动态分配内存;方便地使用字符串;有效而方便地使用数组;在调用函数时能获得一个以上的结果;能直接处理内存单元地址等。
掌握指针的应用,可以使程序简洁、紧凑、高效。
但是任何事情都有两面性,C语言中的指针也是如此。
举几个例子来说:1.指针与数组在新的一年里我们需要将日历(calendar)数组清空:int (*monthp)[31];for(monthp=calendar;monthp<&calendar[12];monthp++){int *dayp;for(dayp=*monthp;dayp<&monthp[31];dayp++)*dayp=0;}在这个程序中用指针monthp以步进的方式遍历数组calendar。
使用指针来操纵数组,常常需要跟C语言中最为“晦暗不明”的部分打交道,并常常遭遇到潜伏着的编译器bug。
因此使用指针时,我们必须得小心翼翼,否则,程序很容易出错。
2.非数组的指针有一段程序如下:char *r,*malloc();r=mollac(strlen(s)+strlen(t));strcpy(r,s);strcat(r,t);这个例子有三个错误:一、malloc函数有可能无法提供请求的内存,这种情况下malloc 函数会通过返回一个空指针来作为“内存分配失败”事件的信号。
二、给r分配的内存在使用完成之后应该立即释放。
避免C语言技术中的常见陷阱与注意事项C语言是一种广泛应用于嵌入式系统和系统级编程的高级编程语言。
然而,由于其灵活性和底层控制能力,C语言也存在一些常见的陷阱和注意事项,容易导致代码错误和安全漏洞。
在本文中,我们将探讨一些避免这些陷阱和注意事项的方法。
1. 内存管理在C语言中,内存管理是一个重要的问题。
使用malloc()和free()函数来动态分配和释放内存时,一定要确保在使用完内存后及时释放它,以避免内存泄漏。
此外,还应注意避免对已释放的内存进行访问,这可能导致程序崩溃或安全漏洞。
2. 数组越界在C语言中,数组越界是一个常见的错误。
在访问数组元素时,一定要确保索引值在数组范围内。
否则,可能会导致程序崩溃或产生未定义的行为。
可以使用条件语句和循环来检查数组索引的有效性,并在越界时采取适当的措施。
3. 字符串操作C语言中的字符串操作需要特别小心。
使用strcpy()函数时,要确保目标字符串有足够的空间来存储源字符串的内容,以避免缓冲区溢出。
推荐使用strncpy()函数,并在复制后手动添加字符串结束符'\0'来确保字符串的正确性。
4. 指针使用指针是C语言中强大但也容易出错的特性之一。
在使用指针时,要确保指针变量已被正确初始化,以避免悬空指针的问题。
此外,要注意避免对已释放的指针进行访问,以及避免指针运算时超出合法范围。
5. 文件操作在C语言中,文件操作是常见的任务。
在使用fopen()函数打开文件时,要确保文件存在并且权限正确。
在读写文件时,要注意检查文件指针是否为NULL,以避免对未成功打开的文件进行操作。
在完成文件操作后,要记得关闭文件,以释放系统资源。
6. 异常处理C语言中没有内置的异常处理机制,因此在编写代码时要格外小心。
要注意检查函数的返回值,并根据返回值采取适当的措施。
此外,可以使用try-catch块来处理可能出现的异常情况,并在发生错误时进行适当的处理。
7. 代码复用代码复用是良好的编程实践之一。