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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With