如何将指针数组存储在内存中以及如何正确地取消引用它们?

时间:2018-06-15 00:38:32

标签: c arrays pointers

int main(void) {

    int* b[3] = {1, 2, 3};      // b is an array of pointers to integer

    printf("%d", b);            // b = address of array of pointers
    printf("\n%d", *b);         // *b should be address of the place where 1 is stored ....right?
    printf("\n%d", *(b+1));     // similarly *(b+1) should be address of place where 2 is stored
    printf("\n%d", *(b+2));     // and so on... But they print 1,2 and 3

    printf("\n%d", b[0]);       // prints 1 . No problem!


    printf("%d", *(*(b+1)+2));  // Also why does this give segmentation fault?


    //PLUS I GET THIS WARNING : array_of_pointers2.c:5:13: warning: initialization makes pointer from integer without a cast [-Wint-conversion]

    //PLEASE EXPLAIN !! I think I have misunderstood something.


    return 0;
}

下面我已经附上了我认为它们存储方式的草图。如果我错了,请用更好的草图纠正我。 enter image description here

3 个答案:

答案 0 :(得分:4)

您的代码存在许多问题,主要来自int *b[3]没有正确的初始值设定项的事实。对于{ 1, 2, 3 }的数组,int是正常的,而不是int *的数组,正如编译器正确诊断的那样:

array_of_pointers2.c:5:13: warning: initialization makes pointer from integer without a cast [-Wint-conversion]

为了与古代代码兼容,编译器仅发出警告,但此类警告表示必须更正的错误。我强烈建议您使用-Wall -Werror编译代码,让编译器产生错误,而不仅仅是针对此类问题的警告。

访问地址123很可能有未定义的行为,这会在您的系统上采用分段错误的形式。

要初始化指针数组,您可以指定int变量的地址。

以下是更正后的版本:

#include <stdio.h>

int main(void) {
    int x = 1, y = 2, z = 3;
    int *b[3] = { &x, &y, &z };           // b is an array of pointers to integer

    printf("        b: %p\n", (void*)(b));         // b = address of array of pointers
    printf("       *b: %p\n", (void*)(*b));        // *b is the address of variable x that has a value of 1
    printf("   *(b+1): %p\n", (void*)(*(b+1)));    // similarly *(b+1) is the address of y where 2 is stored
    printf("   *(b+2): %p\n", (void*)(*(b+2)));    // and so on...

    printf("    *b[0]: %d\n", *b[0]);     // prints 1. No problem!
    printf("*(*(b+1)): %d\n", *(*(b+1))); // prints 2
    // accessing *(*(b+1)+2) would have undefined behavior because b[1] is 
    // not the address of an array of at least 3 ints.
    printf("  b[2][0]: %d\n", b[2][0]);   // prints 3

    return 0;
}

请注意,指针无法使用%d打印,因为它具有未定义的行为,因为指针可能会以不同的方式从整数传递到printf。格式为%p,指针应转换为(void*),以实现完全的可移植性。

答案 1 :(得分:3)

数组在地址空间中连续存储。 它们的分配是静态的,这意味着你不能在运行时为它分配空间,因此它们存储在不同的内存区域 - 堆栈。

数组通过元素数量乘以数据类型的大小来确定其大小(因为您已经将变量打包了n次)。 大小是它在内存中占用的空间(以字节为单位)。它不应该与数组的长度混淆,后者是数组中的元素数量。例如int arr[2]通常是8个字节(sizeof(int[2])),但是是一个长度为2的数组。

有许多方法可以初始化数组,但有两种方法可以取消引用它。 一种是使用索引运算符[],另一种是使用*取消引用它衰减的指针示例:

int arr[3];

arr[0] = 40;
*arr = 40;
arr[2] = 40; // <-- this is functionally equivalent to..
*(arr + 2) = 40; // <--this (Note the pointer arithmetic involved)

int* arr[3] - 这是一个int指针数组。 索引运算符具有非常高的优先级,高于* 要解决这个问题,并基本上创建一个指向3个元素数组的指针,可以使用括号来定义评估的优先级:

int (*arr)[3];

Bracket的第二个用例是制作一个&#34;派生类型&#34; - 一个功能。 ([]*()用于定义派生类型)

如何在申报时初始化它们?

一个字符数组

char arr[3] = {'a', 'b', 'c'}
char arr[]  = {'a', 'b', 'c'}
char arr[3] = "hi" // Note this is a string, not array of characters anymore

要初始化int指针数组的第一个元素,你可以很好地执行此操作:

char ch1 = 'a';
char* arr[3] = { &ch1 };

最后,初始化一个指向3个字符数组的指针:

char arr[3] = {'a', 'b', 'c'};
char (*arr2)[3] = arr;

答案 2 :(得分:1)

int* b[3] = {1, 2, 3};

这样存储如下:

   b
+-----+
|  1  |
+-----+
|  2  |
+-----+
|  3  |
+-----+

你要求一个包含1,2和3的数组,你得到一个包含1,2和3的数组。它创建包含1,2和3的单独变量然后放入指向数组中那些变量的指针。不,它只是在数组中放入1,2和3并继续。

警告是因为直接写入内存地址非常罕见且通常是错误的。内存地址1是什么?如果我知道,该死的。可能它甚至不是一个有效的地址,所以当你执行*b[0]它只会崩溃。

*bb[0]相同,*(b+1)b[1]相同,依此类推。所以这些只是获取数组中的“指针” - 而不是他们指向的东西。

*(*(b+1)+2)段错误,因为它访问地址10(在32位系统上)。可能10也不是任何有效的地址。所以你得到了一个段错误。

相关问题