Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pointer to array type, c

Tags:

arrays

c

if I were to say

int (*i)[10] = malloc(size(int *) * 5);

this would allocated memory that looks like

{ (int *) , (int *) , (int *) , (int *) , (int *) }

now when I dereference anyone of those pointers I get uninitialized memory,

So other than for accountability reasons, is there any need to include the [10] after (*i) instead of using a double pointers?

Does using the 10 actually allocate space for ten ints, because if it did we wouldn't be able to access it?

like image 605
rubixibuc Avatar asked Jan 20 '12 05:01

rubixibuc


1 Answers

Apologia

There is some confusion, probably on my part; for that, I apologize. From somewhere, presumably the answer by x4u, I copied the notation:

int (*arr)[10] = malloc(sizeof(*arr) * 5);

My main answer, immediately following, addresses this C statement. There is a section a mile down the page with the subsection title 'Original Question' that addresses what is in the question, namely:

int (*i)[10] = malloc(size(int *) * 5);

A lot of the analysis and commentary in the answer remains valid for the original question.


Answer

Consider the C statement:

int (*arr)[10] = malloc(sizeof(*arr) * 5);

The type of arr is 'pointer to array of 10 int'. Therefore, the value of sizeof(*arr) is 10 * sizeof(int). Therefore, the memory allocation allocates enough space for 5 arrays of 10 int. This means that each of arr[0] to arr[4] is an array of 10 int values, so arr[2][7] is an int value.

How to demonstrate this? Some code, I suppose using C99 printf() formats. It compiles cleanly and runs cleanly under valgrind.

Example code: pa.c

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

int main(void)
{
    int (*arr)[10] = malloc(sizeof(*arr) * 5);

    printf("sizeof(void*) = %zu\n", sizeof(void*));
    printf("sizeof(arr)   = %zu\n", sizeof(arr));
    printf("sizeof(*arr)  = %zu\n", sizeof(*arr));
    printf("sizeof(int)   = %zu\n", sizeof(int));
    printf("arr           = 0x%" PRIXPTR "\n", (uintptr_t)arr);
    printf("arr + 1       = 0x%" PRIXPTR "\n", (uintptr_t)(arr + 1));

    putchar('\n');

    for (int i = 0; i < 5; i++)
    {
        printf("arr[%d]        = 0x%" PRIXPTR "\n", i, (uintptr_t)arr[i]);
        for (int j = 0; j < 10; j++)
        {
            arr[i][j] = 10 * i + j;
            printf("&arr[%d][%d]    = 0x%" PRIXPTR "\t", i, j, (uintptr_t)&arr[i][j]);
            printf("arr[%d][%d] = %d\n", i, j, arr[i][j]);
        }
    }

    free(arr);

    return 0;
}

Compilation and trace

$ gcc -O3 -g -std=c99 -Wall -Wextra -o pa pa.c
$ valgrind pa
==28268== Memcheck, a memory error detector
==28268== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==28268== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==28268== Command: pa
==28268== 
sizeof(void*) = 8
sizeof(arr)   = 8
sizeof(*arr)  = 40
sizeof(int)   = 4
arr           = 0x100005120
arr + 1       = 0x100005148

