首页 C专家编程(笔记)

C专家编程(笔记)

举报
开通vip

C专家编程(笔记) 第一章: 一:尽量不要在你的代码中使用无符号类型,以免增加不必要的复杂性,尤其是不要仅仅因 为无符号数不存在负值(如年龄,国债)而用它来表示数量。只有在使用位段和二进制掩码 时,才可以使用无符号数,应该在表达式中使用强制类型转换,使操作数均为有符号数或无 符号数。这样就不必由编译器来选择结果的类型。 二:当执行算术运算时:操作数的类型如果不同,就会发生转换,数据类型一般朝着浮点精 度更高,长度更长的方向转变。整型数如果转换为 signed不会丢失信息,就转换为 signed, 否则转换为 unsigned。 ...

C专家编程(笔记)
第一章: 一:尽量不要在你的代码中使用无符号类型,以免增加不必要的复杂性,尤其是不要仅仅因 为无符号数不存在负值(如年龄,国债)而用它来表示数量。只有在使用位段和二进制掩码 时,才可以使用无符号数,应该在表达式中使用强制类型转换,使操作数均为有符号数或无 符号数。这样就不必由编译器来选择结果的类型。 二:当执行算术运算时:操作数的类型如果不同,就会发生转换,数据类型一般朝着浮点精 度更高,长度更长的方向转变。整型数如果转换为 signed不会丢失信息,就转换为 signed, 否则转换为 unsigned。 一个L的NUL用于结束一个ASCALL字符串。ASCALL字符中零的位模式被称为NUL。 两个L的 NULL用于表示什么也不指向,(空指针) 第二章:这不是 BUG,而是语言特性 2.1 多做之过: 这些特性包括容易出错的 switch语句,相邻字符串常量的自动连接和缺省全局范围。 C语言中,几乎从来不进行运行时错误检查---对进行解除引用操作的指针有效性检查大概是 唯一的例外。无效的指针可能成为程序员的噩梦,人们很容易用一个无效的指针来引用内存。 在所有的虚拟内存体系结构里,一旦一个指针进行解除引用操作时,所引用的内存超出内存 地址的虚拟地址空间,操作系统就会结束这个进程。但是MS—DOS并不支持虚拟内存, 即使内存访问失败,它也无法立即捕获这种情况。 C语言的理念,程序员应该知道自己正在干什么,而且保证自己的所做所为是正确的。 各个 case和 default的顺序是可以任意的,但习惯总是把 default放在最后。switch存在的一 些问 快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题 是,其中之一是它对 case 可能出现的值太过放纵了,例如可以在 switch的做好括号 后声明一些变量,从而进行一些局部存储的分配,在最初的编译器中,这是一个技巧--绝大 多数用于处理复杂复合语句的代码都可以被复用。switch语句缺省采用”fall through“,在 97% 的情况下都是错误的。break语句事实上跳出的是最近的那层循环语句或 switch语句。 字符串常量的自动合并意味着字符串数组在初始化时,如果不小心漏掉了一个逗号,编译器 不会发出错误信息,而是悄无声息的合并在一起。在最后一个字符串末尾的逗号并不是打错 字,而是从早期的 C语法中继承下的东西,不管存在是否有意义,ANSI C对它的解释是是C语言自动生成容易些。 太多的缺省可见性 定义C函数时,在缺省情况下名字是全局可见的,可以再名字前面加一个冗余的 extern关键 字,也可以不加,效果一样的。如果想限制这个函数的访问,就必须加个 static关键字。 2.2 误做之过: C语言中属于“误做之过”的特性,就是语言中有误导性质或是不适当的特性,这些特性有些 跟 C语言的简介有关,有些则更操作符的优先级有关。 C语言存在的一个问题就是它太简洁了,仅增加,修改或删除一个字符就会使程序成另外一 个仍然有效却全然不同的程序。更糟糕的是,许多符号是被”重载的“在不同的上下文环境有 不同的意思。当 sizeof的操作数是个类型名时,两边必须加上括号(这常常使人认为他是一 个函数),但操作数如果是一个变量则不必加括号。你让一个符号所表达的意思越多,编译 器就越难检测到这个符号在你的使用中所存在的异常情况。 2.3少做之过 属于少做之过的特性就是语言应该提供但未提供的特性。 C语言有最大一口策略,这种策略表示如果下个标记有超过一种的解释 方案 气瓶 现场处置方案 .pdf气瓶 现场处置方案 .doc见习基地管理方案.doc关于群访事件的化解方案建筑工地扬尘治理专项方案下载 ,编译器将选取 最长的字符序列方案。 第三章 声明 参数按照从右到左的次序压倒堆栈中,这种说法过于简单了,参数在传递时首先尽可能地存 放到寄存器中(追求速度)。 一个 int型变量跟只包含一个 Int型成员的结构变量 S在参数传递时可能完全不同,一个 int 型参数一般会被传递到寄存器中,而结构变量 s 在参数则很可能被传递到堆栈中。 结构体: 在结构中放置数组,如 struct s_tag{int a[100];};现在可以把数组当做第一等级的类型,用赋 值语句拷贝整个数组,一传值的方式传递到函数,或者把它作为函数的返回类型。在典型的 情况下并不需要频繁的对整个数组进行赋值操作。但是如果需要这样做,可以通过放入结构 中实现。 C语言声明的优先级 A 声明从它的名字开始读取,然后按照优先级一次读取。 B 优先级从高到低一次是: 1声明中被括号括起来的 2后缀符号()【】 3前缀符号 * 不要在一个 typedef中放入几个声明器,千万不要把 typedef嵌到声明的中间部分。 不要为了方便起见对结构使用 typedef,这样做唯一的好处是能使你不必书写 struct关键字, 但这个关键字可以向你提示一些信息,你不应该把它省掉。 typedef int x[10],#define x int[10]的区别 正确思考这个问题的方法是把 typedef看成是一种彻底的“封装”类型--在声明它之后不能再 往里面增加别的东西。它和宏的区别体现在两个方面。 首先,可以用其他类型说明符对宏类型进行扩展,但对 typedef所定义的类型名却不能这样 做。如下所示: #define peach int unsigned peach i;/没问题 typedef int peach unsigned peach i;//错误非法。 其次: 在连续几个变量的声明中,用 typedef定义的类型能够保证声明中所有的变量均为同一种类 型,而用#define定义的类型则无法保证。 extern 对象声明告诉编译器对戏那个的类型和名字,对象的内存分配在别处进行。由于并未 在声明中为数组分配内存,所以并不需要提供关于数组长度的信息。 并且 extern int * i; extern int i[]; 是不一样的。 第四章 数组和指针 出现在赋值符号左边的符号有时被称为左值(由于它位于“左手边”或“表示地点”),出现在 赋值符号右边的符号有时则被称为右值,编译器为每个变量分配一个地址(左值),这个地 址在编译时可知,而且该变量在运行时一直保存于这个地址,相反存储于该变量中的值只有 在运行时才可知。如果需要用到变量中存储的值,编译器发出指令从指定地址读入变量的值, 并将它存于寄存器。 char *p="abcdef"; char p[]="abcdef'; 前者编译器告知 p是一个纸箱字符的指针(相反数组的定义告诉编译器 p是一个字符序列) p[i]表示"从 p所指向的地址开始,前进一步,每步都是一个字符",既然把 p声明为指针, 那么不管 p原先是定义为指针还是数组,都会按照上面所示的三个步骤进行操作;。 前者间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果 指针有一个小标[1]就把指针的内容更加上一作为地址,从中去数据 后者 直接访问数据,a[1]指示简单地一 a+1为地址取数据。 第五章;链接器 收集模块准备执行的三个阶段的 规范 编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载 名称是连接-编辑 , 载入, 动态链接。 如果函数库的一份拷贝时可执行文件的物理组成部分,那么我们称之为静态链接;如果可执 行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的库函数,那么我们称之 为动态链接。静态链接的模块被连接编辑并载入运行,动态链接的模块被连接编辑后载入, 并在运行时进行连接以便运行。程序执行时,在 main()函数被调用前,运行时载入器把共享 的数据对象载然入到进程的地址空间。外部函数被真正调用之前,运行时载入器不解析它们, 所以即使链接了函数库,如果没有实际调用,也不会带来而外开销。即使是在静态链接中, 整个文件也没有被全部载如到可执行文件中,所装入的只是所需要的函数。动态链接时是一 种动态链接,这意味着程序在运行时必须能够找到它们所需要的库函数,连接器吧库文件名 或路径名植入可执行文件做到这一点,这意味着,库函数的路径不能够随意移动, 动态链接可以从两个方面提高性能: 一:动态链接可执行文件比功能相同的链接的体积小。 二:所有动态库连接到某个特定的函数库的可执行文件在运行时共享该函数库的一个单独拷 贝。操作系统内核保证映射到内存中的库函数可以被所有使用它的进程共享。这就提供了更 好的 I/O和交换空间利用率,节省了物理内存,提高了系统的整体性能。 静态库被称为 archive,它们通过 ar(用于 archive的实用工具)来创建和更新。 动态链接库由链接编辑器 ld创建根据约定,动态库的文件扩展名为“.so”,表示“shared object 共享对象”。 涉及到 UNIX的部分没有看*********************** 第六章:运行时数据结构 a.out--汇编程序和链接编辑输出格式 它不是胡编程序输出而是连接器输出。 编译器 设计 领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计 者通过不存储为使用的信息来提高速度,其他的优化措施包括把信息保存在寄存 器而不是堆栈中,尽管我们谈到了将过程活动记录压到堆栈中,但是过程活动记录并不一定 要存在于堆栈中。事实上,尽可能地把过程活动记录的内容更放到寄存器中会使函数调用的 速度更快,效果更好。 第七章 对内存的思考 就像堆栈能够根据需要自动增长一样,数据段也包含了一个对象,用于完成这项工作,这就 是堆,它用于动态分配的存储。用 malloc函数,callvoc 函数与 malloc函数类似,但它在返 回指针之前先把分配好的内存的内容清零,realloc函数把内存拷贝到别的地方然后将指向新 地址的指针返回给你。这在动态增长的表很有用。alloca()分配在栈上的动态内存,它分配的 内存会自动释放。这并不适合那些比创建它们的函数生命周期更长的结构。 数据对齐的意思是数据项只能存储在地址是数据项大小的整数倍的内存位置上。 编译器通过自动分配和填充数据(在内存中)来对齐。当然在磁盘或磁带上并没有这样的对 齐要求,所以程序员对它们可以很愉快地不必关心数据对齐,但是当他们把一个 char指针 转换为 Int指针时,就会出现神秘的总线错误。 段错误或段违规是由于内存管理单元(负责支持虚拟内存的硬件)的异常所致,该异常则通 常是由于解除引用一个未初始化或非法值的指针引起的。如果指针引用一个并不位于你的地 址空间的地址,操作系统便会进行干涉。如 int * p=0; *p=17; 一个微妙之处是,导致指针具有非法的值通常不是由于编程错误引起的,和总线错误不同, 段错误更像是一个间接的症状而不是错误的原因。一个更糟糕的微妙之处是,如果未初始化 的指针恰好具有未对其的值(对于指针所要访问的数据而言),它将会产生总线错误,而不 是段错误。 通常导致段错误的几个直接原因: 解除引用一个包含非法值的指针。 解除引用一个空指针 在未得到正确的权限时进行访问。 用完了堆栈或堆空间 下面的说法过于简单,但在绝大多数框架的绝大多数情况下,总线错误意味着 Cpu对进程 引用内存的一些做法不满,而段错误则是MMU对进程引用内存的一席情况发出抱怨。 第八章 为什么程序员无法分清楚万圣节和圣诞节 ANSI C函数原型的目的是使 C语言成为一种更加可靠的语言。建立原型就是为了消除一种 普通(但很难发现)的错误,就是形参和实参类型不匹配。ANSI C的函数原型就是才用一 种新的函数声明形式,把参数的类型也包含在声明之中。函数的定义也作为相应的改变以匹 配声明。在 ANSI C中如果使用了新风格的函数定义,编译器就不会假定参数是准确声明的, 于是便不进行类型提升,并据此产生代码。不要在函数的声明和定义中混用新旧两种风格。 复杂的类型转换可以按下面的 3个步骤编写 (1)一个对象的声明,它的类型就是想要转换的结果类型 (2)删除标示符(以及任何如 extern之类的存储限定符),并把剩余的内容放在一对括号里。 (3)把第二步产生的内容放在需要进行类型转换的对象的左边。 第九章;再论数组 对编译器而言,一个数组就是一个地址,一个指针就是一个地址的地址。 什么时候数组和指针时相同的 规则一: 表达式中的数组名(与声明不同)被编译为一个指向该数组的第一个元素的指针 规则二: 下标总是与指针的偏移量相同 规则三: 在函数参数的声明中,数组名被编译成当做指向该数组的第一个元素的指针。 规则一个规则二合起来理解,就是对数组下标的引用总是可以写成“一个指向数组的起始地 址的指针加偏移量”。你只要记住在表达式中,指针和数组时可以互换的,因为它们在编译 器里最终形式都是指针,并且都可以进行取下标操作,就像加法一样,取下标操作的操作数 是可以交换的这就是为什么 a[6]=6[a]都是正确的。 在处理一维数组时,指针并不见得比数组块,C语言吧数组下标改写为指针偏移量的根本原 因是指针和偏移量是底层硬件所使用的基本模型。 之所以要把传递给函数的数组参数转换为指针时出于效率的考虑,要拷贝整个数组的效率时 太低。 不管程序员实际缩写的是哪种形式,函数并自动知道指针所指向的数组的范围,所以要有个 约定,如数组一 NUL结尾或另一个附加参数表示结束。注意有一样操作只能在指针里进行 二无法再数组中进行,那就是修改它的值。数组名是不可以修改的左值,它的值不能改变的。 多维数组: 定义和引用多维数组唯一的方法就是使用数组的数组。尽管 C语言把数组的数组当作多维 数组,在思维模式中,吧数组当做是一种向量(即某种对象的一维数组,它的元素可以使另 一个数组),就能极大的简化编程语言中这个复杂的领域。 在 C语言的多维数组,最右边的下标是最先变换的,这个约定被称为“行主序”。C语言中, 多维数组的最大用途就是存储多个字符串。 第十章 再论数组 数组名被改写成一个指针参数规则并不是递归定义的。数组的数组会被改写为“数组的指针” 而不是指针的指针。 实参 所匹配的形式参数 数组的数组 char c[8][10]; char(*)[10] 数组的指针 指针数组 char* c[15] char **c 指针的指针 数组指针 char(*c)[64] char(*)[64] 不改变 指针的指针 char **C char **C 不改变 二维数组要复杂一些,数组被改写为指向数组第一行的指针。现在需要两个约定,其中一个 用于提示每行的结束,另一个用于提示所有行的结束。提示单行的结束可以使用一维数组的 两种方法,增加一个额外的参数表示大小,赋予数组最后一个元素一个特殊的值,这个特殊 的值不会作为数组元素出现。提示所有行结束也可以这样。 使用指针向函数传递一个多维数组 C语言没有办法表达“这个数组的边界在不同的调用中可以变化”,我们能够采用的最后的方 法是把 array[x][y]这样的形式改写为一个一维数组 array[x+1],它的元素类型是指向 array[y] 的指针。这样就改变了问题的性质,而改变后的问题是我们已经解决了的。在数组最后的那 个元素 array[x+1]里存储一个 null指针,提示数组的结束。 二维或更多维的数组无法再 C语言中用作一般形式的参数。你无法向函数传递一个普通的 多维数组。可以系那个函数传递预先确定长度的特殊数组。 方法一 f(int array[10][20]) 尽管这是最简单的方法,但同时也是作用最小的,因为它迫使函数只处理 10行 20列的 int 型数组,注意多维数组最主要的一维的长度不必显示写明,所有函数都必须知道数组其他维 的确切长度和数组的基地址。 方法二 f(int array[][20]) 方法三 我们可以采用的这种方法是放弃二维数组,把它的结构改为以个 Hiffe向量。也就 是说,创建一个一维数组,数组中的元素是指向其他东西的指针。注意:::只有把二维数组 改为以指向向量的指针数组的前提下才可以这样做。Hiffe向量的这种数据结构美感在于, 它允许任意的字符串指针数组传递给函数,但必须是指针数组,而且必须是指向字符串的指 针数组。 不要在一行代码里实现太多的功能。
本文档为【C专家编程(笔记)】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_454086
暂无简介~
格式:pdf
大小:119KB
软件:PDF阅读器
页数:6
分类:互联网
上传时间:2011-05-19
浏览量:10