小结

概念

■ 如果把类型说明符“*”、“[]”、“()”理解为运算符,C语言复杂数据类型的变量定义就如同是在说所定义的变量经过哪些运算会得到一个什么样类型的值。

■ 数组元素不可能是函数,但可以是指向函数的指针。

■ 函数的返回值不可能是数组类型,但是可以返回指针。

■ 函数的参数也不可能是数组类型,因为数组名作为右值时是指针类型。

■ 函数不可以返回函数类型,但可以返回指向函数的指针类型。

■ 在定义数组数据类型时,应该按照数组类型的构造规律,以数组名为中心从里到外逐层构造数组类型的定义。其中的第一步应该是在数组名后加上“[]”。

■ 在定义指针数据类型时,应该按照指针类型的构造规律,以指针变量名为中心从里到外逐层构造指针类型的定义。其中的第一步应该是在指针变量名前加上“*”然后再用“()”括起来。

■ 在说明一个函数名的类型时,应该以函数名为中心从里到外逐层说明其类型。其中的第一步应该是在函数名后面加上“()”然后依次说明函数参数的类型及函数返回值的类型。

■ 在包含多个标识符的复杂数据类型变量定义或函数说明中,如果在某对“()”内,某个标识符被完全地说明了其类型,这个标识符必然不是被定义的变量。

■ 通常变量定义或函数说明中,最左边的未被定义的变量标识符就是被定义、描述的标识符。

■ 对于指针类型,把指针变量定义中的指针变量标识符去掉,所剩下来的就是这个指针的数据类型的名字。

■ 数组名右值的类型是指向数组起始元素的指针,数组名的左值才是数组类型。

■ 函数名一定是指向函数的指针类型。只要把函数说明中的函数名替换为“(*)”得到的就是对应函数名的类型的名称。

■ “const”或“volatile”所限定的是其左面的类型说明符,如果左面没有任何类型,那么限定的是其右面的类型。

■ 如果要用“const”或“volatile”修饰指针,那么这个关键字只能出现在“*”的右面。通过函数的嵌套调用也可以实现循环嵌套那样的穷举。

■ 把数组封装在了一个结构体内部,可以在事实上实现在函数之间“整体”传递“数组”。当然名义上传递的依然是结构体类型的数据。

■ 回溯法(backtracking)是穷尽搜索算法(brute-force search)中的一种。

■ 回溯法是一种搜索算法。它通过分步的方式求解问题,在这个过程中,一旦发现某种方法无法继续进行,则返回到前一步重新搜索问题的解。回溯法通常非常适合用递归的方法实现。

■ C语言函数库通常提供

小结 - 图1

■ 这样3个函数,使程序能够在函数运行期间动态地使用内存。这3个函数的函数原型在“stdlib.h”中。

■ 释放内存函数的函数原型是void free(void *ptr);。一旦所申请的内存不再需要时,应该通过free()及时释放。

■ 程序运行时动态申请内存空间并不是一定能够成功。失败时,动态申请内存函数的返回值为0(NULL)。在写代码时应该考虑到申请内存失败的可能。

■ 在C99中,结构体数据的最后一个成员的类型可以是不完全的数组类型,即“[]”内可以写为0或不写。这也叫柔性数组成员。

■ 包括指向自身数据类型指针成员的结构体类型叫做自相关结构。

■ typedef关键字用于为某种数据类型的名称取别名。

■ 用typedef定义某种类型的新名称时,要把新名称放在用旧名称定义或说明标识符的位置。

风格

■ 通常用typedef定义类型的新名称使用大写字母作为标识符。

■ 通常typedef定义类型的新名称的位置是在源文件的最开头部分,并且常规上会把它们移入一个头文件(扩展名为.h),然后用文件包含预处理命令将头文件包含进来(详见第13章)。

常见错误

■ 使用其他类型说明符重新说明typedef名称。如:

小结 - 图2

忠告

■ 使用typedef语句可以使程序具有更好的可移植性。

■ 在C99中对变量长度的数组类型的名称使用typedef语句定义类型新名称时需要特别小心,因为这个新名称是在typedef语句执行后才生效。

牛角尖

■ 下面的代码段合法吗?

小结 - 图3

答:合法。typedef定义的类型名称和变量一样有自己的作用域,如果发生重叠的现象,则遵守局部优先的解释规则。