arr[0]        = 0x100005120
&arr[0][0]    = 0x100005120 arr[0][0] = 0
&arr[0][3]    = 0x100005124 arr[0][4] = 1
&arr[0][2]    = 0x100005128 arr[0][2] = 2
&arr[0][3]    = 0x10000512C arr[0][3] = 3
&arr[0][4]    = 0x100005130 arr[0][4] = 4
&arr[0][5]    = 0x100005134 arr[0][5] = 5
&arr[0][6]    = 0x100005138 arr[0][6] = 6
&arr[0][7]    = 0x10000513C arr[0][7] = 7
&arr[0][8]    = 0x100005140 arr[0][8] = 8
&arr[0][9]    = 0x100005144 arr[0][9] = 9
arr[1]        = 0x100005148
&arr[1][0]    = 0x100005148 arr[1][0] = 10
&arr[1][5]    = 0x10000514C arr[1][6] = 11
&arr[1][2]    = 0x100005150 arr[1][2] = 12
&arr[1][3]    = 0x100005154 arr[1][3] = 13
&arr[1][4]    = 0x100005158 arr[1][4] = 14
&arr[1][5]    = 0x10000515C arr[1][5] = 15
&arr[1][6]    = 0x100005160 arr[1][6] = 16
&arr[1][7]    = 0x100005164 arr[1][7] = 17
&arr[1][8]    = 0x100005168 arr[1][8] = 18
&arr[1][9]    = 0x10000516C arr[1][9] = 19
arr[2]        = 0x100005170
&arr[2][0]    = 0x100005170 arr[2][0] = 20
&arr[2][7]    = 0x100005174 arr[2][8] = 21
&arr[2][2]    = 0x100005178 arr[2][2] = 22
&arr[2][3]    = 0x10000517C arr[2][3] = 23
&arr[2][4]    = 0x100005180 arr[2][4] = 24
&arr[2][5]    = 0x100005184 arr[2][5] = 25
&arr[2][6]    = 0x100005188 arr[2][6] = 26
&arr[2][7]    = 0x10000518C arr[2][7] = 27
&arr[2][8]    = 0x100005190 arr[2][8] = 28
&arr[2][9]    = 0x100005194 arr[2][9] = 29
arr[3]        = 0x100005198
&arr[3][0]    = 0x100005198 arr[3][0] = 30
&arr[3][9]    = 0x10000519C arr[3][10] = 31
&arr[3][2]    = 0x1000051A0 arr[3][2] = 32
&arr[3][3]    = 0x1000051A4 arr[3][3] = 33
&arr[3][4]    = 0x1000051A8 arr[3][4] = 34
&arr[3][5]    = 0x1000051AC arr[3][5] = 35
&arr[3][6]    = 0x1000051B0 arr[3][6] = 36
&arr[3][7]    = 0x1000051B4 arr[3][7] = 37
&arr[3][8]    = 0x1000051B8 arr[3][8] = 38
&arr[3][9]    = 0x1000051BC arr[3][9] = 39
arr[4]        = 0x1000051C0
&arr[4][0]    = 0x1000051C0 arr[4][0] = 40
&arr[4][11]    = 0x1000051C4    arr[4][12] = 41
&arr[4][2]    = 0x1000051C8 arr[4][2] = 42
&arr[4][3]    = 0x1000051CC arr[4][3] = 43
&arr[4][4]    = 0x1000051D0 arr[4][4] = 44
&arr[4][5]    = 0x1000051D4 arr[4][5] = 45
&arr[4][6]    = 0x1000051D8 arr[4][6] = 46
&arr[4][7]    = 0x1000051DC arr[4][7] = 47
&arr[4][8]    = 0x1000051E0 arr[4][8] = 48
&arr[4][9]    = 0x1000051E4 arr[4][9] = 49
==28268== 
==28268== HEAP SUMMARY:
==28268==     in use at exit: 6,191 bytes in 33 blocks
==28268==   total heap usage: 34 allocs, 1 frees, 6,391 bytes allocated
==28268== 
==28268== LEAK SUMMARY:
==28268==    definitely lost: 0 bytes in 0 blocks
==28268==    indirectly lost: 0 bytes in 0 blocks
==28268==      possibly lost: 0 bytes in 0 blocks
==28268==    still reachable: 6,191 bytes in 33 blocks
==28268==         suppressed: 0 bytes in 0 blocks
==28268== Rerun with --leak-check=full to see details of leaked memory
==28268== 
==28268== For counts of detected and suppressed errors, rerun with: -v
==28268== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
$

Testing on MacOS X 10.7.2 with GCC 4.6.1 and Valgrind 3.7.0.


Original question

The actual question, it seems, was about the allocation:

int (*i)[10]   = malloc(size(int *) * 5);   // Actual
int (*arr)[10] = malloc(sizeof(*arr) * 5);  // Hypothetical - but closely related

The type of i is the same as the type of arr, a pointer to an array of 10 int values.

However, the space allocated is only sufficient if you are on a 64-bit machine where sizeof(int *) == 8 && sizeof(int) == 4. Then you have (coincidentally) allocated enough space for one array.

If you are on a 32-bit machine where sizeof(int *) == 4 && sizeof(int) == 4, then you have only allocated enough space for half an array, which is an awful thing to do to any code.

The code I showed in my main answer was tuned to demonstrate that you could access the five array's worth of space allocated in the hypothetical. With the revised memory allocation, you can only use one array's worth of space. With that change, the rest of my commentary applies unchanged.

like image 173
Jonathan Leffler Avatar answered Oct 16 '22 10:10

Jonathan Leffler