C语言陷阱和缺陷

合集下载

C语言中的漏洞利用与渗透测试技术

C语言中的漏洞利用与渗透测试技术

C语言中的漏洞利用与渗透测试技术C语言作为一种广泛应用于编程领域的高级编程语言,由于其灵活和高效的特点,被广泛使用于各种软件开发项目中。

然而,正是因为其广泛的应用,C语言也存在一些漏洞和安全隐患。

本文将重点探讨C 语言中的漏洞利用与渗透测试技术,以帮助读者了解并提高对C语言程序的安全性。

一、C语言中的常见漏洞在介绍漏洞利用与渗透测试技术前,我们首先需要了解C语言中的一些常见漏洞类型。

以下是几种常见的C语言漏洞:1. 缓冲区溢出:这是一种常见的安全漏洞,在C语言中由于缺乏边界检查导致。

当程序接收用户输入时,如果没有正确验证输入的长度,可能会导致缓冲区溢出,使攻击者能够执行恶意代码或破坏系统。

2. 格式化字符串漏洞:当程序使用不正确的格式化字符串函数,或者没有正确检查格式化字符串的输入时,可能会导致攻击者通过格式化字符串漏洞读取或修改内存中的数据,造成信息泄露或系统崩溃。

3. 整数溢出:在C语言中,整数的溢出可能导致程序出现未定义行为,为攻击者提供了利用的机会。

例如,当执行算术运算或数组索引时,如果没有正确检查整数边界,可能会导致溢出。

二、漏洞利用技术漏洞利用是指攻击者利用系统或应用程序中的漏洞,通过注入恶意代码或执行特定操作来获取权限或控制目标系统。

以下是一些常见的漏洞利用技术:1. Shellcode注入:攻击者可以通过利用缓冲区溢出等漏洞,将恶意代码注入到目标系统的内存中。

一旦成功注入,攻击者就可以通过控制指令来执行恶意操作。

2. Return-Oriented Programming(ROP):ROP是一种高级漏洞利用技术,通过利用程序中的已存在的代码段(Gadget)来执行恶意操作。

攻击者通过构造特定的ROP链,在不添加新代码的情况下,利用程序中的现有代码来完成攻击目标。

3. 格式化字符串攻击:攻击者可以通过构造恶意格式化字符串,利用格式化字符串漏洞来读取或修改内存中的数据。

这种技术通常用于泄露内存中的敏感信息或执行特定操作。

这才是你最想要的C语言学习路线

这才是你最想要的C语言学习路线

这才是你最想要的C语言学习路线计算机科班的同学,不出意外,进入编程世界的第一门的语言学的肯定是C语言了。

其他立志做技术行的同学,从C语言入门百利无害。

很多人都觉得 C 语言入门难,不能像 Python 这种编程语言一样快速获得成就感。

为啥现在还有这么多技术大佬建议学一下C语言呢?因为C语言是一门面向过程的语言,运行速度极快,是计算机产业的核心语言,像操作系统、硬件驱动、数据库这些都离不开C语言。

不学C 语言,就很难深入了解计算机底层的运行机制。

现在常见的高级语言的底层几乎都是C语言实现的。

C语言的学习其实就三个阶段就好了:(1)入门阶段这个阶段学习C语言的基础语法知识。

目标是可以开发一些简单的控制台小程序。

(2)提高阶段这个阶段学习C语言自带的库函数,形成C语言的基本知识框架。

目标是开发一些基本的应用程序。

(3)应用阶段这个是实战阶段,要具备一定的综合性应用软件开发能力。

目标是能够开发像贪吃蛇、图书管理系统、学生信息管理系统等项目。

注意!下面都是超极干的干货,记得先帮我点个赞呀,么么哒。

一、入门阶段入门阶段主要需要学习下图的内容:1.视频推荐此时同学们应该是小白阶段。

对于小白来说,不建议上来就看书,因为干看看不懂,容易劝退。

可以先从视频教程开始。

