user7015283
user7015283

Reputation:

How are array of pointers stored in memory and how to properly dereference them?

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;
}

Below I have attatched a sketch of how I think they are stored. Please correct me with a better sketch if I am wrong. enter image description here

Upvotes: 2

Views: 1355

Answers (3)

Edenia
Edenia

Reputation: 2488

Arrays are stored consecutively in the address space. Their allocation is static, meaning you don't allocate space for it at run time and as a result of this, they are stored in different memory region - stack.

An array determines its size by the amount of elements multiplied by the size of the data type, (because you have packed a variable n-times). The size is the space in bytes it occupies in the memory. It should not be confused with the length of the array, which is how many elements are in the array. For example int arr[2] is usually 8 bytes (sizeof(int[2])) but is an array with the length of 2.

There are many ways to initialize array, but two ways to de-reference it. One is by using the index operators [] and the other is to de-reference the pointer it decays to with * Example:

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] - This is an array of int pointers. The index operator has very high precedence, higher than * To get around that, and substantially create a pointer to an array of 3 elements, you use the brackets to define the priority of evaluation:

int (*arr)[3];

Bracket's second use-case is to make a "derived type" - a function. ([], *, () are used to define a derived type)


How to initialize them at declaration time?

An array of characters

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

To initialize the first element of an array of int pointers you can very well do this:

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

And finally, initialize a pointer to an array of 3 characters:

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

Upvotes: 2

chqrlie
chqrlie

Reputation: 144770

Your code has many problems, mostly coming from the fact that int *b[3] does not have a proper initializer. { 1, 2, 3 } is OK for an array of int, not for an array of int *, as the compiler correctly diagnoses:

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

For compatibility with ancient code, the compiler only issues a warning, but such warnings indicate errors that must be corrected. I strongly suggest you compile your code with -Wall -Werror to let the compiler produce errors instead of mere warnings for such problems.

Accessing adresses 1, 2 or 3 most likely has undefined behavior, which takes the form of a segmentation fault on your system.

To initialize the array of pointers, you can specify the addresses of int variables.

Here is a corrected version:

#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;
}

Note that pointers cannot be printed with %d as it has undefined behavior because pointers may be passed differently from integers to printf. The format is %p and the pointer should be cast as (void*) for complete portability.

Upvotes: 3

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

This is stored like this:

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

You asked for an array containing 1, 2, and 3, and you got an array containing 1, 2 and 3. It does not create separate variables containing 1, 2 and 3 and then put pointers to those variables in the array. No, it just puts 1, 2 and 3 in the array and carries on.

The warning is because it is very uncommon and usually wrong to be writing a memory address directly. What's at memory address 1? Damned if I know. Probably it's not even a valid address, so when you do *b[0] it just crashes.

*b is the same as b[0], *(b+1) is the same as b[1] and so on. So those are just fetching the "pointers" in the array - not the things they point to.

*(*(b+1)+2) segfaults because it's accessing address 10 (on a 32-bit system). Probably 10 isn't a valid address of anything either. So you get a segfault.

Upvotes: 0

Related Questions