天天看点

C语言---从指针到二重指针(上)

江湖传言:得指针者得C语言。不知道这句话是从什么时候开始流传的,不过这也不重要,重要的是我们隐约能从中感受到指针在C语言中的重要性。对于初学C语言的人来说,指针可能是大多数人心中的痛。笔者当年学C语言的时候在指针这块还算顺利,自以为已经熟练掌握了指针,等到后来学习数据结构的时候,才发现自己其实一直都理解偏了,那叫一个尴尬,所以至今我都不敢说自己学会了指针,就像大多数人都不敢说自己精通了C语言一样。本文试图用较为浅显易懂的方式来重新认识指针。

先来看一个简单的例子:

#include <stdio.h>
#include <stdlib.h>
void swap(int a, int b)
{
	int temp;
	printf("@@2  a = %d, b = %d\n", a, b);
	temp = a;
	a = b;
	b = temp;
	printf("@@3  a = %d, b = %d\n", a, b);
}
int main(void) {
	int a = 1;
	int b = 3;
	printf("@1  a = %d, b = %d\n", a, b);
	swap(a, b);
	printf("@4  a = %d, b = %d\n", a, b);
	system("pause");
}
           

运行结果如下:

C语言---从指针到二重指针(上)

从程序的运行结果可以看到,在swap函数中明明完成了对a,b两个变量的交换,可回到主函数后a,b还是原来那个a,b,主函数对swap函数的所作所为竟毫无反应,莫非电脑进了水?其实不然,来几条打印语句看看发生了什么。

#include <stdio.h>
#include <stdlib.h>
void swap(int a, int b)
{
	int temp;
	printf("\n @@2  the address of a is: %p\n", &a);
	printf(" @@2  the address of b is: %p\n", &b);
	printf(" @@2  a = %d, b = %d\n", a, b);
	temp = a;
	a = b;
	b = temp;
	printf("\n @@3  the address of a is: %p\n", &a);
	printf(" @@3  the address of b is: %p\n", &b);
	printf(" @@3  a = %d, b = %d\n", a, b);
}
int main(void) {
	int a = 1;
	int b = 3;
	printf(" @1  the address of a is: %p\n", &a);
	printf(" @1  the address of b is: %p\n", &b);
	printf(" @1  a = %d, b = %d\n", a, b);
	swap(a, b);
	printf("\n @4  the address of a is: %p\n", &a);
	printf(" @4  the address of b is: %p\n", &b);
	printf(" @4  a = %d, b = %d\n", a, b);
	system("pause");
}
           

注:printf("\n@@2  the address of a is: %p\n",&a);语句中的%p(转换说明符)和&(取地址运算符)可以打印变量在计算机中的存储地址,“&”即为取地址运算符,这个后面会讲到。

 运行结果如下:

C语言---从指针到二重指针(上)

上面这段代码增添了不少打印语句,显得十分臃肿,但结论是十分清晰的:主函数main中的a,b的地址和swap函数中a,b的地址不同,原来他们不是同一个a,b。既然不是同一个,为了避免出现六耳猕猴的闹剧,我们用c,d来代替swap中的a,b。代码如下:

#include <stdio.h>
#include <stdlib.h>
void swap(int c, int d)
{
	int temp;
	printf("\n @@2  c = %d, d = %d\n", c, d);
	temp = c;
	c = d;
	d = temp;
	printf(" @@3  d = %d, d = %d\n", c, d);
}
int main(void)
{
	int a = 1;
	int b = 3;
	printf(" @1  a = %d, b = %d\n", a, b);
	swap(a, b);
	printf("\n @4  a = %d, b = %d\n", a, b);
	system("pause");
}
           

运行结果如下:

C语言---从指针到二重指针(上)

 这一切该作何解释呢?我曾对此困惑了一段时间,直到看到了下面这句话:“C语言是以传值的方式将参数值传递给被调函数--C语言之父”,所谓传值,即为赋值,在这个程序中调用swap函数具体过程如下:

c = a, d =b;          (此时:a = 1, b = 3,c = 1, d = 3)

temp = c;

c = d;              (此时:a = 1,b = 3, c = 3, d = 4)

d = temp;       (此时:a = 1, b = 3,c = 3, d = 1)

