-6
int **arr = (int **)malloc(r * sizeof(int *));

how should i interpret the meaning of the above C code ?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
user292174
  • 23
  • 3
  • 1
    [Please see this discussion on why not to cast the return value of malloc() and family in C..](https://stackoverflow.com/q/605845/2173917) – Sourav Ghosh Oct 23 '17 at 12:34
  • 2
    Question too broad, but: `int foo;` declares an `int`. `int *foo`; declares a pointer to `int`. `int **foo;` declares a pointer to `int*`, that is a pointer to a pointer to `int`. – Jabberwocky Oct 23 '17 at 12:36
  • This is _probably_ the dynamic allocation of an array of `r` pointers to `int`. These pointers are _probably_ going to be used as part of the construction of a poor man's two-dimensional (or more) array of `int`s. Without seeing more code we cannot be more specific. – zwol Oct 23 '17 at 12:39
  • 1
    Hover your mouse over the "double-pointer" tag to see why you shouldn't call it a "double pointer". – 001 Oct 23 '17 at 12:41

2 Answers2

5

The call to malloc() in:

int **arr = (int **)malloc(r * sizeof(int *));

allocates space for r pointers to int, and returns a void * pointing to the allocation. This pointer is then cast to int ** and assigned to arr, which is a pointer to a pointer to int. Thus arr points to the first element of the allocated region, which is a pointer to int. Such code may be used in creating a "jagged" array, where each row of the array is allocated separately; this means that the allocations may not be contiguous in memory, so this would not be a true 2d array. See Correctly allocating multi-dimensional arrays to read more about this.

The above is considered bad style, though. Most importantly, using explicit types as arguments of the sizeof operator should be avoided when possible:

int **arr = (int **)malloc(r * sizeof *arr);

This is less error-prone, and easier to maintain if the type of arr changes at some point in the life of the code.

Further, there is no need to cast the result of malloc() in C, so this cast is superfluous and only serves to clutter the code:

int **arr = malloc(r * sizeof *arr);

As a final refinement, I prefer to place the sizeof expression first in such cases:

int **arr = malloc(sizeof *arr * r);

This is a useless change in the particular case above, but when there is an additional multiplication, e.g.:

int **arr = malloc(sizeof *arr * r * n);

this guarantees that the arithmetic is carried out with a type at least as wide as size_t, reducing the risk of integer overflow (signed integer overflow causes undefined behavior in C), and may reduce the risk of unsigned integer wrap-around (well-defined, but possibly unexpected or unwelcome).

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • *"... guarantees that the arithmetic is carried out with size_t types..."* but it doesn't guarantee that the arithmetic is carried out within a `size_t` type, though. According to [C11/6.3.1.8p1](https://port70.net/~nsz/c/c11/n1570.html#6.3.1.8p1), *"... the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank..."*. – autistic Oct 23 '17 at 12:56
  • *"... removing the risk of integer overflow..."*. Oh. How does this *remove* risk of integer overflow? If the operands have the same value, the result will be the same. – autistic Oct 23 '17 at 12:57
  • *"... possibly reducing the risk of unsigned integer wrap-around..."* Not really. Your best choices at all of those are to declare `r` and `n` as `size_t` (which you should be doing anyway, right?), and... have you ever heard of the 'F' pattern? It's a heatmap of where most of our eyes focus within the first few minutes of landing on a page. You should try to put the most useful information as close to the left hand side of the page. – autistic Oct 23 '17 at 13:02
  • @Sebivor -- edited. If `r` has a lesser conversion rank, then `sizeof *arr * r` is carried out with `size_t`, then if `n` has a lesser conversion rank, the second multiplication is carried out with `size_t`. If `r` or `n` has a greater conversion rank, the resulting type would be a wider type. Integer overflow is removed since `unsigned` types do not overflow. But, I suppose that a larger `signed` type could overflow. This is really a strategy for risk-reduction. – ad absurdum Oct 23 '17 at 13:03
  • That sure is a wordy way of saying '_it allocates an array of integer pointers_'. – danny Oct 23 '17 at 13:24
  • @Sebivor -- I have read the Standard. This is not a theory, but a result of the fact that multiplication associates to the left in C. See this [Ideone link](https://ideone.com/EmfQth). – ad absurdum Oct 23 '17 at 13:38
  • Which part of the standard describes this? Pointing at a single example doesn't work in C; you could just as easily point [at this](https://stackoverflow.com/questions/1525187/how-do-we-explain-the-result-of-the-expression-xxx) and state that all C implementations produce the same behaviour. At the heart of that issue, however, is the problem that *the operands aren't sequenced to each other*, meaning either way you parse it `(a * b) * c` and `a * (b * c)` where brackets specify grouping of a subexpression, both subexpressions could evaluate simultaneously for example.. – autistic Oct 23 '17 at 13:52
  • @Sebivor -- the linked example shows that the order of the multiplications matters. This is where associativity comes in. You can understand this by looking at [this oft-referenced operator precedence table](http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm). Given `a * b * c`, the _operands_ `a`, `b`, and `c` may be evaluated in any order, but the _result_ of `a * b` is multiplied with `c`. This is the meaning of _operator precedence_. The expression `a * b * c` can't be evaluated as `a * (b * c)` without violating operator precedence. – ad absurdum Oct 23 '17 at 14:14
  • You said you've read the standard... can you show me where the standard says this? I might have missed it. It is fairly easy to miss things in that document, after all. – autistic Oct 23 '17 at 14:23
  • @Sebivor -- Operator precedence is not explicitly stated in the Standard (which is why it easier to use a (not infallible) reference table, such as the one linked to in my last comment. The information can be found in the formal description of the grammar within the Standard. In particular, [see §6.5.5/1](http://port70.net/~nsz/c/c11/n1570.html#6.5.5p1).... – ad absurdum Oct 23 '17 at 14:27
  • @Sebivor -- a multiplicative expression may be a _cast-expression_, or a _multiplicative-expression_ * _cast-expression_. In §6.5.5/4 it is stated that "The result of the binary * operator is the product of the operands." I take this to mean that given `a * b * c`, `a * b` is a multiplicative-expression and `c` is a cast-expression, with the result being the result of the subexpression `a * b` multiplied by `c`. – ad absurdum Oct 23 '17 at 14:31
  • 1
    You're right. Heh, I missed that... though it is pretty explicit, a cast expression can't be a multiplicative expression, so it is left to right as you say. I stand by my point that you shouldn't use examples like that for C, though. How many times has someone told you that *"`fflush(stdin);` works for me"*, for example? Probably none... knowing my luck, none... Thanks for taking the time to find that citation :) – autistic Oct 23 '17 at 14:37
  • Also, `int a = INT_MAX;`? With relevance to `malloc`, can you come up with a sensible reason to use a type which permits negative numbers? If you use `int` there, well, we don't know what values will be there or where they come from because we haven't asked... What I was suggesting earlier, it might be best to use `size_t` for the types of all of those variables... and consider the F-pattern. If you're unfamiliar, [red means "lots of time spent gazing here"](https://conversionxl.com/wp-content/uploads/2016/03/heat-map-f-shape.jpg). – autistic Oct 23 '17 at 14:51
  • @Sebivor -- well, `int a = INT_MAX` was just used to demonstrate differing results due to multiplicative associativity. Concerning relevance to `malloc()`, I agree that using `size_t` is probably always the right choice when designing a program. Yet in working with legacy code one does not get to choose the types which came before. The point is, this is simply a defensive strategy to reduce some risk of UB. I find this pattern to be as easy to read as the alternative. The fact that there is potential for overflow here does come as a surprise to many.... – ad absurdum Oct 23 '17 at 15:06
2

This

int **arr = (int **)malloc(r * sizeof(int *));

is a dynamic allocation of an array of the type int * [r] that is an array of r elements that have the type int *.

To make the declaration more clear consider the following simple program

#include <stdio.h>

int main(void) 
{
    char * s[] = { "Hello", " ", "user292174" };
    const size_t N = sizeof( s ) / sizeof( *s );

    char **p = s;

    for ( size_t i = 0; i < N; i++ ) printf( "%s", p[i] );
    putchar( '\n' );

    return 0;
}

Its output is

Hello user292174

Here in the program instead of the type int there is used type char for simplicity.

Array designators in expressions with rare exceptions are converted to pointers to their first elements.

So as the type of the array s is char * then a pointer to first element of the array s will have the type char **.

In the case of the original declaration from the question then there is allocated dynamically an extent of memory that is interpreted as an array of elements of the type int *. And the starting address of the extent where the first lement of the implyed array will be exist is assigned to the pointer. So the pointer shall have the type int **.

int **arr = (int **)malloc(r * sizeof(int *));

Usually such allocations are used to simulate a two-dimensional array when the number of rows and columns of the array are calculated at run-time.

For example

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

int main(void) 
{
    size_t r = 2;
    size_t c = 3;

    int **arr = (int **)malloc( r * sizeof( int * ) );

    for ( size_t i = 0; i < r; i++ )
    {
        arr[i] = malloc( c * sizeof( int ) );
    }

    for ( size_t i = 0; i < r; i++ )
    {
        for ( size_t j = 0; j < c; j++ )
        {
            arr[i][j] = c * i + j;
        }
    }

    for ( size_t i = 0; i < r; i++ )
    {
        for ( size_t j = 0; j < c; j++ )
        {
            printf( "%d ", arr[i][j] );
        }
        putchar( '\n' );
    }


    for ( size_t i = 0; i < r; i++ )
    {
        free( arr[i] );
    }

    free( arr );

    return 0;
}

The program output is

0 1 2 
3 4 5

If the compiler supports variable length arrays then a two dimensional array can be allocated at once. For example

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

int main(void) 
{
    size_t r = 2;
    size_t c = 3;

    int ( *arr )[c] = (int **)malloc( sizeof( int[r][c] ) );


    for ( size_t i = 0; i < r; i++ )
    {
        for ( size_t j = 0; j < c; j++ )
        {
            arr[i][j] = c * i + j;
        }
    }

    for ( size_t i = 0; i < r; i++ )
    {
        for ( size_t j = 0; j < c; j++ )
        {
            printf( "%d ", arr[i][j] );
        }
        putchar( '\n' );
    }

    free( arr );

    return 0;
}

The porgram output is the same as shown above.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335