前言
各位读者朋友们大家好!前几期我们讲解了指针的基本内容,这期我们来更加深入的了解指针,开启指针详解的第二部分。
一. 数组名的理解
在我们使用指针访问数组时,有这样的代码:
int array[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &array[0];
这里我们通过&array[0]得到了数组首元素的地址,但其实数组名本身就是数组首元素的地址,我们测试一下是不是这样。
通过运行结果,数组名和数组首元素的地址输出的结果一样,我们可以得出数组名就是数组首元素的地址。
这时候,可能就会有疑问,如果数组名是数组首元素的地址,那下面这段代码如何理解呢?
int main()
{
int array[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%zd", sizeof(array));
return 0;
}
代码的输出结果为40,按照前面讲的数组名是数组首元素的地址,是地址输出结果应该是4或者8才对。
数组名是数组首元素地址的这个说法是正确的,只不过有两个例外:
- sizeof(数组名) sizeof的括号中单独放数组名,这里的数组名代表整个数组,计算的是整个数组的大小。
- &数组名 ,这里的数组名代表整个数组,取出的是整个数组的地址。
除了上面的两种情况,其他任何地方使用数组名,都是数组首元素的地址。
上面说了这么多,让我们看一下下面这段代码:
int main()
{
int array[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &array[0];
printf("&array[0] = %p\n", &array[0]);
printf("array = %p\n", array);
printf("&array = %p\n", &array);
return 0;
}
这三者的打印结果一模一样,那&array和array有什么区别呢?
&array是整个数组,而array是数组首元素的地址,通过下面这段代码我来解释这两者有何区别。
int main()
{
int array[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &array[0];
printf("&array[0] = %p\n", &array[0]);
printf("&array[0]+1 = %p\n", &array[0] + 1);
printf("array = %p\n", array);
printf("array + 1 = %p\n", array + 1);
printf("&array = %p\n", &array);
printf("&array + 1 = %p\n", &array + 1);
return 0;
}
通过运行结果我们看到,&array[0]+1 和 array + 1都只跳过了四个字节,而&array + 1跳过了40个字节。
这是因为&array[0] 和 array 都是数组首元素的地址,+1就只跳过一个int类型的大小即4个字节,&array 是整个数组的地址,+1跳过整个数组,也就是40个字节。
二. 使用指针访问数组
通过前面的知识,结合数组的特点,我们就可以使用指针来访问数组了。
int main()
{
int array[10] = { 0 };
int* p = array;
for (int i = 0; i < 10; i++)
{
scanf("%d", p + i);
}
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
在之前打印数组元素的时候,我们通常使用array[i]的方式,我们知道p其实就是array,那么array[i]和*(p+i)有什么区别吗?
其实,array[i]和*(p+i)是等价的,*(p+i)也可以写成p[i]。同理,*(array+i)也和array[i]是等价的。这也就告诉我们为什么数组的下标是从0开始的。
三. 一维数组传参的本质
在数组作为实参进行传递的时候,我们是在主调函数中计算出数组元素个数后传给被调用的函数的,那我们可不可以在被调用的函数内部计算数组的元素个数呢?
void Test(int array[])
{
printf("%d ", sizeof(array) / sizeof(array[0]));
}
int main()
{
int array[] = { 1,2,3,4,5,6,4,7,8,9,10 };
Test(array);
return 0;
}
程序输出的结果是:
根据我们的设想,程序运行的结果应该是10,而实际结果是1,这是为什么?
这就要学习数组传参的本质了。我们知道,数组名是数组首元素的地址,在数组传参调用的时候我们其实将数组首元素的地址传递给了被调函数,也就是说,数组传参的本质上是传递的数组首元素的地址。 这就是为什么在32位环境下得出结果为1。因为数组传递给函数的是指针,所以在函数内部是无法求得数组的元素个数的。
四. 二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里呢?
答案:二级指针
对于二级指针的运算有:
-
- *pp 通过对pp解引用,找到p。
-
- **pp,先通过*pp找到p,再对p解引用,找到了a。
对于二级指针的理解我们同样拆开看:
因此,pp+1跳过4或8个字节,是一个指针变量的大小,p+1跳过4个字节,是一个整型的大小。
五. 指针数组
指针数组是指针还是数组呢?
答:指针数组是用来存储指针的数组。
整型数组存放的每个元素是int类型,指针数组存放的每个元素都是指针变量类型。所以,指针数组的写法是变量类型* 数组名[元素个数]
六. 指针数组模拟二维数组
int main()
{
int array1[5] = { 1,2,3,4,5 };
int array2[5] = { 2,3,4,5,6 };
int array3[5] = { 3,4,5,6,7 };
int* parray[3] = { array1,array2,array3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", *(*(parray + i) + j));
//printf("%d ", parray[i][j]);
}
printf("\n");
}
return 0;
}
通过*parray,找到array1,就是第一个一维数组的数组名,再对一维数组解引用访问得到整个一维数组,然后再重复以上步骤,遍历所有的一维数组。
七. 结语
以上我们就讲完了指针二的内容,希望对大家有帮助,欢迎大家批评指正。