小结

概念与术语

■ 指针(Pointer)是C语言中的一类数据类型的统称,这类数据类型专门用来存储和表示内存单元的编号——地址。

■ 指针数据类型是一种需要借助其他数据类型才能构造出来的数据类型。

■ 指针也泛指具有指针数据类型的数据对象。

■ 指针数据类型特定的类型说明符是“*”。

■ 指针总是和另外一种具体的数据类型联系在一起。

■ 根据指针所关联的数据类型,可以把指针分为三类:数据指针、函数指针和空指针(void*)。这三类指针拥有的运算种类的集合不同。

■ “数据对象”是指内存中一段以byte为单位的、特定长度的、连续的区域,这段内存区域中的内容具有数据类型的含义。

■ 函数类型不属于数据对象。

■ 数据指针的基本运算有“*”和“+1”,这是理解数据指针的基础,而这两个运算都是与指针的类型息息相关的。因此理解数据指针的根本在于理解指针的类型。

■ “&”是求得指向运算对象的指针的运算,“&”,的运算对象通常是变量,但更一般的是它的运算对象是一个左值表达式——表示一块内存的表达式。

■ 称指针“指向某种类型数据”是指这个指针指向那种数据所占据的内存整体。

■ 对于某个左值表达式“E”,“*&E”得到的依然是“E”。

■ 如果“p”有意义,那么“&p”一定是“p”。

■ 所有类型的指针都可以进行赋值运算,但一般应该用相同类型的指针赋值。

■ “[]”运算是用“”运算定义的:el[e2]≡(((e1)+(e2)))

■ 数组名作为一个值参与运算的时候是指向数组首个元素的指针,且是一个指针常量。

■ 数组名参与“sizeof”、“&”等运算时,含义是数组所占据的内存空间。

■ 没有内存含义的非左值表达式做“&”运算是错误的。

■ “*”可以作为乘法运算符,可以作为指针类型说明符,也可以作为间接引用运算符。在具体的场合下才能确定。

■ 指向具体数据类型的指针“p”可以与整数类型数据进行“+”运算,“p+i”的含义是得到指向“p”指向数据对象后面第“i”个这样类型数据对象的指针。但是“p”或“p+i”都应该指向某个有意义的数据对象或某个有意义的数据对象之后的首个“虚拟”的数据对象。

■ 指针可以用来通过调用函数改变本地局部变量的值。

■ 指针必须正确恰当地初始化之后才可以进行“*”或“[]”运算。

■ 两个类型相同且指向同一数组内元素或数组后面一个同类型“数据”的指针可以做减法运算,结果为“int”,含义是元素下标之差。

■ 两个类型相同且指向同一数组内元素或数组后面一个同类型“数据”的指针可以做关系运算,结果表示两个指针的前后相对位置关系。

■ 两个相同类型的数据指针做“==”或“!=”这两个等式运算的含义十分明显,无非是们所指向的数据是否为同一个。

■ “type []”类型的形参等价于“type*”类型的形参。

■ “高维数组名”作为左值表示一个数组,作为右值是一个指针。

■ “*高维数组名”或“高维数组名[0]”作为左值表示一个数组,作为右值是一个指针。

■ 变量长度数组是指尺寸用变量描述的数组,并不是长度可变的数组(C99)。

■ 用变量作为类型说明符的类型叫变量修饰类型(C99)。

■ 在函数原型或函数定义中出现的标识符必须首先说明(C99)。

■ C99允许直接写出数组类型的字面量,这种字面量可以进行与数组名同样的运算。

■ 在C99中,“类型名){字面量列表}”是运算符,优先级同“[]”等运算符。

■ 可以通过指向结构体的指针进行“->”运算访问结构体的成员。

■ 函数名是指向函数类型的指针常量。

■ 函数名==*函数名==&函数名

■ 指向函数的指针可以进行“=”、“*”运算和“()”运算(函数调用运算)。

■ “void *”是纯粹的地址,一般用来传递指针的值。

■ C语言中的函数可分为三类,无参函数、确定参数函数及参数不确定的函数。

■ “…”用来描述参数不确定函数的函数原型和函数定义中参数不确定的部分。

■ “…”只能描述函数最后的几个参数,前面的参数必须是确定的。

■ 为了使程序具有可移植性,一般应通过“stdarg.h”,中定义的宏实现参数不确定函数。

常见错误

■ 典型错误:“int p=3;”,这里“”是类型说明符不是运算符。这个错误写法的真正含义是“int *p; p=3;”。

■ 类似“Suspicious pointer conversion in”的警告通常意味着错误,这个警告一般出现在指针类型与要求不一致的场合。

■ 调用printf()函数输出指针的值应该用“%p”格式说明符,使用“%u”在一些场合会发生错误。

■ 指向数组元素的指针经加减运算后得到的指针不指向数组元素或数组后面一个虚拟的元素。

■ int *f(void) t i; return &i:这个函数返回的是一个指向局部auto类别变量的指针。然而函数调用之后,“i”已经不存在了。

风格

■ 每定义一个指针变量,要么初始化为0(NULL),要么使它指向恰当的位置。

牛角尖

对于下面的程序片段

小结 - 图1

小结 - 图2

需要特别注意的是,每次进入循环体“a”的尺寸是变化的,但是由于“p”是static类别,“p”的值和类型却一直保持不变。这可能会带来很大的问题。