嵌入式零件终端UB之后,正在访问字符串的部分

Is accessing parts of a string after an embedded null terminator UB?

本文关键字:访问 字符串 之后 零件 UB 终端 嵌入式      更新时间:2023-10-16

如果我有一个嵌入式的null终结器[旁边:是ub吗?

#include <stdio.h>
const char foo[] = "abcdef";
int main() {
  printf("%s", foo+4);
  return sizeof(foo);
}

在记录中,它打印了您可能期望的内容:

def

嵌入式null不是未定义的行为。如果您使用期望字符串终止的功能,则可能是逻辑错误。但是,无论其内容如何

不过要观察的一件事:如果您尝试将此数据存储在std::string中(这是您应处理所有字符串的方式),

您存储字符串可能很重要。
std::string str1 = foo; //contents of str1 becomes "abc".
std::string str2 = std::string(foo, sizeof(foo)); //contents of str2 becomes "abcdef"

[dcl.init.string]状态

可以通过狭窄的字符串字面的字符串,char16_t字符串字面的字符串,char32_t string crinter,或宽字字符串字面或宽字字符串字面,或者是由狭窄的字符类型(3.9.1),char16_t数组,char32_t数组或WCHAR_T数组来初始化的括号中封闭的一个适当的字符串(2.14.5)。字符串值的连续字符字面初始化数组的元素。

强调矿山

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

实际上,嵌入式null的唯一问题是,当命中率且不会完全处理字符串时,任何C函数都将停止。您可以考虑使用没有这些问题的std::string

访问c字符串beyound终止null字符本se 永不是未定义的行为。尽管如此,我们 can 以这种方式产生未定义的行为,但出于完全不同的原因:

如果终止的null字符恰好位于为字符串保留的char数组中的最后一个位置,则如果我们访问字符串之后的字符串,我们会从其边界访问此基础数组。而且这种局外访问是真正产生未定义行为的原因...

编辑:

[一边:那是ub吗?]

ub,不确定的行为是无法定义的行为,因为没有有意义的行为。依靠未定义的行为可以导致任何事情,包括获得预期的结果,但可能会惨败(例如,在另一个平台上切换编译器版本,简单地重新编译后,即使只是重新启动一个和同一程序之后)。因此,依靠不确定行为的程序被认为不被很好地定义。

示例:指出指向已经删除的对象("悬挂指针")或接近问题的指针:从范围中访问数组(可能导致试图访问内存而不是访问当前的内存过程甚至不存在,但可以读取或( bad !!! )覆盖完全不同的对象的内存,该对象恰好位于给定的地址每当您的程序运行时,甚至不在一个单个程序中运行)。

不确定的行为不与未指定的行为(或同义词,实现行为)混合在一起:在这种情况下,给定输入的行为是充分定义的,但保留给编译器供应商来定义内部的行为有些给定合理的限制。

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