2

So I wanted to create a simple console calculator that'd take arguments via the int main(int argc, char ** argv) function. It works for regular examples, but trying multiplication with '*' started giving me way too many arguments listed in argv[], so I tinkered around and figured out it was listing all the files in the folder when '*' was used, supposedly for multiplication. I turned the if statement to always be true for debugging. I didn't find anything wrong with cerr, although I could've used the try-catch block.

I looked around the internet to see the reason for that, and I found that the asterisk can be used as some sort of wildcard to look for files in the folder, pretty similar, but not exactly what I was looking for. Thoughts?

Source code:

#include <iostream>
#include <string>

using namespace std;
int main(int argc, char ** argv)
{
    cout << "Argument count: " << argc << endl;
    if(true) // argc != 4
    {
        int i(0), j(0);
        while(argv[i])
        {
            j = 0;
            while(argv[i][j])
            {
                cout << argv[i][j];
                ++j;
            }
            cout << endl;
            ++i;
        }
    }
        //cerr << "Inadequate amount of arguments! " << argc;
    else
    {
        double num1, num2;
        try
        {
            num1 = stoi(argv[1]), num2 = stoi(argv[3]);
        }
        catch(exception e)
        {
            cout << "Unable to transform to integer.\n";
            return -1;
        }
        char op = *argv[2];
        switch(op)
        {
            case '+': cout << num1 + num2; break;
            case '-': cout << num1 - num2; break;
            case '*': cout << num1 * num2; break;
            case '/': cout << num1 / num2; break;
            default: cout << "Invalid operator.";
        }
    }

}
  • Try printing out all the values in argv. Do you see any pattern there? – cigien Aug 08 '20 at 03:23
  • @cigien That's exactly what I did, it printed out the names of the files in the folder. – pompomgachitron Aug 08 '20 at 03:28
  • Then I'm not sure what your confusion is. You also seem to know that `*` expands to the contents of the folder. – cigien Aug 08 '20 at 03:29
  • @cigien The one part to my confusion is that I didn't know what `*` did before finding this out, but I want to understand it better. The posts I found were on Python or using the `boost::` namespace, but nothing for C++ (although I could be blind or looking for the wrong things), so I made this question looking for a source of information. – pompomgachitron Aug 08 '20 at 03:33
  • The runtime will expand a `*` on the command line into all the files that match the pattern, as a convenience for programs that want that behavior. Check your compiler or library documentation. Some toolsets provide a way to disable that, or require you to do something specific to enable it. (For MSVC you need to link with "setargv.obj" to get wildcard expansion.) – 1201ProgramAlarm Aug 08 '20 at 03:37

2 Answers2

3

This has likely nothing to do with C++ and everything to do with your shell.

Bash and other sh-like shells allow you to use "glob" patterns to name multiple files easily. This allows you to, for example, easily move all .png files in a directory to another directory with a command like mv *.png some-other-dir. The shell substitutes *.png for the names of all files in the current directory that end in ".png".

* is the "match anything" pattern, so a * in a command will expand to the names of all files in the current directory. That means that if you run ./my_program 3 * 4 in a directory containing the files "my_program", "foo.txt", and "bar.txt", your shell will expand the * character and actually run ./my_program 3 bar.txt foo.txt my_program 4.

You can disable this expansion by wrapping the * character in single quotes or escaping it with a \. If you run ./my_program 3 '*' 4 or ./my_program 3 \* 4, your shell will not expand the *, and argv will contain {"./my_program", "3", "*", "4"}.


Note that Windows CMD shell does not do this sort of expansion. If you want this behavior on Windows you would have to link your program to setargv.obj to get the runtime library to do it for you or implement the expansion yourself.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • That's interesting, I haven't gotten into Bash yet but pattern matching seems to be a pretty powerful thing. I'll look into it eventually. Thanks for the explanation! – pompomgachitron Aug 08 '20 at 03:51
1

The asterisk * is expanded by your shell before your program is started. There's nothing you can do in your program to change this behavior. With most popular shells like Bash, sh, zsh etc. you have to quote the arguments like so:

program '2*3'

or

program 2\*3
Martin Konrad
  • 1,075
  • 1
  • 10
  • 20