c语言中的指针和数组

来源:互联网 发布:手机安全软件排名 编辑:程序博客网 时间:2024/05/17 02:50
最近在复习c语言,看了C Primer Plus 这本书,对其中难以理解的数组和指针有一些自己小小的体会,现记录下来。
指针和一维数组:
        #include <stdio.h>        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 <stdio.h>            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]而已。
0 0