Search code examples
cpointerssizeofreallocdynamic-allocation

Using realloc by multiplying a pointer integer and sizeof(int) not working


Code:

void main() {
    int *array = calloc(5, sizeof(int));
    int *amount = 9;
    array[0] = 1;
    array[1] = 2;
    array[3] = 5;

    int i = 0;
    while (i < 5) {
        printf("%d ", array[i]);
        i += 1;
    }

    printf("%d", amount); //Printing
    array = realloc(array, amount * sizeof(int)); //Problem is here
    printf("\n");
    i = 0;
    while (i < 9) {
        printf("%d ", array[i]);
        i += 1;
    }
    free(array);
}

It says "invalid operands to binary * (have 'int *' and 'unsigned int'), but when I tried printing "amount", it's actually 9? I'm trying to use a pointer integer so that I can pass it by reference.


Solution

  • A couple of things:

    First,

    int *amount = 9;
    

    does not do the same thing as

    *amount = 9;
    

    In the first case, the * is only there to indicate that amount has pointer type, and we are initializing the pointer value (i.e, the address) to 9, which is most likely not a valid pointer value, and attempting to dereference it may lead to a runtime error.

    In the second case, we are assigning the integer value 9 to the object amount is pointing to.

    Why didn’t this break when you passed amount to printf? Basically, you invoked undefined behavior by passing an argument of the wrong type (%d expects an int, you passed an int *). One of the possible results of undefined behavior is getting the expected result. For whatever reason, printf was able to treat that int * value as an int. Most compilers should flag that type mismatch, but you may heed to crank up the warning level to see it.

    There’s a constraint on the binary * operator that both operands have arithmetic type. int * is not an arithmetic type, hence the diagnostic.

    Based on how you are actually using amount in your code, you should not have declared it as a pointer, but as a regular int:

    int amount = 9;
    

    Secondly, as a rule, you do not want to assign the result of realloc to the original pointer. If realloc fails, it will return NULL and leave the original block of memory as-is. However, if you assign that NULL back to your original pointer, you will lose any access to that memory. Best practice is to assign the result of realloc to a temporary, and then verify that the temporary is valid before assigning it back to the original:

    int *tmp = realloc( array, amount * sizeof *array );
    if ( tmp )
    {
      array = tmp;
    }
    else
    {
      // handle realloc error
    }
    

    Note the use of sizeof *array instead of sizeof (int). sizeof is an operator like unary * or unary +, and its operand can either be a parenthesized type name or an expression. The expression *array has type int, so sizeof *array == sizeof (int). This helps make code a bit easier to read, and if you ever change the type of array (say to double *), you won’t have to update the realloc call. It’s also very useful when allocating multidimensional array types - would you rather write

    int (*arr)[10] = malloc( sizeof (int) * 10 * rows);
    

    or

    int (*arr)[10] = malloc( sizeof *arr * rows );
    

    ?