【问题标题】:Dealing with char arrays in C++在 C++ 中处理 char 数组
【发布时间】:2010-12-25 01:21:01
【问题描述】:

我有这段 C 风格的初始化代码:

const char * const vlc_args[] =
{
    "-I", "dummy",
    "--ignore-config",
    "--extraintf=logger",
    "--verbose=2"
    "--plugin-path=/usr/lib/vlc"
};
//tricky calculation of the char space used
libvlc_new(sizeof(vlc_args)/sizeof(vlc_args[0]), vlc_args, &exc);

由于我需要使--plugin-path 参数动态化,我不能再使用静态数组。所以我想出了一个 C++ 替代方案:

std::string pluginpath = "test";
libvlc_exception_t exc;

std::vector<std::string> args;
args.push_back("-I");
args.push_back("dummy");
args.push_back("--ignore-config");
args.push_back("--extraintf=logger");
args.push_back("--verbose=2");
args.push_back("--ipv4");
args.push_back("--plugin-path=" + pluginpath);

std::string combinedString;
for (size_t idx = 0; idx < args.size(); ++idx)
{
    combinedString.append(args[idx]);
    combinedString.resize(combinedString.size() + 1);
    combinedString[combinedString.size() - 1] = 0;
}
combinedString.resize(combinedString.size() + 1);
combinedString[combinedString.size() - 1] = 0;

size_t size = combinedString.size();
const char * data = combinedString.c_str();
libvlc_new(size, &data, &exc); // => error occurs here (not at end of scope or anything)

但这会导致分段错误。所以我的代码中一定有一个错误,我似乎找不到..有人能发现吗?

解决了!
感谢 Joseph Grahn 和 Jason Orendorff。我对 C 样式数组的内存布局的想法是错误的。我认为所有数据都组织成一个大的顺序块。实际上,它是指向每个单独字符串的第一个字符的指针列表。

此代码有效:

std::vector<const char*> charArgs;
for (size_t idx = 0; idx < args.size(); ++idx)
{
    charArgs.push_back(&(args[idx][0]));
}
mVLCInstance = libvlc_new(charArgs.size(),
                          &charArgs[0],
                          &mVLCException);

【问题讨论】:

  • 在调试器中运行它并告诉我们它出现在哪一行。
  • 它在没有调试符号的情况下在目标代码中出现段错误。在我的代码中,这发生在 libvlc_new 调用期间。
  • 为什么要将 &data 传递给 libvlc_new 函数?在原始代码中,第二个参数是 const char * 类型,现在您传递的是 const char **。从第二个参数中删除“&”。

标签: c++


【解决方案1】:

我也有同样的问题;我想以安全的 C++ 方式动态生成参数。

我想到的解决方案是使用 C++ 11 中新增的 unique_ptr。例如:

unique_ptr<char *[]> vlc_argv;
int vlc_argc;
...
auto vlc(libvlc_new(vlc_argc, vlc_argv.get()));

这给了我一个很好的 RAII 对象来保存我的论点,因为我仍然可以传递给 libvlc_new()。由于参数是 argv 中的原始指针,由操作系统管理,我们可以直接使用 vlc_argv 中的那些。

作为另一个示例,假设我处理了 argv 中的前几个 args(在上面的“...”区域中的某处),并希望将 next_arg 中的所有内容传递给 libvlc_new():

vlc_argv = std::unique_ptr<char *[]>(new char *[2 + argc - next_arg]);
vlc_argv[0] = argv[0];
for (vlc_argc=1; vlc_argc <= argc - next_arg; vlc_argc++) {
    vlc_argv[vlc_argc] = argv[next_arg + vlc_argc - 1];
}
vlc_argv[vlc_argc] = 0;

