Search code examples
csegmentation-faultc-stringspalindromestrcpy

What's causing a segmentation fault in my code and what I should do to fix it?


I've been getting segmentation fault and I don't know why. I have a feeling that it's caused by the code under if(isPalindrome(str_array[i]) == 0) but I don't know which one and what to do about it.

P.S. I'm still a student so I would appreciate it if the suggestions do not go far from the level of code that I have here.

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

int isPalindrome(char check[]);

int main() {
    int array_size = 5, i;
    char *str_array[array_size], *palindrome_array[array_size], str_length[100];

    for(i = 0; i < array_size; i++) {
        printf("Enter word #%d: ", i+1);
        scanf("%[^\n]%*c", str_length); //"%[^\n]%*c : pattern matching - allows inputs to have spaces

        str_array[i] = (char*) malloc(sizeof(char) * strlen(str_length));
        strcpy(str_array[i],str_length); 

        if(strcmp(str_array[i],"exit") == 0) {
            break;
        }
    
        if(isPalindrome(str_array[i]) == 0) {
            palindrome_array[i] = (char*) malloc(sizeof(char) * strlen(str_length));
            strcpy(palindrome_array[i], str_length); 
            printf("'%s' is a palindrome \n", palindrome_array[i]);
        } else printf("'%s' is NOT a palindrome \n", str_array[i]);
    }


    //BONUS: prints all the palindrome strings inputted
    printf("Your palindrome words are: \n");
    for(i = 0; i < array_size; i++) {
        printf("%d.)%s \n", i+1, palindrome_array[i]);
    }

    return 0;
}


int isPalindrome(char check[]) {        // from string to individual characters
    int middle = strlen(check) / 2;     //gets the half of the string's length
    int length = strlen(check);

    for(int i = 0; i < middle; i++) {
        if(check[i] != check[length - i - 1]) {
            return 1;
        } 
    }
    return 0;
}   

Solution

  • There are two main drawbacks.

    To allocate a memory for a string you need to reserve space for the terminating zero character '\0' of the string.

    So you need to write for example

    str_array[i] = (char*) malloc(sizeof(char) * ( strlen(str_length) + 1 ) );
    

    Also the array palindrome_array was not initialized.

    char *str_array[array_size], *palindrome_array[array_size], str_length[100];
    

    As not all tested strings are palindromes then some elements of the array still stay uninitialized for some values of the index i due to this if statement

        if(isPalindrome(str_array[i]) == 0) {
            palindrome_array[i] = (char*) malloc(sizeof(char) * strlen(str_length));
            strcpy(palindrome_array[i], str_length); 
            printf("'%s' is a palindrome \n", palindrome_array[i]);
        } else printf("'%s' is NOT a palindrome \n", str_array[i]);
    

    As a result this for loop

    for(i = 0; i < array_size; i++) {
        printf("%d.)%s \n", i+1, palindrome_array[i]);
    }
    

    can invoke undefined behavior.

    You need to declare a separate index for the array and to use it instead of the index i.

    In fact the array str_array is just redundant. It is enough to use these two arrays.

    char *palindrome_array[array_size], str_length[100];
    

    The parameter of the function isPalindrome should be declared with the qualifier const

    int isPalindrome( const char check[]);
    

    because the passed string is not changed within the function. Also it is much better when the function returns a npn-zero integer when a string is a palindrome and zero otherwise.

    In the call of scanf you should specify the maximum length of the input string and the conversion specifier c should be removed. For example

    scanf( " %99[^\n]", str_length );
    

    Also it is unclear why there is used the magic number 5 in your program.

    I would write the program the following way

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int isPalindrome( const char s[] )
    {
        size_t i = 0, n = strlen( s );
    
        while (i < n / 2 && s[i] == s[n - i - 1]) ++i;
    
        return i == n / 2;
    }
    
    int main( void )
    {
        char **p = NULL;
        size_t total = 0;
        size_t count = 0;
    
        while (1)
        {
            char s[100];
    
            printf( "Enter a word (to exit just press Enter): " );
    
            if (scanf( "%99[^\n]%*c", s ) != 1 || s[0] == '\0') break;
    
            ++total;
    
            if (isPalindrome( s ))
            {
                printf( "\"%s\" is a palindrome\n", s );
                char **tmp = realloc( p, ( count + 1 ) * sizeof( char * ) );
    
                if (tmp == NULL)
                {
                    puts( "Memory allocation error. It is impossible to store the string." );
                    break;
                }
    
                p = tmp;
    
                p[count] = malloc( strlen( s ) + 1 );
    
                if (p[count] == NULL )
                {
                    puts( "Memory allocation error. It is impossible to store the string." );
                    break;
                }
    
                strcpy( p[count], s );
                ++count;
            }
            else
            {
                printf( "\"%s\" is NOT a palindrome\n", s );
            }
        }
    
        printf( "\nAmong the entered %zu words there are %zu palindrome(s).\n", total, count );
        if (count != 0)
        {
            puts( "They are:" );
            for (size_t i = 0; i < count; i++)
            {
                printf( "\"%s\"\n", p[i] );
            }
        }
    
        for (size_t i = 0; i < count; i++)
        {
            free( p[i] );
        }
        free( p );
    }
    

    Its output might look like

    Enter a word (to exit just press Enter): 1
    "1" is a palindrome
    Enter a word (to exit just press Enter): 12
    "12" is NOT a palindrome
    Enter a word (to exit just press Enter): 122
    "122" is NOT a palindrome
    Enter a word (to exit just press Enter): 1221
    "1221" is a palindrome
    Enter a word (to exit just press Enter): 12321
    "12321" is a palindrome
    Enter a word (to exit just press Enter):
    
    Among the entered 5 words there are 3 palindrome(s).
    They are:
    "1"
    "1221"
    "12321"