Update your compiler so it complies with the 1999 C standard (or more recent) and your first way will work. For older compilers it will not.
If you are stuck with an older compiler, there are two ways depending on whether you are adamant that the array has to be two-dimensional.
The first is to malloc() enough memory, but index it as a one-dimensional array;
Code:
/* read value for row and columns */
int *p = malloc(rows*columns*sizeof(*p));
/* get values i and j representing element you want to access */
p[i*rows + j] = 42; /* 42 = meaning of life, the universe, and everything */
or (if you really want a pointer to pointer and 2D array syntax) ....
Code:
/* read value for row and columns */
int **p = malloc(rows*sizeof(*p));
for (i = 0; i < rows; ++i)
p[i] = malloc(columns*sizeof(**p));
/* get values i and j representing element you want to access */
p[i][j] = 42;
It's also necessary to check that ALL of the malloc() calls succeed and, when done with the arrays, to call free(). In the second approach, ALL of the individual malloc() calls need corresponding free() calls.