【讨论】:

    【解决方案2】:

    问题是由于使用了 c_str() 并存储了指针。

    stringstream, string, and char* conversion confusion

    编辑忘记我说的话...已经很晚了...看看 cmets :)

    【讨论】:

    • 这种情况与错误使用字符串流(ss.str().c_str())不同,因为在我的字符串对象中是左值。
    • combinedString.c_str()返回的指针一直有效,直到combinedString被修改或销毁。
    【解决方案3】:

    我认为 Josef Grahn 是对的:API 需要一个实际的指针数组。

    如果您不需要以编程方式添加参数,则可以返回使用数组:

    std::string pluginpath = "test";
    std::string pluginpath_arg = "--plugin-path=" + pluginpath;
    const char *args[] = {
        "-I", dummy, "--ignore-config", ..., pluginpath_arg.c_str()
        };
    
    libvlc_exception_t exc;
    libvlc_new(sizeof(args) / sizeof(args[0]), args, &exc);
    

    编辑:在此处使用c_str() 可能有问题。如果 VLC 保留指针并稍后再次使用它,则这是正确的;我无法从文档中判断是否是这种情况。

    【讨论】:

    • 我记得读过 Google 代码指南规定使用 sizeof(args[0]) 而不是 sizeof(char) 因此在数据类型更改时不需要更改(或忘记) (例如 wchar_t)。
    • 感谢 Jason,您的回答让我以正确的方式思考。
    • epatel:好眼力,我是说sizeof(args[0])
    【解决方案4】:

    关于分段违规

    没有解决办法,可能会有更多问题

    你只发送了一个字符串。(不确定libvlc_new是否允许)所以第一个参数应该设置为1,即size = 1。我相信这将解决分割问题。但我怀疑libvlc_new 是否可以只用一行多个参数调用。

    在原始代码中sizeof(vlc_args)/sizeof(vlc_args[0]) 将参数的数量作为向量中的条目。在您的示例中等于 6。

    你的代码

    size_t size = combinedString.size(); // a long string, size >> 1
    const char * data = combinedString.c_str(); // data is a pointer to the string
    
    libvlc_new(size, &data, &exc);
    
    // size should be 1 because data is like an array with only one string. 
    // &data is the adress to the "array" so to speak. If size > 1 the function
    // will try to read pass the only string available in this "array"
    

    我认为 Jason Orendorff 有一个很好的解决方案来解决这一切......

    【讨论】:

      【解决方案5】:

      库调用需要一个指向const char* 数组的指针(即多个指针),但您传递给它一个指针。更多的字符被附加到该字符串的末尾并不重要。

      要动态构建所需指针的数组,您可以使用另一个 vector

      // store c_str() pointers in vector
      std::vector<const char*> combined;
      for (size_t idx = 0; idx < args.size(); ++idx) {
          combined.push_back(args[idx].c_str());
      }
      
      // pass pointer to begin of array (it is guaranteed that the contents of a vector
      // are stored in a continuous memory area)
      libvlc_new(combined.size(), &(combined[0]), &exc);
      

      Jason Orendorff 的注释在这里也有效:如果libvlc_new 在内部存储传递的指针供以后使用,这将不起作用。

      【讨论】:

        【解决方案6】:

        你试过了吗:

        libvlc_new(size, data, &exc);
        

        而不是

        libvlc_new(size, &data, &exc);
        

        您似乎使用空字节使字符串表现得像一个字符数组,但随后您将指针传递给 char*“数组”而不仅仅是数组。

        【讨论】:

          【解决方案7】:

          您将所有参数附加到单个字符串中,然后将指向 const char * 字符串的指针传递给 libvlc_new,就好像它是一个 char * 数组一样。

          (我不确定是不是这个问题,但看起来有点奇怪。)

          【讨论】:

          • 它与 char* 数组有何不同?两种情况下的内存布局不一样吗?
          • 不,“const char * const vlc_args[]”是一个指针数组。单个字符串中没有任何指针。
          • 我不知道 libvlc 期望什么,但是对于 main() 中的程序参数,您应该能够: argv[3] 为第四个参数获取一个 const char * , 例如。在您的情况下,这会在堆栈的某处(变量数据所在的位置上方)产生一个野指针。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-08-20
          • 1970-01-01
          • 2020-03-11
          • 1970-01-01
          • 1970-01-01
          • 2019-08-22
          • 1970-01-01
          相关资源
          最近更新 更多