Program to understand dynamic array

Discussion in 'Programmer's Corner' started by anukalp, Oct 8, 2018.

  1. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    I have written program to understand dynamic memory allocation for array. I have few doubts with dynamic array

    Please Take a look at this program
    Code (C):
    1.    #include <stdio.h>
    2.    #include <stdlib.h>                              
    3.  
    4.    int main()
    5.    {
    6.      int * pointer;                                      /*Pointer to array. */
    7.      int i, size;                                    
    8.  
    9.      printf( "Size of array : " );
    10.      scanf( "%d", &size );                            /*Get size of array.*/
    11.          
    12.      pointer = (int *) malloc(size * sizeof (int));
    13.  
    14.      if( pointer == NULL )                            /*Check for failure. */
    15.      {
    16.        printf( "Can't allocate memory!" );
    17.  
    18.      }
    19.      printf( "Allocated array! \n" );
    20.  
    21.      for ( i = 0; i < size; i++ )
    22.      {
    23.           printf( "Enter element : " );
    24.           scanf( "%d", &pointer[i] );
    25.      }
    26.      for ( i = 0; i < size; i++ )
    27.  
    28.          printf( "%d ", pointer[i] );
    29.      
    30.      return 0;
    31.    }
    32.    
    Do you see any mistake in program ?

    What is difference between cast and without cast

    Code (C):
    1. int * pointer;
    2. pointer = malloc(10 * sizeof(int));        /* without a cast */
    3. pointer = (int *)malloc(10 * sizeof(int));    /* with a cast */
    if I am replacing one line with another in program. output doesn't change

    What's the specific use of "with a cast" and "without a cast in program" ?
     
    Last edited: Oct 8, 2018
  2. bogosort

    Active Member

    Sep 24, 2011
    326
    175
    Line 6: get in the habit of writing "int *p" instead of "int * p" or, worse, "int* p". The pointer symbol is associated with the variable, not with the type. Writing it correctly will help you avoid mistakes like: "int * p, q" (where 'q' is supposed to be a pointer to int, but here is just an int).

    Line 10 and elsewhere: as discussed in another thread, avoid scanf. Since you're testing malloc, just hard-code the size and then in the loop assign 'i' to 'pointer'. No need to muddle the code with brittle functions like scanf.

    Line 12: do not cast malloc (see below). Also, instead of using sizeof(int) in malloc, use sizeof(*pointer); that way, if you decide to change the type of pointer, your call to malloc won't need to change.

    Line 14: you check the result of malloc, which is good, but then you just keep going with the rest of the code. The program should log the fact and gracefully exit if malloc fails.

    Do not cast the call to malloc. Read the first answer here: https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc
     
    anukalp likes this.
  3. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    Thanks bogosort, I didn't understand this line 14

    I have rewritten program, Please Take a look at this program
    Code (C):
    1.    #include <stdio.h>
    2.    #include <stdlib.h>                          
    3.    int main()
    4.    {
    5.      int *pointer;                                     /*Pointer to array. */
    6.      int i, size = 10;                                
    7.  
    8.      pointer = malloc(size * sizeof (*pointer));
    9.      if( pointer == NULL )                            /*Check for failure. */
    10.      {
    11.        printf( "Can't allocate memory!" );
    12.        exit( 0 );
    13.      }
    14.      printf( "Allocated array! \n" );
    15.      for ( i = 0; i < size; i++ )
    16.      {
    17.           printf( "Enter element : " );
    18.           scanf( "%d", &pointer[i] );
    19.      }
    20.      for ( i = 0; i < size; i++ )
    21.          printf( "%d ", pointer[i] );
    22.  
    23.      return 0;
    24.    }
    I hope this is better
     
  4. bogosort

    Active Member

    Sep 24, 2011
    326
    175
    The idea is that if the allocation fails, then there is no point in continuing with program execution, because the only thing the program does is use the allocated storage! Once the allocation fails, the program should exit, which you do in this version.

    Looks much better, though I'd still get rid of the scanf in the loop. :) But that's a preference, not a statement of correctness.

    My only other nit-pick is that you should exit with -1 rather than 0. The program's exit status should reflect the success or failure of the program to run. In most environments, a zero return status indicates success and any nonzero status is a failure. Returning -1 is an almost universal sign that something went wrong.
     
    anukalp likes this.
  5. WBahn

    Moderator

    Mar 31, 2012
    23,974
    7,425
    Code (C):
    1.    #include <stdio.h>
    2.    #include <stdlib.h>                            
    3.  
    4.    int main()
    5.    {
    6.      int * pointer;                                      /*Pointer to array. */
    7.      int i, size;                                
    8.  
    9.      printf( "Size of array : " );
    10.      scanf( "%d", &size );                            /*Get size of array.*/
    11.  
    Put the pointer indirection against the variable, so

    int *pointer;

    Also, stop using scanf(). Period. Just quit.

    For code like this, just hard code the input

    size = 30;

    and move on. If you really want to get input from the keyboard, then do it properly using fgets().

    Code (Text):
    1.        
    2.      pointer = (int *) malloc(size * sizeof (int));
    3.  
    This is fine as far as it goes, but a better way to do it is to lock the sizeof() to the type being allocated

    pointer = (int *) malloc(size * sizeof (*pointer));

    One of the downsides of this code is that if you change the type of pointer, say to a long *, then this statement needs to be updated. This is one of the reasons that the other school of thought uses to claim that you shouldn't typecast malloc() return values. While it has merit, I don't find it compelling. First, the span of code in which this should be a realistic concern for any given variable should be minimal -- and if it isn't then the code is likely not well laid out to begin with. Second, the compiler will be more than happy to identify for you all of the places where you need to update the code and it is a pretty simple matter to do so. Third, it is possible to use macros maintain a table of types, but this is a pretty advanced topic not for the feint of heart.

    I know I am not alone in wishing that the C standards committee would introduce a typeof() operator, but I'm not aware of anything like that on the horizon. There are some non-standard compiler extensions that appear to include this, but since I try very hard to write only compliant code, I haven't looked at them.

    Code (Text):
    1.  
    2.  
    3.  
    4.      if( pointer == NULL )                            /*Check for failure. */
    5.      {
    6.        printf( "Can't allocate memory!" );
    7.  
    8.      }
    9.  
    Two issues here. The first is that, while you check to see if the memory allocated, you then proceed to not prevent your program from dereferencing the NULL pointer -- but that's the entire point of doing the NULL pointer check!

    Second, you write your check in such a way that if you make the all-too-common-and-easy-to-make mistake of using a single equals sign instead of the double equals sign, you do three very bad things. First, you inadvertently set pointer to NULL. Second, in doing so, you have created a memory leak because you have lost touch with the block of memory that was allocated and now have no way to deallocate it. Finally, your NULL pointer check now fails to detect the NULL pointer and so your code will proceed blindly to dereference it later on.

    A couple of better alternatives are:

    if (!pointer)
    {
    // Code to deal with the NULL pointer
    // Probably should die gracefully in most small programs.
    }

    if (NULL == pointer)
    {
    // Code to deal with the NULL pointer
    // Probably should die gracefully in most small programs.
    }

    The latter is a better way, in general, to check the value of a variable against a constant. If you use "=" instead of "==", then you have just turned a logic error into a syntax error that the compiler can catch.

    Code (Text):
    1.  
    2.      printf( "Allocated array! \n" );
    3.  
    4.      for ( i = 0; i < size; i++ )
    5.      {
    6.           printf( "Enter element : " );
    7.           scanf( "%d", &pointer[i] );
    8.      }
    9.      for ( i = 0; i < size; i++ )
    10.  
    11.          printf( "%d ", pointer[i] );
    12.    
    13.      return 0;
    14.    }
    15.    
    I've already smacked you over the scanf() use, so I won't do it again.

    For correctly written code, it will have no effect because a void * can be implicitly cast to any other pointer type.

    As result, there are two schools of thought on whether you should cast the return value of malloc() or not. I am firmly on the side of casting the return value and that position is consistent with the secure coding community's recommendations.

    You should set your compiler to be as picky as tolerable and then write your code so that it generates no warnings (or, if there's a warning that you opt to accept, it should be thoroughly documented that you are doing so). One of the warnings that should be turned on are potentially unsafe implicit coercions (what are often known as narrowing coercions). This will let you avoid a lot of the common, silly mistakes that all programmers make on an all-too-frequent basis. But if you don't cast the return values of malloc(), then each call will through a warning and most programmers will eventually get sloppy in looking over a sea of superfluous warnings and start missing warnings that reveal real logic errors. So make these warnings go away by using proper casts -- and note that a "proper cast" is NOT just whatever cast happens to make the warning go away, it is the cast that is proper to the logic of the code in which it is being used.

    By casting the return value of malloc(), you enable the ability of the compiler to do type checking which can save you a lot of grief, particularly if you are using several different types of pointers.

    The only real downside to casting the return value from malloc() is when compiling using a C90-compliant compiler (as opposed to the pretty-much ubiquitous C99 and later compilers available today). The earlier compiler allowed implicit function declarations and so if you didn't include stdlib.h (or another library header that included malloc()), then the compiler could assume that it returns an int and that you are now explicitly converting to a pointer. Sometimes such code will run fine, but it is a time bomb waiting to go off.
     
    anukalp likes this.
  6. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    malloc function allocates the specified number of bytes
    realloc increases or decreases the size of the specified block of memory
    calloc allocates the specified number of bytes and initializes them to zero

    How to use realloc calloc and free function in program ?
     
  7. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    @WBahn Thank you very much for providing very detail information

    If I want to take input from the keyboard then I use scanf statement. I don't understand What's the disadvantage of using scanf statement.
     
  8. WBahn

    Moderator

    Mar 31, 2012
    23,974
    7,425
    What are you doing to learn this on your own? There is a WEALTH of information out there to be had for the taking.

    When you are done with a block of memory, you should always free() it. Good housekeeping says that you should do this even when you know that you are terminating the program so that if, later, you extend the program (or someone else does) you don't end up with a memory leak. When you use free(), you give it the address of a previously allocated block of memory. It is good practice to set the pointer to that memory to NULL so that you don't try to dereference it later.

    myMemory = malloc(...);
    free(myMemory);
    myMemory = NULL;

    realloc() is used when you already have a dynamically allocated block of memory and you want to change it's size while not changing the existing contents. The realloc function will do one of three things. If you request a reduction in size, it will almost always keep the memory block in place but just reclaim the portion of the block above the new size. It is not required to do this, since the memory manage may opt to move the smaller initial portion of the block to a new block in order to keep the larger original block intact for later use. If you request an increase in size, the function will either allocate additional memory at the end of the block to increase the size (it will opt for this whenever possible) or it will allocate a new block of memory of sufficient size and move the original contents to the start of the new block. The other option is to return NULL if it was unable to satisfy the request. If the request was satisfied, the value returned is the address of the new block (which may be the same as the old block) and the contents of the new block are identical to the original contents of the old block up to the smaller of the size of the new block or the old block.

    calloc() is intended for allocating memory for arrays, but in that regard it is really syntactic sugar. However, there is a real difference in that calloc() initializes all bytes in the allocated block to zero while malloc() does not. Thus calloc() is significantly slower than malloc() on large blocks, but if initialization to zero is important to you, it will likely do it faster than your own code could.
     
    anukalp likes this.
  9. WBahn

    Moderator

    Mar 31, 2012
    23,974
    7,425
    It is extremely fragile and very insecure. There is TONS of information about this out there.
     
  10. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    I have some question

    1. Do we use dynamic memory in embedded systems. I mean If we are writing code for any microcontroller such as PIC.

    2. How do we get input in embedded system Do we use scanf or fgets function or it depends on compiler
     
  11. WBahn

    Moderator

    Mar 31, 2012
    23,974
    7,425
    That's going to depend on the microcontroller, the libraries available with the compiler, and what you are trying to do. Some microcontrollers have pretty abundant memory while others have extremely little. Similarly, some have clock speeds measured in GHz and others in MHz (and many applications will clock them in kHz). So there are going to be a set of microcontrollers in the fringe where using high-level code is doable, but only if you don't burden them with heavy handed library functions, and many functions, such as scanf() and printf() are VERY heavy handed. Functions like malloc() generally aren't too bad, but it does mean that your code has to have some kind of memory manager, though simple ones are pretty lightweight.
     
  12. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    fgets is a function in the C programming language that reads a limited number of characters from a given file stream source into an array of characters.

    The following code reads characters from input
    Code (C):
    1. #include <stdio.h>
    2.  
    3. #define MAX 15
    4.  
    5. int main()
    6. {
    7.     char buf[MAX];
    8.     fgets(buf, MAX, stdin);
    9.     printf("string is: %s\n", buf);
    10.  
    11.     return 0;
    12. }
    I am reading integer value in my program and get function read characters from input

    so how to use fget function in my original program ?
     
  13. WBahn

    Moderator

    Mar 31, 2012
    23,974
    7,425
    In general, once you get the input into the buffer you want to first check if you got the entire string (i.e., everything entered by the user) by checking if the last character in the string (before the NUL terminator) is a newline character ('\n'). If it is, then you want to replace it with a NUL since most string processing functions will have issues with it. If it isn't, then the the user entered more text than could be captured by the buffer and what you do depends on what makes since for your application. For now, just detect that this has happened and die gracefully.

    Once you have their input and have stripped off the newline character, you can feed it to appropriate functions (either standard library or functions you write) to validate it and/or manipulate it. In this case, you can call strtol() to convert the string to a long integer, which you can then cast down to an int as you store it in your variable of type int. There is an older function called atoi() that does a similar thing.
     
  14. anukalp

    Thread Starter Member

    Jul 28, 2018
    106
    0
    as you said fgets(), I was trying to replace scanf function by fgets() in my program. I tried to write further code with fgets() but I don't understand how to use fget function in my original program?

    I have checked program in post 3 and It's working
     
    Last edited: Oct 9, 2018
Loading...