Thread: Why parenthesis around pointer in function after realloc?

  1. #1
    Registered User
    Join Date
    Jan 2017
    Posts
    13

    Why parenthesis around pointer in function after realloc?

    I've been trying to wrap my isignificant brain around this thing called pointers. So I challenged myself to the following code, which is a push function that "grows" an array dynamically.

    I ran this code through valgrind memcheck, and no errors occured

    There might be more than one thing wrong with this code, don't hold back!

    But my main question is: why do I have to encase the dereferenced double pointer 'p' in parenthesis after realloc in the push function? (i.e: line14)

    the code:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    /* This example has been kept simple and without error handling */
    void push(int **p, int i, int val)
    {
        /* allocate memory */
        if(*p == NULL){
            *p = malloc(sizeof(int));
            *p[i] = val;
        }else{
            /* reallocate memory to grow array */
            *p = realloc(*p, ((i + 1) * sizeof(int)));
            (*p)[i] = val;
        }
    }
    
    int main(void)
    {
        int *p = NULL;
    
        /* Pushing three elements to array, using index and value */
        push(&p, 0, 123);
        push(&p, 1, 321);
        push(&p, 2, 777);
    
    
        printf("value index 0: %d\n", p[0]);
        printf("value index 1: %d\n", p[1]);
        printf("value index 2: %d\n", p[2]);
    
    
        free(p);
    
    
        return 0;
    }

  2. #2
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    668
    Quote Originally Posted by ThomasWink View Post
    But my main question is: why do I have to encase the dereferenced double pointer 'p' in parenthesis after realloc in the push function? (i.e: line14)
    You need to learn the operator table. The array operator, "[]" has a higher precedence than the dereference operator, "*". You need the parens to override this precedence.

    Also, don't realloc() for each call to push! Allocate a reasonable number of array elements, and ONLY realloc() if you need more that previously allocated. Keep track of how many have been used up and the current size of the allocated array.

    I would start with 10 in this example, and realloc() 1 1/2 to 2 times the size of the array, each time, if needed.

    I would also capture the return value from realloc() in a temp variable, in case the realloc() failed, otherwise you might lose the current array address. If the return from realloc() is the same address as the original passed, no need to reassign.

    An yes, you DO need error checking especially using malloc() and realloc()!!!

    Instead of void push(...), I would return a bool or an int to indicate if the function executed correctly!

  3. #3
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,728
    It seems like a typo here, both should be like line #14. Let me explain:

    In line #10, "p" is used like an array of pointers.
    In line #14, "p" is used like a pointer to an array.
    You can't do both with the same array.

    Remember that array subscripting(p[i]) has a higher precedence than dereferencing(*p).
    Devoted my life to programming...

  4. #4
    Registered User
    Join Date
    Feb 2019
    Posts
    533
    This is WRONG:
    Code:
    *p = realloc(*p, ((i + 1) * sizeof(int)));
    What will happen if realloc() fails? You'll get a memory leakage in you hands!
    Do this, instead:
    Code:
    { 
      void *q;
    
      if ( q = realloc( *p, (i+1) * sizeof(int) ) )
        *p = q;
      ...
    }
    If realloc() fails, the pointer at *p remains untouched.

    By the way... what if malloc() fails as well?
    Last edited by flp1969; 09-25-2019 at 09:12 AM.

  5. #5
    Registered User
    Join Date
    Feb 2019
    Posts
    533
    Another thing: realloc() behaves exactly as malloc() if the first argument is NULL (ISO 9989:1999 $7.20.3.4.3), so you don't need to test if *p is NULL to choose between malloc() and realloc(). Here's a modified example:
    Code:
    // Since we have to pass 2 arguments for the stack I would prefer
    // to use a structure like this...
    typedef struct {
      int *stkptr;
      size_t stksize;
    } stack_T;
    
    #define EMPTY_STACK { NULL, 0 }
    
    /* Notice I changed the arguments, so you may do:
    
         // declare an empty stack
         stack_T stk = EMPTY_STACK;
    
         // if push returns different than Ok ('0'k)...
         if ( push( &stk, 1 ) != 0 ) { ... error handling... }
         if ( push( &stk, 2 ) != 0 ) { ... error handling... }
         ...
    */
    int push( stack_T *p, int value )
    {
      int *q;
    
      // I don't test for p here because I assume it will be always valid!
    
      // realloc() could fail!
      if ( ! ( q = realloc( p->stkptr, ( p->stksize + 1 ) * sizeof( int ) ) ) )
        return 1;  // means error or "Invalid": 1 (one) looks like I (letter 'I').
    
      p->stkptr = q;
    
      *(q + p->stksize++) = value;
      // Or: q[p->stksize++] = value;
    
      return 0; // 0 means 'Ok' ('O' (oh) looks like '0' (zero)).
    }
    Last edited by flp1969; 09-25-2019 at 10:12 AM.

  6. #6
    Registered User rstanley's Avatar
    Join Date
    Jun 2014
    Location
    New York, NY
    Posts
    668
    // I don't test for p here because I assume it will be always valid!
    Never ASS/U/ME! ;^)

    You should be testing the values of the arguments to the push function, within the push function. What if NULL is passed through p?
    Code:
      // realloc() could fail!
    
      if ( ! ( q = realloc( p->stkptr, ( p->stksize + 1 ) * sizeof( int ) ) ) )
    
        return 1;  // means error or "Invalid": 1 (one) looks like I (letter 'I').
    If p is NULL, you should be getting a segmentation fault!

    I would also use stdbool.h, declare push() to be "bool push(...)", and return true or false.

  7. #7
    Registered User
    Join Date
    Feb 2019
    Posts
    533
    Quote Originally Posted by rstanley View Post
    Never ASS/U/ME! ;^)

    You should be testing the values of the arguments to the push function, within the push function. What if NULL is passed through p?
    I disagree... take a look at functions of libc... Almost none checks for anomalous inputs (strlen, strcpy, strcat, printf, scanf, ...). If you check everything in each function you make, then your code will be bloated. The trick is to know where to check.

    if you pass a NULL pointer to push() as it is, you'll get a segmentation fault... But the function requires a valid pointer, the task to check if this pointer is valid, in my opinion, is of the caller responsibility.

    Of course, you could add an assertion to debug your code...

    Quote Originally Posted by rstanley
    I would also use stdbool.h, declare push() to be "bool push(...)", and return true or false.
    It is a matter of preference. I prefer to use int as boolean, because using wordsize different from the processor default can create a less efficient code (in terms of performance). And, since we are talking about C (not C++) here, if I choose to use 'bool' I prefer to use the C99+ standard _Bool type instead of including stdbool.h.

  8. #8
    Registered User
    Join Date
    Jan 2017
    Posts
    13
    Interesting, thanks for all the remarks! I will ingest now

  9. #9
    Registered User
    Join Date
    Aug 2019
    Location
    Inside a Singularity
    Posts
    110
    I've been trying to wrap my isignificant brain
    Hey Thomas! Sometimes the simplest of mistakes happen from the best of us.....

    Often, in companies and institutions, the mightiest of bugs may occur due to just a single line of code.
    Take for example, AlgoExperts Development: YouTube

    These mistakes do not happen because one doesn't know how to do something but because the detail might be so minutely insignificant that it might be overlooked.
    What I'm trying to say is that you shouldn't downgrade yourself(I know you're being sarcastic up there in your message but I'm just saying ). Learning from your mistakes is one of the best thing about being human. Atleast now you have something more to learn about i.e. looking into precedence order etc.

  10. #10
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    656
    Quote Originally Posted by flp1969 View Post
    It is a matter of preference. I prefer to use int as boolean, because using wordsize different from the processor default can create a less efficient code (in terms of performance).
    Interesting. Have you seen any cases where using bool instead of int had a significant performance difference? I imagine most good compilers will treat a bool effectively the same as int in most cases, so there's probably very little difference between them.

    I especially like bool because any non-zero value is "automatically" converted to true when it's converted to bool, such as with bool alpha = isalpha(c);. The same can't be said of other types like int or char (and there's danger in using char as a Boolean value--e.g., (char)256 == false).

    Quote Originally Posted by flp1969 View Post
    And, since we are talking about C (not C++) here, if I choose to use 'bool' I prefer to use the C99+ standard _Bool type instead of including stdbool.h.
    bool is part of the C99 standard and is an alias (typedef or macro) of _Bool when you include stdbool.h. The only drawback of bool is that you have to include stdbool.h, but the benefit is that it requires one fewer keystroke each time you use it. So this is just a matter of preference.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. realloc() invalid pointer
    By JayBlay77 in forum C Programming
    Replies: 3
    Last Post: 02-17-2010, 09:55 AM
  2. Strncat and Realloc - pointer issues
    By bhenderson in forum C Programming
    Replies: 6
    Last Post: 08-16-2009, 01:59 PM
  3. pointer to pointer realloc problem
    By prakash0104 in forum C Programming
    Replies: 14
    Last Post: 04-06-2009, 08:53 PM
  4. Replies: 3
    Last Post: 11-11-2003, 03:44 AM
  5. Replies: 2
    Last Post: 02-07-2002, 09:39 AM

Tags for this Thread