3

What I'm trying to do here (for educational purposes) is to have a pointer to a pointer that behaves like an array but with a single memory allocation. Valgrind is complaining about this code and if I do a longer version of this with several allocations I start having segfaults while debugging in gdb.

It's allocating 144 bytes (24 bytes for the 3 pointers, then 3*40 bytes for the integers). l[0] is +24 bytes from l, l[1] +40 from l[0] and so on.

Can someone enlighten me about what I'm getting wrong?

gcc -o program -g -ansi -Wpedantic -Wall -Wextra main.c

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

int main(void) {
    int **l;
    int i, j, k;
    int x = 3;
    int y = 10;
    size_t size_y = sizeof(int) * y;
    size_t x_ptrs = x * sizeof(int*);
    size_t mem_to_alloc = x_ptrs + x * size_y;

    l = malloc(mem_to_alloc);

    l[0] = (int*)l + x_ptrs;
    l[1] = (int*)l + x_ptrs + size_y;
    l[2] = (int*)l + x_ptrs + size_y * 2;

    k = 0;
    for(i = 0; i < x; i++)
    for(j = 0; j < y; j++) 
        l[i][j] = (-k++)*12; /* just some number */

    for(i = 0; i < x; i++) {
        for(j = 0; j < y; j++) {
            printf("%.3d\n", l[i][j]);
        }
        puts("");
    }

    free(l);
    l = NULL;
    return 0;
}

valgrind ./program

==1593== Memcheck, a memory error detector
==1593== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1593== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==1593== Command: ./program
==1593== 
==1593== Invalid write of size 4
==1593==    at 0x4006E8: main (main.c:22)
==1593==  Address 0x51d5140 is 48 bytes inside an unallocated block of size 4,194,000 in arena "client"
==1593== 
000
-012
-024
-036
-048
-060
-072
-084
-096
-108

==1593== Invalid read of size 4
==1593==    at 0x400738: main (main.c:26)
==1593==  Address 0x51d5140 is 48 bytes inside an unallocated block of size 4,194,000 in arena "client"
==1593== 
-120
-132
-144
-156
-168
-180
-192
-204
-216
-228

-240
-252
-264
-276
-288
-300
-312
-324
-336
-348

==1593== 
==1593== HEAP SUMMARY:
==1593==     in use at exit: 0 bytes in 0 blocks
==1593==   total heap usage: 1 allocs, 1 frees, 144 bytes allocated
==1593== 
==1593== All heap blocks were freed -- no leaks are possible
==1593== 
==1593== For counts of detected and suppressed errors, rerun with: -v
==1593== ERROR SUMMARY: 40 errors from 2 contexts (suppressed: 0 from 0)
  • 1
    You should start by using correct variable names. Statements such as `l[i][j]` aren't really readable. – Jabberwocky Aug 27 '15 at 11:34
  • l[0] is +24 bytes from l -> this statement is wrong `l` has the same address as `l[0]` – ex0ns Aug 27 '15 at 11:41
  • 1
    @ex0ns he's talking about the content assigned to `l[0]`, not its address. – Barmar Aug 27 '15 at 11:42
  • 4
    First of all [you can't use a pointer to pointer as a contiguous array of arrays](http://stackoverflow.com/a/18440456/440558), it simply will not work as you expect. Secondly, why not use a simple pointer and then use simple math to get the correct index? Something like e.g. `int *matrix = malloc(xsize * ysize * sizeof(int)); matrix[x * ysize + y] = 5;` – Some programmer dude Aug 27 '15 at 11:44
  • Do not! EIther use a 2D array, or allocate the array in two parts: the array of pointers then the rest. One problem is you might run into alignment problems. – too honest for this site Aug 27 '15 at 12:04

2 Answers2

2

When you add an integer and a pointer, the integer is scaled up by the size of what the pointer points to. So

l[1] = (int*)l + x_ptrs + size_y;

sets l[1] to 40 * sizeof(int) bytes past l, i.e. 160 bytes past l. Since you only allocated 144 bytes, this goes outside the array bounds.

This automatic scaling is what allows you to step through an array with pointer arithmetic, e.g. ptr++ to get to the next element of the array that ptr points to.

Barmar
  • 741,623
  • 53
  • 500
  • 612
1

The error can be related to pointer arithmetic.

As you are doing l[0] = (int*)l + x_ptrs;, the offset x_ptrs is a number of bytes. Instead, it should be the number of integers you want to skip, since l is casted to int*.

Could you try l[0] = (int*)l + 2*x; ? This should work is the size of int* (most likely 8) is twice the size of int (most likely 4). The same logic is to be applied to size_y.

l[0] = (int*)l + 2*x;
l[1] = (int*)l + 2*x + y;
l[2] = (int*)l + 2*x + 2*y;
francis
  • 9,525
  • 2
  • 25
  • 41