Question

I want to initialize/set char *argv[] inside the main() so that I can use argv[1], argv[2]... later in my program.


Up to now, I know how to do this in two ways:

  1. For int main(), use one line as:

    int main()
    {
        char *argv[] = {"programName", "para1", "para2", "para3", NULL};
    }
    

    Note that, using NULL in the end is because the pointers in the argv array point to C strings, which are by definition NULL terminated.

  2. For int main(int argc, char* argv[]), I have to use multiple lines as:

    int main(int argc,char* argv[])
    {
        argv[0] = "programName";
        argv[1] = "para1";
        argv[2] = "para2";
        argv[3] = "para3";
    }
    

My question is that how can I combine these two methods together, i.e. use only one line to initialize it for int main(int argc, char* argv[])? Particularly, I want to be able to do like this (this will be wrong currently):

int main(int argc, char* argv[])
{
    argv = {"programName", "para1", "para2", "para3", NULL};
}

How can I be able to do this?


Edit: I know argv[] can be set in Debugging Command Arguments. The reason that I want to edit them in main() is that I don't want to bother to use Debugging Command Arguments every time for a new test case (different argv[] setting).

Was it helpful?

Solution

The safest way is probably don't write into argv referred memory, (that may not be structured as you think), but having another bulk:

int main(int argc, const char** argv)
{
    const char* n_argv[] = { "param0", "param1", "param2" };
    argv = n_argv;
    ...
}

This will distract argv from it original memory (owned by the caller, that remains there) to another that will exist for the life of main().

Note that const char* is required to avoid the deprecated "*string assigned to char**" message.


NOTE: this code had been compiled with GCC 4.8.1 on Linux giving the following results:

make all 
Building file: ../main.cpp
Invoking: GCC C++ Compiler
g++ -O0 -g3 -pedantic -Wall -c -std=c++11 -o "main.o" "../main.cpp"
Finished building: ../main.cpp

Building target: e0
Invoking: GCC C++ Linker
g++  -o "e0"  ./main.o   
Finished building target: e0

OTHER TIPS

If you're able to use C99, you can use compound literals feature. Here's an example that seems to work when compiled as gcc -std=c99 main.c:

#include <stddef.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    argv = (char *[]){"programName", "para1", "para2", "para3", NULL};

    char **p = argv;
    argc = 0;
    while (*p++ != NULL) {
        argc++;
    }

    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
}

Lets say your program takes a set of strings and does something on them. Lets call it process_strings which needs to be tested. You want to pass different sets of strings for different test cases, say {"abc", "efg", "hij"}, {"thin", "run", "win"}, {"cat", "rat", "mat"}, etc. In future you want to add more such test cases without altering your original function process_strings. Then the right way to go about this problem is to modify the (driver) program such that you don't have to recompile for different test cases added/removed. An example might be:

#include <fstream>
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <vector>
#include <iterator>

// tested function
void process_strings(const std::vector<std::string>& params)
{
    for (auto iter = params.cbegin(); iter < params.cend(); ++iter)
        std::cout << *iter << '\t';
}

// driver program
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        std::cout << "Insufficient data" << std::endl;
        return -1;
    }

    std::ifstream test_file(argv[1]); // pass test cases file as an argument

    std::string test_case;    
    while (std::getline(test_file, test_case))
    {
        // each line is a test case
        std::istringstream iss(test_case);
        std::vector<std::string> params;
        // break parameters separated by ' ' blankspace
        copy(std::istream_iterator<std::string>(iss),
             std::istream_iterator<std::string>(),
             std::back_inserter(params));
        // if there're valid parameters, then pass it to the tested function
        if (!params.empty())
        {
            process_strings(params);
            std::cout << std::endl;
        }
    }
}

Example of a test cases file, tests.txt:

abc efg
cat rat mat

animal man

The output produced for C:\> simple.exe tests.txt:

abc     efg
cat     rat     mat
animal  man

If you've arguments which contains space i.e. if space cannot be used as delimiter, then see this post on how to extract the arguments from the line.

Thus we've separated input data from code. Every time the test cases change, you needn't recompile the program. Just change the input, run it by the already compiled program and verify the output. This saves a lot of development time, and in some sense the basis of data-driven programming.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top