如何理解C语言中的指针 ·中国科大 邓益民 1995-08-11 事实上,变量对应于计算机内存中的一个特定区域,该区域在内存中的位置称为变量的地址;而区域中所存放的数据称为变量的值。所以有时也称“变量的值是存放于变量中的”。指针实际上是一种数据类型,同整型、实型相类似,因而指针变量同整型变量、实型变量一样也是变量。指针变量的特别之处在于,存放于其中的是一个内存地址数。如果这个地址数对应另一个变量(比如某个整型变量)的地址,则称该指针变量“指向”这个变量。故而经常又称指针变量的值(某个地址数)为“指针”。所以当提到指针时一定要区分清楚是指数据类型(比如指针变量)还是指地址(比如某变量的指针)。 例如:int x,*xp; xp=&x; 定义了整型变量x,指针变量xp。xp中存放整型变量的地址,故又称xp为指向整型变量x的指针变量(通过xp=&x)。有了一个存放变量x地址的指针变量xp,则程序就可以通过xp来访问和使用x,方法是使用指针表达式*xp。下面结合几种常用情况讨论一下指针变量的使用方法,从中可以看出指针变量的优点所在,并加深对指针和指针变量的理解。 1.了函数对主函数的多参数返回 C语言中主子函数间参数是值传递,因而不能象Fortran语言那样直接实现多参数返回。由于指针变量中存放的是地址,故可通过把指针变量作为函数参数来解决这一问题。如: 主函数:… int x,y,z; … callsub(&x,&y,&z); … 子函数:callsub (xp,yp,zp) int *xp,*yp,*zp; … 通过主子函数间的参数传递(仍然是值传递),子函数中指针变量xp,yp,zp分别存放了主函数中变量x,y,z的地址。这样,子函数可通过xp,yp和zp来使用x,y,z。也就是说,子函数具备了处理主函数中变量的能力。比如子函数中如果使用语句:“*xp=10;”,则子函数就已经把主函数中的变量x由原先的某个数值改变成现在的10了。所以,确切地说,并不是这样的程序实现了三个参数(x,y,z)的返回,而是由于子函数本身就能够对这三个参数进行处理。 2.处理数组问题以便节省内存提高速度用指针变量来处理数组可以提高程序运行速度,有时还可节省内存空间。如: static char a[15]="This is a test"; 在计算机内存中开出两块区域: 注意到变量区中单独一个a,这是由于C语言中数组名标识符实际上对应一个指针,即该数组在内存中的起始地址(也就是&a[0])。 而如果使用指针变量,如: char *a="This is a test"; 此时a是一个指针变量,其中存放字符串的起始地址。很明显,这种用法只在内存中开出字符串数据区和变量区中的指针变量a,而省去了前一方法中的a[0]~a[14]。另外,也省去了为这些数组元素(字符串中每个字符)赋值,从而也提高了程序运行速度。 再者,若再使用*a、*(a+1)等方式来访问字符串中字符,而不是用数组元素a[0]、a[1]等来访问,则还可省去格式转换的时间。因为实际上C编译器总是先把a[0]转换成*a,把a[1]转换成*(a+1)等的,然后才能获取它们的值。 3.主子函数中数组元素值的双向传递 C语言中将数组名作为函数参数,可实现数组元素值的双向传递,如: 主函数:… int x[10] … gosub(x); … 子函数:gosub(y) 或 gosub(y) int *y; int y[]; … … 事实上这种情况下函数参数的传递仍然是单向的值传递。主函数中的实参x实际上表示的是数组x的起始地址(正如上面数组a[15]中的a一样),而子函数中的形参对应一个指针变量y,这样子函数中的y就指向了主函数中的数组x,因而子函数具备了处理主函数中数组x的能力。子函数通过y对数组x每一元素的改变,实际就改变了主函数中数组x的相应无素,从而产生了数组元素双向传递的效果。 上述几种情况涉及到了指向变量的指针变量(即指针变量中存放的是另一变量的地址)、指向字符串的指针变量(即指针变量中存放的是字符串的起始地址)以及指向数组的指针变量(即指针变量中存放的是数组元素的首地址)。理解了这些指针变量之后,对其他复杂应用的指针变量的理解也就迎刃而解了。比如: int max(), (*p)(); p=max; 定义了一个指向函数max()的指针变量p。这实际上就是说,定义了一个变量p,而p中存放的(也就是p的值)是函数max()在内存中的起始地址。有了这一定义之后,程序就可以通过p来访问函数max(),比如用(*p)()来调用该函数。 其他指针数组、指向多维数组的指针变量、返回指针值的函数以及指向结构、联合的指针变量等等,也都可以类似地加以理解。 因此,我们可以说,只要真正理解了C语言中变量的概念,就不难理解并掌握指针,从而灵活地加以应用。