简体   繁体   中英

Dynamic memory allocation for char**

I'm trying to dynamically allocate memory for an array of strings, but I'm suffering a segmentation fault. If you can show me some ways to do it, that would be really helpful.

My knowledge so far is that char* is a string, and char** is a two-dimensional array of strings. If s[i] is a string, shouldn't *(s + i) also be the same thing? I am confused about the topic.

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int n;

int main(void)
{
    scanf("%i", &n);  //number of strings being inputted

    char **s = calloc(n, 10 * (sizeof(char) + 1));

    for (int i = 0; i < n; i++) //get strings
    {
        fgets(*(s + i), 10 * (sizeof(char) + 1), stdin);
    }

    for (int i = 0; i < n; i++) //print the strings
    {
        fputs(*(s + i), stdout);
    }

    return 0;
}
scanf("%i", &n);  //number of strings being inputted

is incomplete, since scanf (3) can fail (eg if your user enters hello ). You need to add more code checking that scanf was successful. I suggest:

if (scanf("%i", &n) < 0) {
  perror("number of strings expected");
  exit(EXIT_FAILURE);
}

Then

char **s = calloc(n, 10 * (sizeof(char) + 1));

is wrong. Each element of the array is a char* , which on most machines has 4 or 8 bytes (the sizeof(void*) ).

So replace it with

char **s = calloc(n, sizeof(char*));

or with the equivalent char**s = calloc(n, sizeof(*s)); which in my opinion is less readable.

Also, calloc can fail. You need to add a test, like

if (s == NULL) {
   perror("calloc of s");
   exit(EXIT_FAILURE);
}

Read carefully the documentation of each function (like calloc , fgets etc...) which you did not wrote in this C reference .

The next step should be a loop filling each element of s . With another calloc , or some call to strdup (3) if your system provides it. Of course, that calloc (or strdup ) can also fail and you need to test against failure.

You could also use getline (3) (or even readline (3) ) if your system has it. Check with your manager if you are legally and technically allowed to use them.

If your compiler is GCC , invoke it with all warnings and debug info: gcc -Wall -Wextra -g . Once you have no warnings, use a debugger like GDB to understand the behavior of your executable.

Consider also using the Clang static analyzer (after getting permission to use it).

Draw on board (or on paper) a figure with arrows representing pointers (like the doubly linked-list figure on wikipedia) to understand the behavior of your program.

In this declaration

char **s = calloc(n, 10 * (sizeof(char) + 1));

you allocated a memory the address of which is assigned to the pointer s . The memory was zero-initialized due to calling the function calloc .

So in this statement

fgets(*(s + i), 10 * (sizeof(char) + 1), stdin);

the pointer s is dereferenced and either is a null pointer (because the pointed memory was zero-initialized) or has an indeterminate value if the expression s + i points outside the allocated memory.

You need to allocate an array of pointers of the type char * and assign to each pointer the address of an allocated array of characters.

Also in general you should check the return value of a call of calloc or malloc .

There is another problem with your code. After the call of scanf

scanf("%i", &n);  //number of strings being inputted

the input buffer contains the new line character '\n' that will be read by the following call of fgets . So the first call of fgets will read in fact an empty string.

Another problem is that you should remove the new line character that can be appended to the read string by fgets . For example some read string can contain the new.line character while others can be without it depending on how many characters the user typed.

If you want to write the program without using the subscript operator with pointers then it can look for example the following way.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) 
{
    size_t n = 0;
    
    if ( scanf( "%zu", &n ) == 1 )
    {
        scanf( "%*[^\n]" );
        scanf( "%*c" );
    }

    char **s = NULL; 
    
    if ( n != 0 ) s = calloc( n, sizeof( char * ) );
    
    if ( s != NULL )
    {
        size_t len = 11;
        
        size_t m = 0;
        
        while ( m < n && ( *( s + m ) = malloc( len  * sizeof( char ) ) ) != NULL ) m++;
        
        size_t i = 0;
        
        while ( i < m && fgets( *( s + i ), len, stdin ) != NULL ) i++;

        m = i;
        
        for ( i = 0; i < m; i++ ) //print the strings
        {
            ( *( s + i ) )[ strcspn( *( s + i ), "\n" )] = '\0';
            // or without the subscript operator
            // *( *( s + i ) + strcspn( *( s + i ), "\n" ) ) = '\0';

            puts( *( s + i ) );
        }
        
        for ( i = 0; i < n; i++ ) free( *( s + i ) );
    }
    
    free( s );
    
    return 0;
}

The program output might look like

10
one
two
three
four
five
six
seven
eight
nine
ten
one
two
three
four
five
six
seven
eight
nine
ten

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM