【问题标题】:Initialize/set char *argv[] inside main() in one line在 main() 中在一行中初始化/设置 char *argv[]
【发布时间】:2014-01-19 15:04:10
【问题描述】:

我想在main() 中初始化/设置char *argv[],以便稍后在我的程序中使用argv[1], argv[2]...


到目前为止,我知道如何通过两种方式做到这一点:

  1. 对于int main(),使用一行:

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

    注意,最后使用NULL是因为argv数组中的指针指向C字符串,根据定义NULL终止。

  2. 对于int main(int argc, char* argv[]),我必须使用多行作为:

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

我的问题是如何将这两种方法结合在一起,即只使用一行来为int main(int argc, char* argv[]) 初始化它?特别是,我希望能够这样做(目前这是错误的):

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

我怎样才能做到这一点?


编辑:我知道argv[]可以设置在Debugging Command Arguments中。我想在main() 中编辑它们的原因是我不想每次都为新的测试用例使用Debugging Command Arguments(不同的argv[] 设置)。

【问题讨论】:

  • 奇怪的想法。您的案例 2 很有可能使程序崩溃。 argc 和 argv 应该是只读的。
  • 也许你应该描述一下你更大的目标是什么。我怀疑可能有一种不同的方法对你有用(虽然我不确定我是否真的理解这个问题,所以我可能错了)。
  • 这很明显是X-Y problem。变量argcargv(或任何你命名的变量)是main() 的本地变量,你可以用它们做任何你想做的事情。实际的命令行参数在标准 C++ 中是只读的,如果可以以不同的方式访问它们,那就是通过 OS API。您想通过修改参数来解决什么问题?
  • 如果您使用 C++,请不要直接使用 argc/argv:使用 std::vector<std::string>。您可以轻松地从 argc 和 argv 初始化这样的向量(通过std::vector<std::string> const args(argv, argv + argc);),或者如果您想在运行时使用一组已知字符串,您可以使用您选择的任何字符串自己初始化它。如果您正在学习 C++,我会推荐这种方法。它更安全,更难搞砸,并且需要更少的星号。
  • @herohuyongtao 这是个坏主意。为什么不编写一个能正确执行程序的脚本,而不是每次想要测试时都修改测试代码

标签: c++ main argv


【解决方案1】:

最安全的方法可能是不要写入 argv 引用的内存,(可能不是你想象的那样结构化),而是有另一个批量:

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

这会将 argv 从它的原始内存(由调用者拥有,保留在那里)转移到另一个将在 main() 的生命周期中存在的内存。

请注意,const char* 是必需的,以避免出现已弃用的“*string assignment to char**”消息。


注意:此代码已在 Linux 上使用 GCC 4.8.1 编译,结果如下:

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

【讨论】:

  • 编译为 -std=C++11 -Wall -pedantic,使用 GCC 4.8.1 没有错误。另外,我的代码中没有 5,所以你编译了其他东西。
  • VS中的错误是error C2440: '=' : cannot convert from 'const char *[3]' to 'char *[]'
  • 请注意我的代码中的const char** argv(不仅仅是char** argv
  • 成功了。谢谢。很抱歉错过了const愚蠢地。有没有办法通过main(int argc,char* argv[])工作?
  • 不一致:初始化列表根据定义是常量。您可以在分配中使用const_cast<char**>(n_argv),但无论如何您都在强制 const 释放它的 const-ness。
【解决方案2】:

假设您的程序接受一组字符串并对它们执行一些操作。让我们称之为需要测试的process_strings。您想为不同的测试用例传递不同的字符串集,例如{"abc", "efg", "hij"}{"thin", "run", "win"}{"cat", "rat", "mat"} 等。将来您希望在不改变原始函数process_strings 的情况下添加更多这样的测试用例。那么解决这个问题的正确方法是修改(驱动程序)程序,这样您就不必为添加/删除的不同测试用例重新编译。一个例子可能是:

#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;
        }
    }
}

测试用例文件示例,tests.txt

abc efg
cat rat mat

animal man

C:\&gt; simple.exe tests.txt 生成的输出:

abc     efg
cat     rat     mat
animal  man

如果您的参数包含空格,即如果空格不能用作分隔符,请参阅this post,了解如何从行中提取参数。

因此,我们将输入数据与代码分开。每次测试用例发生变化时,您都无需重新编译程序。只需更改输入,通过已编译的程序运行它并验证输出。这节省了大量的开发时间,在某种意义上也是data-driven programming的基础。

【讨论】:

  • 嗯,这取决于您遇到的错误 :) 当您在公共论坛上提问时,请尽可能明确,以便其他人容易理解您。
  • 这种错误很难找到解决方案:(嗯..我会检查更多
【解决方案3】:

如果您能够使用 C99,则可以使用 compound literals 功能。这是一个编译为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]);
    }
}

【讨论】:

  • 这无法在 VS 中编译。
  • @herohuyongtao 这就是为什么它以“如果你可以使用 C99”开头,VS 不支持。
  • 请注意该问题被标记为 C++ (not C)
  • 请注意,如果您这样做,则原始 argv 的有效用途与替换 argv 无效:用户代码可以修改原始参数字符串,但不能修改替换参数字符串(因为它们是字符串文字)。
  • 我的错,不知道为什么我认为它是关于 C 语言的。我认为缺少msvc 标签,这可能很重要,因为 VS 并不总是符合 C++ 标准。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-20
  • 2021-09-18
  • 2012-08-25
  • 2020-11-03
  • 2016-09-10
  • 1970-01-01
  • 2011-01-29
相关资源
最近更新 更多