从中可以看出:调用swap函数时,a和b对c和d赋完值后就没它们什么事了,交换数值只发生在c,d之间,所以swap函数结束后,a,b并没有实现数据交换。

      那么我们就无法实现数据交换这么简单的功能吗?路总是有的。一种方法是直接在主函数中对a,b进行数据交换,这样不会涉及值传递,但不符合C语言模块化的设计思路,当然也可以在swap()函数中使用return语句,但只能返回一个值,所以并不可行。另一种方法就是运用指针。

      所谓指针者,即为保存变量地址的变量(在计算机中,数据是按地址在内存中存放)。有两个关于指针的运算符,分别是:取地址运算符“&”(用于获取变量的内存地址), 间接寻址运算符“*”(用于访问指针变量的内容)。可以这么理解:有一个int类型的变量a,记作int a ,对变量a进行取地址运算即可得到一个指针变量b,b = &a(即变量b为变量a的地址,由于a是int类型的变量,所以b是一个int类型的指针变量,记作int* b(int*为变量b的数据类型)。在对b进行间接寻址运算获得b所代表的地址所存放的值(即为变量a的值),a = *b(即a = *(&a)).由于存在这种关系,所以我们可以理解为指针变量b指向了另一个变量a。具体可以参考以下代码:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	int a = 1;
	int* b = &a;
	printf(" a = %d\n", a);
	printf(" the address of a is: %p\n", &a);
	printf(" b = %p\n", b);
	int c = *b;
	printf(" c = %d\n", c);
	system("pause");
}
           

运行结果如下:

C语言---从指针到二重指针(上)

好了,现在我们回到前面的问题:用指针实现数值交换,代码如下:

#include <stdio.h>
#include <stdlib.h>
void swap(int* c, int* d)		//①
{
	int tmp;
	int* temp = &tmp;
	*temp = *c;			//②
	*c = *d;			//③
	*d = *temp;			//④
}
int main(void)
{
	int a = 1;
	int b = 2;
	printf(" @1 a = %d\n", a);
	printf(" @1 b = %d\n", b);
	swap(&a, &b);
	printf("\n @2 a = %d\n", a);
	printf(" @2 b = %d\n", b);
	system("pause");
}
                ①
{
	int tmp;
	int* temp = &tmp;
	*temp = *c;			//②
	*c = *d;			//③
	*d = *temp;			//④
}
int main(void)
{
	int a = 1;
	int b = 2;
	printf(" @1 a = %d\n", a);
	printf(" @1 b = %d\n", b);
	swap(&a, &b);
	printf("\n @2 a = %d\n", a);
	printf(" @2 b = %d\n", b);
	system("pause");
}
      

运行结果如下:

C语言---从指针到二重指针(上)

好了,我们再来看看这中间发生了什么,为了更清晰,这次使用图说话:

C语言---从指针到二重指针(上)

从图中可以看出,在swap函数中我们只是交换了指针变量c和d所指的变量(即1和2),由于他们是由&a和&b赋值而得,但c和d本身并没有任何改变,所以swap函数结束后,c就是&a, d就是&b,如下图所示:

C语言---从指针到二重指针(上)

至此我们完成了两个数的交换。

好了,说了这么多,指针究竟有什么用呢?这里只说几点:

一:指针可以使我们直接操作内存,这是很多其他高级语言所不具备的(比如Java),直接对内存进行操作可以使得程序的运行效率更高。比如现在你需要对一个函数传入一个结构体参数,这个结构体如下:

struct para {
	int data1;
	int data2;
	char data3;
           

//此处略去一万字.....

double dataN; };

这么一大块数据,直接它传入函数,,这是一件十分费力不讨好的事,如果传入这个结构体的指针,那就简单高效多了,这就相当于你想把自己一大屋子的零食送人,你可以直接送到对方的屋子里,比如叫辆卡车来拉,你也可以告诉对方零食在哪个房间,叫他自己来取,传指针明显属于后者。

二:由于C语言函数只能返回一个值(使用return语句),当程序需要返回多个值时,return已经满足不了人民日益增长的物质文化需要,这时我们可以考虑使用指针来实现,具体可参考上面用指针实现两数交换的程序。

三:指针和结构体进行组合可以使我们更好地来组织数据,比如实现数据的动态存储,­­­­­­­­­­­­­­­­对数据快速的进行查找和排序。限于篇幅,在这里就先不展开来谈了。(未完待续)。

继续阅读