Using pointers for string manipulation I'm taking a sentence and determining whether or not it is a palindrome. I'm doing this while learning about stacks.


Is there a way I can use pointers instead of char array 'sent' so that the number of input characters need not be constrained to 20 in the following code?
The code is working fine, but should there be any improvements in terms of performance or anything else?
Is there anything important about pointers I should remember while using stacks, like initializing it to NULL?




#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdbool.h&gt;

typedef struct node
{
    char data;
    struct node *link;
}StackNode;

void insertData(StackNode **);
void push(StackNode **, char);
void checkData(StackNode **);
bool pop(StackNode **,char *);

char sent[20] = "";

void main()
{
   StackNode *stackTop;
   stackTop = NULL;
   insertData(&amp;stackTop);
   checkData(&amp;stackTop);
   printf("\n");
   return;
}

void insertData(StackNode **stackTop)
{
    char c;
    int len;

    printf("Enter the Sentence\n");
    while( ( ( c = getchar() ) != '\n'))
    {   
        if( ( ( c&gt;='a' &amp;&amp;c&lt;='z') || (c&gt;='A' &amp;&amp; c&lt;='Z')))
        {
            if((c&gt;='A' &amp;&amp; c&lt;='Z'))
            {
                int rem;
                rem = c-'A';
                c='a' + rem;
            }
            push(stackTop,c);
            len = strlen(sent);
            sent[len++]=c;
            sent[len]='\0';
        }
    }
    printf("Letters are %s\n\n",sent);
}

void push(StackNode **stackTop,char c)
{
    StackNode *pNew;
    pNew = (StackNode*) malloc(sizeof(StackNode));
    if(!pNew)
    {
        printf("Error 100:Out of memory\n");
        exit(100);
    }
    pNew-&gt;data = c;
    pNew-&gt;link = *stackTop;
    *stackTop = pNew;
}

void checkData(StackNode **stackTop)
{
    char c;
    int i=0;
    while(pop(stackTop,&amp;c))
    {
        if( c !=sent[i++])
        {
            printf("Not palindrome");
            return;
        }
    }
    printf("Palindrome");
}

bool pop(StackNode **stackTop,char *c)
{
    StackNode *pNew;
    pNew = *stackTop;
    if(pNew == NULL)
        return false;
    *c = pNew-&gt;data;
    *stackTop = pNew-&gt;link;
    printf("char poped %c\n",*c);
    free(pNew);
    return true;
}
One MAJOR problems here.

char sent[20] = "";


Don't use global variables. Globally mutable state makes your code hard to debug/test and generally behave correctly. You not only need to know what a function does but also what external state the function depends on. Pass all state into functions via the parameters (that way you know exactly what it depends on).

Note: Global constants are fine.

This is not a valid declaration of main() even in C

void main()

/* TRY */

int main(void)
/* OR */
int main(int argc, char* argv[])


Lets declare and initialize variables on the same line:    

   StackNode *stackTop;
   stackTop = NULL;

   /* Try */
   StackNode *stackTop = NULL;


Note if you correct main you will also need to correct the return statement so it returns a value.

   return;


But when the function is void there seems little point in putting a return statement.

OK. This reads a line.

void insertData(StackNode **stackTop)


But there are already functions that do that.
You can use the scanf series of commands. Your code drops any non alphabetic characters (but you could read a line then post processes to remove and invalid characters).

You should read up on the functions for handling characters.

isalpha()       /* Note it is locale aware and thus will return true
                   for characters other than a-z A-Z if your locale is
                   not set to the default (which is C)
                 */

tolower()       /* Lowercases letters. No affect on anything else */


I would have done:

char  data[50]; /* You wanted a max of 20 letters.
                   But if the user types a space between every word
                   it can get longer and we post processes to remove
                   space so. So we will cut off at 50.
                */

fscanf(stdin, "%50[^\n]", data); /* read upto 50 characters
                                    that are not '\n'
                                    into data
                                 */
fscanf(stdin, "%*[^\n]");        /* Read the rest of the line if
                                    the user types more than 50 and
                                    throw it away
                                 */
fscanf(stdin, "\n");             /* Read the '\n' off the input */

/* Now remove any non letter characters and lowercase at the same time */
int removed = 0;
for(int loop = 0; data[loop]; ++loop)
{
    if (!isalpha(data[loop]))
    {
        removed++;
        continue;       // starts next iteration.
    }
    data[loop-removed] = tolower(data[loop]);
}
data[loop-removed] = '\0';  /* Add string terminator */


Back to reviewing your version.

This is going to enter an infinite loop if you hit an error or end of file before you hit '\n'.

    while( ( ( c = getchar() ) != '\n'))


Then end of file is a real possibility if somebody pipes a file into your command from the command line.

    cat file | ./palindromeCommand


This check:

        if( ( ( c&gt;='a' &amp;&amp;c&lt;='z') || (c&gt;='A' &amp;&amp; c&lt;='Z')))


Is really covered by: isalpha()

This check:

            if((c&gt;='A' &amp;&amp; c&lt;='Z'))
            {
                int rem;
                rem = c-'A';
                c='a' + rem;
            }


Is the same as:  c = tolower(c); 

This string append:

            len = strlen(sent);
            sent[len++]=c;
            sent[len]='\0';


Can be achieved with: strncat(sent, &amp;c, 1);

Normally I don't want checking functions to mutate the state of any objects. The name "check" implies I am looking not modifying.

void checkData(StackNode **stackTop)


Also you are mixing business logic and presentation logic into a single function. Make this function check to see if it a palindrome and return an appropriate value (0 for false and non zero for true (for example)). The code can then use this function to check for a palindrome and take appropriate action (like display text) in the display logic:

Initialize and declare in the same line:

    StackNode *pNew;
    pNew = *stackTop;

    /* Easier to read as */
    StackNode *pNew = *stackTop;


Overall I think you overcomplicated the processes with your StackNode concept. But it works. It would have been much simpler to use pointers to either end of the sent array and work towards the middle checking letters.

int checkForPalindrome(char* sent, int length)
{
     char*    st   = sent;
     char*    ed   = sent + length - 1;

     int   result = true;

     while(st &lt; ed &amp;&amp; result)
     {
         if (*st != *ed) { result = false;}
         ++st;
         --ed;
     }
     return result;
}