1

I'm supposed to make a code that determines if entered string is a palindrome, the code is supposed to have 3 functions. The first is to determine if string is palindrome, the second is to use isalpha() to remove all non letters and the third is to use tolower() to remove the difference between upper and lower case letters. I made all 3 separate functions but now that I have to combine them. The code just gives exception thrown.

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

int isPalindrome(char inputString[])
{
    int middle = strlen(inputString) / 2;
    int len = strlen(inputString);
    for (int i = 0; i < middle; i++)
        if (inputString[i] != inputString[len - i - 1])
        {
            return 0;
        }
        else
        {
            return 1;
        }
}

void checker(char stringWithoutSpace[], char bigArray[]) 
{
    int j = 0;
    for (int i = 0; i < strlen(stringWithoutSpace); i++)
    {
        if (isalpha(stringWithoutSpace[i]) != 0)
        {
            bigArray[j++] = stringWithoutSpace[i];
        }
    }
} 

void lowerCase(char* string)
{
    int i;
    for (i = 0; string[i]; i++) 
    {
        string[i] = tolower(string[i]);
    }
}

#define SIZE 1000

int main(void) {
    int repeat = 1;
    char arrayPalindrome[SIZE];
    char bigArray[SIZE];
  
    while (repeat == 1)
    {   
        printf("Enter a sentence: ");
        scanf_s("%s", &arrayPalindrome);

        checker(arrayPalindrome, bigArray);
        lowerCase(arrayPalindrome, bigArray);

        if (isPalindrome(arrayPalindrome) == 1) 
        {
            printf("This is a palindrome.");
        }
        else
        {
            printf("this in not a palindrome: ");
        }
        printf("Do you want to enter another sentence (0 for no, 1 for yes)?");
        scanf_s("%d", &repeat);
    }

    return 0;
}
Rachid K.
  • 4,490
  • 3
  • 11
  • 30
