1

I have come across a C code similar to the following:

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

int main(void) {

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

    int i;

    for (i = 0; i < 200; i++)
    {
      a[i] = i;
    }

    int (*b)[10] = (void*) a;

    printf("\nsizeof(int):\t%d\n", sizeof(int));

    printf("\nb[0]:\t%d\n", b[0]);
    printf("\na:\t%d\n", a);

    printf("\nb[19]:\t%d\n", b[19]);
    printf("\na+190:\t%d\n", a+190);

    printf("\nb[0][8]:\t%d\n", b[0][8]);
    printf("\nb[19][9]:\t%d\n", b[19][9]);

    return 0;
}

As per my understanding, the line int (*b)[10] = (void*) a; is trying to assign pointer b (which is supposed to point to an array of 10 integers) to the starting address of array a typecast as a void pointer. I would have expected b[i] to hold the same data as a[i] for i=0..9 (and any index other than 0 to 9 for b resulting in some undefined behavior). However, the program produces outputs similar to the following sample:

sizeof(int):    4

b[0]:   9768976

a:      9768976

b[19]:  9769736

a+190:  9769736

b[0][8]:        8

b[19][9]:       199

Clearly, b has become an array of 20 pointers, with each elemental pointer pointing to a unique portion of the a array corresponding to 10 integers (or 40 bytes) each. Can someone please explain what exactly int (*b)[10] = (void*) a; does? Specifically, how does the typecast (void *) help in distributing the entire a across multiple elements of b? The above code would not compile without the (void *) cast.

NI3
  • 11
  • 2
  • "I would have expected b to be a pointer that can only access the first 10 elements of a" - so would you also expect that `int *b = (void *)a;` would only be able to access one `int` ? – M.M Oct 04 '16 at 00:08
  • No. I guess I did not express myself well in that sentence. I will edit my question to better explain what I meant. – NI3 Oct 04 '16 at 00:13
  • `b[0]` is an array consisting of the first 10 ints, `b[1]` is the next 10 ints, and so on – M.M Oct 04 '16 at 00:14
  • Correct. That's what I figured from the output I posted. I am just not sure how this works inside the `int (*b)[10] = (void*) a;` statement. I could not find other such examples in my online searches. This seems like a way of creating separate pointers for chunks of a large array, something I have not come across earlier. – NI3 Oct 04 '16 at 00:18
  • 1
    It's the same principle as `int *b = (void *)a` except you are pointing to 10 ints instead of pointing to 1 int. – M.M Oct 04 '16 at 00:40
  • The cast from `void *` works. A bit more legible would be `int (*b)[10] = (int (*)[10])a;` See also http://stackoverflow.com/questions/11454376/cast-void-pointer-to-integer-array although it does not have a lot more information but lead to http://stackoverflow.com/questions/1810083/c-pointers-pointing-to-an-array-of-fixed-size which has a more thorough answer – deamentiaemundi Oct 04 '16 at 00:56
  • @deamentiaemundi The second link was a good read to understand proper declarations for pointers to fixed size arrays. Still, it did not clarify my doubt with the assignment mentioned in my question. – NI3 Oct 04 '16 at 17:25

2 Answers2

0

Look at it like this:

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

int (*b)[10];
b = (void *)a;

So, the first line says that a should point to an int. The second line allocates a block of memory that can hold 200 ints, and assigns the address of the start of this block to a. The third line says that b should point to an array of 10 ints. The fourth line says that b should be assigned the address held by a.

So now b points to an array of 10 ints starting at the address returned from the call to malloc(). Since memory was allocated for 200 ints, b points to a block of memory that can hold 20 10-element arrays of int.

The cast to void is needed because a was declared to be a pointer to int; this allows you to store the value held by a in b, even though they are of different types. I think that this was a mere convenience in the allocation of the appropriate amount of memory for the array. An alternative method, avoiding the declaration of a altogether, would be:

int (*b)[10] = malloc(20 * sizeof(*b));

This second method might or might not seem a bit more cryptic, depending upon the readers tastes. I guess one advantage of the first method is that it allows you to access the elements of the array both sequentially and as a 2d array.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
0

Clearly, b has become an array of 20 pointers, with each elemental pointer pointing to a unique portion of the a array corresponding to 10 integers (or 40 bytes) each.

No, b is just a pointer, not an array. The type b points to is array-of-10-ints. Perhaps this would be simpler if you consider it as:

typedef int row[10]; // A `row` is an array of 10 ints
row* b = (void*) a;

For any pointer T* p, p[n] is sizeof (T) bytes offset from p[n - 1] (assuming that n and n - 1 are valid indices, of course). This case is no different; here T is a 10-element int array, so each element of b is 10 * sizeof (int) bytes away from its adjacent elements.

Specifically, how does the typecast (void *) help in distributing the entire a across multiple elements of b? The above code would not compile without the (void *) cast.

In C, pointers can be converted between void* and T* without explicit casting. The above case uses a void* cast for convenience by reducing typing; it allows the right-hand-side to be converted to whatever pointer type is needed by the left-hand-side.

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • Your example is finally getting me somewhere. So, `int (*b)[10]` should be interpreted as `b` being a pointer to a _type that consists of a 10-interger array_. Then, after assigning `b` to a larger memory portion (with >10 integers, such as the 200-integers pointed to by `a` in my question), we can access that memory in 10-element chunks by using indices for `b`, starting from `b[0][0..9]` or `(*(b))[0..9]` for the first 10 elements and `b[19][0..9]` or `(*(b+19))[0..9]` for the last 10. – NI3 Oct 04 '16 at 17:47
  • @NI3 Also, if you found my answer helpful, you should upvote it. =) – jamesdlin Oct 04 '16 at 21:50
  • @jamesdin Thanks. I have upvoted your answer. However, due to my new account, it's not visible yet. I need at least a reputation of 15 for my upvotes to matter. – NI3 Oct 05 '16 at 18:51