Member Avatar for I_m_rude
float* values = calloc(m*n, sizeof(float));
    float** rows = malloc(n*sizeof(float*));
    for (int i=0; i<n; ++i)
    {
        rows[i] = values + i*m;
    }

What is this code trying to so ? Can you explain me the first 2 lines in detail ? I am not getting it. thanks.

Recommended Answers

All 3 Replies

What is this code trying to so ?

It's trying to create a simulated 2D array without calling malloc() for each row but still allow array indexing for both dimension. Let's say you want a 2D array, but the size must be a runtime value. malloc() is essentially your only option (barring C99 VLAs, but we'll ignore those for now).

There are basically three ways you can go about it. First is a straight allocation to a pointer:

#include "stdio.h"
#include "stdlib.h"

int main(void)
{
    int rows = 3, cols = 4;
    int *p = malloc(rows * cols * sizeof *p);
    int i, j, k = 0;

    /* Populate */
    for (i = 0; i < rows; ++i) {
         for (j = 0; j < cols; ++j)
             p[i * cols + j] = ++k;
    }

    /* Display */
    for (i = 0; i < rows; ++i) {
         for (j = 0; j < cols; ++j)
             printf("%4d", p[i * cols + j]);

         putchar('\n');
    }

    /* Release */
    free(p);

    return 0;
}

Notable in this option is only a single call to malloc(), but also the awkward indexing calculation. What we'd really like to do is index the simulated array just like we would a real 2D array: p[i][j]. But to do that we need to simulate a 2D array in the form that it would be as a real array, that is, each p[i] evaluates to a pointer to the first element of the subarray. This leads us to the more common solution of a pointer to a pointer and a malloc() call for each row:

#include "stdio.h"
#include "stdlib.h"

int main(void)
{
    int rows = 3, cols = 4;
    int **p, i, j, k = 0;

    /* Allocate the rows */
    p = malloc(rows * sizeof *p);

    /* Allocate the columns for each row */
    for (i = 0; i < rows; ++i)
        p[i] = malloc(cols * sizeof *p[i]);

    /* Populate */
    for (i = 0; i < rows; ++i) {
         for (j = 0; j < cols; ++j)
             p[i][j] = ++k;
    }

    /* Display */
    for (i = 0; i < rows; ++i) {
         for (j = 0; j < cols; ++j)
             printf("%4d", p[i][j]);

         putchar('\n');
    }

    /* Release the columns */
    for (i = 0; i < rows; ++i)
        free(p[i]);

    /* Release the rows */
    free(p);

    return 0;
}

Notable here is the natural way in which the 2D "array" is indexed, but also the extra calls to malloc() and free() for each row. This can be a significant overhead for a large number of rows because malloc() is an expensive operation.

Since the second solution really just doles out blocks of cols elements, why not just call malloc once to allocate memory for all of the elements in every row, and then use a similar calculation from the first solution to initialize the array of row pointers? That way you get the best of both worlds: natural indexing, and a constant number of calls to malloc:

#include "stdio.h"
#include "stdlib.h"

int main(void)
{
    int rows = 3, cols = 4;
    int **p, *q, i, j, k = 0;

    /* Allocate the rows */
    p = malloc(rows * sizeof *p);

    /* Allocate the columns for all rows */
    q = malloc(rows * cols * sizeof *q);

    /* Point each row to an appropriate slice of the columns */
    for (i = 0; i < rows; ++i)
        p[i] = &q[i * cols];

    /* Populate */
    for (i = 0; i < rows; ++i) {
         for (j = 0; j < cols; ++j)
             p[i][j] = ++k;
    }

    /* Display */
    for (i = 0; i < rows; ++i) {
         for (j = 0; j < cols; ++j)
             printf("%4d", p[i][j]);

         putchar('\n');
    }

    /* Release the columns */
    free(q);

    /* Release the rows */
    free(p);

    return 0;
}

Now you only call malloc() twice for any sized 2D "array", but still get the natural indexing syntax.

On a side note, the following raises a red flag for me:

float* values = calloc(m*n, sizeof(float));

When one uses calloc(), it's usually because of an expectation that the elements of the array will be initialized to 0. However, this only applies for integer types. Pointers and floating point types need not have a 0 representation that's equivalent to all bits zero, which is how calloc() initializes the allocated block. calloc() is basically this:

void *calloc(size_t n, size_t size)
{
    void *mem = malloc(n * size);

    if (mem)
        memset(mem, 0, n * size);

    return mem;
}

And memset() is this:

void *memset(void *s, int c, size_t n)
{
    unsigned char *p = (unsigned char*)s;
    size_t i;

    for (i = 0; i < n; ++i)
        *p++ = (unsigned char)c;

    return s;
}

If you don't understand why that's a problem, it's okay, because the problem is subtle. Just remember that a pointer with all bits set to 0 is not guaranteed to compare equal to a NULL. Likewise, a floating point value with all bits set to 0 isn't guaranteed to compare equal to 0.0. So that call to calloc() is making an unwarranted assumption and introducing a subtle bug if there's not an explicit loop later that initializes the elements to true zero for the type:

for (int i = 0; i < m * n; ++i)
    values[i] = 0;
commented: bow to you james sir! hats off to you! +2
Member Avatar for I_m_rude

ohh god! how deep your awesome knowledge is! it is just superb! this explaination is incredible. But one thing, i wana ask, Can you please explain the problem with calloc a little more ? please! thanks.

The problem with calloc() stems from underlying bit patterns not being all bits zero. Most notable in history is how null pointers are represented (read more here). Once you understand that problem, it's pretty easy to see how memset() to all 0s on a pointer type won't necessarily produce a null pointer. The same problem exists for essentially any type that isn't strictly integral.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.