dundern
  • 37
  • 3
  • If you're receiving errors, please include them in your question. – Chris Sep 25 '22 at 18:45
  • 3
    Microsoft's `scanf_s` is **not** a direct replacement for `scanf`, and the call is missing one argument, which the compiler should **warn** you about. *"Unlike `scanf` ... `scanf_s` ... requires the buffer size to be specified for all input parameters of type c, C, s, S, or string control sets that are enclosed in []. The buffer size in characters is passed as an additional parameter immediately following the pointer to the buffer or variable."* – Weather Vane Sep 25 '22 at 18:47
  • 1
    You say that _"but now that I have to combine them the code just gives exception thrown."_ Did you test to ensure all three functions worked individually? – Chris Sep 25 '22 at 18:49
  • 1
    ... and other, so it should be `if(scanf_s("%s", arrayPalindrome, SIZE) != 1) { /* handle error */ }` – Weather Vane Sep 25 '22 at 18:49
  • 1
    Neither `checker` nor `lowercase` appears to null terminate `bigArray`. – Chris Sep 25 '22 at 18:50
  • 2
    If MSVC is trying to force you to use its own functions you can disable that nonsense by placing `#define _CRT_SECURE_NO_WARNINGS` at the top of each file. But a better way, is to [stop using `scanf`](https://stackoverflow.com/questions/58403537/what-can-i-use-for-input-conversion-instead-of-scanf). – Weather Vane Sep 25 '22 at 18:53
  • Fwiw, `printf("Enter a sentence: ");` isn't exactly accurate either, since only the first word will be extracted. Also, the argument to the succeeding `scanf_s` call should be `arrayPalindrome`, not `&arrayPalindrome`, in addition to fixing the missing buffer size argument you left out. – WhozCraig Sep 25 '22 at 18:58

2 Answers2

2

For starters this call of scanf_s is incorrect.

scanf_s("%s", &arrayPalindrome);

You have to write

scanf_s("%s", arrayPalindrome, rsize_t( sizeof( arrayPalindrome ) ) );

The function checker does not build a string in the array bigArray because you forgot to append the stored sequence of characters in the array with the terminating zero character '\0'.

Moreover the function does not make sense because the array bigArray is not used anymore in the program.

The function lowerCase is declared with one parameter

void lowerCase(char* string);

but is called with two arguments.

lowerCase(arrayPalindrome, bigArray);

Pay attention to that there is no need to declare the auxiliary array bigArry.

And the program should not change the original string.

The program can look for example the following way

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

int lowerCase( char c )
{
    return tolower( c );
}

int checker( char c )
{
    return isalpha( ( unsigned char )c ) != 0;
}


int isPalindrome( const char s[] )
{
    int is_palindrome = 1;

    for (const char *first = s, *last = s + strlen( s ); is_palindrome && first < last; ++first)
    {
        while (first < last && !checker( *first )) ++first;
        if (first != last)
        {
            while (--last != first && !checker( *last ));
        }

        is_palindrome = first == last || lowerCase( *first ) == lowerCase( *last );
    }

    return is_palindrome;
}

int main( void )
{
    const char *s = "12A b B a345";

    printf( "\"%s\" is %spalindrome\n", s, isPalindrome( s ) ? "" : "not " );
}

The program output is

"12A b B a345" is palindrome
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Curious, why `( unsigned char )c` with `isalpha()`, yet no cast with `tolower( c )`? – chux - Reinstate Monica Sep 25 '22 at 23:29
  • @chux-ReinstateMonica isalpha returns true if isupper or islower return also true. So if isalpha returns true then in the C locale the corresponding code can not be negative. – Vlad from Moscow Sep 26 '22 at 09:56
  • OK. I was considering a non-C locale case like [IEC_8859-1](https://en.wikipedia.org/wiki/ISO/IEC_8859-1) where `isalpha((unsigned char)c)` is true yet `c < 0`. Yet this code does not switch locales. – chux - Reinstate Monica Sep 26 '22 at 10:51
1

Others, in comments and answers, have addressed the shortcomings in the code you presented. This answer addresses the heuristic approach to the problem.

As you are experiencing, every line of code is an opportunity for a bug to occur.

Acknowledging that the problem statement may have specified "write three functions", there is no need for so many. A single function does all that is necessary:

#include <stdio.h> // all that is needed

bool isPalindrome( const char s[] ) {
    // a "custom" 7bit ASCII "look up" table with exclusion ('.') and 'tolower()' equivalence
    char *tbl =
        "................................................0123456789......"
        ".abcdefghijklmnopqrstuvwxyz......abcdefghijklmnopqrstuvwxyz.....";

    // 'L'eft and 'R'ight indexing into the passed string
    size_t L = 0, R = 0;

    // find the end of the string (its 'r'ight index)
    while( s[R] ) R++; // poor boy's 'strlen(s)'. '\0' does not matter.

    do {
        // adjust indexes L & R rejecting characters
        // seek to "meet in the middle" of a palindromic string
        while( L <= R && tbl[ s[L] ] == '.' ) L++;
        while( L <= R && tbl[ s[R] ] == '.' ) R--;
    } while( L <= R && tbl[ s[L] ] == tbl[ s[R] ] && (L+=1) > 0 && (R-=1) > 0 );
    // string is palindromic if it has passed the tests above
    return L >= R;
}

int main() {
    // without the drudgery of re-entering test strings,
    // this "test harness" examines 3 different strings
    // giving the evaluation of each. Easy to add more test cases.
    char *strs[] = {
        "LeVrEl",
        "level",
        "123Madam I'm    Adam321",
    };

    for( int i = 0; i < sizeof strs/sizeof strs[0]; i++ )
        printf( "%s - '%s'\n", isPalindrome( strs[i] ) ? "Yes" : "No ", strs[i] );

    return 0;
}
No  - 'LeVrEl'
Yes - 'level'
Yes - '123Madam I'm    Adam321'

Far fewer lines of code, and should be "easy to follow" when one envisions a string as simply a contiguous array of characters.

If the assignment is to write 3 functions, one could add two more "no-op" functions to fulfill that criteria:

int thing1(void) { return 1; }
int thing2(void) { return 2; }

EDIT: With the (very good) advice to utilise standard library functions, here is much the same thing again (still considering digits to be 'testable' characters.)

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

int isPalindrome( const char s[] ) {
    const unsigned char *u = (unsigned char *)s;
    size_t L = 0, R = strlen( s );

    do {
        while( L <= R && !isalpha(u[L]) && !isdigit(u[L]) ) L++;
        while( L <= R && !isalpha(u[R]) && !isdigit(u[R]) ) R--;
    } while( L <= R && tolower(u[L]) == tolower(u[R]) && (L+=1) > 0 && (R-=1) > 0 );
    return L >= R;
}

int main() {
    /* same as above */
}
Fe2O3
  • 6,077
  • 2
  • 4
  • 20
  • Overall approach is good. Yet `tbl[ s[L] ]` risks access out of `tbl[]` range - risky. Code assumes ASCII encoding - reasonable, but not necessary. Also UB with `isPalindrome("")` due to `R-=1`. – chux - Reinstate Monica Sep 25 '22 at 23:25
  • @chux-ReinstateMonica Yes, this is based on the premise of 7-bit ASCII. (That's in the tbl[] definition.) And, yes, it'd be reasonable to guard against an empty string (and being passed a NULL pointer, too!) _C'est la guerre_... (This also treats digits as characters to be compared... The purpose was to show there are alternatives.) `:)` – Fe2O3 Sep 25 '22 at 23:31
  • @chux-ReinstateMonica ACTUALLY, when I look at this again... The `""` case would NOT trigger UB. 0 is < +1, so the code _wouldn't_ try to access s[-1]... Those `&&` conditionals will be checked "left to right"... :-) – Fe2O3 Sep 25 '22 at 23:36
  • 1
    On review, no UB. I missed the `tbl[ s[L] ] == '.'` as true. IAC, good to check `isPalindrome("")` success. – chux - Reinstate Monica Sep 26 '22 at 01:06
  • @chux-ReinstateMonica Turns out, coders' favourite expression (`"*#$$*&*&$%!!"`) **IS** a palindrome under the given criteria... `:-)` – Fe2O3 Sep 26 '22 at 01:28