C和指针-百句心得理解

在阅读完《C和指针》后得出的重要心得,在编程遇到困难时,或许将起到醍醐灌顶的作用。也将更好的帮助理解C语言的执行过程,带来对C语言新的感悟。

1、gets()函数从输入缓冲区(键盘输入)读取一个字符串存储到指针变量str所指向的内存空间。

2、“Hello”字符串在内存中占6个字节,最后一个是“NUL”。但字符串长度并不包含“NUL”

3、“NUL”是ASCII字符集中‘\0’字符的名字。而“NULL”是值为0的指针。‘\0’位于字符串末端,表示字符串结束。“NULL”则一般用于空指针的初始化。

4、函数声明中对数组,可只指出数组类型,而长度不指定,无论调用函数传来的数组长度多少,都照收不误。(实际传来的也是数组首元素地址)

5、数组名作为实参时,传递的是指向数组起始位置的指针(地址)

6、初始化时:int ch;可未赋初值,但初值会不可预计

7、scanf(“%d %d”,&columns[num1], &columns[num2])中每个%d对应一个输入的整形,每一个&colums[ ]表示这个整形输入后存储的地址。%s对应一个字符串。

8、整形值可容纳字符。

9、EOF:end of file(在stdio.h)中定义,用于提示文件结尾或结束。EOF被定义为一个整数常量,标准C库中,通常为-1。

10、const作用于函数参数(int const columns[ ])表示该参数不能被修改。编译器也会验证是否违背该意图。

11、保证数组不越界,否则会报错。

12、声明(函数原型等)放在头文件中,只需编写一次,维护和修改更容易,同时避免了多份拷贝中写法不一致

13、程序编译过程

(1)预处理器处理:a、#define:用实际值代替符号 b、读入#include包含的文件内容。(2)解析:判断每行语句意思(大部分编译和警告产生的地方)。

(3)生成目标代码(机器指令的初步形式)。

14、程序翻译阶段(源代码转化为可执行的机器指令):

(1)程序每个源文件编译为目标代码

(2)目标文件再由链接器捆绑,形成单一完成的可执行文件。

(3)链接器同时检测并引入程序中用到的标准C函数库中被该程序用到的函数,并搜索个人函数库,将其中使用的函数页链接到程序中。

15、可只单独编译一个C文件,和先存的可执行文件链接在一起(C编译器称为cc):

cc main.o lookup.o sort.c  节省编译的时间

16、程序执行时必须先载入内存中

17、程序由声明和函数组成:

(1)函数:要执行的工作

(2)声明:描述函数及其参数

18、标识符(变量、函数、类型等的名字),由大小写字母、数字和下划线组成,不能以数字开头。

19、关键字由系统保留,不能用作标识符。

数据

20、字面值:C语言中的固定值。如整数常量:十进制‘42’、八进制以0开头‘052’、十六进制以0x开头‘0x2A’;浮点数:“3.14”;字符:‘A’等

21、变量三个属性:作用域、链接属性、存储类型。(1)作用域:指明变量在程序中的作用范围。(2)链接属性:变量是否可在不同文件共享。(3)变量的生命周期和存储位置。如动态存储还是静态存储,存储于寄存器还是内存中等。

22、指针:变量值存储于计算机内存中,每个变量都有一个唯一确定的内存地址,该地址即指针。

23、指针变量:值为另一个变量的内存地址

24、字符串表达式值为存储地址(字符串存储的首地址,即指针),故无法赋值给字符串数组(数组中每个元素是单个字符值)。

25、说明符(一个或多个)用于描述被声明标识符的类型,包括有无符号或长度+类型

26、定义时*(间接访问符号)表示该变量类型是指针变量,并仅表示与*相近的变量。如:int * b,c,d;只有b是指向整形的指针变量,c、d为整形

27、char *message = “Hello world!”;相当于两条语句(1)char *message;(2)message = “ Hello world! ”;

28、函数若不显式声明返回值类型,则默认返回整形。(类似情况,都会默认为整形)

29、typedef可将各种数据类型定义为新名字,int可变为u8

30、const关键字可用于声明常量:int const a=15;a的值固定不能修改

31、int const *pci则为一个指向整形常量的指针,即可更改指向的常量,但无法改变指向的常量的值。

