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)