C语言的视频教程我只推荐一人:浙江大学翁恺老师的 C 语言课,yyds!是一个课程质量非常棒,讲课幽默,深入浅出的课程,非常容易理解!当时学C语言的时候,自己还是个从来没接触过编程的菜鸡,当时的学习全靠翁恺老师了!不多说,看过的都知道。

翁恺老师的课是在MOOC上开的。

主要分两门:第一门是面向高考结束想提前自学一些编程的或者是刚开始学习的大一新生,叫《程序设计入门-C语言》,涵盖了主要的C语言知识点。

完成本课程之后,就能具有初步的运用C语言编写程序的能力。

2.第二门是《C语言程序设计进阶》,这门课就是要告诉你C语言到底有哪些独特的地方,为什么能长期占据15%上下的编程语言份额。

嵌入式C语言的八大难点揭秘

嵌入式C语言的八大难点揭秘

嵌入式C语言的八大难点揭秘本文将带您了解一些良好的和内存相关的编码实践,以将内存错误保持在控制范围内。

内存错误是C 和C++ 编程的祸根:它们很普遍,认识其严重性已有二十多年,但始终没有彻底解决,它们可能严重影响应用程序,并且很少有开发团队对其制定明确的管理计划。

但好消息是,它们并不怎么神秘。

▶引言C 和 C++ 程序中的内存错误非常有害:它们很常见,并且可能导致严重的后果。

来自计算机应急响应小组(请参见参考资料)和供应商的许多最严重的安全公告都是由简单的内存错误造成的。

自从 70 年代末期以来,C 程序员就一直讨论此类错误,但其影响在至今年仍然很大。

更糟的是,如果按我的思路考虑,当今的许多C 和C++ 程序员可能都会认为内存错误是不可控制而又神秘的顽症,它们只能纠正,无法预防。

但事实并非如此。

本文将让您在短时间内理解与良好内存相关的编码的所有本质:▶正确的内存管理的重要性存在内存错误的C 和C++ 程序会导致各种问题。

如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击。

从1988 年著名的莫里斯蠕虫攻击到有关Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关:“大多数计算机安全漏洞都是缓冲区溢出”,Rodney Bates 在 2004 年写道。

