本节专门对第二节曾讲述过的指针作一详述。并介绍Turbo C新的数据类型:
结构、联合和枚举, 其中结构和联合是以前讲过的五种基本数据类型(整型、浮
点型、字符型、指针型和无值型)的组合。 枚举是一个被命名为整型常数的集合。
最后对类型说明(typedef)和预处理指令作一阐述。
1、 指 针(point)
学习Turbo C语言, 如果你不能用指针编写有效、正确和灵活的程序, 可以
认为你没有学好C语言。指针、地址、数组及其相互关系是C语言中最有特色的部
分。规范地使用指针, 可以使程序达到简单明了, 因此, 我们不但要学会如何正
确地使用指针, 而且要学会在各种情况下正确地使用指针变量。
1. 指针和地址
1.1 指针基本概念及其指针变量的定义
1.1.1 指针变量的定义
我们知道变量在计算机内是占有一块存贮区域的, 变量的值就存放在这块区
域之中, 在计算机内部, 通过访问或修改这块区域的内容来访问或修改相应的变
量。Turbo C语言中, 对于变量的访问形式之一, 就是先求出变量的地址, 然后
再通过地址对它进行访问, 这就是这里所要论述的指针及其指针变量。
所谓变量的指针, 实际上指变量的地址。变量的地址虽然在形式上好象类似
于整数, 但在概念上不同于以前介绍过的整数, 它属于一种新的数据类型, 即指
针类型。Turbo C中, 一般用"指针"来指明这样一个表达式&x的类型, 而用 "地
址"作为它的值, 也就是说, 若x为一整型变量, 则表达式&x的类型是指向整数的
指针, 而它的值是变量x的地址。同样, 若
double d;
则&d的类型是指向以精度数d的指针, 而&d的值是双精度变量d的地址。所以, 指
针和地址是用来叙述一个对象的两个方面。虽然&x、&d的值分别是整型变量x 和
双精度变量d的地址, 但&x、&d的类型是不同的, 一个是指向整型变量x的指针,
而另一个则是指向双精度变量d的指针。在习惯上, 很多情况下指针和地址这两
个术语混用了。
我们可以用下述方法来定义一个指针类型的变量。
int *ip;
首先说明了它是一指针类型的变量, 注意在定义中不要漏写符号"*", 否则它为
一般的整型变量了。另外, 在定义中的int 表示该指针变量为指向整型数的指针
类型的变量, 有时也可称ip为指向整数的指针。ip是一个变量, 它专门存放整型
变量的地址。
指针变量的一般定义为:
类型标识符 *标识符;
其中标识符是指针变量的名字, 标识符前加了"*"号, 表示该变量是指针变
量, 而最前面的"类型标识符"表示该指针变量所指向的变量的类型。一个指针变
量只能指向同一种类型的变量, 也就是讲, 我们不能定义一个指针变量, 既能指
向一整型变量又能指向双精度变量。
指针变量在定义中允许带初始化项。如:
int i, *ip=&i;
注意, 这里是用&i对ip初始化, 而不是对*ip初始化。和一般变量一样, 对于外
部或静态指针变量在定义中若不带初始化项, 指针变量被初始化为NULL, 它的值
为0。Turbo C中规定, 当指针值为零时, 指针不指向任何有效数据, 有时也称指
针为空指针。因此, 当调用一个要返回指针的函数(第五节中介绍)时, 常使用返
回值为NULL来指示函数调用中某些错误情况的发生。
1.1.2 指针变量的引用
既然在指针变量中只能存放地址, 因此, 在使用中不要将一个整数赋给一指
针变量。下面的赋值是不合法的:
int *ip;
ip=100;
假设
int i=200, x;
int *ip;
我们定义了两个整型变量i, x, 还定义了一个指向整型数的指针变量ip。i, x中
可存放整数, 而ip中只能存放整型变量的地址。我们可以把i的地址赋给ip:
ip=&i;
此时指针变量ip指向整型变量i, 假设变量i的地址为1800, 这个赋值可形象理解
为下图所示的联系。
ip i
┏━━━┓ ┏━━━┓
┃ 1800 ╂──→ ┃ 200 ┃
┗━━━┛ ┗━━━┛
图1. 给指针变量赋值
以后我们便可以通过指针变量ip间接访问变量i, 例如:
x=*ip;
运算符*访问以ip为地址的存贮区域, 而ip中存放的是变量i的地址, 因此, *ip
访问的是地址为1800的存贮区域(因为是整数, 实际上是从1800开始的两个字节),
它就是i所占用的存贮区域, 所以上面的赋值表达式等价于
x=i;
另外, 指针变量和一般变量一样, 存放在它们之中的值是可以改变的, 也就
是说可以改变它们的指向, 假设
int i, j, *p1, *p2;
i='a';
j='b';
p1=&i;
p2=&j;
则建立如下图所示的联系:
p1 i
┏━━━┓ ┏━━━┓
┃ ╂──→ ┃ 'a' ┃
┗━━━┛ ┗━━━┛
p2 i
┏━━━┓ ┏━━━┓
┃ ╂──→ ┃ 'b' ┃
┗━━━┛ ┗━━━┛
图2. 赋值运算结果
这时赋值表达式:
p2=p1
就使p2与p1指向同一对象i, 此时*p2就等价于i, 而不是j, 图2.就变成图3.所示:
p1 i
┏━━━┓ ┏━━━┓
┃ ╂──→ ┃ 'a' ┃
┗━━━┛ ┌→ ┗━━━┛
p2 │ j
┏━━━┓ │ ┏━━━┓
┃ ╂─┘ ┃ 'b' ┃
┗━━━┛ ┗━━━┛
图3. p2=p1时的情形
如果执行如下表达式:
*p2=*p1;
则表示把p1指向的内容赋给p2所指的区域, 此时图2.就变成图4.所示
p1 i
┏━━━┓ ┏━━━┓
┃ ╂──→ ┃ 'a' ┃
┗━━━┛ ┗━━━┛
p2 j
┏━━━┓ ┏━━━┓
┃ ╂──→ ┃ 'a' ┃
┗━━━┛ ┗━━━┛
图4. *p2=*p1时的情形
通过指针访问它所指向的一个变量是以间接访问的形式进行的, 所以比直接
访问一个变量要费时间, 而且不直观, 因为通过指针要访问哪一个变量, 取决于
指针的值(即指向), 例如"*p2=*p1;"实际上就是"j=i;", 前者不仅速度慢而且目
的不明。但由于指针是变量, 我们可以通过改变它们的指向, 以间接访问不同的
变量, 这给程序员带来灵活性, 也使程序代码编写得更为简洁和有效。
指针变量可出现在表达式中, 设
int x, y *px=&x;
指针变量px指向整数x, 则*px可出现在x能出现的任何地方。例如:
y=*px+5; /*表示把x的内容加5并赋给y*/
y=++*px; /*px的内容加上1之后赋给y [++*px相当于++(px)]*/
y=*px++; /*相当于y=*px; px++*/