2017-09-08 32 views
0

如果我有一个嵌入的空终止符[是:那个UB?],它是为我准备好后访问它的值吗?正在访问嵌入的空终止符UB后的字符串部分?

#include <stdio.h> 

const char foo[] = "abc\0def"; 
int main() { 
    printf("%s", foo+4); 
    return sizeof(foo); 
} 

为了记录在案,它打印你所期望的:

def 
+3

不,内存是分配的,没有UB这样做。 – user0042

+1

这很好,除了'sizeof'将返回数组的大小,而不是字符串的长度(如果这是你的意图)。 –

回答

3

嵌入式null不是未定义行为。它可能是一个逻辑错误,如果您使用期望字符串为空终止的函数。但是,访问已成功分配的数组的全部范围,无论其内容如何,​​都没有任何错误,邪恶或未定义。

一件事,虽然观察:如果你试图存储在std::string这个数据(这是你应该如何处理所有的字符串,TBH),如何您存储字符串也很重要。

std::string str1 = foo; //contents of str1 becomes "abc". 
std::string str2 = std::string(foo, sizeof(foo)); //contents of str2 becomes "abc\0def" 
2

[dcl.init.string]状态

窄字符类型(3.9.1),char16_t阵列,char32_t阵列或阵列的wchar_t的阵列可以由一个窄字符串文字来初始化,char16_t字符串字面量,char32_t字符串字面量或宽字符串字面量,或者通过大括号(2.14.5)中包含的适当类型的字符串字面量。 字符串字面值的连续字符初始化数组的元素。

重点煤矿

所以嵌入式空它不是一个问题,它只是成为数组的元素。由于数组的大小可容纳所有字符并转义序列,因此我们知道在嵌入null之后存在元素,并且访问这些元素是安全的。

真的,嵌入式null的唯一问题是任何C函数在它命中null时都会停止,并且不会完整地处理该字符串。您可以考虑使用std::string而不是这些问题。

2

访问C字符串beyound终止空字符本身从来没有是未定义的行为。尽管如此,我们可以产生不确定的行为这种方式,但对于一个完全不同的原因:

如果终止空字符恰好居住在该字符串保留的字符数组中的最后一个位置,那么我们访问此基础数组如果我们在字符串末尾访问字符串,就会超出其范围。而这出界外的访问是真正产生了不确定的行为...

编辑:

[旁白:?是UB]

UB,不确定的行为,是无法定义的行为,因为没有有意义的行为。依赖于未定义的行为可能导致任何事情,包括获得预期的结果,但可能在任何其他时间惨败(例如,在另一个平台上,在切换编译器版本之后,在简单地重新编译之后,甚至在重新启动一个和相同的程序之后)。因此,一个依赖未定义行为的程序被认为是不明确的。

示例:取消引用指向已删除对象的指针(“悬挂指针”),或者接近问题:访问数组超出边界(可能导致尝试访问内存不与当前进程甚至不存在,但可能会读取或(错误!!!)覆盖恰好位于给定地址的完全不同对象的内存(每次您的程序都不必是相同的对象运行,甚至在一次程序运行中都没有)

未定义的行为不应与未指定的行为(或同义词,实现定义的行为)混淆:在这种情况下,给定输入的行为已定义良好,但它留给编译器供应商来定义一些给定的合理限制内的行为。

示例:负整数的右移 - 它可以在有或没有符号扩展的情况下发生(因此可以是算术或逻辑移位)。尽管标准没有规定哪一个适用,但在负整数上使用右移是明确的。

+0

关于旁边:我问,因为它似乎是一个合理的优化可能是放弃第一个空终止符后的一切 - 就像它可以指定,但我不知道它是否是。 [当然,我试着在Godbolt上编译,并注意到实际发生的情况] – wrhall

+0

“在第一个空终止符后下降” - 不是100%你的意思是 - 数组在那里,它有一个固定的大小,而且你不能释放它的一部分。如果你的意思是“为了不同的目的而重用” - 你完全可以自由地做到这一点 - 只要确保你没有超出数组的边界...... – Aconcagua

+0

对不起 - 它似乎是一个合理的*编译器*优化可能是在空终止符之后放下所有东西......即不在空终止符之外分配一个数组。 – wrhall