【问题标题】:Populate char* array using loop使用循环填充 char* 数组
【发布时间】:2020-09-09 17:51:52
【问题描述】:

我有一个遗留函数,其输入参数类型为 const char* 数组

myfunc(const char* names[]);

我有一组std::set<std::string> mynames;。该集合已被填充。 我必须将mynames 传递给myfunc

使用基于范围的循环,我有

const char* names[];
int i = 0;
for (const auto &name : mynames)
{
    //allocate memory to names here
    names[i] = name.c_str();
    ++i;
}

//call myfunc
myfunc(names);

有没有更好的方法来做到这一点?

【问题讨论】:

  • 为什么不使用std::vector<std::string> names; 而不是const char* names[];?有什么好的理由吗?如果你真的需要这些指针,你可以随时使用std::string::c_str() 来获取这些。
  • “更好”是非常主观的。恕我直言,最好修复myfunc

标签: c++ arrays char


【解决方案1】:

您没有为names[] 数组分配任何内存。你需要做更多这样的事情:

const char** names = new const char*[mynames.size()]; // +1 if needed

size_t i = 0;
for (const auto &name : mynames)
{
    names[i++] = name.c_str();
}
/* if needed:
names[i] = nullptr;
*/

/* alternatively
std::transform(mynames.begin(), mynames.end(), names,
    [](const std::string &name){ return name.c_str(); }
);
// if needed :
// names[mynames.size()] = nullptr;
*/

myfunc(names);
delete[] names;

然后可以通过使用std::vector 而不是new[] 来进一步简化,例如:

std::vector<const char*> names;
names.reserve(mynames.size()); // +1 if needed
for (const auto &name : mynames)
{
    names.push_back(name.c_str());
}
/* if needed:
names.push_back(nullptr);
*/

/* alternatively:
std::vector<const char*> names(mynames.size()); // +1 if needed
std::transform(mynames.begin(), mynames.end(), names.begin(),
    [](const std::string &name){ return name.c_str(); }
);
// if needed:
// names.back() = nullptr;
*/

myfunc(names.data());

【讨论】:

  • 我喜欢矢量。我很确定你需要 size+1,然后推送一个 nullptr。
  • @KennyOstrom 仅当函数需要一个以 null 结尾的数组并且没有其他方式预先告知数组大小时。
  • 是的。他们实际上并没有说它是哪个图书馆......我的“有根据的猜测”。
  • 语法myfunc(const char* names[]);myfunc(const char** names);是否相同?
  • @ontherocks 在这种情况下,是的。在函数声明中,未绑定的数组参数T[] 只是指针参数T* 的语法糖。在这种情况下,Tconst char *
【解决方案2】:

如果你可以重写这个函数,那就去做吧。但是,有很多像这样长期存在的第三方库,您不想尝试与它们混淆。

您将不得不分配一个临时数据结构,以函数期望的格式提供数据。你将不得不弄清楚它必须存在多长时间。我将在这里展示一个示例,假设您可以跳过分配单个字符串,而只为它们的指针分配一个数组(就像您所做的那样)。

您没有为指针分配空间,也没有在末尾添加 nullptr。请注意,该函数采用原始数组,因此它不知道有多少名称。它几乎肯定会期望 NULL 来表示姓氏。

#include <iostream>
#include <string>
#include <set>

// This demonstrates how a legacy function like this might work
int myfunc(const char* names[]) {
    if (names) {
        int index = 0;
        const char* name = names[index];

        // Notice that this function cannot know how many names there are.
        // The function documentation will almost certainly tell you 
        // to add a NULL as the last entry.
        while (name) {
            std::cout << name << std::endl;
            name = names[++index];
        }
    }
    return 0;
}

int main()
{
    const char * legacy [] = {
        "one",
        "two",
        "three",
        nullptr,
    };

    std::cout << "legacy" << std::endl;
    myfunc(legacy);


    std::set<std::string> modern { "one", "two", "three" };

    // this temp structure is valid as long as it is 
    // allocated, and original set doesn't change
    // although you probably should allocate and clean up
    // a copy of each string within this temp array.
    const char ** temp = new const char* [modern.size()+1];
    int index = 0;
    for (const auto &name : modern) {
        temp[index++] = name.data();
    }
    temp[index] = nullptr;

    // if myfunc just uses them and exits, you're okay
    // if it remembers any of the pointers for later, that would be unfortunate
    std::cout << "using temp" << std::endl;
    myfunc(temp);

    // delete what we allocated. The pointers inside are owned by the set, so we don't care about that
    delete [] temp;
    return  0;
}

【讨论】:

  • 请注意,字符串由 std::set 重新排序,但不要担心。
  • 再重复一遍——阅读该函数的文档很重要
猜你喜欢
  • 2020-09-11
  • 2014-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-21
  • 2013-06-25
  • 2015-01-07
相关资源
最近更新 更多