C Primer Plus(第5版)的笔记

2.2.2 第二遍 程序细节

2014-03-08 18:00:31

int指明了main()函数的返回类型。这意味着main()函数返回值的类型是整数。返回到哪里呢?返回给操作系统。

2.4 使程序可读的技巧

2014-03-08 17:46:11

选择有意义的变量名和使用注释。注意这两种技巧的互补性。如果变量名是width,就不需要用注释来说明该变量表示宽度,但是如果变量名是video_routine_4,那么就需要解释一下video routine 4的意义了。

注: 增加可读性

2.7.1 语法错误

2014-03-08 17:48:53

下面的句子:Bugs frustrate be can。句子中的英语单词都是正确的,但是没有按照正确的顺序组织句子,而且单词用得也不是非常准确。

注: 语法错误

2014-03-08 17:48:18

语义错误就是在意思上的错误。例如,考虑下面的句子:Furry inflation thinks greenly。句子中形容词、名词、动词和副词的位置都很正确,所以语法没有错,但是句子却什么意思也没表达出来。

3.4.1 int类型

2014-03-08 17:44:23

要用八进制而不是十进制显示整数,请用%0代替%d。要显示十六进制整数,请使用%x。

2014-03-08 17:43:23

C提供3个附属关键字修饰基本的整数类型:short、long和unsigned。应当记住以下几点:

● short int类型(或者简写为short类型)可能占用比int类型更少的存储空间,用于仅需小数值的场合以节省空间。同int类型一样,short类型是一种有符号类型。

● long int类型(或者简写为long类型)可能占用比int类型更多的存储空间,用于使用大数值的场合。同int类型一样,long类型是一种有符号类型。

