【问题标题】:second argument of the command line arguments in a format other than char** argv or char* argv[] [closed]命令行参数的第二个参数,格式不是 char** argv 或 char* argv[] [关闭]
【发布时间】:2020-05-19 17:23:09
【问题描述】:

为了解决我的问题here,我想知道是否/如何以char** argvchar* argv[] 以外的格式定义命令行参数的第二个变量。 The reason 是 pybind11 不允许函数输入中的任何一个。以下是我尝试过的方法:

方法一:

#include <stdio.h>

int main(int argc, int* argv_){
    for (int i = 0; i < argc; ++i){
        printf("%s\n", (char *)(argv_[i]));
    }
}

此方法背后的基本原理是指针本质上是一个整数,通过将地址转换为char 指针,应该能够获取字符串。提前感谢您的支持。

方法二:

#include <stdio.h>
#include <string>

int main(int argc, std::string* argv_){
    for (int i = 0; i < argc; ++i){
        printf("%s\n", argv_[i].c_str());
    }
}

方法三:

#include <stdio.h>
#include <string>
#include <vector>

int main(int argc, std::vector<std::string> argv_){
    for (int i = 0; i < argc; ++i){
        const char* argv__ = argv_[i].c_str();
        printf("%s\n", argv_[i].c_str());
    }
}

问题:

不幸的是,上述所有方法都导致了臭名昭著的segmentation fault

如果您能帮助我了解问题所在(即内存泄漏在哪里)以及如何解决问题,我将不胜感激。

解决方法/破解:

在 cmets 中,有人告诉我,如果使用任何其他形式而不是 main()main(int argc, char** argv)main(int argc, char* argv[]),它将不可避免地导致 segmentation fault。但是,下面的代码有效:

#include <stdio.h>

int main(int argc, long* argv_){
    for (int i = 0; i < argc; ++i){
        printf("%s\n", (char *)(argv_[i]));
    }
}

这适用于 Ubuntu 最小和 g++ 7.4.0,以及 Windows 10 Visual Studio 2019 编译器。但是,它不能用clang 编译。正如其他人指出的那样,这不是一个解决方案,也是一种非常糟糕的做法。根据编译器、操作系统和内存的当前状态,它可能会导致未定义的行为。这不应该在任何实际代码中使用。任何 C/C++ 代码中的 main 函数必须采用 main()main(int argc, char** argv)main(int argc, char* argv[]) 的形式。

【问题讨论】:

  • 我很困惑。什么都不应该打电话给main,所以这个问题对我来说没有意义
  • @Foad:实际上并没有混合代码这样的东西。由于您使用的是 C++ 类,因此您正在用 C++ 清楚地编译它。
  • @Foad:无论如何,它们都不是一个可验证的最小完整示例,因为那里没有足够的代码来验证问题。正如所有 cmets 和所有答案中所指出的,main 的使用也增加了很多混乱。如果main 不是所讨论的方法,那么混乱就会消失。
  • (ctd.) 由于您根本不会调用 main(如您所说),因此段错误问题与您的实际情况完全无关努力实现;所以为它寻求“解决方案”是没有效率的。
  • 唯一不专业的一点是您忽略/不承认您给出的答案。你不要“绕过标准”,句号。接受,如果你愿意,回答你的人确实确​​实知道我们在说什么,并且确实理解你的问题——甚至比你更好。

标签: c++ segmentation-fault command-line-arguments argv


【解决方案1】:

毕竟它看起来不需要是main,所以你可以这样做:

#include <iostream>
#include <string>
#include <vector>

int cppmain(std::string program, std::vector<std::string> args) {
    std::cout << program << " got arguments:\n";
    for(auto& arg : args) {
        std::cout << " " << arg << "\n";
    }
    return 0;
}

int main(int argc, char* argv[]) {
    // create a string from the program name and a vector of strings from the arguments
    return cppmain(argv[0], {argv + 1, argv + argc});
}

如果您需要调用类似于闭源的 main 函数(您无法更改),请创建一个您可以pybind 的包装函数,并让该函数调用闭源函数。

#include <cstddef>
#include <iostream>
#include <string>
#include <vector>

