-1

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.

Richard
  • 7,037
  • 2
  • 23
  • 76
  • 2
    What is `int *amount = 9;` supposed to do? That makes `amount` *point to address `9`*, and there's probably not anything useful there. Why make `amount` a pointer to begin with? What problem is that supposed to solve? – Some programmer dude Jun 13 '18 at 09:40
  • 1
    One curious thing is that you assing 9 to int pointer. – dbrank0 Jun 13 '18 at 09:40
  • Yeah - why a pointer? Get rid of that star! – Martin James Jun 13 '18 at 09:43
  • @Someprogrammerdude As far as I know, you can immediately initialise the value of which the pointer is pointing when you're declaring the variable, thus `int *amount = 9;`. I tried doing this instead: `int *amount` and `amount = 9;` and it turnt out to be the same. I assume what I'm doing is correct (as I also remember my lecturer telling me that)? Also, I am trying to pass it by reference later on to a function. Is there another way I can do that? – Richard Jun 13 '18 at 09:56
  • If you want to *emulate* "pass by reference" you pass a pointer to a variable using the address-of operator `&`. As in `int amount = 9; ...; some_function(&amount);` and use the dereference operator `*` to access what the pointer is pointing to inside the function. – Some programmer dude Jun 13 '18 at 09:58
  • @Someprogrammerdude I see, thank you! However, what did I do wrong in my previous code? Isn't it supposed to be correct? – Richard Jun 13 '18 at 10:11
  • As I said in my first comment, `int *amount = 9;` makes `amount` *point to the address `9`*. It doesn't make `amount` point to some integer which have the value `9`, that would be something like `int real_amount = 9; int *amount = &real_amount;` – Some programmer dude Jun 13 '18 at 10:19
  • @Someprogrammerdude Hmm... I see. Thank you once again for the answers :-D! – Richard Jun 13 '18 at 10:22

4 Answers4

1

You are just using the fact that your implementation allows for safe conversion between pointers and integers, but this:

int *amount = 9;      // only use that for memory mapped hardware registers
printf("%d", amount); //re-interpreting the pointer value as an int
array = realloc(array, amount * sizeof(int));   // hopefully you got a diagnostic

is terrible. A pointer should only be a null pointer of point to a valid object. Full stop. And pointer arithmetics only makes sense inside an array.

If a variable is supposed to contain integer values, then it shall be of an integer type:

int amount = 9;
printf("%d", amount); //Printing
array = realloc(array, amount * sizeof(int)); //Problem is here

If you need a pointer to it, just declare it and use it as a pointer:

int amount = 9;
int *p_amount = &amount;
printf("%d - %d\n", amount, *p_amount); //Printing
array = realloc(array, (*p_amount) * sizeof(int)); //No problem now
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • I don't quite understand what you meant by "A pointer should only be a null pointer of point to a valid object" and "only use that for memory mapped hardware registers". Could you explain them in simpler terms? – Richard Jun 13 '18 at 10:10
  • @WealthyPlayer: A pointer should only be assigned addresses. `int a; int *pt = &a`, null, `int *pt = NULL;` of values of other pointers `int a; int *pa = &a; int *pb = pa;`. `int *amount = xxx;` where xxx is an integer value is a code smell. The only correct use case I know is when you directly access the hardware (in kernel mode or embedded systems). In that case you may have io ports or display memory mapped at well known addresses. For example in MS/DOS, the text screen display was mapped at address 0xB0000. – Serge Ballesta Jun 13 '18 at 11:26
  • Ah! I see, I think you meant "or" instead of "of"? Thank you! – Richard Jun 13 '18 at 11:33
  • @WealthyPlayer: Yes, and sorry for it. `F` and `R` keys are too close for me :-( – Serge Ballesta Jun 13 '18 at 11:40
  • No problem :-) You've answered my questions, I should be thanking you. – Richard Jun 15 '18 at 03:41
1

amount has the type int *. The reason printf prints 9 is not that amount points to the value 9, but rather it is the value 9 casted to a pointer.

Now in your array = realloc(array, amount * sizeof(int)); statement. You try to multiply the pointer (not the value pointed to by amount). Ask yourself what the semantics of that should be. Rather than int *amount = 9; you probably want

int *amount = calloc(1, sizeof(int));
*amount = 9;

which declares a pointer and allocates space for one integer and rather than array = realloc(array, amount * sizeof(int)); you probably want

array = realloc(array, *amount * sizeof(int));

you should try to learn the concept of pointers and pointer arithmetics. Just declaring a pointer does not reserve space at the end of it.

Iead
  • 11
  • 3
  • Hmm I see. So is it safe to say that from the very beginning (i.e. `int *amount = 9`), I've already misinterpreted the meaning of that line? I think I know how it works now, I've also tried reading https://www.gribblelab.org/CBootCamp/8_Pointers.html while waiting for an answer. Thank you! – Richard Jun 13 '18 at 10:27
  • yes. You wanted something that points to the value 9, but got something that points to position 9. Sometimes C and C++ can be quiet tricky to what a line exactly means. – Iead Jun 13 '18 at 11:37
1

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 );

?

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • I see, thank you for the very clear answer! Some questions, though, how or when does `realloc` fail to work? Is it very technical and a difficult issue and should I just ignore it for now? And thank you for the tip about using `sizeof(pointervariable)` instead of typing the variable type explicitly :-D – Richard Jun 15 '18 at 03:40
  • Oh and how do I print out the memory address of which the pointer is pointing (i.e. what type should I use since `%d` is incorrect)? – Richard Jun 15 '18 at 03:45
  • @WealthyPlayer: Use `%p` to print pointer values. `realloc` will fail if there’s not a large enough chunk of available memory to satisfy the request. That won’t happen unless you’re trying to set aside *very large* chunks of memory, or if you’ve made a very large number of small allocations such that the heap is very fragmented (i.e., you may have so many Mb available, but not in a single, contiguous block). I’ve never encountered it in practice, but it’s something to be aware of. – John Bode Jun 15 '18 at 03:59
  • Okay thank you! I've read this too (and the answer was also from you :-D): https://stackoverflow.com/questions/18217525/why-or-when-do-you-need-to-dynamically-allocate-memory-in-c and I found it very helpful on trying to understand for what purpose dynamic memory allocation is. – Richard Jun 15 '18 at 06:11
0

amount should be defined as an int and not int *.

int amount = 9;
eyalm
  • 3,366
  • 19
  • 21