32、int const * const pci则为一个指向整形的常量指针。指针是常量,无法修改,但可修改它所指向的整形的值

33、代码块作用域:代码块中内层标识符的作用域优先。

34、文件作用域:任何代码块之外的标识符具有文件作用域,从该标识符声明处至它所在源文件结尾。

35、链接属性共三种—external(外部)、internal(内部)、none(无):

(1)external链接属性的标识符无论声明多少次、位于几个源文件都表示一个实体。

(2)internal链接属性的标识符同一源文件的所有声明都为一个实体。

(3)none即没有链接属性的标识符(正常的定义),每个声明都当作不同实体,即同名的变量,但是不同的实体。

36、extern修改标识符链接属性为外部,static修改为内部(标识符处于代码块外)

37、变量的缺省类型即对变量类型的自动推断(未定义变量类型的情况),一般默认为’int’。 缺省的存储类型则取决于变量定义的位置,若在代码块内部则为‘auto’自动变量,存储于堆栈中;代码块外定义的则为‘static’,存储于静态内存中。

38、静态、自动、寄存器变量:

(1)静态变量(static)存储于静态内存中,程序执行过程中始终存在。

(2)自动变量(auto)存储于堆栈中,程序执行到该代码块时,才被创建,执行流离开后自行销毁。

(3)寄存器变量存储于机器的硬件寄存器中,相比于内存中的变量访问效率更高。

39、堆栈实质上也是一块指定的内存,只是数据存储的方式,和存储在此内存块中的时间有专门的规定。

40、static的作用:

(1)代码块外:修改链接属性为internal,但存储类型和作用域不变。

(2)代码块内:修改为静态变量,但链接属性和作用用户不变。

41、静态变量在程序运行过程中,始终存在,对值的更改也一直为同一变量。若多次调用同一函数,动态变量(在该函数中定义的)实际上是不同的变量,不会记忆上一次该变量的值,因为在上次函数结束调用后,该变量就已被删除一次。

42、属于文件作用域(代码块外定义)的声明默认情况下为external链接属性,如int a = 5;但还是建议加上extern:extern int a = 5;

43、无符号变量和相同长度的有符号变量容量值是一样的。如-64~64和0~128

语句:

44、C语言语句的结尾以分号’ ; ’表示

45、C语言表达式都会有一个值,值“零“为假,“非零”为真。赋值表达式除了将值赋给变量外,还作为表达式的值返回。

如‘x = 10’,将‘10’赋值给x,同时返回‘10’作为表达式的值

46、else子句从属于最靠近它的不完整if语句

47、break和continue只对最内层的循环起作用。break直接结束整个循环,continue终止当前的当次循环

48、switch中case标签值一但和express表达式的匹配,则从当前语句起,到switch语句底层之间的所有语句都被执行。

49、若要划分switch语句的不同部分,需要使用break。

50、当switch表达式的值不匹配所有case标签值时,则执行default后面的语句。default语句只能出现一次,但可出现于任何位置。

操作符:

51、算术操作符(+ - * / %):除了%只能作用于整数计算,其他操作符作用于整数、浮点数都可以。

52、移位操作符(《 》):(1)左移位:值最左边的几位丢弃,右边多出来的空位则由0补齐。(2)右移位:a、逻辑移位:左边移入用0填充 b、算术移位:左边移入取决符号位(最左位)与其一致

53、无符号值都是逻辑移位

54、位操作符(AND、OR和XOR):操作数要求为整形。异或XOR:位不同“1”,位同“0”。按位进行操作。

55、赋值(=):结合性(求值的顺序)从右到左。左值需为可以存储变量值(结果值)的地址,右值为要赋值的变量值。左值意味着一个位置,右值意味着一个值。

56、单目操作符(&、*、sizeof):(1)&产生它操作数的地址。(2)*间接访问操作符,与指针一同使用时,访问指针所指向的值。(3)sizeof返回操作数的类型长度,数组名时返回数组的长度。

57、++、--得到的是变量值的拷贝,而非变量本身,不能放置在赋值符号左侧(无法对一个值赋值)。

58、++、--优先级低于*

59、关系操作符(>、>=、<、<=、!=、==):产生的结果是整形,而不是布尔值。操作符两端的操作数得出的结果(1)符合:1(2)不符合:0

60、“=”赋值!“==”比较!

61、C语言并不具备显式的布尔类型,所以用整形替代。零为假,非零值为真

