最近在复习c语言,看了C Primer Plus 这本书,对其中难以理解的数组和指针有一些自己小小的体会,现记录下来。
指针和一维数组:
#include
int urn[5] = {100, 200, 300, 400, 500};
int main(void)
{
int *ptr1, *ptr2, *ptr3;
ptr1 = urn; // 把数组的第一个元素的地址赋给指针
ptr2 = &urn[2]; // 把数组的第三个元素的地址赋给指针
printf("pointer value, dereferenced pointer, pointer address:\n");
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
ptr3 = ptr1 + 4; // 把ptr1所指内存的地址加上ptr1保存的类型的字节数*4之和赋给ptr3 即把&urn[4]赋给ptr3
printf("\nadding an int to a pointer:\n");
printf("ptr1 + 4 = %p, *(ptr1 + 3) = %d\n", ptr1 + 4, *(ptr1 + 3));
ptr1++; // 对ptr1使用自增运算符,注意自增运算符只能当ptr1被声明为变量时才能使用,若ptr1为一个数组名称则不能这样用,因为数组的首地址是一个常量。
printf("\nvalue after ptr1++:\n");
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
ptr2--; // 同理
printf("\nvalue after ptr2--:\n");
printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %p\n", ptr2, *ptr2, &ptr2); //此时ptr1 和ptr2 都指向同一个内存地址
--ptr1; // 把ptr1和ptr2的值恢复
++ptr2;
printf("\npointers reset to original values.\n");
printf("ptr1 = %p, ptr2 = %p\n", ptr1, ptr2);
printf("subtracting one pointer from another.\n");
printf("ptr1 = %p, ptr2 = %p\n, ptr2 - ptr1 = %d\n", ptr1, ptr2, ptr2 - ptr1);
printf("subtracting an int from a pointer.\n");
printf("ptr3 = %p, ptr3 - 2 = %p\n", ptr3, ptr3 - 2); // 同理此时ptr3 - 2表示ptr3所指的地址减掉2 * 它所指向的字节数
}
// 输出结果如下:
/*
pointer value, dereferenced pointer, pointer address:
ptr1 = 00407000, *ptr1 = 100, &ptr1 = 0061ff08
adding an int to a pointer:
ptr1 + 4 = 00407010, *(ptr1 + 3) = 400
value after ptr1++:
ptr1 = 00407004, *ptr1 = 200, &ptr1 = 0061ff08
value after ptr2--:
ptr2 = 00407004, *ptr2 = 200, &ptr2 = 0061ff04
pointers reset to original values.
ptr1 = 00407000, ptr2 = 00407008
subtracting one pointer from another.
ptr1 = 00407000, ptr2 = 00407008
, ptr2 - ptr1 = 2
subtracting an int from a pointer.
ptr3 = 00407010, ptr3 - 2 = 00407008
*/
其中使我更加深刻的理解了指针与数组配合的工作机制:
1,最基本的,一维数组名就是它的首地址,当以数组为实际参数传递给其他函数时,其他函数的形式参量实际就是指向数组元素的指针类型,而此时传递实际参数时,就可以直接传递数组名。
2,可以对指向数组元素的指针做增量运算,(可以使用自增运算符),因为指针本身是变量
3,当对指向数组的指针做加法运算时(加一个常数时),表示的是这个常数与指针所指的数据类型相乘后加到原始地址上
4,也可以做减法运算,可以减去一个整数,结论同上;也可以减去指向同一数组中较小的指针,注意不要越界,剩下的整数即为这个数组中两个指针指向元素之间的距离,注意并不一定是实际距离,这个距离指的是元素的个数。
指针和多维数组:
#include
int main(void)
{
int zippo[4][2] = {{2,4}, {6,8}, {1,3}, {5,7}};
printf(" zippo = %p, zippo + 1 = %p.\n", zippo, zippo + 1); // 打印二维数组名的内容(即其所指向的地址)
printf(" zippo[0] = %p, zippo[0] + 1 = %p.\n", zippo[0], zippo[0] + 1);
printf(" *zippo = %p, *zppo + 1 = %p.\n", *zippo, *zippo + 1);
printf(" zippo[0][0] = %d\n", zippo[0][0]);
printf(" *zippo[0] = %d\n", *zippo[0]);
printf(" **zippo = %d\n", **zippo);
printf(" zippo[2][1] = %d.\n", zippo[2][1]);
printf("*(*(zippo + 2) + 1) = %d.\n", *(*(zippo + 2) + 1));
}
/*
zippo = 0061fef0, zippo + 1 = 0061fef8.
zippo[0] = 0061fef0, zippo[0] + 1 = 0061fef4.
*zippo = 0061fef0, *zppo + 1 = 0061fef4.
zippo[0][0] = 2
*zippo[0] = 2
**zippo = 2
zippo[2][1] = 3.
*(*(zippo + 2) + 1) = 3.
*/
指针和多维数组这里主要讨论二维数组,相较于一维数组,二维数组麻烦多了,而且一不小心就被绕进去,当初也是花了很多时间才稍有理解。
由上面代码即输出结果可得
1, zippo 和 zippo[0] 和 *zippo 中所储存的地址值是一样的。 但我知道这不是一种巧合,因为zippo是二维数组名,实际上它保存的内容是含有两个int型元素的数组的地址,而zippo[0],
实际上,它保存的是一个int型数的地址,用符号表示即 zippo = &zippo[0], zippo[0] = &zippo[0][0], 所以zippo保存的是两个整数大小的对象的,而zippo[0]实际上 保存的是一个整数大小的对象的地址,但是由两个int型数和一个int型数组成的数组都开始于同一地址,所以zippo才会和zippo[0]中所保存的地址是同一个。
2, 由于zippo相当于指向了含有两个int型元素数组,所以当想要访问zippo[0][0],即整个二维数组的第一个元素时,需要用 **zippo。
原因如下:
zippo = &zippo[0], 所以*zippo = *&zippo[0] 即 *zippo = zippo[0],
然而实际上zippo[0]中存放的也是地址,只不是是一个int型数的地址,所以此时 *zzipo只能访问到一个int型数的地址。
所以由 zippo[0] = &zippo[0][0] 可得 *zippo[0] = zippo[0][0],而*zippo[0] = **zippo,
所以可得**zippo = zippo[0][0];
这样才访问到了二维数组中第一个元素的内容。
3, 由第一点,因为zippo是二维数组名,实际上它保存的内容是含有两个int型元素的数组的地址,而zippo[0],实际上,它保存的是一个int型数的地址,用符号表示即 zippo = &zippo[0], zippo[0] = &zippo[0][0], 所以zippo保存的是两个整数大小的对象的,而zippo[0]保存的是一个整数大小的对象的地址,所以才有了下面zippo + 1 的内容比 zippo 的内容大 Ox8。相应的(zippo[0] + 1)比zippo[0]大 Ox4;
总结:实际上,zippo的内容是&zippo[0],而zippo[0]的内容是&zippo[0][0],他们或许本来应该不一样的,zippo可以说是一个指向指针的指针,而zippo[0] 只是一个简单的指向一个int元素的指针,但是关键的地方是,zippo中存放的内容和zippo[0]中存放的内容相等,也就是说他们实际上指向的是同一块区域,这是为什么? 是因为二维数组的开始就是第一行第一列的那个元素,也就是zippo[0][0]的地址,无论多少重指针指向它,所得的内容还是这个元素的地址,(即使是zippo[0][0][0],zippo[0][0][0][0]还是同一个元素),所以实际上zippo[0]在某种程度上来说就是zippo[0][0],但因为它是第一行的数组的数组名,所以它里面的元素是&zippo[0][0]而已。