1

I'm reading about dynamic memory allocation for 2D arrays, and I'm looking at this example:

int nrows = 2;
int ncols = 5;

char **pvowels = malloc(nrows * sizeof(char));

pvowels[0] = malloc(ncols * sizeof(char));
pvowels[1] = malloc(ncols * sizeof(char));

My understanding is that the 2nd and 3rd malloc both allocate memory the size of 5 chars and pvowels[0] and pvowels[1] point to each, but I'm having trouble understanding the first malloc.

The first malloc looks like it allocates memory the size of 2 chars, and uses it to store the two pointers. But isn't a char only 256 possible values, and pointers can go up to billions? So if it's allocating memory for storing pointers, doesn't it need to be bigger than chars?

user3876042
  • 15
  • 1
  • 4
  • 1
    The first `malloc` is wrong. `char **pvowels` is a pointer to a character pointer. If you want two rows of characters, you need two pointers. You only allocated two bytes, `nrows * sizeof(char)`. You need room for two pointers, `nrows * sizeof(char*)`. Your code as it stands can cause an access violation. – lurker Feb 18 '17 at 02:38
  • Possible duplicate of [How do I work with dynamic multi-dimensional arrays in C?](https://stackoverflow.com/questions/917783/how-do-i-work-with-dynamic-multi-dimensional-arrays-in-c) – Shubham Aug 25 '18 at 20:32

4 Answers4

4

Firstly, your first malloc() is incorrect. Using nrows * sizeof(char) only allocates 2 bytes, whereas you need 2 rows of char* pointers. You have to allocate like this instead:

char **pvowels = malloc(nrows * sizeof(char*));

Or:

char **pvowels = malloc(nrows * sizeof(*pvowels));

Also note that char **pvowels is not a 2D array, but simply a pointer to a char pointer. If you were using a 2D array, such as char pvowels[][], you wouldn't need to dynamically allocate pointers on the heap. You could also just use a 2D array for your problem, such as char pvowels[2][5], as ncols and nrows seems to be fixed in this case.

Secondly, your allocations for pvowels[0] and pvowels[1] will only make space for 4 valid characters, such as "abcd", because 1 space is needed for the null terminating character \0. You should write instead:

pvowels[0] = malloc(ncols+1); /* +1 for '\0' */
pvowels[1] = malloc(ncols+1);

Note: sizeof(char) is always 1, so their is no need to include it. You should also check that malloc() returned NULL or not.

RoadRunner
  • 25,803
  • 6
  • 42
  • 75
  • I think that you have mostly answered OP's questions, but one quibble: I think that OP is right to say that "the 2nd and 3rd malloc both allocate memory the size of 5 chars". After all, `\0` is just another `char`, and I don't see that OP means "allocate memory for a string of length 5" here. YMMV – ad absurdum Feb 18 '17 at 04:16
  • @DavidBowling True, I'll edit my answer :). It might be a false observation, but nonetheless, adding `+1` to `char*` allocations is always a good habit. – RoadRunner Feb 18 '17 at 04:24
  • 1
    Your point is well taken; I only meant to suggest that the OP was correct in the assessment that the second two allocations each allocate space for 5 `char`s. +1 – ad absurdum Feb 18 '17 at 04:27
2

Allocating for an array of nrows pointers to char, and then separately allocating for nrows arrays of ncols chars has a disadvantage, in that the separate memory allocations are not guaranteed to be contiguous in memory. This fragmentation can lead to performance penalties.

A better approach is to allocate enough memory to hold a 2d array, and assign the resulting pointer to a pointer to an array of ncols chars. As shown here, this approach does rely on VLA's, but these have been a part of C since C99. This has the advantage of allocating memory at once, with only one allocation to free.

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

int main(void)
{
    size_t nrows = 2;
    size_t ncols = 5;

    /* Allocate space for a 2d array */
    char (*pvowels)[ncols] = malloc(sizeof (char[nrows][ncols]));

    /* Another alternative */
//    char (*pvowels)[ncols] = malloc(nrows * ncols);

    if (pvowels == NULL) {
        fprintf(stderr, "Unable to allocate memory\n");
        exit(EXIT_FAILURE);
    }

    for (size_t i = 0; i < nrows; i++) {
        for (size_t j = 0; j < ncols; j++) {
            pvowels[i][j] = 'a' + i * ncols + j;
        }
    }

    for (size_t i = 0; i < nrows; i++) {
        for (size_t j = 0; j < ncols; j++) {
            printf("%5c", pvowels[i][j]);
        }
        putchar('\n');
    }

    free(pvowels);

    return 0;
}

Program output:

a    b    c    d    e
f    g    h    i    j
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • 1
    +1 for using `char (*pvowels)[ncols]`, this is a very nice way to do it. It's also good that you use `size_t` instead of `int`, as `malloc()` requires `size_t`. – RoadRunner Feb 18 '17 at 04:35
1

char **pvowels is a pointer to a pointer, meaning it behaves like an array declared like this: char * pvowels[a number]; So basically, pointers to chars, and not pointers are being assigned in the example you provided.

0

A simple and easy to understand code:(Using just a single pointer to store and access)

#include<stdio.h>
#include<stdlib.h>
int main(){
    int *a,n,r,c,i,j;
    scanf("%d",&n);
    r=c=n;
    a=(int *)malloc(r*c*sizeof(int));
    for(i=0;i<r;i++)
    {
        for(j=0;j<c;j++){
            scanf("%d",(a+i*c+j));
        }
    }
    for(i=0;i<r;i++)
    {
        for(j=0;j<c;j++){
            printf("%d",*(a+i*c+j));
        }
    }
}

References: geeksforgeeks.org

nitin aditya
  • 119
  • 1
  • 3