62、char型变量长度只有8位,当两个字符变量执行加法运算得到的结果超过8位时,结果将被截短。

63、操作符的操作数要转换为同一类型。

64、左值表示一个位置,右值为一个值。每个左值表达式是个右值,但右值无法作为左值。因为地址也可以作为值赋给指针变量,但无法把一个值赋给一个值。

(操作符的优先级表格在最后面)

指针

65、计算机中内存由数亿万个的位(bit)组成,每个位可容纳值0或1。但单个位作用有限,通常多个位组成字节作为一个单位(8个bit)

66、内存的最小寻址单元是字节(byte),而非位,内存被组织成一个连续的字节序列,位运算符对位的操作也是基于字节的地址而非位。

67、每个字节都有一个专门的地址,包含8个位,可以存储无符号值0~255或有符号值-128~127

68、多个字节(2个或4个,取决于计算机的位数)可合为一个字存储更大的值,比如以字位单位存储整数。通常地址为它的最左侧字节的地址

69、内存中一个字的大小是4个字节时,容纳的无符号整数范围变为0~4294967295(232-1),有符号对应增加,则可见内存中5个字内容如下:

但我们通常通过变量名而非地址来访问内存位置:

70、对变量存储值的解释,取决于变量的类型的声明(一定程度上也解释了类型转换的原理),相同的存储值在不同类型的解释下,得出的值不同:

如01100111011011000110111101100010:

71、该例一个单一值可解释为5种不同类型。

72、间接访问(也称解引用指针):访问指针中地址存储的值,间接访问操作符*。

73、指针必须要初始化,否则该指针指向位置无法预知,将导致

     (1)初始值为非法地址,报错。(试图改变一个未分配给内存位置的值)

     (2)初始值为合法地址,改变此地址存储的值(无意修改)

74、NULL指针:特殊的指针变量,表示不指向任何东西。

75、当指针变量初值不知指向何处时,可初始化为NULL。测试一个指针变量是否为NULL,可直接和零值比较。

76、不能将一个值赋给一个值,也不能赋给一个指针变量(存储的是地址,而非数值),只能赋值给内存中可以存储值的一个变量(一个位置,该位置存储的是数值)

77、指向指针的指针,该指针的值是另一个指针的地址。如:int a = 12; int *b = &a; int **c = &b; *操作符从右向左的结合性,则相当于int *(*c),表示该变量的类型是一个指向整形的指针(int *c)的指针(外层的*),值为b指针的地址。

78、*在定义时说明该变量的类型,使用时,则访问存储地址指向的值。

79、cp为一个指针。*(cp+1)为指针+1:指针指向的内存地址增加一个单位,单位大小取决于指针指向的数据类型。相当于若为整形则地址加1*4。(一个整形4个字节)

80、*cp++:(1)++产生cp的一份拷贝 (2)++增加cp的值 (3)对cp的拷贝产生间接访问操作。

81、++优先级高于*

82、++*cp:两个操作符结合性都是从右向左,所以(1)执行*间接访问操作,(2)再执行++,使cp指向的位置的值+1 (3)该表达式结果为增值后该值的拷贝(++在前)

83、只有操作符与变量相邻才看优先级,操作符彼此相邻看结合性。

84、指向指针数组的指针:char **strings; while(**strings != ‘\0’); 第一个间接访问指针数组中的当前地址,第二个间接访问当前地址中字符串的当前字符(地址中的值)。

85、指针间的算术运算,需要两个指针指向同一数组中的元素,相减为两指针在内存中的距离(数组元素的长度,运算时会自动除以类型长度)

86、指针即是地址,地址即是指针,当使用间接访问时,可以想到这点。

函数

87、编译器调用函数时,需要向编译器提供如何调用这个函数的信息,通常由两种方式:(1)源文件前面已出现了函数定义。

(2)函数原型(函数声明的一种形式):总结函数定义起始部分的声明,包括函数名称、返回值类型、参数列表。

88、最好令函数原型处于一个单独的文件中,当其他源文件需要该函数原型时,就使用#include指令包含该文件。

89、函数只获得参数值的拷贝,参数都为“传值调用”,但若是传的是指针,则可能改变它指向地址中的值

90、函数被调用时,变量的空间创建于运行堆栈上;若在执行完前,有新函数调用,原先变量仍保留在堆栈上,但无法访问。

