将 strncpy 放入一个没有空终止符空间的字符串中是否安全?

Is it Safe to strncpy Into a string That Doesn't Have Room for the Null Terminator?

考虑以下代码:

const char foo[] = "lorem ipsum"; // foo is an array of 12 characters
const auto length = strlen(foo); // length is 11
string bar(length, '[=10=]'); // bar was constructed with string(11, '[=10=]')

strncpy(data(bar), foo, length);
cout << data(bar) << endl;

我的理解是 string 总是分配有一个隐藏的空元素。如果是这种情况,那么 bar 真的分配了 12 个字符,第 12 是一个隐藏的 '[=13=]',这是非常安全的......如果我是错误的话 cout 将导致未定义的行为,因为没有空终止符。

有人可以帮我确认一下吗?这合法吗?


关于为什么使用 strncpy 而不是仅使用 string(const char*, const size_t) 构造函数的问题很多。我的意图是使我的玩具代码接近包含 vsnprintf 的实际代码。不幸的是,即使在这里得到了很好的答案,我发现 vsnprintf 的行为与 strncpy 不同,我在这里提出了一个后续问题:

是的,根据 char * strncpy(char* destination, const char* source, size_t num) 是安全的:

Copy characters from string

Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.

这里有两个不同的东西。

首先,strncpy 是否在此实例中添加了一个额外的 [=11=](11 个非 [=11=] 元素被复制到一个大小为 11 的字符串中)。答案是否定的:

Copies at most count characters of the byte string pointed to by src (including the terminating null character) to character array pointed to by dest.

If count is reached before the entire string src was copied, the resulting character array is not null-terminated.

所以通话完全没问题。

然后 data() 给你一个正确的 [=11=] 结尾的字符串:

c_str() and data() perform the same function. (since C++11)

看来对于 C++11,你是安全的。字符串是否分配了额外的 [=11=] 文档中似乎没有说明,但是 API 很清楚你在做什么是完全正确的。

您分配了 11 个字符 std::string。您不会尝试读取或写入任何超出该范围的内容,因此该部分是安全的。

所以真正的问题是你是否弄乱了字符串的内部结构。既然你没有做任何不允许的事情,那怎么可能?如果字符串需要在内部保留一个 12 字节的缓冲区并在末尾填充空值以履行其合同,那么无论您执行什么操作,情况都会如此。

这是安全的,只要将 [0, size()) 个字符复制到字符串中即可。每 [basic.string]/3

In all cases, [data(), data() + size()] is a valid range, data() + size() points at an object with value charT() (a “null terminator”), and size() <= capacity() is true.

所以 string bar(length, '[=16=]') 给你一个 size() 为 11 的字符串,最后有一个不可变的空终止符(实际大小总共有 12 个字符)。只要您不覆盖该空终止符,或尝试超越它,就可以了。