可以说,指针是C语言的灵魂,指针提供一种以符号形式使用地址的方法。因为计算机的硬件指令非常依赖地址,指针在某种程度上把程序员想要传达的指令以更接近机器的方式表达。因此,使用指针的程序更有效率。尤其是,指针能有效地处理数组。所以如何用指针来表示数组呢?
我们举一个变相使用指针的例子:数组名是数组首元素的地址。也就是说,如果flizny是一个数组,下面的语句成立:
flizny = &flizny[0];
这段代码的核心就是,数组名是该数组首元素的地址。
flizny 和&flizny[0]都表示数组首元素的内存地址(&是地址运算符)。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值。注意指针加上一个数时,它的值发生了什么变化。
以下等式体现了C语言的灵活性(假设date是数组的指针变量,dates是数组变量):
date + 2 = &datas[2]; //相同的地址
*(date + 2) = dates[2]; //相同的值
以上关系表明了数组和指针的关系十分密切,可以使用指针标识数组的元素和获得元素的值。从本质上看,同一个对象有两种表示法。实际上,C语言标准在描述数组表示法时确实借助了指针。也就是说,定义ar[n]的意思是*(ar + n)。可以认为*(ar + n)的意思是“到内存的ar位置,然后移动n个单元,检索储存在那里的值”。
顺带一提,不要混淆 (dates+2)和dates+2。间接运算符()的优先级高于+,所以dates+2相当于(*dates)+2:
*(dates + 2) // dates第3个元素的值
*dates + 2 // dates第1个元素的值加2
明白了数组和指针的关系,便可在编写程序时适时使用数组表示法或指针表示法。
假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用该函数?也许是下面这样:
total = sum(marbles); // 可能的函数调用
那么,该函数的原型是什么?记住,数组名是该数组首元素的地址,所以实际参数marbles是一个储存int类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向int的指针:(注意ar是数组marbles的指针)
int sum(int * ar); // 对应的函数原型
sum()从该参数获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数。注意,该参数并未包含数组元素个数的信息。代码如下:
int sum(int * ar) // 相应的函数定义
{
int i;
int total = 0;
for (i = 0; i < 10; i++) // 假设数组有10个元素
total += ar[i]; // ar[i] 与 *(ar + i) 相同
return total;
}
既然能使用指针表示数组名,也可以用数组名表示指针。另外,回忆一下,+=运算符把右侧运算对象加到左侧运算对象上。因此,total是当前数组元素之和。
关于函数的形参,还有一点要注意。只有在函数原型或函数定义头中,才可以用int ar[]代替int * ar:
int sum (int ar[], int n);
int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只能用于声明形式参数。第2种形式(int ar[])提醒读者指针ar指向的不仅仅一个int类型值,还是一个int类型数组的元素。
因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,C才会把int ar[]和int * ar解释成一样。也就是说,ar是指向int的指针。由于函数原型可以省略参数名,所以下面4种原型都是等价的:
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
但是,在函数定义中不能省略参数名。下面两种形式的函数定义等价:
int sum(int *ar, int n)
{
// 其他代码已省略
}
int sum(int ar[], int n);
{
//其他代码已省略
}
可以使用以上提到的任意一种函数原型和函数定义。
我们总结一下以上所述,首先我们分析了C语言中数组与指针的关系,其核心就是数组名就是该数组首元素的地址,这也从侧面表示了C语言中的数组就是一种侧面利用指针关系的存在;其次我们对C语言中数组指针的引用做了分析,我们明白了指针可以表示数组名,数组名也可以表示指针。
另外不知小伙伴们在写代码的时候有没有发现这种错误:
warning: passing arg 1 of `你的某个函数' from incompatible pointer type
看完了上面的讲解,这里出现的问题自然不言而喻了,这往往是因为你在自定义函数的时候引用了数组的指针所致,下次在自定义函数中引入数组,直接将数组设为形参即可,不必再加指针!
[1] Prata S. C primer plus[M]. Pearson Education, 2014.