91、递归函数所需特征:(1)存在限制条件,符合则不再继续(2)每次递归后,越来越接近这个条件

数组

92、标量:单一的值(数组中的每个元素都是)

93、数组名是个指针常量,而非变量,类型取决于数组类型

94、复制数组,只能挨个复制每个元素。

95、数组名对应着每个数组的首元素地址,该数组名的值是常量!不能被修改

96、对数组的下标引用和间接访问完全相同

97、下标引用可以作用于任意指针,而不仅是数组名

98、数组作为函数参数时,无需写明元素数目,函数并不为数组参数分配内存空间,形参只是一个指针(数组第一个元素的地址),指向已经在其他地方分配好的内存。

99、多维数组:相当于第一维元素的每个元素中都包含了一个数组,其中有多个元素,后各维以此类推。

每个元素存储了3个元素,相当于该元素也是个数组

100、多维数组的数组名为指向数组的指针,即指向第1维第1个元素的指针(第一个元素包含了多个元素,相当于一个数组)

101、多维数组的数组名+1,相当于指向了下一个子数组

如:int matrix[3][10]; matrix+1  

102、数组指针(指向数组的指针):int (*p)[10] = matrix; 可知为指向整形数组的指针

(1)(*p)指出该变量为指针变量

(2)[10]指出该指针指向个数组,该数组拥有10个元素

(3)int指出指向的数组是整形

103、多维数组可以不指定第一维长度,但必须指定第2维及以后各维的长度。(为确保内存正确分配存储空间)

104、对数组初始化时花括号作用:(1)利于显示数组结构(2)每个子列表都可以省略末尾几个初始值

105、指针数组(存放指针的数组):int *api[10];(1)下标引用[ ]优先级高于*,首先执行下标引用,创建数组。(2)取得数组元素后,执行间接访问操作,使每个数组元素为指向整形的指针。

106、数组名的例外:

(1)sizeof:返回整个数组占用的字节

(2)单目操作符&:返回一个指向数组的指针,而非首元素

107、多维数组实质是一维数组的一种特性,它的每个元素也是一个数组。多维数组名为指向第1个元素(数组)的指针

练习:

假定ints数组在内存中起始位置是100,整形值和指针长度都是4个字节

int  ints[20] = {

10, 20,30,40,50,60,70,80,90,100,

110,120,130,140,150,160,170,180,190,200 };

int  *ip = ints + 3

表达式

表达式

ints

100

ip

112

ints[4]

50

ip[4]

80

ints+4

116

ip+4

128

*ints+4

14

*ip+4

44

*(ints+4)

50

*(ip+4)

80

ints[-2]

非法

ip[-2]

20

&ints

100

&ip

未知

&ints[4]

116

&ip[4]

128

&ints+4

116

&ip+4

未知

&ints[-2]

非法

&ip[-2]

104

ints[-2]:该数组并没有这个元素

&ip:指针类型自身的地址未指定

&ints+4:&优先级高于+,地址+需要乘以类型所占的字节数

&ip[-2] :[ ]优先级高于&

字符串、字符和字节

108、字符串以字符串常量的形式出现或存储于字符数组中

结构和联合

109、聚合类型共有两种:(1)数组(2)结构:无法通过下标访问,因为成员类型可能不同,长度也不同

110、结构声明:struct tag { member-list } variable-list ;

(1)struct:为结构 (2)tag(结构标签):结构名(作为类型名声明变量)(3)member-list:结构内容(成员列表)(4)variable-list使用该类型结构的变量

111、创建结构时,可用结构名创建相同类型的结构变量,省去在每次声明中重复成员的列表。

112、对变量的创建分为两个步骤:(1)声明:包括变量的名称和类型,告知编译器有个变量将被使用。int x ;(2)定义:为变量分配内存,并可选为其的分配初始值,可与声明同时完成,也可稍后进行。int x = 42。

113、点操作符(.):(1)左操作数:结构变量的名字(2)右操作数:需要访问的成员的名字(3)结合性为从左到右

114、点操作符(.)优先级高于间接访问*

115、当定义指向结构的指针时,所指向的是整个结构。

