明白了指针是什么东西是不够的,要会用才是关键,所以这节我们来说说指针的运用。
1.做为函数的In/Out参数传递
指针只是一个地址,至于地址上有什么样的东西,有多大,完全是灵活的,所以当一个参数是输入又是输出时就可以用指针,但要注意的是当程序对这个地址赋值后,也就是返回后,原来做为In的时候的值已被改变。
如下面,当执行完函数f,i的值会变成20
void main(void)
{
int i = 10;
f(&i);
}
void f(int* p)
{
*p = *p*2;
}
2.让函数可以传递更多的参数,并节省空间
指针不管是什么类型,它都是一个地址,我们按32位算也就占用4个字节的空间,可指针所指的内容理论上是可以无限大,当然限制于你的内存。
如下面程序,在函数f的调用过程中,只传给f函数的是一个地址,也就是对于main函数来说,内存的消耗就是A结构的大小加上4个字节的址,如果是用整个结构的传递,那么首先它会产生更多的内存消耗,因为A的结构体比较大,而值传递是要进行内容复制的,这也相应的增加了一个复制的过程,也就相应的增加性能消耗。但是传值的优势在于因为进行的复制操作,所以在函数f内部的执行过程中都不会修改到aa这个变量,可是如果是指针(传址),那么f函数执行过程中对p成员的修改也就是对aa成员的修改(利用这个特性可以干很多事^_^)。
struct A
{
int a;
int b;
char c[1024];
}
void main(void)
{
A aa;
f(&aa);
}
void f(A* p)
{
p->a = p->*2;
}
3.指针与数组
前面说过指针是一个地址,对于这个地址是什么,那是灵活的所以先看看下面的代码
第1、2两句就不用说了吧,我们从第三句说起:
第3句:数组的名称就是数组0的首地址,所以这句就是让p指向i这个数组的0元素
第4句:对数组元素的取址,将p指向数组的第5元素
第5句:这才是重点,任何指针都可当数组来使用,p是指向的数组的第5元素,那么p[0]就等同于i[5],所以这句就是对i数组的最后5元素赋值0~5(也就是k的值)
第6句:对p进行重定向,这也是常用的操作,和第3句类似,让p指向j这个数组的0元素
第7名:与第5句类似,但注意这时候p的指向,对j数组的最前面5个元素赋值k
void main(void)
{
int i[10];
int j[1024];
int *p = i;
p = &i[5];
for (int k = 0;k<5;k++) p[k] = k;
p = j;
for (int k = 0;k<5;k++) p[k] = k;
}
4.类、结构的指针
类比结构更先进的是增加的方法,也让它更容易实现面向对象的设计思想,对于成员变量跟普通变量没什么区别。你可以把它当成是一个有不同数据类型成员的数组。
5.函数指针
函数指针,这是指针比较高级的应用,一般学习完基本教程,大概对函数指针的理解就是做为回调使用。我想告诉大家的是函数指针无处不在,当然基本还是做为回调。
Window编程里最常见的控件的事件机制,其实就是函数指针的最常见的运用。比如我们写一按钮的单击事件,只要在界面上双击按钮就可以直接跳进这个事件的函数体,实际上是开发环境帮你做了很多事,你也完全可以手动来完成这些事。就是手动写好你的函数,并指定OnClick事件函数指针指向你写的函数,这些动作也是开发环境帮你做的。
事件的触发机制主要也是靠回调:先为事件指定一个回调函数,当某个事件来的时候调用这个回调函数,这也是程序的主动机制,也叫主动程序,上面所提到的控件事件不就是这样的一个原理吗。