● long long int类型(或者简写为long long类型(都是在C99标准中引入的),可能占用比long类型更多的存储空间,用于使用更大数值的场合。同int类型一样,long long类型是一种有符号类型。

● unsigned int类型(或者简写为unsigned类型)用于只使用非负值的场合。这种类型同有符号类型的表示范围不同。例如,16位的unsigned int取值范围为0到65535,而带符号int的取值范围为-32768到32767。由于指示数值正负的位也被用于二进制位,所以无符号数可以表示更大的数值。

3.4.4 _Bool类型

2014-03-08 17:42:40

因为C用值1表示true,用值0表示false,所以_Bool类型实际上也是一种整数类型。

3.5 使用数据类型

2014-03-08 17:58:54

很多程序员和组织都有系统化的变量命名规则,其中变量的名字可以表示它的类型。例如:使用i_前缀表示int变量,使用us_表示unsigned short变量。这样通过名字就可以确定变量i_smart为int类型,变量us_verysmart为unsigned short类型。

4.2.3 strlen()函数

2014-03-08 22:21:27

scanf()开始读取输入以后,会在遇到的第一个空白字符空格(blank)、制表符(tab)或者换行符(newline)处停止读取。

2014-03-08 22:26:24

根据sizeof运算符的报告,数组name有40个内存单元。不过只用了其中前6个单元来存放Morgan,这是strlen()所报告的。

2014-03-08 22:26:07

是否使用圆括号取决于您是想获取一个类型的大小还是想获取某个具体量的大小。圆括号对于类型是必需的,而对于具体量则是可选的。

4.3 常量和C预处理器

2014-03-08 22:28:23

#define TAXRATE 0.015 当编译您的程序时,值0.015将会在TAXRATE出现的每个地方替代它。这称为编译时代入法

2014-03-08 22:29:04

为什么TAXRATE要大写呢?键入大写的常量是一个明智的C传统。这样,当您在程序中间遇到大写的符号名时,您会立即知道这是一个常量而非变量。大写常量只不过是使程序更易阅读的技术之一。如果您没有大写常量,程序也会照常工作,但是应该培养大写常量的好习惯。

2014-03-08 22:30:04

请记住,符号名后的所有内容都被用来代替它。不要犯这样的常见错误: /* 下面的定义是错误的 */ #define TOES = 20 如果您这样做,TOES将会被=20而不是20所代替。如果这样,那么下面的语句: digits = fingers + TOES; 经转换后会变成下面所示的错误表示方法: digits = fingers + =20;

4.4.2 使用printf()

2014-03-09 10:28:17

因为printf()函数使用%符号来标识转换说明,所以打印%符号本身就成了一个小问题。如果您单独使用一个%符号,那么编译器就会认为您丢掉了一个转换说明符号。解决办法很简单,就是使用两个%符号。

4.4.5 使用scanf( )

2014-03-09 11:19:49

printf( )函数也有一个返回值,它返回所打印的字符的数目。

2014-03-09 11:25:20

● 如果使用scanf( )来读取前面讨论过的某种基本变量类型的值,请在变量名之前加上一个&。

● 如果使用scanf( )把一个字符串读进一个字符数组中,请不要使用&。

2014-03-11 21:43:00

scanf( )函数返回成功读入的项目的个数

5.2.6 除法运算符:/

2014-03-10 19:24:30

实际上,赋值运算符左边必须指向一个存储位置。

2014-03-10 19:24:45

“指针”也可以用于指向一个存储位置。更普遍地,C使用术语“可修改的左值”(modifiable lvalue)来标志那些我们可以为之赋值的实体。“可修改的左值”或许不是那么直观易懂,

2014-03-10 19:26:57

许多程序语言将在本程序里的三重赋值处卡壳,但是C可以顺利接受它。赋值是从右到左进行的。

2014-03-10 19:34:05

浮点类型的除法运算得出一个浮点数结果,而整数除法运算则产生一个整数结果。整数不能有小数部分,这使得用3去除5很让人头痛,因为结果有小数部分

5.5 类型转换

2014-03-11 09:36:30

1.当出现在表达式里时,有符号和无符号的char和short类型都将自动被转换为int,在需要的情况下,将自动被转换为unsigned int

2014-03-11 09:36:55

在包含两种数据类型的任何运算里,两个值都被转换成两种类型里较高的级别。

2014-03-11 09:37:06

3.类型级别从高到低的顺序是long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int和int。

2014-03-11 09:38:46

.在赋值语句里,计算的最后结果被转换成将要被赋予值的那个变量的类型。

2014-03-11 09:40:27

5.当作为函数的参数被传递时,char和short会被转换为int,float会被转换为double。

2014-03-11 09:44:04

您也有可能需要准确的类型转换,或者需要在程序中表明您是知道您正在做类型转换的。完成这一任务的方法被称为指派(cast),其步骤是在某个量的前面放置用圆括号括起来的被希望转换成的类型名

6.3.5 关系运算符的优先级

2014-03-12 09:25:32

关系运算符也可以用于浮点数。但要小心,在浮点数比较中只能使用<和>。原因在于舍入误差可能造成两个逻辑上应该相等的数不相等。例如,3和1/3的乘积应该是1.0。但是如果您用6位小数来表示1/3,乘积就是.999999而不等于1。

2014-03-12 10:27:59

更一般地,所有的非零值都被认为是真,只有0被认为是假。

2014-03-12 10:28:07

可以说只要while循环的判断条件的值非零,它就可以执行循环。这使得判断条件是建立在数值的基础上而不是在真/假的基础上。

2014-03-12 10:38:37

如果进行比较的双方中有一个是常量,则可以把它放在比较表达式的左边,这样做有助于发现错误:     5 = canoes 语法错误 5 == canoes 检查canoes的值是否为5

2014-03-12 10:43:34

把==表达式括起来的圆括号不是必需的,因为==运算符的优先级比=要高,但是它们可以使代码更容易阅读。同时也要注意变量名称的选择使while循环判断更容易理解了:

2014-03-12 10:44:57

关系运算符的优先级要低于包括+和-在内的算术运算符,但是要高于赋值运算符。

6.6 更多赋值运算符:+=、-=、*=、/=和%=

2014-03-12 11:12:08

scores += 20 等于 scores = scores + 20 dimes -= 2 等于 dimes = dimes - 2 bunnies *= 2 等于 bunnies = bunnies * 2 time /= 2.73 等于 time = time / 2.73 reduce %= 3 等于 reduce = reduce % 3

6.7 逗号运算符

2014-03-12 11:15:25

x =(y = 3,(z = ++y + 2) + 5); 的效果是首先把y赋值为3,把y递增为4,然后把4加上2,把结果6赋值给z,接下来把z加5,最后把x赋为结果值11。

2014-03-12 11:16:03

houseprice = 249, 500; 这并没有语法错误。C把它解释为一个逗号表达式,houseprice = 249是左子表达式,而500是右子表达式。因此整个逗号表达式的值就是右边表达式的值,并且左边的子语句把变量houseprice赋值为249。这样它的效果与下面的代码相同: houseprice = 249; 500;

2014-03-12 11:16:11

语句: houseprice = (249, 500); 把houseprice赋值为500,因为该值是右子表达式的值。

6.8 退出条件循环:do while

2014-03-12 11:20:32

do while循环至少要被执行一次,因为在循环体被执行之后才进行判断。与之相反,for或者while循环可以一次都不执行,因为它们是在执行之前进行判断。

6.8 退出条件循环:do while

2014-03-12 11:20:58

do while语句创建了一个在判断表达式为假(或零)之前重复执行的循环。do while语句是一个退出条件循环,是否再次执行循环的决定是在执行了一次循环之后做出的。因此循环必须至少被执行一次。该形式的statement部分可以是一个简单语句或一个复合语句。

6.11 数组

2014-03-12 11:30:39

声明debts是一个具有20个元素的数组,其中的每个元素都是一个类型为float的值。这个数组的第一个元素称为debts[0],第二个元素称为debts[1],这样直到debts[19]。

注: float debt(20)

2014-03-12 11:33:52

使用#define指令创建一个指定数组大小的明显常量(SIZE)是一个好主意,您可以在定义数组和设置循环限制时使用这个常量。如果您以后需要把程序扩展为处理20个分数,简单地把SIZE重新定义为20就可以了,不需要改变程序中使用了数组大小的每个地方。

注: score(size)

6.12.2 使用具有返回值的函数

2014-03-12 13:21:02

编译器在程序中第一次遇到power()时,它需要知道power()是什么类型。而此时编译器还没有遇到power()的定义,所以它并不知道定义中说明了返回类型为double。为了帮助编译器,要通过使用一个前向声明(forward declaration)来预先说明它是什么类型。

2014-03-12 13:20:47

为什么无须声明scanf()?这是因为您已经声明过了。stdio.h头文件中含有scanf()、printf()以及其他一些I/O函数的函数声明。scanf()的声明说明它的返回类型为int。

7.2.4 把else与if配对

2014-03-12 22:38:10

如果没有花括号指明,else与和它最接近的一个if相匹配

7.6.1 continue语句

2014-03-13 21:06:43

该语句可以用于三种循环形式。当运行到该语句时,它将导致剩余的迭代部分被忽略,开始下一次迭代。如果continue语句处于嵌套结构中,那么它仅仅影响包含它的最里层的结构。

7.7.4 switch和if else

2014-03-13 21:18:52

如果选择是基于求一个浮点型变量或表达式的值,就不能使用switch。如果变量必须落入某个范围,也不能很方便地使用switch。

8.1 单字符I/O:getchar()和putchar()

2014-03-27 22:49:18

getchar()和putchar()每次输入和输出一个字符。

8.2 缓冲区

2014-03-14 09:59:44

为什么需要缓冲区?首先,将若干个字符作为一个块传输比逐个发送这些字符耗费的时间少。其次,如果您输入有误,就可以使用您的键盘更正功能来修正错误。当最终按下回车键时,您就可以发送正确的输入。

9.1.2 程序分析

2014-03-15 20:11:42

为什么使用函数?第一,函数的使用可以省去重复代码的编写。

2014-03-15 20:11:55

第二,即使某种功能在程序中只使用一次,将其以函数的形式实现也是有必要的,因为函数使得程序更加模块化,从而有利于程序的阅读、修改和完善。

2014-03-15 20:18:32

函数同变量一样有多种类型。任何程序在使用函数之前都需要声明该函数的类型。

2014-03-15 20:19:46

圆括号表明starbar是一个函数名。第一个void指的是函数类型;它的意思是该函数没有返回值。第二个void(位于圆括号内)表明该函数不接受任何参数。分号的作用是表示该语句是进行函数声明而不是函数定义。

注: void starbar(void);

2014-03-15 20:21:04

如果您把函数写在了另外一个单独的文件中,则在那个文件中必须加入#define 和#include指令。

9.1.9 函数类型

2014-03-15 20:26:01

实际参数是函数调用时出现在圓括号中的表达式。而形式参量则是函数定义中在函数头部声明的变量。

2014-03-15 20:27:37

黑盒子方法的核心部分在于ch、num和count都是show_n_char()私有的局部变量。也就是说,如果在main()中使用相同名字的变量,它们相互独立,互不影响。例如,如果main()中存在一个count变量,那么该变量值的改变不会影响show_n_char()中的count变量,其余变量也是如此。黑盒子内的一切操作对调用函数来说是不可见的。

2014-03-15 20:32:04

这个语句会终止执行函数并把控制返回给调用函数。因为return后没有任何表达式,所以没有返回值,这种形式只能用于void类型的函数之中。

注: return;

2014-03-15 20:33:36

不要把函数声明和函数定义混淆。函数声明只是将函数类型告诉编译器,而函数定义部分则是函数的实际实现代码。

9.2.1 产生的问题

2014-03-16 10:10:39

使用PC或VAX时,程序执行过程是这样的:调用函数首先把参数放在一个称为堆栈(stack)的临时存储区域里,然后被调函数从堆栈中读取这些参数。但是这两个过程并没有相互协调进行。调用函数根据调用过程中的实际参数类型确定需要传递的数值类型,但是被调函数是根据其形式参数的类型进行数据读取的。

9.3.3 尾递归

2014-03-16 10:21:37

当一个函数调用自己时,如果编程中没有设定可以终止递归的条件检测,它会无限制地进行递归调用,所以需要进行谨慎处理。

2014-03-16 10:47:22

下面将讲述几个基本要点以便于理解该过程。 第一,每一级的函数调用都有自己的变量。也就是说,第1级调用中的n不同于第2级调用中的n,因此程序创建了4个独立的变量,虽然每个变量的名字都是n,但是它们分别具有不同的值。

2014-03-16 10:47:38

第二,每一次函数调用都会有一次返回。当程序流执行到某一级递归的结尾处时,它会转移到前第1级递归继续执行。程序不能直接返回到main()中的初始调用部分,而是通过递归的每一级逐步返回,

2014-03-16 10:49:35

第三,递归函数中,位于递归调用前的语句和各级被 调函数具有相同的执行顺序

2014-03-16 10:49:45

第四,递归函数中,位于递归调用后的语句的执行顺序和各个被调函数的顺序相反。

2014-03-16 10:50:02

第五,虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。

2014-03-16 10:50:10

最后,递归函数中必须包含可以终止递归调用的语句。

2014-03-16 11:01:23

既然循环和递归都可以用来实现函数,那么究竟选择哪一个呢?一般来讲,选择循环更好一些。首先,因为每次递归调用都拥有自己的变量集合,所以就需要占用较多的内存;每次递归调用需要把新的变量集合存储在堆栈中。其次,由于进行每次函数调用需要花费一定的时间,所以递归的执行速度较慢。

9.3.5 递归的优缺点

2014-03-16 11:37:15

一个程序中的每个C函数和其他函数之间是平等关系。

2014-03-16 11:37:20

min()函数是否与其他的函数不同?是的,函数main()是一个有点特殊的函数。因为在程序中当几个函数放在一起时,计算机将从main()中的第一个语句开始执行,但这也是其局限之处。同时main()也可以被其本身递归调用或被其他函数调用——尽管很少这么做。

9.7.2 指针声明

2014-03-16 12:19:02

为什么不能这样声明?因为这对于声明一个变量为指针是不够的,还需要说明指针所指向变量的类型。原因是不同的变量类型占用的存储空间大小不同,而有些指针操作需要知道变量类型所占用的存储空间。同时,程序也需要了解地址中存储的是何种数据。

10.1.1 初始化

2014-03-16 18:43:02

当数值数目少于数组元素数目时,多余的数组元素被初始化为0。也就是说,如果不初始化数组,数组元素和未初始化的普通变量一样,其中存储的是无用的数值;但是如果部分初始化数组,未初始化的元素则被设置为0。

2014-03-16 18:45:13

如果初始化列表中项目的个数大于数组大小,编译器会毫不留情地认为这是一个错误。然而,可以采用另外一种形式以避免受到编译器的此类奚落:您可以省略括号中的数字,从而让编译器自动匹配数组大小和初始化列表中的项目数目

注: 举例:int a[]={1,4,7,3}

2014-03-16 18:45:57

注意for循环的控制语句。由于人工计算容易出错,因此可以让计算机来计算数组的大小。运算符sizeof给出其后的对象或类型的大小(以字节为单位)。因此sizeof days是整个数组的大小(以字节为单位),sizeof days[0]是一个元素的大小(以字节为单位)。整个数组的大小除以单个元素的大小就是数组中元素的数目。

10.3 指针和数组

2014-03-17 18:47:45

short类型使用2个字节,double类型使用8个字节。在C中,对一个指针加1的结果是对该指针增加1个存储单元

10.5 指针操作

2014-03-18 16:10:32

下面的列表描述了可对指针变量执行的基本操作:

2014-03-18 16:10:47

● 赋值(assignment)——可以把一个地址赋给指针。通常使用数组名或地址运算符&来进行地址赋值。

2014-03-18 16:11:27

● 求值(value-finding)或取值(dereferencing)——运算符*可取出指针指向地址中存储的数值。

2014-03-18 16:11:37

● 取指针地址——指针变量同其他变量一样具有地址和数值,使用运算符&可以得到存储指针本身的地址。

2014-03-18 16:11:44

● 将一个整数加给指针——可以使用+运算符来把一个整数加给一个指针,或者把一个指针加给一个正数。两种情况下,这个整数都会和指针所指类型的字节数相乘,然后所得的结果会加到初始地址上。

2014-03-18 16:11:52

于是,ptr+4的结果等同于&urn[4]。

2014-03-18 16:13:54

● 增加指针的值——可以通过一般的加法或增量运算符来增加一个指针的值。

2014-03-18 16:14:03

● 从指针中减去一个整数——可以使用–运算符来从一个指针中减去一个整数。指针必须是第一个操作数,或者是一个指向整数的指针。

2014-03-18 16:14:17

● 减小指针的值——指针当然也可以做减量运算。

2014-03-18 16:15:36

● 求差值(Differencing)

2014-03-18 16:14:38

ptr2-ptrl的值是2,表示指针所指向对象之间的距离为2个int数值大小,而不是2个字节。

2014-03-18 16:15:45

● 比较——可以使用关系运算符来比较两个指针的值,前提是两个指针具有相同的类型。

2014-03-18 16:18:38

当创建一个指针时,系统只分配了用来存储指针本身的内存空间,并不分配用来存储数据的内存空间。因此在使用指针之前,必须给它赋予一个已分配的内存地址。

2014-03-18 16:19:34

使用指针时一定要注意,不能对未初始化的指针取值!

10.6.1 对形式参量使用const

2014-03-18 16:26:18

这告知编译器:函数应当把ar所指向的数组作为包含常量数据的数组对待。这样,如果您意外地使用了诸如ar[i]++之类的表达式,编译器将会发现这个错误并生成一条错误消息,通知您函数试图修改常量。

注: const

2014-03-18 16:26:42

需要理解的是这样使用const并不要求原始数组是固定不变的;这只是说明函数在处理数组时,应把数组当作是固定不变的。

10.7.2 指针兼容性

2014-03-18 17:13:38

因为zippo[0]是其首元素zippo[0][0]的地址,所以*(zippo[0])代表存储在zippo[0][0]中的数值,即一个int数值。

2014-03-18 17:17:38

二维数组名必须两次取值才可以取出数组中存储的数据。这可以两次使用间接运算符(*)来实现,或两次使用方括号运算符([])(也可以采用一次*和一次[]来实现,但我们不讨论这么多的情况)。具体地:zippo[2][1]的等价指针符号表示为*(*(zippo+2)+1)。

2014-03-18 18:28:42

把const指针赋给非const指针是错误的,因为您可能会使用新指针来改变const数据。但是把非const指针赋给const指针是允许的,这样的赋值有一个前提:只进行一层间接运算

10.8 变长数组(VLA)

2014-03-20 13:59:05

处理二维数组的函数有一处可能不太容易理解:数组的行可以在函数调用时传递,但是数组的列却只能被预置在函数内部。

11.1.1 在程序中定义字符串

2014-03-25 20:29:03

字符串常量(string constant),又称字符串文字(string literal),是指位于一对双引号中的任何字符。双引号里的字符加上编译器自动提供的结束标志\0字符,作为一个字符串被存储在内存里

2014-03-25 20:30:43

如果想在字符串中使用双引号,可以在双引号前加一个反斜线符号,

2014-03-25 20:31:45

字符串常量属于静态存储(static storage)类。静态存储是指如果在一个函数中使用字符串常量,即使是多次调用了这个函数,该字符串在程序的整个运行过程中只存储一份。

2014-03-25 20:34:53

指定数组大小时,一定要确保数组元素数比字符串长度至少多1(多出来的1个元素用于容纳空字符)。未被使用的元素均被自动初始化为0。这里的0是char形式的空字符,而不是数字字符0。

2014-03-25 20:48:28

指针形式(*m3)也在静态存储区为字符串预留38个元素的空间。此外,一旦程序开始执行,还要为指针变量m3另外预留一个存储位置,以在该指针变量中存储字符串的地址。

11.2.4 scanf()函数

2014-03-27 19:12:27

在读入name的时候,name会覆盖程序中的数据和代码,并可能导致程序异常终止。这是因为scanf()把信息复制到由参数给定的地址中,而在这种情况下,参数是个未初始化的指针;name可能指向任何地方。

2014-03-27 19:16:55

● gets()的代码使用return关键字返回字符串的地址,程序把这个地址分配给ptr。注意到ptr是一个char指针,这意味着gets()必须返回一个指向char的指针值。

2014-03-27 19:22:23

不要混淆空指针和空字符。空指针是一个地址,而空字符是一个char类型的数据对象,其值为0。数值上两者都可以用0表示,但是它们的概念不同:NULL是一个指针,而0是一个char类型的常量。

2014-03-27 19:24:04

gets()的一个不足是它不检查预留存储区是否能够容纳实际输入的数据。多出来的字符简单地溢出到相邻的内存区。fgets()函数改进了这个问题,它让您指定最大读入字符数。

2014-03-27 19:26:36

fgets()和gets()有三方面不同:

● 它需要第二个参数来说明最大读入字符数。如果这个参数值为n, fgets()就会读取最多n-1个字符或者读完一个换行符为止,由这二者中最先满足的那个来结束输入。

● 如果fgets()读取到换行符,就会把它存到字符串里,而不是像gets()那样丢弃它。

● 它还需要第三个参数来说明读哪一个文件。从键盘上读数据时,可以使用stdin(代表standard input)作为该参数,这个标识符在stdio.h中定义。

2014-03-27 19:46:31

scanf()使用两种方法决定输入结束。无论哪种方法,字符串都是以遇到的第一个非空白字符开始。如果使用%s格式,字符串读到(但不包括)下一个空白字符(比如空格、制表符或换行符)。如果指定了字段宽度,比如%10s, scanf()就会读入10个字符或直到遇到第一个空白字符,由二者中最先满足的那一个终止输入

11.3.2 fputs()函数

2014-03-27 22:50:44

fputs()函数是gets()的面向文件版本。两者之间的主要区别是:

● fputs()需要第二个参数来说明要写的文件。可以使用stdout(代表standard output)作为参数来进行输出显示,stdout在stdio.h中定义。

● 与puts()不同,fputs()并不为输出自动添加换行符。

11.4 自定义字符串输入/输出函数

2014-03-28 14:51:55

string指向空字符时,*string的值为0,这将结束循环

11.5.4 strcmp()函数

2014-03-28 21:17:58

ANSWER和try实际上是指针,因此比较式try! =ANSWER并不检查这两个字符串是否一样,而是检查这两个字符串的地址是否一样。

11.5.4 strcmp()函数

2014-03-28 21:21:49

这些结果说明如果第一个字符串在字母表中的顺序先于第二个字符串,则strcmp()函数返回的是负数;相反,返回的就是正数。

12.1.4 自动变量

2014-03-30 10:25:39

作用域描述了程序中可以访问一个标识符的一个或多个区域。一个C变量的作用域可以是代码块作用域、函数原型作用域,或者文件作用域。

2014-03-30 10:40:28

传统上,具有代码块作用域的变量都必须在代码块的开始处进行声明。C99放宽了这一规则,允许在一个代码块中任何位置声明变量。一个新的可能是变量声明可以出现在for循环的控制部分,

2014-03-30 10:43:59

一个在所有函数之外定义的变量具有文件作用域(file scope)。具有文件作用域的变量从它定义处到包含该定义的文件结尾处都是可见的。

2014-03-30 10:44:07

这里,变量units具有文件作用域,在main()和critic()中都可以使用它。因为它们可以在不止一个函数中使用,文件作用域变量也被称为全局变量(global variable)。

2014-03-30 10:48:25

怎样知道一个文件作用域变量具有内部链接还是外部链接?您可以看看在外部定义中是否使用了存储类说明符static:

2014-03-30 10:54:03

和该文件属于同一程序的其他文件可以使用变量giants。变量dodgers是该文件私有的,但是可以被该文件中的任一函数使用。

2014-03-30 11:01:29

具有代码块作用域的变量一般情况下具有自动存储时期。在程序进入定义这些变量的代码块时,将为这些变量分配内存;当退出这个代码块时,分配的内存将被释放。

2014-03-30 11:03:51

代码块作用域和空链接意味着只有变量定义所在的代码块才可以通过名字访问该变量(当然,可以用参数向其他函数传送该变量的值和地址,但那是以间接的方式知道的)。

2014-03-30 11:08:33

如果在内层代码块定义了一个具有和外层代码块变量同一名字的变量,将发生什么?那么在内层代码块定义的名字是内层代码块所使用的变量。我们称之为内层定义覆盖(hide)了外部定义,但当运行离开内层代码块时,外部变量重新恢复作用。

12.1.7 具有外部链接的静态变量

2014-03-30 11:11:11

倘若一个非常量表达式中所用到的变量先前都定义过的话,可将自动变量初始化为该表达式:

2014-03-31 19:38:15

通过使用存储类说明符register可以声明寄存器变量:

2014-03-31 19:44:54

静态变量(static variable)这一名称听起来很矛盾,像是一个不可变的变量。实际上,“静态”是指变量的位置固定不动。

2014-03-31 19:47:31

这些变量具有代码块作用域、空链接,却有静态存储时期。从一次函数调用到下一次调用,计算机都记录着它们的值。

2014-03-31 20:01:57

化 和自动变量一样,外部变量可以被显式地初始化。不同于自动变量的是,如果您不对外部变量进行初始化,它们将自动被赋初值0。

12.2 存储类说明符

2014-03-31 20:56:25

特别地,不可以在一个声明中使用一个以上存储类说明符,这意味着不能将其他任一存储类说明符作为typedef的一部分。

12.6.1 free()的重要性

2014-04-03 10:46:06

主要工具是函数malloc(),它接受一个参数:所需内存字节数。然后malloc()找到可用内存中一个大小适合的块。

2014-04-03 10:56:52

一般地,对应每个malloc()调用,应该调用一次free()。函数free()的参数是先前malloc()返回的地址,它释放先前分配的内存。

2014-04-03 10:58:07

在头文件stdlib.h中有malloc()和free()的原型。

2014-04-03 11:25:38

但循环执行了1000次,因此在循环最终结束时,已经有1600万字节的内存从内存池中移走。事实上,在到达这一步前,程序很可能已经内存溢出了。这类问题被称为内存泄漏(memory leak),可以通过在函数末尾处调用free()防止该问题出现。

注: 忘记使用free()

12.7.2 类型限定词volatile

2014-04-05 19:40:20

限定词volatile告诉编译器该变量除了可被程序改变以外还可被其他代理改变。典型地,它被用于硬件地址和与其他并行运行的程序共享的数据

13.1.4 标准文件

2014-04-05 21:21:22

C程序自动为您打开3个文件。这3个文件被称为标准输入(standard input),标准输出(standard output)和标准错误输出(standard error output)。默认的标准输入是系统的一般输入设备,通常为键盘;默认的标准输出和标准错误输出是系统的一般输出设备,通常为显示器。

13.2.1 检查命令行参数

2014-04-05 21:27:36

如果main()在一个递归程序中,exit()仍然会终止程序;但return将控制权移交给递归的前一级,直到最初的那一级,此时return才会终止程序。return和exit()的另一个区别在于,即使在除main()之外的函数中调用exit(),它也将终止程序。

13.2.5 fclose()函数

2014-04-05 21:30:21

 警  告 小心!如果使用任何一种“w”模式打开一个已有的文件,文件内容将被删除,以便程序以一个空文件开始操作。

2014-04-05 21:32:12

指针fp并不指向实际的文件,而是指向一个关于文件的信息的数据包,其中包括文件I/O使用的缓冲区信息。因为标准库中的I/O函数使用缓冲区,所以它们需要知道缓冲区的位置,还需要知道缓冲区的当前缓冲能力以及所使用的文件。

2014-04-05 21:35:27

是在stdio.h中定义的与标准输出相关联的文件指针,所以putc(ch, stdout)和putchar(ch)的作用是一样的。

2014-04-05 21:36:02

为了避免试图读取空文件带来的问题,应该对文件输入使用入口条件循环(而不是do while循环)。

2014-04-05 21:40:59

fclose(fp)函数关闭由指针fp指定的文件,同时根据需要刷新缓冲区。

2014-04-05 21:41:10

如果文件成功关闭,fclose()函数将返回值0,否则返回EOF。

13.4.3 注释:gets()函数和fgets()函数

2014-04-06 20:08:39

fgets()函数接受3个参数,而gets()函数只接受1个参数。fgets()函数的第一个参数和gets()函数一样,是用于存储输入的地址(char *类型)。

2014-04-06 20:11:09

输入的第二行包含了 44个字符,而line数组中只能容纳20个字符(包括换行符在内)。到底是怎么回事?当fgets()函数读取第二行时,它只读入前19个字符,直到down中的w。它把这些字符复制到line数组中,由fputs()进行打印。由于fgets()还没有遇到行尾,line数组中不包含换行符,所以fputs()也就不会打印换行符。第三次调用时,fgets()函数在第二次调用停下的地方重新开始,因此它把接下来的19个字符(从down中w后面的n开始)读到数组line中。这一块内容代替了line数组中以前的内容,然后依次打印到与上次的输出相同的那行上,因为上一次的输出不包含换行符。

2014-04-06 20:11:59

为什么程序没有在键入第二行的前19个字符后立刻将其打印出来?这是由于有屏幕缓冲区存在。直到遇到了换行符时,才把第二行发送到屏幕上。

2014-04-06 20:14:04

因为fgets()函数可以防止存储溢出,所以对于严格的编程来说,它是一个更好的选择。由于它将换行符读入到字符串,而puts()函数会在输出中追加一个换行符,所以fgets()函数应该和fputs()而不是puts()一起使用。否则,输入时有一个换行符而在输出时却变成两个。

13.5 随机存取:fseek()和ftell()函数

2014-04-06 20:26:07

如果在命令行环境下运行这个程序,程序要求文件名参数代表的文件和该可执行程序在同一个目录(或者文件夹)中。如果在IDE中运行程序,情况随具体实现不同而有差异。比如Microsoft Visual C++在包含源代码的目录中查找文件,而Metrowerks CodeWarrior在包含可执行文件的目录中查找。

14.3.2 访问结构成员

2014-04-07 10:04:45

如何访问结构中的各个成员呢?用结构成员运算符点(.)就可以。例如,library.value就是指library的value部分。可以像使用任何其他float变量那样使用library.value。

14.4.2 标识结构数组的成员

2014-04-07 10:15:44

这是由第3个结构(library[2]部分)描述的书本的名称的第5个字符(title[4]部分)。

注: library[2].value[4]

14.7.2 使用结构地址

2014-04-07 11:06:14

注意,必须使用&运算符才能得到结构的地址。和数组名不一样,单独的结构名不是该结构地址的同义词。

14.12 typedef简介

2014-04-07 23:54:29

typedef工具是一种高级数据特性,它使您能够为某一类型创建您自己的名字。在这个方面,它和#define相似,但是它们具有3个不同之处:

● 与#define不同,typedef给出的符号名称仅限于对类型,而不是对值。

● typedef的解释由编译器,而不是预处理器执行。

● 虽然它的范围有限,但在其受限范围内,typedef比#define更灵活。

2014-04-07 23:54:37

假设要对1字节的数值使用术语BYTE,您只须像定义一个char变量那样定义BYTE,然后在这个定义前面加上关键字typedef,

14.14 函数和指针

2014-04-08 00:08:02

声明一个指向特定函数类型的指针,首先声明一个该类型的函数,然后用(*pf)形式的表达式代替函数名称;pf就成为可指向那种类型函数的指针了。

时间: 2024-10-12 02:12:57

C Primer Plus(第5版)的笔记的相关文章

C++ Primer(第五版) 笔记 C01-02

C01 ++val; 优于 val++; 对数量不定的输入数据:while(cin>>value)... 遇到无效的输入或eof后,cin变为无效状态,条件变为假. 来自标准库的头文件用<>包围,不属于标准库的用""包围. 文件重定向工作:exename.exe <infile >outfile 点运算符:左侧运算对象是类类型的,右侧是该类型的成员. 参数 = 实参 = 值,形参指出调用函数可使用什么实参. 定义在函数内部的内置类型通常不初始化. C

C Primer Plus (第6版) 读书笔记_Chapter 1

第 1 章 初识 C 语言 ■ C 的历史和特性 ■ 编写程序的步骤 ■ 编译器和链接器的一些知识 ■ C 标准 1.1  C 语言的起源   1972年,贝尔实验室的 丹尼斯 ? 里奇(Dennis Ritch) 和 肯 ? 汤普逊(Ken Thompson)在开发 UNIX 操作系统 时设计了 C 语言.然而,C 语言不完全是里奇突发奇想而来,他是在 B 语言(汤普逊发明)的基础上进行设计.至于 B 语言的起源,那是另一个故事.C 语言设计的初衷是将其作为程序员使用的一种编程工具,因此,其主

C++ Primer(中文第五版)学习笔记

递增(++)和递减(--)运算符 递增和递减运算符有两种形式:前置版本和后置版本,经常在面试的基础题中出现. 前置版本:先将运算对象加1(或减1),然后将改变后的对象作为求值结果: 后置版本:也将运算对象加1(或减1),但是求值结果是运算对象改变之前的那个值得副本,我们通过下面的代码比较: int i = 0; int j = 0; j = ++i; cout << "j="<<j <<"\t"<< "i=&

C++Primer第5版学习笔记(三)

C++Primer第5版学习笔记(三) 第四/五章的重难点内容 你可以点击这里回顾第三章内容 因为第五章的内容比较少,因此和第四章的笔记内容合并.   第四章是和表达式有关的知识,表达式是C++的基础设施,本章由三部分组成:         1.表达式概念基础,包括表达式的基本概念,左值和右值的概念,优先级结合律,求值顺序.  2.各种运算符,主要包括算数\关系\逻辑\赋值\递增递减\成员访问\条件\位运算\sizeof\逗号运算符 这10种运算符.  3.类型转换,包括隐式和显式两种转换的规则

C++Primer第5版学习笔记(一)

C++Primer第5版学习笔记(一) 第一.二章的重难点内容 本篇文章主要记录了我在学习C++Primer(第5版,中文版)中遇到的重难点及其分析.因为第一.二章比较简单,因此这里合并这两章我遇到的问题.        第一章 开始 这一章在第一部分之前,是一个helloworld式的章节,包含基本的函数,io流以及类的介绍. 知识点1:P19,1.5,文件重定向 可以在windows下的cmd中或者mac,linux系统的终端窗口中用输入命令的形式执行程序并使它从一个文件中读入数据,再把标准

c++ primer(第五版)学习笔记及习题答案代码版(第十四章)重载运算与类型转换

笔记较为零散,都是自己不熟悉的知识点. 习题答案至于一个.h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行14.30题为#define NUM1430: Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful f

C++ Primer 第五版学习笔记

<C++ Primer>第五版中文版学习笔记 ? C++ Primer 第五版学习笔记

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器 多重映照容器multimap与map结构基本相同,但由于重复键值存在,所以multimap的元素插入.删除.查找都与map的方法不相同. 1.multimap对象创建.元素插入 插入元素时,需要使用insert()方法和类似pair<string,double>("Jack", 300.5)的元素结构.可以看到,重复的元素是按照插入的先后顺序排序的. #include <iostre

C++ Primer(第五版)学习笔记_5_标准模板库string(2)

C++ Primer(第五版)学习笔记_5_标准模板库string(2) 10.搜索string对象的元素或子串 采用find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定):如果查到,则返回下标值(从0开始计数),如果查不到,则返回一个很大的数string:npos(即:4294967295). #include <iostream> #include <stdio.h> #include <string> using nam