116、px是一个指向结构的指针。(1)*px+1是非法的,*p结果是个结构,C语言未定义一个结构(值)与整型值之间的假发运算。(2)*(px+1)也是非法的,结构并不像数组元素,元素的类型相同,它的字段之间的便宜是不确定的,并不能像数组一样用*(px+1)简单的访问下一个结构。

117、结构的第一个成员的地址和结构的地址是相同的,所以px同时指向整个结构和第一个成员,但*px为对整个结构的访问,而非对第1个成员的访问,对成员的访问要用到->和 . 操作符。

118、当px是指针时,通过->对结构体成员进行间接访问px->a;px当为结构名时,用(.)点操作对成员进行访问px.a。

119、将整个结构作为函数参数传递效率低,要拷贝整个结构占用空间大,往往只需要其中的一个成员,可用传递该结构的指针。

120、const可防止函数通过指针修改结构成员的值,当不希望被修改时。

121、联合:和结构类似,但它的所有成员引用的是内存中的相同位置。即不同时刻将不同的东西存放于同一位置。不同的成员引用的位相同,区别在于不同的成员类型对位的解释。(前面提到的对存储空间中值的解释)

122、联合的长度就算它最长成员的长度。

123、在联合中可用存储指向不同成员的指针,而非成员本身,可用大量节约空间(指针的长度都是相同的,而且较短)。

124、联合变量可被初始化,但初始值需要是联合第1个成员的类型,该初始值也需位于花括号内。

125、结构中的成员可以包含指向该结构自身的指针。

练习:

struct NODE {

                     int                 a;

                     struct NODE  *b;

                     struct NODE  *a;

}

struct NODE  nodes[5]={

{ 5,      nodes + 3,       NULL },

{ 15,    nodes + 4,       nodes + 3 },

{ 22,    NULL,              nodes + 4 },

{ 12,    nodes + 1,       nodes },

{ 18,    nodes + 2,       nodes + 1},

};

struct NODE         *np         = nodes + 2

struct NODE      **npp       = &nodes[1].b

假定nodes数组在内存中起始位置为200,并且该机器上整数和指针长度都为4个字节。

表达式

表达式

nodes

200

&nodes[3].c->a

200

nodes.a

非法

&nodes->a

200

nodes[3].a

12

np

224

nodes[3].c

200

np->a

22

nodes[3].c->a

5

np->c->c->a

15

*nodes

{5,node+3,NULL}

npp

216

*nodes.a

非法

npp->a

非法

(*nodes).a

5

*npp

248

nodes->a

5

**npp

{18,node+2,node+1}

nodes[3].b->b

248

*npp->a

非法

*nodes[3].b->b

{18,nodes+12,nodes+1}

(*npp)->a

18

&nodes

200

&np

未知

&nodes[3].a

236

&np->a

224

&nodes[3].c

244

&np->c->c->a

212

nodes.a:nodes是个数组,而非单一结构。nodes是结构数组首元素的地址(指针),无法使用点操作符,只能使用->

*nodes:对结构的指针进行间接访问,得到结构。

*nodes.a:.操作符优先级高于间接访问*,首先得到a的值5,无法继续对值进行间接访问操作

(*nodes).a:对结构成员进行访问

*nodes[3].b->b:间接访问优先级低于点操作符.和->。

&nodes[3].c->a:&优先级低于点操作符.和->

npp:&取得是成员b的地址(该值的地址)

*npp:对b地址进行间接访问,得到nodes + 4

&np:指针变量自身的地址是未知的

数组中的值nodes,可直接视作200(地址的值)。

动态内存分配

126、malloc:申请一块连续内存,参数为要分配的内存字节数,若可以,则返回该内存块的起始位置。

127、malloc返回值是一个void *的指针,可以转化为其他任何类型的指针。

128、calloc:calloc和malloc的区别在于,会对分配的内存初始化,初始化为0。参数为要分配的元素个数和每个元素的长度

129、realloc:扩大或缩小已分配的内存

130、free:释放一块内存,需要是malloc、calloc或realloc返回的值(分配内存块的起始位置),要么是NULL。

131、输入缓存区:从标准输入设备(如键盘)读取的字符数据临时存储区域。

结构和指针

132、单链表起始位置,用一个根指针指向第1个节点。

133、链表尾部节点指向NULL指针

134、当需要在头部插入节点时,需要用一个指向指针的指针(指向根节点root的指针的指针)

