5

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

Charles
  • 50,943
  • 13
  • 104
  • 142
herohuyongtao
  • 49,413
  • 29
  • 133
  • 174
  • 4
    Strange idea. Your case 2 has a good chance to crash the program. argc and argv should be read-only. – Alex F Jan 02 '14 at 07:34
  • http://stackoverflow.com/questions/4207134/what-is-the-proper-declaration-of-main This link is better – Suvarna Pattayil Jan 02 '14 at 07:35
  • 2
    Maybe you should describe what your larger goal is. I suspect there might be a different approach that will work for you (though I'm not really sure I really understand the question, so I might be wrong). – Michael Burr Jan 02 '14 at 07:39
  • @AlexFarber Although it's read only, I can still use new value in the `mani()` scope. – herohuyongtao Jan 02 '14 at 07:50
  • @herohuyongtao Why would it be readonly? –  Jan 02 '14 at 07:53
  • @remyabel I mean `argc` is read-only. Actually, I need to set `argc` as well. Deleted already as not relevant to this question. – herohuyongtao Jan 02 '14 at 07:57
  • 1
    This is very clearly an [X-Y problem](http://mywiki.wooledge.org/XyProblem). The variables `argc` and `argv` (or whatever you name them) are local to `main()` and you can do anything you want with them. The actual command-line parameters are read-only in standard C++, and if they can be accessed in a different way, it's through the OS API. What is it you're trying to solve by modifying the arguments? – Angew is no longer proud of SO Jan 02 '14 at 08:04
  • 2
    If you're using C++, don't muck with argc/argv directly: use a `std::vector`. You can easily initialize such a vector from argc and argv (via `std::vector const args(argv, argv + argc);`) or you can initialize it yourself with whatever strings you choose if you want to use a set of known strings at runtime. If you're learning C++, I'd recommend this approach. It's much safer, harder to screw up, and requires fewer asterisks. – James McNellis Jan 02 '14 at 08:12
  • @Angew 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[] settings). – herohuyongtao Jan 02 '14 at 08:12
  • 2
    @herohuyongtao This is a bad idea. Why not write a script that executes your program correctly instead of **modifying the tested code** every time you want to test something? – arne Jan 02 '14 at 08:27
  • @arne Yes, it seems a bad idea in practice. But I want to be able to edit it during debugging. – herohuyongtao Jan 02 '14 at 08:31
  • @herohuyongtao: If you modify `argv` in main, it'll be only at compile-time, your hands are tied at debug time even then. – legends2k Jan 02 '14 at 08:32
  • @legends2k What do you mean by `only at compile-time`? – herohuyongtao Jan 02 '14 at 08:34
  • 1
    I mean if you assign another string array to `argv` say like Emilio's solution, wouldn't you have to recompile it every time you change `n_argv`? If yes, then how does it help you debug, since every time you need a different string, you'll've to modify `n_argv`, compile and run the program. Correct? – legends2k Jan 02 '14 at 08:37
  • Also, most debuggers are able to assign new values to variables during debug-time. Not sure about C-Strings though. – arne Jan 02 '14 at 08:46
  • @legends2k Yes. But without this, how can I debug my program by adding some `breakpoints` for different test cases? – herohuyongtao Jan 02 '14 at 08:46
  • @herohuyongtao: See the answer I've just posted where you don't need to worry about setting breakpoints and modifying variables on-the-fly. – legends2k Jan 02 '14 at 09:49
  • 1
    "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." – that has precisely **nothing** to do with the strings being NUL- (and **not** "NULL") terminated. You are NULL-terminating the array, not the strings. (0-termination of string literals is done by the compiler anyway.) – The Paramagnetic Croissant Mar 16 '15 at 17:47

3 Answers3

11

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
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
5

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]);
    }
}
xaizek
  • 5,098
  • 1
  • 34
  • 60
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.

Community
  • 1
  • 1
legends2k
  • 31,634
  • 25
  • 118
  • 222
  • Well, that depends on the error you're getting :) When you're asking something in a common forum, be as explicit as possible so that it's easy for others to understand you. – legends2k Dec 05 '17 at 13:37
  • it's hard to find a solution for this kind of error :( hmm.. i'll check more – AVI Dec 05 '17 at 13:40