0

This creates a pointer to the beginning of the array of two pointers.

void **p;
p = malloc(2 * sizeof(void *));
for (int i = 0; i < 2; ++i) {
    p[i] = (void *)malloc(sizeof(void *));
}

This creates a pointer to the beginning of the array of two pointers.

void *c = *p;

And this should change the value of the first pointer in the array, right?

I want the first item in array points to my int.

int *a = (int *)malloc(sizeof(int));
*a = 5;

// c points to *p, the first item in array
// *c should be value of the first item in array then
*c = a;

But this doesn't work.

c should be same as *p(p[0]), so when I change c value, it should change *p(p[0]) value as well.

What am I missing?

John Doe
  • 11
  • 2
  • 2
    You don't need to cast the return value of `malloc`. – 1201ProgramAlarm Nov 27 '20 at 01:36
  • Why, `malloc` returns `void *`. – John Doe Nov 27 '20 at 01:53
  • 2
    In C, a `void *` pointer can be assigned to any pointer type. See [Do I cast the result of malloc?](https://stackoverflow.com/q/605845/5231607). – 1201ProgramAlarm Nov 27 '20 at 01:56
  • 2
    An you can NOT derefernce a void pointer -- it is an incomplete type. – David C. Rankin Nov 27 '20 at 01:58
  • Even after you get this working, exposing such a data structure directly is foolish. You should expose it through an opaque type, and provide a set of functions that are used to manipulate it and ensure that all the invariants are kept. One invariant would be that all memory that gets allocated gets freed. It’s much easier to keep the enforcement of this in a small group of functions that you can test and debug, rather than all over the place. Use `typedef struct 2DArray 2DArray` for example, and use too inter to it as the type exposed in the API of the array. – Kuba hasn't forgotten Monica Nov 27 '20 at 03:44

3 Answers3

2

I believe the point you are missing is that when you loop allocating p[i] = malloc (sizeof (void*)) you are replacing the addresses at both p[0] and p[1] -- which were already valid pointers to begin with.

Specifically, p = malloc(2 * sizeof(void *)); allocates a block of memory for 2-pointers and assigns the beginning address of that block to p. At this point, both p[0] and p[1] are valid (but Uninitialized) pointers. When you create c, it holds the same Uninitialized address of the first pointer in the allocated block assigned to p. At this point neither c or p[0] point anywhere and neither points at the other -- they just hold the same indeterminate address as their value.

When you assign c = a, now c holds a valid address, but p[0] remains Uninitialized. You must initialize p[0] in the same way before you can use the address it holds. There is no need to allocate further. For example:

#include <stdio.h>
#include <stdlib.h>

int main (void) {
    
    void **p = malloc (2 * sizeof *p),  /* allocate storage for 2 pointers */
        *c = *p;    /* c holds the UNINITIALIZED/indeterminate address of the 1st pointer  */
    int *a = malloc (sizeof *a);
    
    if (!p || !a) {
        perror ("malloc-p_or_a");
        return 1;
    }
    *a = 5;         /* assign 5 to allocated int */
    c = a;          /* initialize address held by pointer c to a, p[0] still uninitialized */
    *p = c;         /* initialize address help by pointer p[0] to c */
    printf ("\n   a - value at %p : %d\n   c - value at %p : %d\np[0] - value at %p : %d\n",
            (void*)a, *a, c, *(int*)c, p[0], *(int*)p[0]); 
    ...

As mentioned in the comment, if you are only storing single addresses at p[0] and p[1], then any further allocation is superfluous. You already have valid storage for a pointer you can reference at both p[0] and p[1]. That doesn't mean you can't allocated further, p itself still holds the beginning address for the original block allocated -- so it can still be freed (no memory leak), but you don't provide any additional storage by replacing the pointers at p[0] and p[1] with individually allocated pointers.

Now when you do allocate, you assign a new address as the values for both p[0] and p[1], e.g.

    for (int i = 0; i < 2; i++)                     /* now loop initializeing p[i] */
        if (!(p[i] = malloc (sizeof (void*))))  {   /* assigns NEW address to p[i] */
            perror ("malloc-p[i]");
            return 0;
        }
    ...

(In C, there is no need to cast the return of malloc, it is unnecessary. See: Do I cast the result of malloc?)

Again, at this point, you must initialize the address stored in the block of memory with it's beginning address at p[0] and p[1], e.g.

    int b = 10;     /* new integer value */
    p[0] = &b;      /* assign address to pointer at p[0] */
    c = p[0];       /* assign to pointer at c */
    
    printf ("\n   a - value at %p : %d\n   c - value at %p : %d\np[0] - value at %p : %d\n",
            (void*)a, *a, c, *(int*)c, p[0], *(int*)p[0]); 
}

Example Use/Output

Above is a complete program that makes use of p[0] and p[1], both before and after your for loop allocations. If you compile and run it, you would receive:

$ ./bin/voidptr_int

   a - value at 0x12a6280 : 5
   c - value at 0x12a6280 : 5
p[0] - value at 0x12a6280 : 5

   a - value at 0x12a6280 : 5
   c - value at 0x7ffc47fdec6c : 10
p[0] - value at 0x7ffc47fdec6c : 10

It's an interesting exercise, but I believe where you were confused is after you declare void *c = *p; neither c or *p (which is the same as p[0]) point to each other -- they simply hold the same uninitialized/indeterminate address. The compounding things you allocation in the for loop changed the addresses held by both p[0] and p[1] so that whatever address was there before is overwritten with the address of the newly allocated pointers. I'm not sure which of the those two were causing the most confusion, but I suspect it was either one or the other.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

c will get a copy of *p. After that, there is no connection between the two. Changing one will not change the other.

If you want to change p[0] thru c, c must be a pointer-to-pointer-to-void, void **c = p;. Then you can assign *c = a; to change p[0] (and leak the memory you allocated for p[0]).

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
1

I want the first item in array points to my int.

There are two cases (read x as 0 < x < N, where N is number of void * allocated to p) :
CASE I: p[x] pointers pointing to valid memory locations and use another pointer to make changes to the value of those memory locations.

CASE II: p[x] pointers not pointing to valid memory locations and use another pointer to make p[x] pointers pointing to some valid memory location.

Looks like you are confused between these two cases because you are allocating memory to p[x] pointers and then trying to assign some other memory location to *p (p[0]) using some other pointer c. With this, you end up leaking memory in your program because of not properly handling the allocated memory.

First I would like to point out couple of things in your code:

  • Here, you are allocating memory of void * size:

    p[i] = (void *)malloc(sizeof(void *));
                                ^^^^^^^^
    

If you want void pointers p[x] to point to memory location which hold an int value, you should do:

p[i] = (void *)malloc(sizeof (int));
  • The type of c is void *, so *c type is void. Compiler must be throwing warning/error on this statement because void is an incomplete type and its not assignable:

      *c = a;
    

From C11 Standard#6.2.5p19:

19 The void type comprises an empty set of values; it is an incomplete object type that cannot be completed.

Now, lets discuss the CASE I and II in detail:

CASE I: p[x] pointers pointing to valid memory locations and use another pointer to make changes to the value of those memory locations.

void **p;
p = malloc(2 * sizeof(void *));
if (NULL == p) {
    fprintf (stderr, "Failed to allocate memory");
    exit(EXIT_FAILURE);
}
for (int i = 0; i < 2; ++i) {
    p[i] = malloc(sizeof (int));
    if (NULL == p[i]) {
        fprintf (stderr, "Failed to allocate memory");
        exit(EXIT_FAILURE);
    }
}

Now, the void pointers p[x] pointing to memory locations of type int.
Assume, you want to assign value (or make changes) to p[0] memory location using some other pointer:

void *c = *p;   // or void *c = p[0];
int a = 5;

*(int *)c = a;   // need to type cast c to the type of memory it will point to
printf ("%d\n", *(int *)*p);  // or printf ("%d\n", *(int *)p[0]);

Once done with pointers, make sure free the allocated memory. Here, you need to free p[x] pointers and then pointer p.

CASE II: p[x] pointers not pointing to valid memory locations and use another pointer to make p[x] pointers pointing to some valid memory locations.

In this case, you don't need to allocate memory to p[x] pointers.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    void **p;
    p = malloc(2 * sizeof(void *));
    if (NULL == p) {
        fprintf (stderr, "Failed to allocate memory");
        exit(EXIT_FAILURE);
    }

    void **c = p;
    int *a = malloc(sizeof(int));
    if (NULL == a) {
        fprintf (stderr, "Failed to allocate memory");
        exit(EXIT_FAILURE);
    }
    *a = 5;
    *c = a;

    printf ("p: %p\n", (void *)p);
    printf ("c: %p\n", (void *)c);
    printf ("a: %p\n", (void *)a);
    printf ("*c: %p\n", (void *)*c);
    printf ("*p: %p\n", (void *)*p);
    printf ("%d\n", *(int *)*p);

    free(a);
    free(p);

    return 0;
}

Output:

p: 0x7fe603c05710
c: 0x7fe603c05710
a: 0x7fe603c05720
*c: 0x7fe603c05720
*p: 0x7fe603c05720    // *p pointer is pointing to memory location allocated to pointer a
5

Note: Whether to type cast malloc result or not is opinion based. So, whatever you choose is up to you but make sure to be consistent everywhere in the code that you write.

H.S.
  • 11,654
  • 2
  • 15
  • 32