135、双向链接中的节点包含两个指针,一个指向前一个节点,一个指向后一个节点。根节点的指针一个指向链表的第一个节点,一个指向最后一个。

136、root根节点并不算双链表内的节点。

137、链表是动态分配的,可增长至几百或几千个节点,可能分布于内存的各个地方

138、链表的节点就是结构,由数值和一个或多个指针组成

高级指针

139、指针也是变量,只是存储的是地址,当用变量的方法调用它时,输出的是它存储的地址(变量的值)

140、指向指针的指针:两次间接访问得到i的值(1)*访问ppi存储地址pi中的值(i的地址)(2)*访问i的值

141、函数指针(每个函数都位于内存中的某个位置):int (*f)( ); 指向函数返回值为整形,名字为f。

142、返回值为整形指针的函数指针:int *(*f)( );

143、指针数组:[ ]下标优先级高于*。int *f[ ];(1)先数组(2)数组元素为指针。

144、int f( )[ ]:非法。f是个函数,函数只能返回标量值,不能放回数组。

145、int f[ ]( ):非法。数组元素长度需相同,但不同函数往往长度不同。

146、int (*f[ ])( ):合法。(1)数组元素为某种类型的指针(2)数组元素类型为函数指针(3)所指向的函数返回值为整形

147、int *(*f[ ])( ):函数返回值为整形指针

148、在函数优先级之后的符号为函数返回值的类型,即从确定为函数起。

149、回调函数:作为另一个函数的参数,传递给另一个函数。定义的类型一般为void*型,以便能作用于任何类型的值

练习:

类型

表达式

含义

int

abc()

返回值为int的函数

int

abc[3]

int型数组

int

**abc()

返回值为“int型指针的指针”的函数

int

(*abc)()

返回值为int的函数指针

int

(*abc)[6]

指向“int型数组”的指针

int

*abc()

返回值为“int型指针”的函数

int

**(*abc[6])()

指向“返回值为int型指针的指针的函数”的指针的数组

int

**abc[6]

int型指针的指针数组

int

*(*abc)[6]

指向“int型指针数组”的指针

int

*(*abc())()

返回值为“返回值为int型指针的函数指针”的函数

int

(**(*abc)())()

返回值为“返回值为int的函数指针的指针”的函数指针

int

(*(*abc)())[6]

返回值为“指向int型数组的指针”的函数指针

int

*(*(*(*abc)())[6])()

返回值为“指向’返回值为int型指针的函数指针’的数组的指针“的函数指针

**abc():()优先级高于*,先是函数,函数之后的符号都为返回值的类型。

(*abc)():先是指针,该指针类型是函数。看哪个先成立

*(*abc)[6]:先是指针,指向的是个数组,该数组类型是int型指针。

*(*abc())():函数指针即,先是指针,后接着是函数

(**(*abc)())():指针后紧接的是函数,那么则为函数指针

(*(*abc)())[6]:数组前跟指针是指向数组的指针,数组后跟指针是元素为指针的指针数组

通过观察优先级,看哪个先成立,再一步步推导就可。数组、指针或函数。

预处理器

150、源码编译前的文本性质操作—预处理:(1)删除注释(2)插入#include包含的内容(3)定义和替换#define定义的符号(4)条件编译:部分代码是否编译、

151、#define为数值命名一个符号

152、#define基本语法:#define 标识符 替换文本

