Thread: Array of pointers, pointers to pointers.

  1. #1
    Registered User
    Join Date
    Sep 2018
    Posts
    9

    Array of pointers, pointers to pointers.

    This program ask to the user to enter words, then it shows the words sorted. My question is If I've used correctly malloc and realloc correctly since I'm still proccessing the concept of pointer to pointer but anyways I tried to go for it, trying to allocated and array of strings dynamically.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define WORD_LEN 20
    
    int read_line(char str[], int n);
    int compar_string(const void *p, const void *q);
    
    int main(void)
    {
        char buffer[WORD_LEN+1], **words;
        int n = 10;
        int j;
        
        words = malloc(n * sizeof(char *));  //Allocating space for an array of strings
        if (words == NULL) {
            printf("malloc failed.\n");
            exit(EXIT_FAILURE);
        }
        
        for (j = 0;; j++) {
            if (j < n) {
                printf("Enter a word:");
                if(read_line(buffer, WORD_LEN)) {
                    words[j] = malloc(strlen(buffer)+1);
                    strcpy(words[j],buffer);
                } else
                    break;                   //Break from loop if an empty word is entered
                } else {
                    char **temp;
                    n *= 2;
                    temp = realloc(words, n * sizeof(char *)); //reallocating space if the user enters more than 10 words
                    if(temp == NULL) {
                        printf("realloc failed.\n");
                        exit(EXIT_FAILURE);
                    } else {
                        printf("array reallocated.\n");
                        words = temp;
                        j--;
                    }
                }
        
        }
        
        qsort(words, j, sizeof(words[0]), compar_string);  //sorting the array of strings 
        
        printf("In sorted order: ");
        for(int i = 0; i < j; i++)
            printf("%s ", words[i]); 
        
        printf("\n");
        
        return 0;
    }
    
    int read_line(char str[], int n)
    {
        int ch, i = 0;
        
        while((ch = getchar()) != '\n')
            if(i < n)
                str[i++] = ch;
        str[i] = '\0';
        
        return i;
    }
    
    int compar_string(const void *p, const void *q)
    {
        return strcmp(*(char **)p, *(char **)q);
    }
    This function also gave me questions, it's an example that the book shows for use strcmp with qsort

    Code:
    int compar_string(const void *p, const void *q)
    {
        return strcmp(*(char **)p, *(char **)q);
    }
    If I declare a char **p, the variable p is a pointer to a pointer that points to a char right? If later in the program I use *p, im refering to the "pointer that points to the char? [ p ] -> [ char * ](Is this *p?) -> char
    Im I correct?

  2. #2
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    A pointer is an integer value. When you declare:
    Code:
    char *p;
    You are telling the compiler to allocate space for a variable p (not "*p"!) which will store a memory address. Later, you can use the indirection operator * to use this address stored in p to access a char stored in memory.

    This:
    Code:
    int a[2] = { 1, 2 };
    Declares an array (a sequence) of 2 ints. The symbol a is a pointer (yes, the name of the array is a pointer!) to the first element of this array. You can use *a to access the element at position 0 of this array or a[0], which is the same as writing *(a+0).

    When you declare a "double pointer", like:
    Code:
    char **p;
    The variable p will store a memory address where is stored a memory address "pointing" to a char.

    When you do:
    Code:
    char **p;
    
    p = malloc( sizeof( char * ) * 10 );
    You are allocating a buffer big enough to acomodate 10 "memory addresses" and writing the address of this buffer to p. Notice this array of pointers isn't initialized yet... Each element of the array pointed by p must point to a valid region of memory or NULL.

    So, what you did, so far, is correct... Here's a tip: Instead of using an intermediary buffer and copy its contents to an allocated element of this array, you can read and allocate at the same time using getline() function (POSIX standard):

    Code:
    #define NUM_ELEMENTS 10
    
    char **p, **q;
    int count;
    
    // Use calloc to zero all elements of the array (fill with NULLs).
    // I'll allocate one more element to use it as array boundary (see example below)
    if ( ! ( p = calloc( NUM_ELEMENTS + 1, sizeof( char *) ) ) )
    {
      fputs( "ERROR allocating array of pointers.\n", stderr );
      exit(1);
    }
    
    q = p;
    count = NUM_ELEMENTS;
    while ( count-- )
    {
      size_t size;
    
      // getline prototype:
      //   int getline( void **ptr, size_t *size, FILE *stream );
      size = 0;
      if ( getline( q, &size, stdin ) == -1 )
      {
        fputs( "ERROR reading/allocating string.\n", stderr );
    
        // let the C runtime to get rid of previously allocated buffers when exiting...
        exit(1);
      }
    
      // get rid of the final '\n' if present:
      // notice: q points to a pointer, so *q is a pointer to a buffer, allocated by getline(),
      // containing the string...
      // Another way to do it (and safer) is:
      //    { char *p = strchr( (*q), '\n' ); if (p) *p = '\0'; }
      if ( (*q)[size-1] == '\n' ) (*q)[size-1] = '\0';
    
      q++; // points to the next element.
    }
    
    // Here p is still pointing to the begining of this array of pointers.
    
    // Example of how to print all strings read by the loop:
    // still don't touching p because we'll need it later (to free all the buffers, for example).
    count = 0;
    for ( q = p; *q; q++ )
      printf( "p[%zu] = \"%s\"\n", count++, *q );
    ...
    Last edited by flp1969; 05-02-2019 at 01:47 PM.

  3. #3
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by flp1969
    The symbol a is a pointer (yes, the name of the array is a pointer!) to the first element of this array.
    No, an array is not a pointer, but it is converted to a pointer to its first element in most contexts. I think we shouldn't present them as pointers since that simplification fails in those contexts when such conversion doesn't happen: sizeof, address-of, or even declaring an actual pointer to the array itself for use with 2D arrays.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  4. #4
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Another way of think about pointer declarations (and casting) is to write them like this:
    Code:
    char* p;
    Here p type is a "pointer to char". So, this:
    Code:
    char** p;
    declares p as a pointer to a pointer to char.

    I prefer the other kind of declaration because we could "qualify" the pointers, as in:
    Code:
    char s[10];
    char const * const p = s;
    Here p is a constant pointer (cannot be changed) to a constant char (cannot be changed). And p is initialized with the address of the array s.

  5. #5
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Quote Originally Posted by laserlight View Post
    No, an array is not a pointer
    I said "the name of the array is a pointer", not "the array is a pointer"...
    And, yes... you are right about sizeof (which is an operator)...

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by flp1969
    I said "the name of the array is a pointer", not "the array is a pointer"...
    Ah, I see. I think it is just easier to follow the semantics of C and talk about the implicit conversion from array to pointer along with exceptions to that rule, rather than to draw upon a notion of an array name as a pointer, which isn't really the case in C: identifiers have scope and linkage, but it is expressions that have type, and in the case of an array, the name of the array is an expression with array type, not pointer type (hence why sizeof(some_array) results in the size of the array, not the size of a pointer).
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Array of pointers to point to another array of pointers
    By DaRealFonz in forum C++ Programming
    Replies: 5
    Last Post: 05-17-2018, 07:26 PM
  2. Replies: 4
    Last Post: 08-29-2015, 01:15 PM
  3. Replies: 43
    Last Post: 05-23-2013, 03:01 PM
  4. Replies: 7
    Last Post: 05-19-2010, 02:12 AM
  5. Replies: 4
    Last Post: 07-01-2009, 01:53 PM

Tags for this Thread