C程序设计语言 2版(Brian W.Kernighan & Dennis M.Ritchie 阅读笔记
2011-12-05  by  dadoneo  (blog.csdn/dadoneo)
第1章 导言
    一般来说,main()函数返回值为0表示正常终止,非0表示出现异常情况或出现结束条件。Main本身也是函数,其调用者实际上就是程序的执行环境。程序要身其妊环境返回状态。
   
    外部变量必须定义在所有函数之外,且只能定义一次,定义后编译程序将为它分配存储单元。
    (1)在一个源文件中,如果外部变量的定义出现在使用它的函数之前,那么那个函数中就没有必要使用extern声明;
    (2)在多个源文件中,变量在file1中定义,在file2与file3中使用,那么在文件file2与file3中就需要使用extern声明该变量与其定义之间的关系。
    通常把变量和函数的extern声明放在一个单独的文件中(习惯上称之为头文件),并在每个源文件的开头使用#include把所要用的头文件包含进来。
    “定义”表示创建变量或分配存储单元,而“声明”指的是说明变量的性质,但不分配存储单元。
   
第2章 类型、运算符与表达式
    常量表达式是仅仅只包含常量的表达式。这种表达式在编译时求值,而不在运行时求值;它可以出现在常量可以出现的任何位置。
    默认情况下,外部变量与静态变量将被初始化为0,未经显式初始化的自动变量的值为未定义值(无效值);
    逻辑非运算符(!)的作用是将非0操作数转换为0,将操作数0转换为1;
    在if, while, for等语句的测试部分中,“真”就意味着“非0”。如-1,也表示为真。
    表达式中float类型的操作数不会自动转换为double类型。
    使用float类型主要是为了在使用较大的数组时节省存储空间,有时也为了节省机器执行时间(双精度算术运算特别费时)。
当表达式中包含unsigned 类型的操作数时,转换规则要复杂一些。主要原因在于,带符号值与无符号值之间的比较运算是与机器相关的,因为它们取决于机器中不同整数类型的大小。例如,假定int类型占16位,long类型占32位,那么,-1L<1U,这是因为unsigned int 类型的1U被提升为signed long类型;但-1L>1UL,这是因为-1L将被提升为unsigned long类型从而变成一个比较大的数。
    自增与自减运算符只能作用于变量,类似于(i+j)++是非法的。
    在不需要使用任何具体值且仅需要递增变量的情况下,前缀方式和后缀方式的效果相同。
    C语言提供了6个位操作运算符。这些运算符只能作用于整形操作数,即只能作用于带符号或无符号的char, short, int, long类型:
    &  |  ^(异或)  <<  >>  ~(按位求反)
第4章 函数与程序结构
如果函数定义中省略了返回值的类型,则默认为int类型;
外部变量或函数的作用域是从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。如果要在外部变量的定义之前使用该变量,或者外部变量的定义与变量的使用不在同一个源文件中,则必须在相应的变量声明中强制性地使用关键字extern。
将外部变量的声明与定义严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此以外还将引起存储器的分配。
外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度。外部变量的初始化只能出现在其定义中。
用static声明限定外部变量与函数,可以将其后声明的作用域限定为被编译源文件的剩余部分。通过static限定外部对象,可以达到隐藏外部对象的目的。
Static类型的内部变量是一种只能在某个特定函数中使用但一直占据存储空间的变量。
Register声明只适用于自动变量以及函数的形式参数。它告诉编译器,它所声明的变量在程序中使用频率较高。其思想是,将register变量放在机器的寄存器中,这样可以使程序更小,执行速度更快。但编译器可以忽略此选项。
每次进入程序块时,在程序块内声明以及初始化的自动变量都将被初始化。静态变量只在第一次进入程序块时被初始化一次。
在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义(即初值为无用的信息)。
对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(从概念上讲是在程序开始执行前进行初始化)。对于自动变量与寄存器变量,则在每次进入函数或程序块时都将被初始化。
对于自动变量与寄存器变量来说,初始化表达式可以不是常量表达式:表达式中可以包含任意在此表达式之前已经定义的值,包括函数调用。
当省略数组的长度时,编译器将把花括号中初始化表达式的个数作为数组的长度。
字符数组的初始化char pattern[] = “ould”; 与 char pattern[] = {‘o’, ’u’, ’l’, ’d’, ‘’\0};是等价的;此数组的长度是5;
两个最常用的预处理器指令是:#include指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记)。
如果对各种类型的参数的处理是一致的,则可以将同一个宏定义应用于任何数据类型,而无需针对不同的数据类型来定义不同的函数;
但需要注意的一点是,在宏定义中,作为参数的表达式要重复计算两次,如果表达式存在副作用(比如含有自增运算符或输入/输出),则会出现不正确的情况,如:
#define Max(a, b) ((a)>(b) ? (a) : (b)),在这里,如果用max(i++, j++)它将对每个参数执行两次自增操作。同时还必须注意,要适当使用圆括号以保证计算次序的正确性。
可以用#undef 指令取消名字的宏定义,这样做可以保证后续的调用是函数调用,而不是宏调用。
形式参数不能用带绰号的字符串替换。但是,如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。P77
条件包含预处理:#if, #endif, #elif, #else,    #ifndef, #ifdef
第5章 指针与数组
    地址运算符只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式、常量或register类型的变量。
    通过数组下标所能完成的任何操作都可以用指针来实现(一个通过数组和下标实现的表达式可等价地通过指针和偏移量来实现)。一般来说,用指针编写的程序比用数组下标编写的程序执行速度要快,但也难于理解一些。
    在计算a[i]的值时,C语言实际上先将其转换为*(a+i)的形式,然后再进行求值,因此在程序中这两种形式是等价的。
    但必须注意的是,指针是一个变量,所以如pa=a或pa++都是合法的。但数组名不是变量,因此,类似于pa=a和a++形式的语句是非法的。
    C语言保证,0永远不是有效的数据地址,因此,返回值0可用来表示发生了异常事件。
   
    因为指针数组的一个重要优点在于,数组的每一行长度可以不同;因此,它最频繁的用处是存放具有不同长度的字符串。
    调用主函数main时,它带有两个参数。第一个参数argc用于参数计数,表示运行程序时命令行中参数的数目;第二个参数argv用于参数向量,表示一个指向字符串数组的指针,其中每个字符串对应一个参数。
    按照C语言的约定,argv[0]的值是启动该程序的程序名。因此argc的值至少是1。
    任何类型的指针都可以转换为void * 类型,并且在将它转换回原来的类型时不会丢失信息。
    函数指针有两个用途:调用函数和作函数的参数baike.baidu/view/1604730.htm
函数指针用法示例:
Int max(int x, int y){
    Return x>y?x:y;
}
Int (*p)(int, int) = &max;
Int maxnum = (*p)((*p)(a,b),c);    //a,b,c中的最大值,并赋给maxnum
传递动态内存:
    如果一定要用指针参数去申请内存,那么应该采用指向指针的指针。《宝典P67》;
    也可以通过函数返回值来传递动态内存;
Int (*(*Fun)(int, int))(int)
Fun是一个函数指针,指向的函数的类型是有两个int类型并且返回一个函数指针的函数,返回的函数指针指向有一个int参数且返回int的函数。
Int a[] = {1,2,3,4,5};
Int *ptr = (int*)(&a + 1);
Printf(“&d %d”, *(a+1), *(ptr-1));    //输出2 5
数组名本身就是指针,再加个&,就变成了双指针,这里的双指针指的是二维数组,加1,就是数组整体加一行,prt指向a的第6个元素(虽然这个元素不存在)。(宝典P79
    delete一个指针的时候,实际上仅是让编译器释放内存,但指针本身依然存在。这时它就是一个迷途指针。使用以下语句可将其变为空指针:myPtr = 0;使用迷途指针或空指针是非法的,而且有可能造成程序崩溃。如果指针是空指针,尽管同样是崩溃,但它同迷途指针造成的崩溃相比是一种可预料的崩溃,这样高度起来会方便得多。(宝典P81
    New/delete不是库函数,而不运算符。它能完成动态内存分配和初始化工作及清理与释放内存工作。弥补了malloc/free不能执行构造函数与析构函数的缺陷。
第6章 结构
    在所有运算符中,下面4个运算符的优先级最高:结构运算符”.””->”,用于函数调用的”()”以及用于下标的”[]”,因此,它们同操作数之间的结合也最紧密。
    条件编译语句#if中不能使用sizeof,因为预处理器不对类型名进行分析。但预处理器并不计算#define 语句中的表达式,因此,在#definec程序设计语言第几版最好中使用sizeof是合法的。
   
第7章 输入与输出
    格式化输出函数printf,将内部数值转换为字符的形式输出。
   

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。