What should I fill in ?.
In general when you have
X = malloc(sizeof ? * NUMBER);
the ? is to be replaced with the type that X points to. That can simply written as *X.
So the line:
p[i]=malloc(sizeof ? * cols);
is to be:
p[i]=malloc(sizeof *p[i] * cols);
Notice that a 2D array can be created much simpler. All you need is
int (*p)[cols] = malloc(sizeof *p * rows);
Here p is a pointer to an array of cols int. Consequently sizeof *p will be the size of an array of cols int.
Using this VLA based technic means that you can allocate the 2D array using a single malloc. Besides making the code more simple (i.e. only 1 malloc) it also ensures that the whole 2D array is in consecutive memory which may give you better cache performance.