Initialize/set char *argv[] inside main() in one line

回眸只為那壹抹淺笑 提交于 2020-05-12 03:45:06

问题


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).


回答1:


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



回答2:


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]);
    }
}



回答3:


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.



来源:https://stackoverflow.com/questions/20878322/initialize-set-char-argv-inside-main-in-one-line

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!