std::array<char, size> 是否以空字符结尾?

5
以下代码片段是否安全?它调用了std::string的第四个构造函数,该构造函数接受指向以null结尾的字符串的指针。问题是,我不确定下面的word是否以null结尾。它是吗?
std::array<char, 4> word{'a', 'b', 'c', 'd'};

int main()
{
    std::string p = word.data();
    return 0;
}

11
不,它不是以空字符结尾的。要以空字符结尾,数组需要5个字符,最后一个为0。你正在调用未定义的行为。 - stark
1
std::string的第四个构造函数不接受指向以null结尾的字符串的指针,它接受指向char的指针。开发人员必须注意空终止符。 - 273K
2
@S.M. char指针和空终止字符串指针的区别在于指针的使用方式。语言本身并没有区分。确保空终止符是开发人员的责任,你是正确的。 - Mark Ransom
2
std::string p(word.begin(), word.end()); - Evg
@Evg 这就是我之前的代码,但是我当时在处理 std::array<char, 50>,使用 beginend 迭代器将其转换为字符串后,其中有46个空终止符。哈哈... - user11910061
在这种情况下,您可以使用std::string p(word.begin(), word.begin()+desired_length)或者简单地使用std::string p(word.data, desired_length) - Remy Lebeau
4个回答

8
> 是否以空字符结尾?
它可以包含一个以空字符结尾的字符串,但不一定包含一个以空字符结尾的字符串。
std::array<char, 4> word{'a', 'b', 'c', 'd'};

该数组不包含以 null 结尾的字符串。可以通过元素中没有 null 结尾字符来判断。

std::string p = word.data();
这个程序的行为是未定义的。

以下代码片段是否安全?

不安全。

如何使单词以空字符结尾。

这里是一个例子:
std::array<char, 5> word{'a', 'b', 'c', 'd'};

或者如果您想更明确:

std::array<char, 5> word{'a', 'b', 'c', 'd', '\0'};

如果您提供一个如何使word成为空终止的示例,那么这个答案将是完美的。 - Mark Ransom
1
@MarkRansom 我敢打赌我可以举一个例子来使word成为空终止,这将使得这个答案远非完美。Bwahahahahahaha ahum. - Yakk - Adam Nevraumont
@MarkRansom 已添加。 - eerorika
现在它完美了。谢谢。 - Mark Ransom

4

这个数组并不以空字符结尾。一种简单的方法是使用字符串字面值来初始化它,使其以空字符结尾,如下所示:

std::array<char, 5> a{"abcd"};

1
有趣的是,std::array<int, 5> 不会将数组中的整数默认初始化为0,但 std::array<char, 5> 会将所有字符默认初始化为0。这是为什么呢? - user11910061

3
以下的代码片段安全吗?
不安全。它会调用“未定义行为”。
它调用了std::string的第四个构造函数,该构造函数接受指向以空字符结尾的字符串的指针。问题是,我不确定下面的“word”是否以空字符结尾。 它没有。
一个简单的解决方法(假设您不想将数组以空字符结尾)是使用另一个接受所需长度作为参数的std :: string构造函数,例如:
std::array<char, 4> word{'a', 'b', 'c', 'd'};

int main()
{
    std::string p(word.data(), word.size());
    // more generally:
    // std::string p(word.data(), desired_length);
    return 0;
}

另外,您可以使用不同的std::string构造函数,它接受迭代器参数,例如:

std::array<char, 4> word{'a', 'b', 'c', 'd'};

int main()
{
    std::string p(word.begin(), word.end());
    // more generally:
    // std::string p(word.begin(), word.begin() + desired_length);
    return 0;
}

无论哪种方式,你都不需要一个空终止符。


我考虑使用.data的原因是因为我不想使用endbegin。为什么?因为我有一个包含4个非空字符和46个空终止符(std::array<char, 50>)的数组。这将创建一个长度为50且带有46个空终止符的字符串。当此字符串作为映射的键时,你会遇到麻烦...实际上,“abcd”(1个空终止符)看起来与“abcd”(46个空终止符)相同,但对于映射来说,它们是完全不同的键。https://godbolt.org/z/MKPs7MW4v - user11910061
在这种情况下,std::string p(word.data(), 4); 或者 std::string p(word.begin(), word.begin()+4); 对您来说都可以工作。 - Remy Lebeau
是的,但我不知道数组内容的确切长度。换句话说,我得到了一个大小为50的数组,但我不确定所有内容是否都是空终止符,有些是空终止符,还是没有空终止符。 - user11910061
@TeeZadAwk 你可以使用 std::find() 来检查数组中是否存在空终止符。如果存在,从结果中减去 begin() 来获取其索引(或使用 std::distance()),这就是你的长度。如果不存在,则使用数组的完整大小,例如:auto iter = std::find(word.begin(), word.end(), '\0'); size_t length = (iter != word.end()) ? (iter - word.begin()) : word.size(); std::string p(word.data(), length); - Remy Lebeau
1
@TeeZadAwk 或者,您可以使用迭代器来简化它,例如:std :: string p(word.begin(),std :: find(word.begin(),word.end(),'\ 0')); 如果找到空终止符,则仅复制直到终止符,否则将复制到数组的末尾。 - Remy Lebeau

2

如果您的目标是使用std::find初始化std::array中的std::string,但又不会超出边界,则可以像这样使用std::find

std::string p{std::begin(word), std::find(std::begin(word), std::end(word), '\0')};

如果没有null终止符,它会将初始化值设置到数组的结尾;否则,它将初始化值设置到第一个null终止符之前。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接