指针
在 C 语言中,要表示列表,就必须正确的使用指针。C 语言中的指针一直如洪水猛兽般存在。虽然概念上非常简单,但是用起来却变幻多端,神秘莫测,这使得指针看上去比实际要可怕得多。幸运的是,在本书中我们只会用一些指针在 C 语言中最常规的用法。
我们之所以需要指针,主要是由 C 语言中函数的工作方式决定的。C 语言函数的参数全部是通过值传递的。也就是说,传递给函数的实际是实参的拷贝。对于 int
、long
、char
等系统类型以及用户自定义的结构体都是成立的。这种方式适用于绝大多数情况,但也会偶尔出现问题。
一种常见的情况是,如果我们有一个巨大结构体需要作为参数传递,则每次调用函数,就会对实参进行一次拷贝,这无疑是对性能和内存的浪费。
另外一个问题是,结构体的大小终究是有限的,无论多大,也只能是个固定的大小。而如果我们想向函数传递一组数据,而且数据的总数还是不固定的,结构体就明显的无能为力了。
为了解决这个问题,C 语言的开发者们想出了一个聪明的办法。他们把内存想象成一个巨大的字节数组,每个字节都可以拥有一个全局的索引值。这有点像门牌号:第一个字节索引为 0,第二个字节索引为 1,等等。
在这种情况下,计算机中的所有数据,包括当前运行的程序中的结构体、变量都有相应的索引值与其对应(数据的开始字节的索引作为整个数据的索引)。所以,除了将数据本身拷贝到函数参数,我们还可以只拷贝数据的索引值。在函数内部则可以根据索引值找到需要的数据本身(译者注:我们将这个索引值称为地址,存储地址的变量称为指针)。使用指针,函数可以修改指定位置的内存而无需拷贝。除此之外,指针还可以做其他很多事情。
因为计算机内存的大小是固定的,表示一个地址所需要的字节数也是固定的。但是地址指向的内存的字节数是可以变化的。这就意味着,我们可以创建一个大小可变的数据结构,并将其指针传入函数,对其进行读取及修改。
所以,所谓的指针也仅仅是一个数字而已。是内存中的一块数据的开始字节的索引值。指针的类型用来提示程序员和编译器指针指向的是一块什么样的数据,占多少个字节等。
指针类型是在现有类型的后面加一个星号组成,我们之前已经见过一些指针的示例了,如:mpc_parser_t*
、mpc_ast_t*
以及 char*
。
要创建指针,我们就需要获取数据的地址。C 语言提供了取地址符(&
)来获取某个数据的地址。在前面的章节中,我们也曾传给过 mpc_parse
函数一个指针,以便其能将输出放到我们声明的 mpc_result_t
变量中。
最后,为了获取指针所指向的地址的数据值(称为解引用),我们需要在指针左边使用 *
操作符。要获取结构体指针的某个字段,需要使用 ->
操作符,而不是 .
,这你在第七章已经见过了。