153、宏由define定义:将参数替换到文本中(即#define中有变量)

154、宏定义的尾部无需加上分号(当宏定义单独出现时)

155、宏被调用时,文本被参数原样!替代:

如:#define SQUARE(x)  x * x

printf(“ %d\n, SQUARE(a+1) ”);-->printf(“%d\n”,a+1*a+1)

文本为x,参数为a+1

156、为避免出现问题,宏中的变量和表达式最好都加上括号

157、宏与类型无关,即可以用于任何类型的数据

158、宏定义不用分号结尾(定义的时候),分号于调用该宏的语句中出现

159、宏名全大写

160、条件编译:选择代码的一部分时正常编译还是忽略。基本结构如下:

#if constant-expression(常量表达式,由预处理器求值)

              statements

#endif

常量表达式值为真则statements部分正常编译,否则预处理删除

161、常量表达式:(1)字面值常量(2)#define定义的符号

162、条件编译还可用#elif编译时选择不同的代码部分。#else只有前面表达式都为假才编译。

163、文件包含#include:预处理器删除该指令,用包含文件的内容取代。

164、头文件被包含时,文件内所有内容都要被编译

165、两种不同类型的#include文件包含:(1)函数库文件(2)本地文件

166、#include <>:函数库头文件用<>,标准库文件要以.h后缀结尾

167、#include “filename“:本地文件包含。本地文件查找策略:现在源文件当前目录查找,再在标准位置(同查找函数库头文件一样)查找

168、#error用以报告错误

输入/输出函数

169、I/O:对程序而言,所有I/O操作只是简单的从程序移进或移出字节。该字节流便称为流。

170、绝大多数流是完全缓冲的,意味着“读取“和”写入“实际上是从一块称为缓冲区的内存区域来回复制数据。

171、用于输出流的缓冲区只有它被写满时才会被刷新(flush,物理写入)到设备或文件中,这样效率更高。输入类似,当它为空时,会从读取一块较大的输入。

172、为避免输入和输出时缓冲混淆,通常在输入的同时刷新输出。

173、printf可能不会立即显示,当用printf查找错误时,可能用于确认错误的位置有误,可以使用fflush迫使缓冲区的数据立即写入。

174、FILE结构:一个数据结构,用于访问一个流,每个流都有一个FILE与之关联。系统运行时至少提供三个流:

(1)标准输入(stdin),通常为键盘设备

(2)标准输出(stdout),通常为终端或屏幕。

(3)标准错误(stderr),错误信息写入的地方。

175、文件I/O:

(1)程序需要为每个处于活动状态的文件声明一个指针变量,其类型为FILE*。该指针指向这个FILE结构,当它处于活动状态时由流使用。

(2)打开一个流:fopen函数。关闭一个流:fclose函数

(3)标准流的I/O不需要打开或关闭

176、I/O以三种基本形式处理数据:(1)单个字符(2)文本行(3)二进制数据。每种形式都有一组特定的函数对其进行处理。

数据类型

输入

输出

描述

字符

getchar

putchar

读取(写入)单个字符

文本行

gets

scanf

puts

printf

文本行末未格式化的输入(输出)

格式化的输入(输出)

二进制数据

fread

fwrite

读取(写入)二进制数据

这些函数只用以下任务:

(1)只用于标准输入stdin或标准输出stdout

(2)作为参数的流使用

(3)使用内存中的字符串,而不是流

需要一个流参数的函数将接收stdin或stdout作为它的参数。

177、scanf:将输入的值存储至指针参数指向的内存位置(要加上&的原因)

标准函数库

178、函数(rand、srand)产生的随机数并非真正的随机数,而是由计算得到的,也称伪随机数。

179、srand函数能够指定一个随机数产生器的种子,每次可产生相同的随机数序列

180、clock函数可用于计算程序中代码的运行时间

181、goto函数可使程序跳转到指定位置,但一般不用,不利于对程序的理解。

182、perror函数用以报告错误,打印出用于解释errno错误代码的信息。

经典抽象数据类型(ADT)

183、ADT的内存分配方式:(1)静态数组:结构固定(2)动态分配的数组:运行时才决定数组长度(3)动态分配的链式结构:数量无限制,但占内存

184、堆栈:后进先出。基本操作有push和pop;push将一个新值压入堆栈顶部。pop则是将堆栈顶部的值移除并返回这个值。有的堆栈提供top,只返回顶部元素的值,但不移除。

185、对堆栈需要两个函数来检查堆栈是否为空和堆栈是否已满。

186、堆栈的创建(1)静态数组实现,长度固定(2)动态数组实现,堆栈长度在创建堆栈的函数被调用时给出(3)链式堆栈,只需知起始位置。

187、队列:先进先出。

(1)需要两个指针,一个指向队头front,一个指向队尾rear。

(2)使用循环数组。

(3)当留有一个元素记录队列为空或满时。队列为空时rear值比front小1:(rear+1)% QUEUE_SIZE == front;队列满时:(rear+2)% QUEUE_SIZE = front

188、二叉树:每个节点至多两个孩子。每个节点的值比它左子树所有节点都要大,比右子树所有节点都要小。四种遍历方式:

(1)前序:首先检查节点的值,然后遍历左子树和右子树。

(2)中序:首先遍历左子树,然后检查当前节点的值,最后遍历右子树

(3)后序:首先遍历左右子树,再检查节点的值

(4)层次:首先处理根节点,接着是孩子,再是孙子。

189、当使用数组存储二叉树,其规则为按照下标寻找:

(1)节点N的双亲是节点N/2

(2)节点N的左孩子是节点2N

(3)节点N的右孩子是节点2N+1

若将1,2,3,4,5,6,7按此顺序插入,将存储在数组中的1,2,4,8,16,32和64的位置。

空间利用不充分,存在大量浪费。最好采用链式二叉树。

190、链式二叉树节点用一个结构来容纳值和两个指针。数组由一个指向根节点的指针代替,初值为NULL,表示此时为一个空树。

191、为防树丢失,可由函数向用户提供根指针,以防用户自行修改根指针。

运行时环境(汇编)

192、编译器会在声明前加上下划线_,以免与各个库函数使用的名字冲突。

193、函数分为三部分:

(1)函数序:执行函数启动需要的一些工作,例如为局部变量保留堆栈中的内存

(2)函数跋:在函数返回之前清理堆栈。

(3)函数体:执行有用工作的地方。

194、寄存器a0~a7:地址。d0~d7:数据。d0和d1用于函数返回值,不能存储寄存器变量

195、堆栈由高向低地址方向生长

196、函数参数以相反次序压入堆栈,函数的第1个参数始终位于堆栈顶部。(当实际传入的参数和期望参数数量不同时,可以防止错误)保证函数可访问到它想要的参数。

遇到的坑

197、C/C++工程文件的路径不要包含任何的中文,否则可能编译错误。

198、移位操作符得到的是一个值,当使用时,需要赋给左值。如i=i>>1;不能在for循环中直接for(;;i>>1),这样子得到的只是一个值。

199、当使用gets从键盘输入一个字符串时,只需要给gets一个字符串指针就行(地址),如:char *string;作为参数gets(string);

200、for (  exprssion1     ;               expression2          ;                      expression3      )

                     初始化               条件部分(判断中止循环)          调整部分(循环体后,条件部分前执行)

201、当判断语句中表示范围需要两次比较时,要用 && 。不能写为if('0' < *string < '9'),当左边第一个比较完,为真时值为1,为假时值为0。始终<'9'。该表达式无意义,也不是我们想要的意思。应写为if(*string > '0' && *string < '9'),最好变量都习惯写在左边。

202、printf需要通过格式说明符%d,获知该如何解释数据,不能省略。

203、使用realloc重新分配内存后,会有新内存块的地址,就不要再使用旧内存块的地址了(旧内存块可能已经被释放)

204、free只能用于释放malloc等动态分配的内存,不能用于释放数组中元素的内存。因为数组在栈上静态分配的,它们的内存会在程序的作用域结束时自动释放。

简单点说,free无法作用于栈,只能作用于堆上动态分配的内存。

205、栈和堆是两种不同的内存分配区域,局部变量通常在栈上分配,而全局变量和通过动态内存分配函数(如malloc)分配的变量在堆上分配。

206、printf中用双引号“ ”,字符用单引号' ',字符串用双引号“ ”。

207、若要求输入行至多80个字符,则使用gets函数,缓冲区长度至少81个字节,以保存80个字符和一个结尾的NUL字节。

208、gets函数在读取失败时,会返回NULL指针。

操作符的优先级

操作符

描述

( )

聚组

( )

函数调用

[ ]

下标引用

.

访问结构成员

->

访问结构指针成员

++

后缀自增

--

后缀自减

逻辑反

~

按位取反

+

单目,表示正值

-

单目,表示负值

++

前缀自增

--

前缀自减

*

间接访问

&

取地址

sizeof

取其长度,以字节表示

(类型)

类型转换

*

乘法

/

除法

%

整数求余

+

加法

-

减法

<<

左移位

>>

右移位

>

大于

>=

大于等于

<

小于

<=

小于等于

==

等于

!=

不等于

&

位与

^

位异或

|

位或

&&

逻辑与

||

逻辑或

?:

条件操作符

=

赋值

+=

以…加

-=

以…减

*=

以…乘

/=

以…除

%=

以…取模

<<=

以…左移

>>=

以…右移

&=

以…与

^=

以…异或

|=

以…或

逗号

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值