int closed_source_function(int argc, char* argv[]) {
    for(int i = 0; i < argc; ++i) {
        std::cout << argv[i] << '\n';
    }
    return 0;
}

int pybind_to_this(std::vector<std::string> args) {
    // create a char*[]
    std::vector<char*> argv(args.size() + 1);

    // make the pointers point to the C strings in the std::strings in the
    // std::vector
    for(size_t i = 0; i < args.size(); ++i) {
        argv[i] = args[i].data();
    }

    // add a terminating nullptr (main wants that, so perhaps the closed source
    // function wants it too)
    argv[args.size()] = nullptr;

    // call the closed source function
    return closed_source_function(static_cast<int>(args.size()), argv.data());
}

【讨论】:

  • 不幸的是,其他用户在没有事先咨询我的情况下编辑了我的问题,导致了这种混乱。 char* arg[]char** argv 不能在任何函数的输入中。
  • 他们不是。 cppmain 函数的输入是 std::stringstd::vector&lt;std::string&gt;
  • @Foad 问题的变化是为什么这是正确的唯一答案
【解决方案2】:

让我们尝试一一解决在冗长讨论中出现的过多问题。


问题一:为什么我在使用main的一些非标准参数(如字符串向量或int指针)时会出现段错误?

int, char ** 的参数类型是由 C 和 C++ 标准以这种方式定义的。除了非标准扩展,你不能使用其他类型。

来自 ISO/IEC 9899(C 语言),5.1.2.2.1 程序启动

程序启动时调用的函数名为main。实现没有声明这个函数的原型。它应使用int 的返回类型定义并且不带参数:

int main(void) { /* ... */ }

或带有两个参数(此处称为argcargv,尽管可以使用任何名称,因为它们是声明它们的函数的本地名称):

int main(int argc, char *argv[]) { /* ... */ }

或等价物;或以其他一些实现定义的方式。

最后一句话允许我提到的那些扩展。我知道的一个这样的扩展是 GCC 的 environ:

https://www.gnu.org/software/libc/manual/html_node/Program-Arguments.html#Program-Arguments


问题 2:我该如何解决这个问题?

你没有。

使用与标准或编译器扩展定义的类型不同的类型是未定义的行为,它可能(但不需要)导致段错误。 不要调用未定义的行为。不要“破解”标准。它不是“解决方法”,更不是“解决方案”,它是随时可能在你面前炸毁的损坏代码。


问题3:我如何pybind一个以char **为参数的第三方函数?

你没有,因为这不是datatype supported by pybind


问题4:那我如何通过pybind接口这样的功能呢?

您编写一个包装函数,在前端接收pybind(例如std::vector&lt; std::string &gt;)支持的参数支持,适当地编组这些参数,然后调用第三方后端函数为你用编组的参数。 (当然,如果需要,对返回类型执行相同的操作。)

有关如何执行此操作的惯用示例,请参阅the answer by @TedLyngmo


问题 5:我可以将pybind 发送给第三方main 吗?

这是不明智的,因为main 是一个特殊函数,并且被调用的代码可能会做出假设(如atexit 回调),而您的调用代码不会,而可以 不会,遵守。它肯定不是第三方期望作为库函数调用的函数。

【讨论】:

  • 感谢您的帖子。我知道标准,但如果可能的话,我正在寻找一种绕过它的方法。基本上,如果我可以将输入存储为除char* argv[]char** argv 之外的任何类型,然后将它们转换为这些形式,那么问题应该得到解决。
  • @Foad:请参阅更新的答案,以快速将argv 转换为字符串向量。这真的是你能做的最好的了。
  • @DevSolar:混淆是因为Foad正在绑定一个随机方法到pybind,而这个问题与main无关。
  • @Foad:完全修改了答案。如果您的问题(和 cmets)的任何部分仍未得到解答,请留言。
  • @DevSolar 当然。现在有点忙。但我会研究它并给你反馈。
猜你喜欢
  • 2015-01-28
  • 2015-04-08
  • 2011-01-29
  • 1970-01-01
  • 2011-07-08
  • 2011-08-14
  • 2012-05-27
  • 1970-01-01
  • 2020-05-19
相关资源
最近更新 更多