我常常看到很多人无法理解何为指针,以至于产生了许多奇怪的理解。有人说,一维数组就是一级级指针,二维数组就是二级指针。因为一维数组的数组名就是该数组的首元素的地址,而二维数组的数组名是一个一维数组的首元素地址。所以他们得出结论一维数组就是一级级指针,二维数组就是二级指针。这种荒谬的结论
有人说,指针不能简单了理解为就算地址。至于为什么,我也听的云里雾里。
何为指针
首先我们需要理解什么叫做指针,指针说白了就是地址。我们将地球当作一个一级指针,我们说小明住在地球,中国,北京。那么地球是一级指针,中国是二级指针,北京是三级指针。当我们说小明是北京人的时候,就已经知道他是中国人,是一个地球人了。北京指向中国,中国指向地球。
然后,我们还需要知道,像地球,中国,北京这样的词汇都表示地域,我们称之为地址。这些地址唯一的作用就是表明位置的。而小明不一样,他是人,我们称之为数据。数据可以进行算数运算+-*/,能够进行进行逻辑运算,是真还是假。他们能够表示一个数字123,能够表示一个字符a,能够表示一个字符串bcd,能够表示一个中文“您好”。这些都不是地址所能做到的,地址唯一作用就是表明我们要访问哪一个地方。
一个指针里面包含了什么
我们看一个指针如何写的
int* p1; //int型一级指针int** p2;//int型二级指针char* p3;//char型一级指针char** p4; //char型二级指针
我们看到,指针居然还有类型!有int型,char型。为什么我们要划分这么多类型呢?指针不应该只是表示一个地址吗?
我们来看地图。当我们说北京的时候,脑海里面想到的就是他所处的位置,其次,我们还能够想到他的占地面积大概多大。不然,我们说北京,那么北京位置我们是知道了,那么他有多大呢?如果北京大小是无穷大,那么还需要划分这么多区域干嘛?世界就叫北京算了撒。很显然这很荒谬。
指针也是如此,当我们知道指针所指向的地址的时候,我们需要知道他之后需要访问几个bit。比如说,现在如果有一个指针,储存的是0x5565。那么现在我访问到0x5565这个地址的时候,需要查看他是什么类型的指针,如果是char型,就从0x5565向后访问8个bit,如果是int型,就从0x5565向后访问32个bit(假设是32位系统,不同位系统int大小不同)。
所以说,一个指针不但表示了地址,同时还表示他需要访问地址的大小。
数组名到底是啥
其实数组名是指针,这句话没错。错就错在,数组就是指针。这句话有问题。为什么这么说呢?因为数组储存的是数据,而指针储存的是地址!我上面说了数据和地址的含义,就不再赘述。
int arr1[5] = { 0, 1, 2, 3, 4 };; //这是一个数组arr1 = &arr[0];//这两个表示该数组的首元素地址,打印如下结果一致printf("arr1:%d\n", arr1);printf("&arr1[0]:%d\n",&arr1[0]);arr[0] = *arr; //这两个都表示该数组的首元素数据arr[1] = *(arr+1); //这两个都表示该数组的第二个元素数据printf("arr1[0]:%d\n", arr1[0]);printf("*arr1:%d\n", *arr1);printf("arr1[1]:%d\n", arr1[1]);printf("*(arr1+1):%d\n", *(arr1+1));
我们上面说了,指针唯一的作用就算表示地址,数据能够进行逻辑运算+-*/。为什么这里的指针也能逻辑运算呢?很好理解,我们知道北京了,北京包含了他的地址,也包含了他的大小。那么假设现在小明在黑龙江省,+表示向东走,-表示向西走。我们可以说,小明是在北京+4,表明小明现在在以北京为坐标原点,向东走4个北京距离的地方(是不是的不清楚,乱讲的)。
一维数组和二维数组数组名区别
我们都知道一维数组和二维数组的数组名都表示地址,但是两者有什么区别呢?我们写如下函数
int main(){int arr1[5] = { 0, 1, 2, 3, 4 };int arr2[5][5];printf("arr1:%d\n", arr1);printf("arr1+1:%d\n", arr1+1);printf("arr2:%d\n", arr2);printf("arr2+1:%d\n", arr2+1);return 0;}
结果如下,我们发现打印arr1和arr+1两者之间相差了一个4,为什么呢?因为我现在是出于32位的编译环境,32位系统下int是4个字节。一维数组的数组名表示首元素地址,元素为int型,指针每次访问4个字节,所以相差了一个4。
而arr2与arr2+1相差了一个20。这又是为什么呢?因为二维数组的数组名表示该二维数组的第一个一维数组的地址。我们这里规定了,这个二维数组里面是包含有5个一维数组,而这5个一维数组里面有5个int型数据。所以每次访问的字节数为5(一维数组元素个数)*4(int为4个字节)=20次
我个人认为,可能还有人不能理解。我们继续实验。
int main(){int arr1[5] = { 0, 1, 2, 3, 4 };printf("arr1:%d\n", arr1);printf("arr1+1:%d\n", arr1+1);printf("&arr1:%d\n", &arr1);printf("&arr1+1:%d\n", &arr1+1);return 0;}
结果如下,arr1和arr1+1相差4我们已经知道了。而&arr1和&arr1+1呢?我们上面说了,一个指针不但要包含地址,还需要包含大小。当我们只是arr1和arr1+1的时候,包含的是一个int数据,4个字节。而我们是&arr1和&arr1+1的时候,包含的是5个int,20个字节。两者所包含的要访问数据的大小不同!
但是我们需要注意一点,arr1与&arr1是一样的,因为他们所指向的地址都是一样的。
数组指针和指针数组又是啥
有些人肯定听过数组指针是指针,指针数组是数组。听完之后依旧云里雾里,啥东西麻。首先我们要了解数组指针和指针数组的写法
int *p1[10]; //指针数组int(*p2)[10];//数组指针
数组指针
数组指针就是指针。数组指针和二维数组的数组名有点类似,但是又有不同。
相同点
数组指针和二维数组的数组名共同点是,二维数组的数组名表示多个连续的一维数组的第一个一维数组地址。二维数组的数组名的大小由第一个【x】中的x和数组类型所决定。
数组指针中的存储的是一个一维数组的地址,而这个一维数组的大小和类型由第一个【x】中的x和什么类型的指针决定。
int(*p2)[10];//数组指针,指针所指向的是一个int型数组,该数组由10个元素组成
不同点
二维数组的数组名所指向的地址在二维数组创立之时,就已经固定了。但是数组指针所指向的地址,可以随意改变。
int main(){int arr1[5][5]; //arr1所指向地址已经固定int arr2[5][5]; //arr2所指向地址已经固定int(*p)[5] = arr1;//此数组指针所指向的地址可以改变printf("p:%d\n", p);printf("arr1:%d\n", arr1);p = arr2;printf("p:%d\n", p);printf("arr2:%d\n", arr2);return 0;}
指针数组
一般的数组,里面存放的是数据。而指针数组有所不同,他所存放的是指针。但是指针数组还是指针。
int *p1[10]; //指针数组//这个可以理解为,有一个数组里面有10个元素,里面存放的都是int*的指针
指针数组某种意义上来说,可以理解为一维数组的数组名。区别在于一维数组的数组名所指向的地址一开始就已经确认好了,而且一维数组的数组所指向的地址必然是连续的。但是指针数组里面所存放的指针可以不是连续的,指针数组里面的第一个元素所指向的地址可以小于第二个元素所指向的地址。
指针数组和数组指针区别
讲了这么多,可能还有人分不清楚指针数组和数组指针。
指针数组和数组指针最大的区别在于,他们所访问的大小不同,所拥有的指针数量不同。
int(*p)[5];//数组指针,只有一个指针,能够访问4*5=20个字节int *p1[5];//指针数组,拥有5个指针,但是每一个指针只能访问4个字节