在可以使用C 或C++ 的地方,也广泛支持使用其他许多通用语言(如Java?、Ruby、Haskell、C#、Perl、Smalltalk 等),每种语言都有众多的爱好者和各自的优点。

但是,从计算角度来看,每种编程语言优于C 或C++ 的主要优点都与便于内存管理密切相关。

与内存相关的编程是如此重要,而在实践中正确应用又是如此困难,以致于它支配着面向对象编程语言、功能性编程语言、高级编程语言、声明性编程语言和另外一些编程语言的所有其他变量或理论。

与少数其他类型的常见错误一样,内存错误还是一种隐性危害:它们很难再现,症状通常不能在相应的源代码中找到。

c语言常见问题集

c语言常见问题集

c语言常见问题集C语言作为一种古老而强大的编程语言,在使用过程中可能会遇到各种常见问题。

以下是一些C语言常见问题及解决方法的集合:1.指针问题:问题:指针使用不当导致内存泄漏或段错误。

解决方法:谨慎使用指针,确保正确的内存分配和释放,避免野指针。

2.内存泄漏:问题:未正确释放动态分配的内存。

解决方法:在不再使用内存时,使用free函数释放动态分配的内存。

3.数组越界:问题:访问数组元素时超出了数组边界。

解决方法:确保数组索引在合法范围内,使用循环时注意控制循环边界。

4.未初始化变量:问题:使用未初始化的变量。

解决方法:在使用变量之前确保对其进行初始化,避免产生未定义行为。

5.逻辑错误:问题:程序的输出与预期不符。

解决方法:仔细检查代码逻辑,使用调试工具进行单步调试,查找错误的源头。

6.编译错误:问题:编译时出现错误。

解决方法:仔细阅读编译器报错信息,检查代码语法错误,确保使用正确的语法和标准库函数。

7.字符串处理问题:问题:字符串操作时未考虑字符串结束符\0。

解决方法:确保字符串以\0结尾,使用字符串处理函数时注意边界条件。

8.文件操作问题:问题:未正确打开、关闭文件,或者在未打开文件的情况下进行文件操作。

解决方法:在使用文件之前确保正确打开,使用完毕后关闭文件,检查文件是否成功打开。

9.结构体使用问题:问题:结构体成员的访问不当。

解决方法:确保使用正确的结构体成员名,避免结构体成员越界访问。

10.数据类型不匹配:-问题:不同数据类型之间的不匹配导致错误。

-解决方法:确保进行运算或赋值时,数据类型一致或符合隐式转换规则。

以上问题及解决方法提供了一些基本的指导,但在实际编码中,关键在于谨慎、仔细和严谨,同时善用调试工具和编程工具,及时修复潜在问题。

C语言经典书籍

C语言经典书籍
10、C标准库
作者: (美)普劳格 著,卢红星,徐明亮,霍建同 译
出版社: 人民邮电出版社
出版时间: 2009-7-1
C语言经典书籍
1、C程序设计语言(第2版·新版)
作者: (美)克尼汉,(美)里奇 著,徐宝文,李志 译
出版社: 机械工业出版社 出版间: 2004-1-1 2、你必须知道的495个C语言问题
作者: (美)萨米特 著,孙云,朱群英 译
出版社: 人民邮电出版社
出版时间: 2009-2-1
出版时间: 2008-4-1
6、C Primer Plus(第五版)中文版
作者: (美)普拉塔(Prata,S.) 著,云巅工作室 译
出版社: 人民邮电出版社
出版时间: 2005-2-1
7、C语言程序设计现代方法
作者: (美)金(King,K.N.) 著,吕秀锋 译
出版社: 人民邮电出版社
出版时间: 2007-11-1
8、C语言详解(第5版)
作者: (美)汉利(Hanly,J.R.),(美)科夫曼(Koffman,E.B.) 著,万波,潘蓉,郑海红 译
出版社: 人民邮电出版社
出版时间: 2007-11-1
9、C语言核心技术
出 版 社: 机械工业出版社
出版时间: 2007-8-1
3、C专家编程
作者: (美)林登(LinDen,P.V.D) 著,徐波 译
出版社: 人民邮电出版社
出版时间: 2008-2-1
4、C 陷阱与缺陷
作者: (美)凯尼格 著,高巍 译
出版社: 人民邮电出版社
出版时间: 2008-2-1
5、C和指针

(经典)C语言陷阱和缺陷

(经典)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语言虽然功能强大,但也有很多潜在的陷阱和缺陷。

比如,C语言中的指针和内存管理容易让人犯错误,不正确的使用可能导致程序崩溃或者出现不可预期的行为。

此外,C语言中的类型转换和类型提升也可能让人感到困惑,不注意细节就可能导致错误的程序结果。

其次,这本书强调了防御性编程的重要性。

在编程过程中,我们应该始终保持警惕,对可能出错的地方进行预防性编程。

这不仅可以帮助我们发现和避免错误,还可以使我们的程序更加健壮和可靠。

同时,书中还介绍了如何使用一些工具和技术来辅助编程,如代码审查、测试和调试等,这些都是非常有用的实践方法。

最后,我认为这本书对于C语言的学习者和使用者都有很大的参考价值。

通过阅读这本书,我不仅对C语言有了更深入的理解,还学到了很多实用的编程技巧和方法。

这些
知识和经验将对我未来的编程工作产生积极的影响。

总之,《C陷阱与缺陷》是一本非常有价值的书籍,它让我认识到编程不仅需要技术能力,更需要良好的习惯和严谨的态度。

我相信这本书对于任何一位从事编程工作的人都会有很大的帮助。

C语言编程时常犯的17种错误

C语言编程时常犯的17种错误

C语言编程时常犯的17种错误C语言编程时常犯的错误1、书写标识符时,忽略了大小写字母的区别。

main(){ int a=5; printf("%d",A);}编译程序把a和A认为是两个不同的变量名,而显示出错信息。

C认为大写字母和小写字母是两个不同的字符。

习惯上,符号常量名用大写,变量名用小写表示,以增加可读性。

2、忽略了变量的类型,进行了不合法的运算。

代码如下:main(){ float a,b; printf("%d",a%b);}%是求余运算,得到a/b的整余数。

整型变量a和b可以进行求余运算,而实型变量则不允许进行“求余”运算。

3、将字符常量与字符串常量混淆。

char c;c=”a”;在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。

C规定以“\”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a’和‘’,而把它赋给一个字符变量是不行的。

4、忽略了“=”与“==”的区别。

在许多高级语言中,用“=”符号作为关系运算符“等于”。

如在BASIC 程序中可以写if (a=3) then …但C语言中,“=”是赋值运算符,“==”是关系运算符。

如:if (a==3) a=b;前者是进行比较,a是否和3相等,后者表示如果a和3相等,把b值赋给a。

由于习惯问题,初学者往往会犯这样的错误。

5、忘记加分号。

分号是C语句中不可缺少的一部分,语句末尾必须有分号。

a=1b=2编译时,编译程序在“a=1”后面没发现分号,就把下一行“b=2”也作为上一行语句的一部分,这就会出现语法错误。

改错时,有时在被指出有错的一行中未发现错误,就需要看一下上一行是否漏掉了分号。

代码如下:{ z=x+y; t=z/100; printf("%f",t);}对于复合语句来说,最后一个语句中最后的分号不能忽略不写(这是和PASCAL不同的)。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

[修订说明]第一次修订。

改正了文中的大部分错别字和格式错误,并对一些句子依照中文的习惯进行了改写。

[译序]那些自认为已经“学完”C语言的人,请你们仔细读阅读这篇文章吧。

路还长,很多东西要学。

我也是……[概述]C语言像一把雕刻刀,锋利,并且在技师手中非常有用。

和任何锋利的工具一样,C会伤到那些不能掌握它的人。

本文介绍C语言伤害粗心的人的方法,以及如何避免伤害。

[内容]0 简介1 词法缺陷1.1 = 不是==1.2 & 和| 不是&& 和||1.3 多字符记号1.4 例外1.5 字符串和字符2 句法缺陷2.1 理解声明2.2 运算符并不总是具有你所想象的优先级2.3 看看这些分号!2.4 switch语句2.5 函数调用2.6 悬挂else问题3 连接3.1 你必须自己检查外部类型4 语义缺陷4.1 表达式求值顺序4.2 &&、||和!运算符4.3 下标从零开始4.4 C并不总是转换实参4.5 指针不是数组4.6 避免提喻法4.7 空指针不是空字符串4.8 整数溢出4.9 移位运算符5 库函数5.1 getc()返回整数5.2 缓冲输出和内存分配6 预处理器6.1 宏不是函数6.2 宏不是类型定义7 可移植性缺陷7.1 一个名字中都有什么?7.2 一个整数有多大?7.3 字符是带符号的还是无符号的?7.4 右移位是带符号的还是无符号的?7.5 除法如何舍入?7.6 一个随机数有多大?7.7 大小写转换7.8 先释放,再重新分配7.9 可移植性问题的一个实例8 这里是空闲空间参考脚注0 简介C语言及其典型实现被设计为能被专家们容易地使用。

这门语言简洁并附有表达力。

但有一些限制可以保护那些浮躁的人。

一个浮躁的人可以从这些条款中获得一些帮助。

在本文中,我们将会看到这些未可知的益处。

正是由于它的未可知,我们无法为其进行完全的分类。

不过,我们仍然通过研究为了一个C程序的运行所需要做的事来做到这些。

我们假设读者对C语言至少有个粗浅的了解。

第一部分研究了当程序被划分为记号时会发生的问题。

第二部分继续研究了当程序的记号被编译器组合为声明、表达式和语句时会出现的问题。

第三部分研究了由多个部分组成、分别编译并绑定到一起的C程序。

第四部分处理了概念上的误解:当一个程序具体执行时会发生的事情。

第五部分研究了我们的程序和它们所使用的常用库之间的关系。

在第六部分中,我们注意到了我们所写的程序也许并不是我们所运行的程序;预处理器将首先运行。

最后,第七部分讨论了可移植性问题:一个能在一个实现中运行的程序无法在另一个实现中运行的原因。

1 词法缺陷编译器的第一个部分常被称为词法分析器(lexical analyzer)。

词法分析器检查组成程序的字符序列,并将它们划分为记号(token)一个记号是一个由一个或多个字符构成的序列,它在语言被编译时具有一个(相关地)统一的意义。

在C中,例如,记号->的意义和组成它的每个独立的字符具有明显的区别,而且其意义独立于->出现的上下文环境。

另外一个例子,考虑下面的语句:if(x > big) big = x;该语句中的每一个分离的字符都被划分为一个记号,除了关键字if和标识符big的两个实例。

事实上,C程序被两次划分为记号。

首先是预处理器读取程序。

它必须对程序进行记号划分以发现标识宏的标识符。

它必须通过对每个宏进行求值来替换宏调用。

最后,经过宏替换的程序又被汇集成字符流送给编译器。

编译器再第二次将这个流划分为记号。

在这一节中,我们将探索对记号的意义的普遍的误解以及记号和组成它们的字符之间的关系。

稍后我们将谈到预处理器。

1.1 = 不是==从Algol派生出来的语言,如Pascal和Ada,用:=表示赋值而用=表示比较。

而C语言则是用=表示赋值而用==表示比较。

这是因为赋值的频率要高于比较,因此为其分配更短的符号。

此外,C还将赋值视为一个运算符,因此可以很容易地写出多重赋值(如a = b = c),并且可以将赋值嵌入到一个大的表达式中。

这种便捷导致了一个潜在的问题:可能将需要比较的地方写成赋值。

因此,下面的语句好像看起来是要检查x是否等于y:if(x = y)foo();而实际上是将x设置为y的值并检查结果是否非零。

再考虑下面的一个希望跳过空格、制表符和换行符的循环:while(c == ' ' || c = '\t' || c == '\n')c = getc(f);在与'\t'进行比较的地方程序员错误地使用=代替了==。

这个“比较”实际上是将'\t'赋给c,然后判断c的(新的)值是否为零。

因为'\t'不为零,这个“比较”将一直为真,因此这个循环会吃尽整个文件。

这之后会发生什么取决于特定的实现是否允许一个程序读取超过文件尾部的部分。

如果允许,这个循环会一直运行。

一些C编译器会对形如e1 = e2的条件给出一个警告以提醒用户。

当你确实需要先对一个变量进行赋值之后再检查变量是否非零时,为了在这种编译器中避免警告信息,应考虑显式给出比较符。

换句话说,将:if(x = y)foo();改写为:if((x = y) != 0)foo();这样可以清晰地表示你的意图。

1.2 & 和| 不是&& 和||容易将==错写为=是因为很多其他语言使用=表示比较运算。

其他容易写错的运算符还有&和&&,以及|和||,这主要是因为C语言中的&和|运算符于其他语言中具有类似功能的运算符大为不同。

我们将在第4节中贴近地观察这些运算符。

1.3 多字符记号一些C记号,如/、*和=只有一个字符。

而其他一些C记号,如/*和==,以及标识符,具有多个字符。

当C编译器遇到紧连在一起的/和*时,它必须能够决定是将这两个字符识别为两个分离的记号还是一个单独的记号。

C语言参考手册说明了如何决定:“如果输入流到一个给定的字符串为止已经被识别为记号,则应该包含下一个字符以组成能够构成记号的最长的字符串”([译注]即通常所说的“最长子串原则”)。

因此,如果/是一个记号的第一个字符,并且/后面紧随了一个*,则这两个字符构成了注释的开始,不管其他上下文环境。

下面的语句看起来像是将y的值设置为x的值除以p所指向的值:y = x/*p /* p 指向除数*/;实际上,/*开始了一个注释,因此编译器简单地吞噬程序文本,直到*/的出现。

换句话说,这条语句仅仅把y的值设置为x的值,而根本没有看到p。

将这条语句重写为:y = x / *p /* p 指向除数*/;或者干脆是y = x / (*p) /* p指向除数*/;它就可以做注释所暗示的除法了。

这种模棱两可的写法在其他环境中就会引起麻烦。

例如,老版本的C使用=+表示现在版本中的+=。

这样的编译器会将a=-1;视为a =- 1;或a = a - 1;这会让打算写a = -1;的程序员感到吃惊。

另一方面,这种老版本的C编译器会将a=/*b;断句为a =/ *b;尽管/*看起来像一个注释。

1.4 例外组合赋值运算符如+=实际上是两个记号。

因此,a + /* strange */ = 1和a += 1是一个意思。

看起来像一个单独的记号而实际上是多个记号的只有这一个特例。

特别地,p - > a是不合法的。

它和p -> a不是同义词。

另一方面,有些老式编译器还是将=+视为一个单独的记号并且和+=是同义词。

1.5 字符串和字符单引号和双引号在C中的意义完全不同,在一些混乱的上下文中它们会导致奇怪的结果而不是错误消息。

包围在单引号中的一个字符只是编写整数的另一种方法。

这个整数是给定的字符在实现的对照序列中的一个对应的值。

因此,在一个ASCII实现中,'a'和0141或97表示完全相同的东西。

而一个包围在双引号中的字符串,只是编写一个有双引号之间的字符和一个附加的二进制值为零的字符所初始化的一个无名数组的指针的一种简短方法。

下面的两个程序片断是等价的:printf("Hello world\n");char hello[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\n', 0 };printf(hello);使用一个指针来代替一个整数通常会得到一个警告消息(反之亦然),使用双引号来代替单引号也会得到一个警告消息(反之亦然)。

但对于不检查参数类型的编译器却除外。

因此,用printf('\n');来代替printf("\n");通常会在运行时得到奇怪的结果。

([译注]提示:正如上面所说,'\n'表示一个整数,它被转换为了一个指针,这个指针所指向的内容是没有意义的。

)由于一个整数通常足够大,以至于能够放下多个字符,一些C编译器允许在一个字符常量中存放多个字符。

这意味着用'yes'代替"yes"将不会被发现。

后者意味着“分别包含y、e、s 和一个空字符的四个连续存储器区域中的第一个的地址”,而前者意味着“在一些实现定义的样式中表示由字符y、e、s联合构成的一个整数”。

这两者之间的任何一致性都纯属巧合。

2 句法缺陷要理解C语言程序,仅了解构成它的记号是不够的。

还要理解这些记号是如何构成声明、表达式、语句和程序的。

尽管这些构成通常都是定义良好的,但这些定义有时候是有悖于直觉的或混乱的。

在这一节中,我们将着眼于一些不明显句法构造。

2.1 理解声明我曾经和一些人聊过天,他们那时正在在编写在一个小型的微处理器上单机运行的C程序。

当这台机器的开关打开的时候,硬件会调用地址为0处的子程序。

为了模仿电源打开的情形,我们要设计一条C语句来显式地调用这个子程序。

经过一些思考,我们写出了下面的语句:(*(void(*)())0)();这样的表达式会令C程序员心惊胆战。

但是,并不需要这样,因为他们可以在一个简单的规则的帮助下很容易地构造它:以你使用的方式声明它。

每个C变量声明都具有两个部分:一个类型和一组具有特定格式的、期望用来对该类型求值的表达式。

最简单的表达式就是一个变量:float f, g;说明表达式f和g——在求值的时候——具有类型float。

由于待求值的是表达式,因此可以自由地使用圆括号:float ((f));这表示((f))求值为float并且因此,通过推断,f也是一个float。

相关文档
最新文档