在开发中,data structure 越复杂,算法就越简单。
Data structure 依靠的就是指针。不能靠内存的绝对位置表示数据的关系吧,那样的数据移动操作能把 data bus 都烧掉。
指针的存在可以让程序直接操纵内存
这在很多没有指针的程序设计语言中是做不到的
因为可以直接操作内存,就有两点优势
1、更灵活
2、更高效
之所以说指针是C语言的精髓,在于,你会用指针、用好指针之后,能发挥C语言的强大威力;如果你不会用,C语言绝对不会比其他的任何一种语言好。
举两个我自己比较熟悉的例子。一个是函数指针,比如一个计算一元实函数定积分的函数,可以写成如下形式
double integrate( double (*f)( double x ), double lb, double ub );
其中,lb是积分下界,ub是积分上界,f是被积函数(的指针)。这样就可以对所有double (*)(double x)形式的函数进行积分计算了。但是,如果是在Java中,实现这个功能可能就会比较麻烦。首先需要为这个计算积分的函数声明一个积分计算器的类,然后声明一个可积分的接口,能够被积分的函数的类需要继承并实现这个接口,然后再把这个被积函数的类的一个实例传给这个这个积分计算器进行计算....
另外一个用得比较多的是结构体指针。如果只把结构体当成一个数据的集合的话,那么结构体并没有什么好用的。在处理二进制格式的数据,尤其是网络数据的数据包的时候,结构体指针非常好用。比如我们定义一个以太网帧首部的格式
struct eth_header {
unsigned char dst[6];
unsigned char src[6];
unsigned short int ptype;
};
我们用socket读到一段二进制数据的时候,把指向该缓存的指针,用一个强制类型转换变成一个struct eth_header*类型的指针,那么这个数据包的内容就可以很容易的读出来了。比如读源地址,只需要这样
unsigned char* buffer = .......
struct eth_header* header = (struct eth_header*) buffer;
printf( "SRC-MAC: %02X-%02X-%02X-%02X-%02X-%02X ",
header->src[0], header->src[1], header->src[2],
header->src[3], header->src[4], header->src[5] );
这样的特性,在Java这些高级语言里面就比较难以做到。
关键在于参数的传递吧。
因为,我们知道,c语言在参数传递的时候,只有一种,那就是值传递类型。
比如从函数A中跳到函数B中。我们调用函数B肯定是想为A服务的,但是现在跳到函数B中后,就是换了另外一个环境,所有的东西都是新的,它在栈上面重新分配了一块新的内存供函数B使用。所以从A传到B的变量只是在B中的栈中复制了一份,等到B调用完后,B中的所有东西,就没有了,销毁了,被OS回收了。所以A是找不到的。
再来看,如果你想要B来该表A中的变量a,那么你只是把A中变量的值a传入B的话,你是白做的了,因为B中的a和A中的a知识值一样而已,它们是完完全全的两个变量啊,你对B中的a做了如何改变,丝毫不会影响A中的a的值。
那么问题来了,怎么办?答案就是传递A中a的地址过去,因为A中a的地址是不变的啊,你在B中使用这个地址,依然是A中a的地址。所以,你传递&a过去,那么在B中操作这个地址,你就达到了改变A中a的值的目的。
理解上面的过程,你必须对函数的调用过程很熟悉,包括函数栈,内存回收的知识,以及一个进程的内存分布情况。
上面的传递过程还有一个优点:那么就是,如果变量a特别大,你只需要在B中复制一个地址值就可以了,这个代价是很小的。而且,如果你不想在B中改变a的值,你可以在传递的时候加上const关键字,任君选择。
总之,指针是很灵活的用法,它的可用性在于在内存中每个存储单元的地址都是唯一的。