0

This is program exercise from The C Programming Language by Kernighan and Ritchie (Chap 5). In this program, when I use '*' as multiplication operator the the pointer **argv points to argv[0] i.e. 1st(Zeroth) argument and reads 1st character 'P' instead of '*'.

Upon execution, with arguments: ./ProgE5-10 +124 -3 * =

it returns incorrect answer instead of -372.

But if replace '*' with 'x' the program works fine. All other operations (viz. +, -, /, =) are also working fine.

Please tell me why * makes argv to point to Program Name.

//Exercise 5-10. Write the program expr, which evaluates a reverse Polish
//expression from the command line, where each operator or operand is a
//separate argument. For example, expr 2 3 4 + *
//evaluates 2 x C+4).

//For multiplication character '*' is not working

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

#define MAXLINE 1000
#define NUMBER 0

int sign = 1;
char s[MAXLINE];

void calc (int type);

int main(int argc, char *argv[])
{   
    if (argc < 4)
        printf("Usage: ./<programName> op1 op2 operator\n");
    else 
    {
        int i, d;
        int c;

        while (--argc > 0 && (c = **++argv) != '\n')
        {   
            i = 0;
            printf("\nargc = %d\tc = %c:%d", argc, c, c);
            if (c == '+' || c == '-' || c == '*' || c == '/' || c == '=' || c == '\n')
            {
                printf("\nNon-Digit: %c : ", c);
                if ((c == '+' || c == '-') && isdigit(d = *++(argv[0])))
                {   
                    printf("\tSign");
                    sign = (c == '-') ? -1 : 1;
                    c = d;
                    goto DOWN1;
                }
                else
                {   
                    printf("Operator");
                    printf("\nRead Operator: %c\n", c);
                    calc(c);
                    goto DOWN2; //To avoid re-executing calc(Number) which
                                //is outside any loop in main when operator
                                //is read and operation is performed.
                }   
            }

DOWN1:      while (isdigit(c = *argv[0]))
            {
                s[i++] = c;
                c = *++(argv[0]);

                if (**argv == '.')
                {
                    s[i++] =  **argv;
                    while (isdigit(*++(argv[0])))
                        s[i++] = **argv;
                }
                s[i] = '\0';
                printf("\ns[] = %s", s);
            }
            calc(NUMBER);   //Outside while to get single push of s[]
                            //after reading the complete number
    DOWN2:  ;
        }   
    }
    return 0;
}

void push (double f);
double pop(void);

void calc (int type)
{
    double op2, res;
    switch(type)
    {
        case NUMBER:
            push(sign*atof(s));
            sign = 1;
            break;
        case '+':
            push(pop() + pop());
            break;
        case '-':
            op2 = pop();
            push(pop() - op2);
            break;
        case '*':
            push(pop() * pop());
            break;
        case '/':
            op2 = pop();
            push(pop() / op2);
            break;
        case '=':
            res = pop();
            push(res);
            printf("\t\t\t||Result = %lg||\n", res);
            break;
        case '\n':
            break;
        default:
            printf("\nError: Invalid Operator!\n");
            break;
    }
}

#define STACKSIZE 1000
double val[STACKSIZE];
int sp = 0;

void push(double f)
{
    if (sp >= STACKSIZE)
        printf("\nError: Stack Overflow!\n");
    else
        val[sp++] = f, printf("\nNum %lg Pushed to Stack\n", f);
}

double pop(void)
{
    if (sp != 0)
    {
        double ret = val[--sp];
        printf("\nNum %lg Popped from the Stack\n", ret);
        return ret;

    }
    else
    {
        printf("\nError: Stack Empty!\n");
        return 0.0;
    }
}
CrownedEagle
  • 125
  • 7
  • 1
    Enter `echo *` in your shell and see what happens. You might want to try `"*"` instead. – Blaze Sep 13 '19 at 08:15
  • Sorry forgot to paste code.. Added the code.. – CrownedEagle Sep 13 '19 at 08:18
  • 1
    1. I am not sure how this works on Windows, but on any Unix system, the first `argv` value is the executable's name; 2. If you are running this in a bash shell, unquoted `*` is interpreted as a wildcard. – Eli Korvigo Sep 13 '19 at 08:19
  • Okk.. So In the i/p also I have to use '*' instead of * right? – CrownedEagle Sep 13 '19 at 08:21
  • 2
    I've glanced over the code you have added: it doesn't seem to handle `*` as an operator at all. – Eli Korvigo Sep 13 '19 at 08:23
  • 2
    either of `'*'`, `"*"` or `\*` will work. Read about [shell expansion](https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html#Filename-Expansion) to understand – phuclv Sep 13 '19 at 08:26
  • 2
    Possible duplicate of [How do I pass in the asterisk character '\*' in bash as arguments to my C program?](https://stackoverflow.com/questions/2755795/how-do-i-pass-in-the-asterisk-character-in-bash-as-arguments-to-my-c-program) – phuclv Sep 13 '19 at 08:26
  • 3
    There's a whole lot of bad coding practice in this snippet. Once you got everything up and running, I strongly suggest submitting it to https://codereview.stackexchange.com/. Since most of the harmful practice here originates from K&R, you might want to consider a better source for learning C. – Lundin Sep 13 '19 at 08:27
  • @Eli Sorry I had replaced '*' with 'x' since it was not working.. Now it is working if i/p \* or '*' – CrownedEagle Sep 13 '19 at 08:28
  • All the text on and after ". Also suggest " ruins this otherwise good question. – Bathsheba Sep 13 '19 at 08:28
  • @Lundin Can you suggest a better read than K&R to learn C Programming? My entrance exam course syllabus suggested this book.. – CrownedEagle Sep 13 '19 at 08:31
  • @Bathsheba Corrected! Thanks.. But since I added that part 'Lundin' suggested to submit it to Code Review. – CrownedEagle Sep 13 '19 at 08:37
  • @CrownedEagle: Even more reason to remove it. By the way Lundin and I sit at opposite ends of the "K & R" argument. In my less facetious moments I do agree that the book teaches you some pretty poor habits. – Bathsheba Sep 13 '19 at 08:38
  • 1
    @CrownedEagle I haven't read a beginner-level C book in ages, so I have no recommendations. "Modern C" by Gustedt is pretty good overall (and free), even though there's some things in it that I would object against teaching to newbies. We used to have a C book recommendation list on SO, but it lies in shambles after moderators destroyed it last year. – Lundin Sep 13 '19 at 08:49
  • @Lundin [There you go](https://stackoverflow.com/a/562377/1014587) – Mast Sep 13 '19 at 10:16
  • @Mast The contents of that link are utter trash and nobody maintains it. https://meta.stackoverflow.com/questions/355588/the-c-book-list-has-gone-haywire-what-to-do-with-it – Lundin Sep 13 '19 at 10:41
  • @Lundin Some of those are *still* better than K&R. YMMV. – Mast Sep 13 '19 at 10:44
  • @Mast Yes, but the list now contains pretty much every C book mentioned, for good and bad. – Lundin Sep 13 '19 at 10:58

1 Answers1

5

Your shell (e.g. bash) is treating the * character as a glob pattern, which gets replaced with a list of files in the current directory.

$ ls
file1
file2
$ echo *
file1 file2

This is why the * in your case gets replaced with ProgE5-10, which is a file in the current directory.

If you escape the * with a backslash when running your program, or surround it in single or double quotes, this will solve the issue

$ ./ProgE5-10 +124 -3 \* =
Candy Gumdrop
  • 2,745
  • 1
  • 14
  • 16