C陷阱与缺陷_笔记
c陷阱与缺陷学习笔记
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和指针》《C专家编程》《C陷阱与缺陷》《C语言编程要点》《编程精粹--Microsoft编写优质无错C程序秘诀》总结说明:总结的知识点主要源于上面的4本书,《编程精粹--Microsoft编写优质无错C程序秘诀》这本书未做总结,该书有清晰版的pdf格式的电子版。
--wuliming--2007-04-25wuliming_sc@指针和数组相关概念*************************************************字符与字符串的区别指针与数组1指针与数组2指针和数组的相同与不同用malloc为字符串分配存储空间时的注意事项作为常数的数组声明(c缺陷与陷阱3.3节.在其它部分有包含该节的知识点,了解or略过) 字符串常量用字符串常量初始化指针和数组二维数组下标操作的相关概念指向一维、二维数组的指针array_name和&array_name的异同数组作为函数的参数时,不能通过sizeof运算符得到该数组的大小用strlen()求字符串的长度…char **‟ 和…const char **‟的兼容性问题空指针相关的问题NULL和NUL的区别未初始化的指针和NULL指针的区别理解函数的声明函数参数的传值调用函数指针作为函数参数的多维数组强制类型转换相关概念可变参数相关问题malloc()、calloc()、realloc()在程序退出main()函数之后,还有可能执行一部分代码吗?总线错误和段错误相关概念数字和字符串之间转换相关的函数*************************************************怎样判断一个字符是数字、字母或其它类别的符号?怎样将数字转换为字符串?怎样将字符串转换为数字?字符串以及内存操作相关函数************************************************* 字符串拷贝和内存拷贝函数:strcpystrncpymemcpymemmovememccpybcopy字符串和内存数据比较函数:strcmpstrcasecmpstrncasecmpmemcmpstrcollbcmp连接字符串的函数:strcatstrncat查找字符/字符串的函数:strstrstrchrstrrchrmemchr其它相关的函数:indexrindexstrlenstrdupmemsetbzerostrspnstrcspnstrpbrkstrtok数据结构及算法相关函数qsort()bsearch()lsearch(线性搜索)lfind(线性搜索)srand(设置随机数种子)OTHER************************************************* 什么是标准预定义宏?断言assert(表达式) 相关概念连接运算符“##”和字符串化运算符"#"有什么作用?注释掉一段代码的方法Typedef相关概念= 不同于==词法分析中的“贪心法”运算符的优先级问题变量的存储类型及初始化相关概念左值和右值相关的概念变量的值和类型相关的概念怎样删去字符串尾部的空格?怎样删去字符串头部的空格?怎样打印字符串的一部分?结构的自引用结构的存储分配边界计算与不对称边界整数溢出返回整数的getchar函数更新顺序文件随机数的相关概念用递归和迭代两种办法解fibonacci字符与字符串的区别(c缺陷与陷阱1.5节)#include <stdio.h>int main(){char ch = 'abcdefghijklmnopqrstuvwxyz';char str[] = "abcdefghijklmnopqrstuvwxyz";printf("-----%c-----\n%s\n",ch, str );return 0;}编译该程序可以通过,但是会产生警告;输出结过为:-----z-----指针与数组1(c缺陷与陷阱3.1节)c语言中的数组值得注意的地方有以下两点:1、c语言中只有一维数组,而且数组的大小必须在编译期间就作为一个常数确定下来(C99标准允许变长数组,GCC编译器中实现了变长数组)。
c语言 陷阱的书 -回复
c语言陷阱的书-回复
在C语言编程学习过程中,确实存在一些常见陷阱和难点,专门针对这些内容的书籍并不多,但有一些经典书籍在详细讲解C语言的同时,也注重对初学者易犯错误和陷阱的剖析。
以下是一些推荐书籍:
1. 《C陷阱与缺陷》(英文原名:C Traps and Pitfalls)
作者:Andrew Koenig
这本书是C语言编程的经典之作,作者以自己丰富的编程经验揭示了C语言中许多不易察觉的陷阱和缺陷,并给出了相应的解决策略。
2. 《C语言深度解密》(Deep C Secrets)
作者:Peter van der Linden
虽然这本书并未直接以“陷阱”为主题,但在介绍C语言特性时,作者深入浅出地分析了许多C语言编程中的难点和易错点。
3. 《C Primer Plus》(第6版及以上)
作者:Stephen Prata
这是一本经典的C语言入门教材,书中不仅全面介绍了C语言的基础知识,还通过实例和练习帮助读者识别并避免常见的编程陷阱。
4. 《Expert C Programming: Deep C Secrets》
这本书虽然更偏重于高级C语言编程技巧,但也详细讨论了一些C语
言编程中可能遇到的陷阱和问题。
以上书籍都适合不同程度的C语言学习者阅读,通过研读可以提升对C语言的理解,降低在实际编程中掉入陷阱的概率。
C语言嵌套注释
C语言嵌套注释《C陷阱与缺陷》中有一个有意思的问题:“某些C编译器允许嵌套注释。
请写一个测试程序,要求:无论是对允许嵌套注释的编译器,还是对不允许嵌套注释的编译器,该程序都能正常通过编译(无错误消息出现),但是这两种情况下程序执行的结果却不相同。
(提示:在用引号括起来的字符串中,注释符I*属于字符串的一部分,而在注释中出现的双引号””又属于注释的一部分。
)”我觉得这对C的词法分析挺有意思的。
要是聪明的您,有什么办法呢?以下是作者给出的答案,确实很精妙,尤其第二个。
——为了判断编译器是否允许嵌套注释,必须找到这样一组符号序列,无论是对于允许嵌套注释的编译器,还是不允许嵌套注释的编译器,它都是合法的;但是,对于两类不同的编译器,它却意味着不同的事物。
这样一组符号序列不可避免地要涉及嵌套注释,让我们从这里开始讨论:/*/**/对于一个允许嵌套注释的C编译器,无论上面的符号序列后面跟什么,都属于注释的一部分;而对于不允许嵌套注释的C编译器,后面跟的就是实实在在的代码内容。
也许有人因此想到可以在后面再跟一个用一对引号引起的注释结束符:/*/**/ "*/"如果允许嵌套注释,上面的符号序列就等效于一个引号;如果不允许,那么就等效于一个字符串"*I"。
因此,我们可以接着在后面跟一个注释开始符以及一个引号: /*/**/ "*/"/*"如果允许嵌套注释,上面就等效于用一对引号引起的注释开始符"/*";如果不允许,那么就等效于一个用引号括起的注释结束符,后跟一段未结束的注释。
我们可以简单地让最后的注释结束:/*/**/ "*/"/*" /**/这样,如果允许嵌套注释,上面的表达式就等效于"/*",:如果不允许,那么就等效于,"*/"。
在我用基本上类似于上面的形式解决这个问题之后,Doug McIlroy发现了下面这个让人拍案叫绝的解法:/*/*/0*/**/1这个解法主要利用了编译器作词法分析时的“大嘴法”规则。
黑马程序员C语言教程:初学者或是开发人员常遇到的c语言陷阱
初学者常遇到的c语言陷阱在初学者学习编程的过程中的会遇到一些经常会犯的错误,也许在我们成长起来之后会觉得这些错误实在太低级,但是在牛掰的大虾也是从菜鸟过来的,针对于初学者下边总结了一些我们会经常遇到的陷阱,希望对菜鸟们有帮助:正题:陷阱1:忽略大小写的区别#include<stdio.h>void main(){int a=10;a+=a;printf("%d\n",A);}这个很简单,是基础,c语言变量区分大小写。
代码中的a与A不是同个变量,编译出现A 没定义的错误。
陷阱2:“{}”与“()”使用不当造成错误#include <stdio.h>void main(){int i,j;int a[2][3]={(1,2,3),(4,5,6)};printf("array a:\n");for(i=0;i<=1;i++){for(j=0;j<=2;j++){printf("%d",a[i][j]);}printf("\n");}}}程序结果不能正常输出数组每个元素,编译{(1,2,3),(4,5,6)};时,先进行括号内的逗号运算(取逗号最后的数值),编译生成{3,6};其它元素为0。
正确的写法:{{1,2,3},{4,5,6}};陷阱3:在if,while,#include,#define,for后直接加分号,如for(int a=1;a<10;a++);如果是while,程序一般执行死循环,int a=1;while(a);如果是if,判断语句无效果,比如。
if(a>0);a=-1;无论a是否大于0,结果都是a=-1;如果是#include,程序编译的时候提示错误,无法引用库文件;如果是#define,比如#define a 200;程序在预编译的时候,200;包括分号一同被替换进程序,程序不可能正常编译如果是for循环,跟if一样,事与愿违,循环做无用功,本想循环的printf语句只执行一次。
《C陷阱与缺陷》笔记
第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陷阱与缺陷》,忍不住要重新翻一下,记录一下与自己的惯性思维不符合的地方。
记录的是知识的增量,是这几天的流量,而不是存量。
这本书是在ASCI C/C89订制之前写的,有些地方有疏漏。
第一章词法陷阱1.3 C语言中解析符号时使用贪心策略,如x+++++y将被解析为x++ ++ +y,并编译出错。
1.5 单引号引起的一个字符代表一个对应的整数,对于采用ASCII字符集的编译器而言,'a'与0141、97含义一致。
练习1.1 嵌套注释(如/*/**/*/)只在某些C编译器中允许,如gcc4.8.2编译时是不支持的。
第二章语法陷阱∙ 2.6 else始终与同一个括号内最近的未匹配的if结合第三章语义陷阱∙ 3.1 int a[12][31]表示的是一个长度12的数组,每个元素是一个长度31的数组。
∙ 3.1 在需要指针的地方如果使用数组名来替换,那么数组名就被视为其下标为0的元素的指针,p = &a的写法是非法的(gcc4.8.2只是警告)。
∙ 3.2 如何连接两个给出的字符串s、t?细节很重要,书中给出的答案如下:char *r,*malloc()//原文称不能直接声明一个s、t长度之和的数组,但c99可以声明变长数组,已经可以了//记得要把长度加1r = malloc(strlen(s) + strlen(t) +1);//必须判断内存是否分配成功if(!r){complain();exit(1);}strcpy(r,s);strcat(r,t);......//完成之后一定要释放rfree(r);∙ 3.6 如何正确计算数组的边界?原则一,考虑最简单情况下的特例;原则二,仔细计算边界。
∙ 3.6 以下一段代码为何引起死循环?这是因为在内存地址递减时,a[10]就是i。
int i,a[10];for(i = 1; i<=10; i++)a[i] = 0;∙∙ 3.6 边界的编程技巧:用第一个入界点和第一个出界点表示数值范围,即[low,high)。
(经典)C语言陷阱和缺陷
C语言陷阱和缺陷[1]原著:Andrew Koenig - AT&T Bell Laboratories Murray Hill, New Jersey 07094原文:收藏翻译:lover_P[译序]那些自认为已经“学完”C语言的人,请你们仔细读阅读这篇文章吧。
路还长,很多东西要学。
我也是……[概述]C语言像一把雕刻刀,锋利,并且在技师手中非常有用。
和任何锋利的工具一样,C会伤到那些不能掌握它的人。
本文介绍C语言伤害粗心的人的方法,以及如何避免伤害。
[内容]·0 简介· 1 词法缺陷o 1.1 =不是==o 1.2 &和|不是&&和||o 1.3 多字符记号o 1.4 例外o 1.5 字符串和字符· 2 句法缺陷o 2.1 理解声明o 2.2 运算符并不总是具有你所想象的优先级o 2.3 看看这些分号!o 2.4 switch语句o 2.5 函数调用o 2.6 悬挂else问题· 3 链接o 3.1 你必须自己检查外部类型· 4 语义缺陷o 4.1 表达式求值顺序o 4.2 &&、||和!运算符o 4.3 下标从零开始o 4.4 C并不总是转换实参o 4.5 指针不是数组o 4.6 避免提喻法o 4.7 空指针不是空字符串o 4.8 整数溢出o 4.9 移位运算符· 5 库函数o 5.1 getc()返回整数o 5.2 缓冲输出和内存分配· 6 预处理器o 6.1 宏不是函数o 6.2 宏不是类型定义·7 可移植性缺陷o7.1 一个名字中都有什么?o7.2 一个整数有多大?o7.3 字符是带符号的还是无符号的?o7.4 右移位是带符号的还是无符号的?o7.5 除法如何舍入?o7.6 一个随机数有多大?o7.7 大小写转换o7.8 先释放,再重新分配o7.9 可移植性问题的一个实例·8 这里是空闲空间·参考·脚注0 简介C语言及其典型实现被设计为能被专家们容易地使用。
c语言进阶的书籍
c语言进阶的书籍C语言是一门广泛应用于系统开发、嵌入式系统和科学计算等领域的编程语言。
对于初学者来说,学会基本的语法和常用的函数可能并不困难,但要想进一步提升自己的C语言编程能力,深入理解C 语言的特性和高级技巧是必不可少的。
下面我将为大家推荐几本适合进阶学习的C语言书籍。
1.《C专家编程》《C专家编程》是由Peter Van der Linden所著,是一本经典的C语言进阶书籍。
该书通过大量实例和深入的讲解,帮助读者掌握C语言的高级编程技巧和陷阱避免方法。
涵盖了指针、内存管理、函数指针、位操作等高级主题,对于想要成为C语言专家的读者来说是一本不可多得的参考书。
2.《C陷阱与缺陷》《C陷阱与缺陷》是由Andrew Koenig和David R. Hanson合著,是一本揭示C语言常见陷阱和缺陷的书籍。
通过对各种C语言常见错误的深入分析和解释,帮助读者避免在编程中犯类似错误。
阅读该书可以帮助读者更加深入地理解C语言的语法和语义,提高编程的准确性和效率。
3.《C和指针》《C和指针》是由Kenneth A. Reek所著,是一本重点讲解C语言指针的书籍。
指针是C语言中非常重要的概念,也是初学者常常困惑的地方。
该书通过大量的示例和详细的讲解,帮助读者理解指针的概念、用法和实际应用。
掌握指针的知识可以提高编程的灵活性和效率,是进阶学习C语言的重要一步。
4.《深入理解计算机系统》《深入理解计算机系统》是由Randal E. Bryant和David R. O'Hallaron合著,虽然不是一本专门讲解C语言的书籍,但对于想要深入理解C语言底层原理和系统编程的读者来说是一本非常有价值的参考书。
该书通过介绍计算机系统的各个层次,包括硬件、操作系统和编译器等,帮助读者理解C语言程序在计算机系统中的运行机制和优化方法。
5.《C程序设计语言》《C程序设计语言》是由Brian W. Kernighan和Dennis M. Ritchie合著,被誉为C语言的圣经。
C语言缺陷与陷阱
C语言缺陷与陷阱(笔记)作者:来源:/skyyunmi发表时间:2006-12-28 浏览次数:40004 字号:大中小C语言缺陷与陷阱(笔记)C语言像一把雕刻刀,锋利,并且在技师手中非常有用。
和任何锋利的工具一样,C会伤到那些不能掌握它的人。
本文介绍C语言伤害粗心的人的方法,以及如何避免伤害。
第一部分研究了当程序被划分为记号时会发生的问题。
第二部分继续研究了当程序的记号被编译器组合为声明、表达式和语句时会出现的问题。
第三部分研究了由多个部分组成、分别编译并绑定到一起的C程序。
第四部分处理了概念上的误解:当一个程序具体执行时会发生的事情。
第五部分研究了我们的程序和它们所使用的常用库之间的关系。
在第六部分中,我们注意到了我们所写的程序也许并不是我们所运行的程序;预处理器将首先运行。
最后,第七部分讨论了可移植性问题:一个能在一个实现中运行的程序无法在另一个实现中运行的原因。
词法分析器(lexical analyzer):检查组成程序的字符序列,并将它们划分为记号(token)一个记号是一个由一个或多个字符构成的序列,它在语言被编译时具有一个(相关地)统一的意义。
C程序被两次划分为记号,首先是预处理器读取程序,它必须对程序进行记号划分以发现标识宏的标识符。
通过对每个宏进行求值来替换宏调用,最后,经过宏替换的程序又被汇集成字符流送给编译器。
编译器再第二次将这个流划分为记号。
1.1= 不是==:C语言则是用=表示赋值而用==表示比较。
这是因为赋值的频率要高于比较,因此为其分配更短的符号。
C还将赋值视为一个运算符,因此可以很容易地写出多重赋值(如a = b = c),并且可以将赋值嵌入到一个大的表达式中。
1.2 & 和| 不是&& 和||1.3 多字符记号C语言参考手册说明了如何决定:“如果输入流到一个给定的字符串为止已经被识别为记号,则应该包含下一个字符以组成能够构成记号的最长的字符串” “最长子串原则”1.4 例外组合赋值运算符如+=实际上是两个记号。
C陷阱与缺陷
C陷阱与缺陷C陷阱与缺陷第⼀章词法“陷阱”符号(token):指的是程序的⼀个基本组成单元,起作⽤相当于⼀个句⼦中的单词=不同于==&和|不同于&&和||词法分析中的“贪⼼法”C语⾔规则:每个符号应该包含尽可能多的字符整型常量整形常量的第⼀个字符是数字0,那么这个常量将被视作⼋进制数字符和字符串⽤单引号引起的⼀个字符实际上代表⼀个整数,整数值对应于该字符在编译器采⽤的字符集中的序列值。
⽤双引号引起的字符串,代表的却是⼀个指向⽆名数组起始字符的指针。
第⼆章词法“陷阱”理解函数声明构造表达式的规则:按照使⽤的⽅式来声明。
硬件调⽤⾸地址为0位置的⼦例程(*(void(*)())0)();第⼀步,0为地址,对0地址取内容:(*0)()第⼆步,*必须要⼀个指针来做操作数,这个指针还应该是函数指针,因此对0作类型转换,转换后类型为:“指向返回值为void类型的函数的指针:void (*)()0从⽽得到:(*(void(*)())0)();若⽤typedef解决typedef void (*funcptr)();(*(funcptr)0)();运算符的优先级运算符结合性() [] -> .⾃左向右! ~ ++ -- - (type) * & sizeof⾃右向左* / %⾃左向右+ -⾃左向右<< >>⾃左向右< <= > >=⾃左向右== !=⾃左向右&⾃左向右^⾃左向右&&⾃左向右| ?: | ⾃右向左 || assignnments | ⾃右向左 |任何⼀个逻辑运算符的优先级低于任何⼀个关系运算符移位运算符的优先级⽐算术运算符要低,但是⽐关系运算符要⾼注意作为语句结束标志的分号if(...);xx = xx;switch语句break别遗漏函数调⽤“悬挂”else引发的问题else始终与同⼀对括号内最近的未匹配的if结合第三章语义“陷阱”指针和数组C语⾔只有⼀维数组,⽽且数组的⼤⼩必须在编译期就作为⼀个常数确定下来。
c语言陷阱与技巧九节
c语言陷阱与技巧九节
1. 指针陷阱:指针的类型与指针值之间的关系
2. 数组陷阱:数组名与指针的区别与联系
3. 内存泄漏陷阱:动态内存分配与释放的方法与注意事项
4. 字符串陷阱:字符串的定义、初始化、操作及注意事项
5. 结构体陷阱:结构体的定义、初始化、操作及注意事项
6. 文件操作陷阱:文件的打开、读写、关闭及错误处理方法
7. 编译预处理陷阱:宏定义、条件编译、头文件及注意事项
8. 代码风格陷阱:注释、缩进、命名规范及代码可读性
9. 调试技巧:断点调试、调试信息、日志输出及代码调试工具。
- 1 -。
c陷阱与缺陷《C陷阱和缺陷》读书笔记 ——前车的覆 后车的鉴
c陷阱与缺陷:《C陷阱和缺陷》读书笔记——前车的覆 后车的鉴疯狂代码 / ĵ:http://BlogDigest/Article76354.html《C陷阱与缺陷》,作者:Andrew Koenig [美], 译:高 巍 。
; ; ; 这本书是作者以自己发表过的一篇论文为基础,结合自己的工作经验扩展而成。
我看过之后“吃了一斤”,它跟以往我看过的教程完全不一样,它涉及到C的各个方面,细微、精辟。
用两个字形容,那就是实用。
我不打算把整本书抄一遍,只对我认为“比较重要”(其实没有哪一点是不重要的)的部分做了笔记,并且是简要提取了其中的主要内容,以及相应的小例子,希望再次回顾的时候提高效率。
如果你感到意犹未尽,可以下载来看。
C陷阱与缺陷 下载 ; 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);; ★ ★ 在用双引号括起来的字符串中,注释符 /* 属于字符串的一部分,而在注释中出现的双引号""属于注释的一部分。
编程语言语法中的常见陷阱与解决方案
编程语言语法中的常见陷阱与解决方案在计算机编程领域,编程语言是程序员与计算机之间进行沟通的重要工具。
不同的编程语言有着各自独特的语法规则和特性,但同时也存在一些常见的陷阱,这些陷阱可能导致程序逻辑错误、性能问题或安全漏洞。
在本文中,我们将探讨一些常见的编程语言语法陷阱,并提供相应的解决方案。
1. 类型转换错误在很多编程语言中,类型转换是一项常见的操作。
然而,不正确的类型转换可能导致程序运行时错误。
例如,在将一个浮点数转换为整数时,如果直接截断小数部分,可能导致结果不符合预期。
解决这个问题的一种方法是使用合适的类型转换函数,如将浮点数转换为整数时使用取整函数,而不是简单的截断操作。
2. 数组越界访问在许多编程语言中,数组是一种常见的数据结构。
然而,访问数组时经常会出现越界错误,即访问超出数组边界的元素。
这可能导致程序崩溃或产生不可预测的结果。
为了避免这种错误,程序员需要确保在访问数组元素之前先检查索引的有效性,并在必要时进行边界检查。
3. 内存泄漏内存泄漏是指程序在分配内存后未能正确释放该内存,导致内存占用不断增加,最终可能导致系统资源耗尽。
在一些编程语言中,特别是C和C++,程序员需要手动管理内存分配和释放。
为了避免内存泄漏,程序员应该在每次分配内存后都确保相应的释放操作,并注意避免出现悬空指针。
4. 死锁在多线程编程中,死锁是一种常见的问题。
死锁发生在两个或多个线程相互等待对方释放资源的情况下,导致程序无法继续执行。
为了避免死锁,程序员需要仔细设计和管理线程之间的资源竞争关系,并使用适当的同步机制,如互斥锁和条件变量,来避免资源竞争和死锁的发生。
5. SQL注入在与数据库交互的编程中,SQL注入是一种常见的安全漏洞。
它发生在程序未能正确过滤用户输入并将其用作SQL查询的一部分时,导致恶意用户可以执行未经授权的数据库操作。
为了防止SQL注入,程序员应该使用参数化查询或预编译语句,确保用户输入被正确转义和过滤。
C陷阱与缺陷笔记
◆术语“符号”(token)指的是程序的一个基本组成单元,其作用相当于一个句子中的单词。
◆在C语言中,符号之间的空白(包括空格符,制表符或换行符)将被忽略。
◆赋值运算符的优先级要低于任何一个比较运算符。
◆C语言中只有一维数组?(说的是本质上);◆对于数组结尾之后的下一个元素,取它的地址是合法的。
而试图实际读取这个元素的值,这种做法的结果是未定义。
◆连接器的输入是一组目标模块和库文件,连接器的输出是一个载入模块。
连接器读入目标模块和库文件,同时生成载入模块。
◆Toupper() 将所有的小写字母转换为相应的大写字母。
◆#define T1 struct foo*:○1T1 a,b; a被定义为一个指向结构的指针,而b却被定义为一个结构(而不是指针)◆NULL指针并不指向任何对象,因此,除非是用于赋值或比较运算,出于其他任何目的的使用NULL指针都是非法的。
◆在调试时强制不允许对输出进行缓冲,要做到这一点可调用setbuf(stdout, (char*)0);◆格式字符串中的每个格式项都由一个%符号打头,后面接一个称为格式码的字符,格式码指明了格式转换的类型,格式码不一定要紧跟在%符号之后,它们中间可能夹一些可选取字符。
○1%d以10进制形式打印一个整数;○2%u打印无符号10进制整数;○3%o、%x和%X格式项用于打印8进制或16进制的整数;○4%c用于打印单个字符;○5%s用于打印字符串;如果与%s对应的字符并不是以空字符(’\0’)作为结束标志,那么printf函数将不断打印出其后的字符直到内存中某处打到一个空字符。
○6%g(去掉该数值尾缀的零或四舍五入,保留六位有效数字)、%f和%e这3个格式项用于打印浮点值。
○7精度修饰符(还有几种是宽度修饰符和标志符详细请参考C陷阱与缺陷P155)包括一个小数点,和小数点后面的一串数字如:%.8d;✧对于整数格式项%d,%o,%x和%u,精度修饰符指定了打印数字的最少位数。
c语言 陷阱的书
c语言陷阱的书
《C陷阱与缺陷》是一本关于C语言程序员在编程中容易掉入的陷阱的书,作者以自己1985年在贝尔实验室时发表的一篇论文为基础,结合自己的工作经验扩展成了这本经典著作。
本书分为8章,分别从词法陷阱、语法陷阱、语义陷阱、链接、库函数、预处理器、可一致性缺陷等几个方面分析了C编程中可能遇到的问题。
最后,作者用一章的篇幅给出了若干具有实用价值的建议。
这本书适合有一定经验的C程序员阅读学习,即便是C编程高手,本书也应该成为你的案头必备图书。
没有一种语言是完美无缺的,它都是有缺陷和漏洞的,《C陷阱与缺陷》可以帮助C程序员绕过编程过程中的陷阱和障碍。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C陷阱与缺陷笔记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是一个指向返回值为浮点类型的函数的指针。
所以(float (*)())表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。
一旦我们知道如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:只需要把声明中的参量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个“封装”起来即可。
(*(void(*)())0)()表示什么意思呢?如果fp是一个函数指针,那么(*fp)()就表示对其所指的函数的调用。
简写为fp()。
但这只是简写而已。
而*((*fp)())可以简写为*fp()根据上文(void(*)()) 表示一个“指向返回值为void的函数的指针”的类型。
这里不过是对0作强制转换而已。
其实用typedef更好:typedef void (*funcptr)();(*(funcptr)0)();signal的声明如下:void (*signal(int, void(*)(int)))(int);或者用typedef:typedef void (*HANDLER)(int);HANDLER signal(int, HANDLER);2.2 运算符的优先级问题注意条件运算符优先级比赋值运算符高,书上第22页是错的。
&> ^ > |2.3 分号2.4 switch 语句2.5 函数调用f();是个函数调用。
而f;则计算函数f的地址。
2.6 elseC语言允许初始化列表中出现多余的逗号。
Chapter 3 语义陷阱3.1 指针与数组C语言中只有一维数组,而且数组的大小必须在编译期间就作为一个常数确定下来。
多维数组是通过一维数组仿真的,因为数组的元素可以是任何对象,当然也可以是数组。
对数组,我们只能做两件事,确定其大小,以及获得指向该数组下标为0的元素的指针。
其它的有关数组的操作,实际上是通过指针进行的。
如果两个指针指向的是同一个数组中的元素,我们可以把这两个指针相减。
如果它们指向的不是同一个数组中的元素,即使它们指向的地址在内存中的位置正好间隔一个数组元素的整数倍,所得的结果仍然是无法保证其正确性的。
如果在应该出现指针的地方出现了数组名,则数组名就被当作指向该数组下标为0的元素的指针。
int a;p = a;int *p;是对的。
但p = &a在ansi C中则是非法的。
因为&a 是一个指向数组的指针,而p是一个指向整型变量的指针,它们的类型不匹配。
由于a[i] 即*(a+i);而a+i即i+a;所以a[i]即i[a];但不推荐后者的写法int cal[12][31];int *p;int i;i = cal[4][7]等于i = *(cal[4] + 7);也等于i = *(*(cal + 4) +7);p = cal; 是错误的,类型不匹配,后者是指向数组的指针。
我们来声明指向数组的指针:int (*ap)[31];于是我们可以这样写:int cal[12][31];int (*monthp)[31];monthp = cal;两个指针不能相加。
负数的移位运算不等于相应的乘或除运算。
3.2 非数组的指针我们要将s和t连接成r.s = "abc";t = "efg";char *r;strcpy(r,s);strcat(r,t);这并不能达到目的。
因为一是不能确定r指向何处, 二是不能保证r所指向的地址处还应该有内存空间可供容纳字符串。
较好的是把第一行改为char r[100];只是这样的话,大小固定了。
正确的应该是:#include <stdio.h>#include <ctype.h>int main (void){char s[10];char t[10];char *r;char *malloc();r = malloc(strlen(s) + strlen(t) + 1);if(!r){complain();exit(1);}scanf("%s",s);/*getchar();*/scanf("%s",t);strcpy(r,s);strcat(r,t);printf("%s\n",r);free(r);}3.3 作为参数的数组声明我们没有办法将一个数组作为函数参数直接传递。
数组名会被转为指向该数组第一个元素的指针。
与下面的写法完全相同:int strlen(char* s){}但其它地方就未必相同了。
下面两个语句是完全不同的。
extern char *hello;extern char hello[];下面则是一样的main(int argc, char* argv[]){}main(int argc, char** argv){}3.4 避免“举隅法”复制指针并不同时复制指针所指向的数据。
3.5 空指针并非空字符串把常数0转为指针,则指针不等于任何有效的指针,即 void 指针。
其它将整数转为指针得到的结果未定义。
当常数0被转为指针时,这个指针绝对不能被解除引用(dereferenc)。
换句话说,当我们将0赋给一个指针变量时,绝对不能企图使用该指针所指向的内存中存储的内容。
下面的是合法的:但下面是非法的if (strcmp(p, (char *) 0) == 0)如果p是一个空指针,即使printf(p);和printf("%s",p);的行为也是未定义的。
3.6 边界计算与不对称边界数组的下标如果用入界口加出界口来表达(即10个元素,其下标为0 <= n < 10 ),则元素个数即为上界与下界之差,即下界。
若为空,则上界等于下界。
任何情况下上界也永远不可能小于下界。
尽量采用非对称边界法。
一个有N个元素的数组,我们可以使用a[N]进行比较和赋值,但不能引用其内容。
3.7 求值顺序C语言只有四个运算符(&&, ||,和!)存在规定的求值顺序。
另外,分隔函数参数的逗号并非逗号运算符。
例如,在x和y在函数f(x,y)中的求值顺序是未定义的,而在函数g((x,y))是先算x,再算y,y的值为参数。
特别是赋值运算符没有规定求值顺序。
3.8 运算符(&&,||,和!)3.9 整数溢出无符号算术运算中,没有所谓的“溢出”一说。
有符号运算中发生溢出,则结果未定义。
下面检测溢出的方法不可靠:if(a + b <0)complain();应该这样:if((unsigned) a + (unsigned) b >INT_MAX)complain();或者这样if(a > INT_MAX - b)complain();3.10 为函数main提供返回值如果没有为函数声明返回类型,则默认为int.free之后最好马上就p = NULL;Chapter 4 连接4.1 什么是连接器连接器通常把目标模块看成是由一组外部对象组成的。
第个外部对象都代表着机器内存中的某个部分,并通达一个外部名称来识别。
因此,程序中的每个函数和每个外部变量,如果没有被声明为static,就都是一个外部对象。
某些编译器会对静态函数和静态变量的名称做一定改变,将它们也作为外部对象。
除了外部对象,目标模块还可能包括了对其它模块中的外部对象的引用。
4.2 声明与定义每个外部变量只能定义一次。
4.3 命名冲突与static修饰符4.4 形参、实参与返回值每个函数都要在调用之前进行声明定义,不然返回类型为int如果一个函数没有float,short或者char类型的参数,在函数声明中完全可以省略类型声明(定义不能省略)4.5 检查外部类型同一个外部变量在不同的地方被声明为不同的类型,这种错误大部分编译器是检不出来的。
char file[]= "/etc/password";与extern char* file;是不一样的。
4.6 头文件Chapter 5 库函数C标准没有定义执行底层I/O操作的read和write函数。
5.1 返回整数的getchar函数5.2 更新顺序文件为了与以前的程序保持兼容,一个输入操作不能随后紧跟一个输出操作,反之亦然。
如果要同时进行输入和输出操作,必须在其中插入fseek函数的调用。
FILE *fp;struct record rec;while (fread((char *)&rec, sizeof(rec),1,fp) = 1)/* */if(/* */){fseek(fp, -(long)sizeof(rec), 1);fwrite((char *)&rec, sizeof(rec), 1,fp);fseek(fp, 0l,1);}}5.3 缓冲输出与内存分配#include <stdio.h>void main(void){int c;char buf[BUFSIZ];setbuf(stdout,buf);while((c = getchar()) != EOF)putchar(c);}这个是不对的。