在工作中又遇到和数组,指针有关的代码了,我看的有点懵,但是我明白数组和指针肯定是拥有完全不同的特征的。先从数组下手吧,好好回顾一下,查漏补缺。
下面定义一个数组。
int num[3];
我们大多数时候都会使用到数组名。但是你真的清楚数组名是什么类型吗?
首先,num[1]毫无疑问是int类型,那么num是什么类型?它到底表示了什么?
在C中,绝大多数时候(有例外情况,下文我们会讨论),数组名是一个指向XXX类型的常量指针。例如num,它的类型应该是指向int类型的指针常量。但是这并不是说数组和指针是相同的。当数组名在表达式中使用的时候,编译器会给它产生一个指针常量。例如num,会被编译器处理为int *const num。即num只能指向固定的内存区域。这也是合理的,如果你能改变num的指向,那么就必须移动整个数组(因为数组是一段连续的内存空间)。也就是说数组名不代表整个数组,切记。
当数组名作为sizeof操作符或是&(取地址)操作符的操作数时。
sizeof(num); 返回整个数组的长度,而不是指针的长度。故应该返回12,而不是指针的大小。测试如下所示。
代码:
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int num[3] = { 1,2,3 };
cout << sizeof(num) << endl;
return 0;
}
运行结果:
# 这个操作产生的是一个指向数组的指针(int (*num) [3]),而不是一个指向某个指针常量的指针。例如:
很明显,对数组名取地址,产生的是一个 int (*)[3]的类型,而不是一个指向指针的指针。正确的使用如下所示,但是不建议这么使用。
除了上述两种例外情形,其余时候,数组名只是一个指针常量,也就是数组起始元素的地址。
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int num[3] = { 1,2,3 };
cout << num[1] << endl; //下标引用
cout << *(num + 1) << endl; //间接访问
return 0;
}
运行结果:
上面的代码表明,下标引用和间接引用并没有任何不同。
下面在看一些诡异的写法,这些写法将会导致程序的可读性,维护性大大下降。
int num[3] = { 1,2,3 };
int* p = &num[1];
cout << p[-1] << endl; //使用负数作为下标,输出结果是num[0],也就是1
cout << 2[num] << endl; //2[num] == num[2]
这段代码是合法的,能够通过编译,但是我们不应当在程序中书写这类代码。但是有的人在硬件驱动程序中可能书写了这样的代码,所以我们可能还是需要理解这些代码。
数组做函数参数时,我们一般将数组名作为参数传递给函数,也就是说传递给了函数指向该数组起始元素的指针。因此很容易就明白了,这是把指针拷贝了一份,而非整个数组。所以,我们说在C中所有传递给函数的参数都是通过传值方式进行的。因为,数组名在作为函数参数进行传递的时候,实际上一个指针。
不过编译器为了我们编写代码方便, 支持数组形式的函数形参。因此,下面的两种形式声明是等价的。
int function(int *num);
int function(int num[]);
我们并没有在num[]的[]中写上数组长度,实际上,你写上也可以。但是这在一维数组这里无关紧要。因为这传递的是指针,而不是整个数组,所以,如果函数需要知道数组的长度,那么长度必须作为一个显式的参数传递给函数。如下所示:
int function(int* num, unsigned int len);