使用 %s 调用 printf 并传递零长度的字符*是未定义的行为吗?

Is it undefined behavior to call printf with %s and pass a zero-length char*?

本文关键字:字符 未定义 printf 调用 使用      更新时间:2023-10-16

以下代码中的第三行是否定义良好?

char* result = new char[0];                                                                                                                                                                                                                    
printf("%dn", strlen(result));                                                                                                                                                                                                                                                        
printf("%sn", result);                                                                                                                                                                                                                                                                
delete[] result;

当我运行代码时,我得到预期的输出(长度为 0,后跟打印的两个换行符(。但是,我对这是否是一个明确定义的行为或我只是幸运没有信心。

对三线的呼叫是否定义明确?

简短回答:这是未定义的行为

长答案:在C++中,分配大小0数组将生成指向没有元素的数组的有效指针。 从标准(取自这个答案(:

从 5.3.4/7

当直接新声明符中的表达式值为零时,将调用分配函数来分配没有元素的数组。

从 3.7.3.1/2

取消引用作为零大小请求返回的指针的效果是未定义的。

(强调我的(

这意味着无法正确读取(或写入(从new T[0]请求返回的指针。

字符串格式"%s"的strlenprintf都被定义为处理以特殊NUL字符结尾的字符串。它们需要从提供的指针读取字符序列,以尝试找到此NUL字符才能正常运行(这会导致 UB,因为这需要取消引用指针(。这些行为在 C 标准中定义,因为C++标准将大多数 C 库类型/函数的定义委托回 C 标准。

%sprintf访问权限定义为执行以下操作:

来自 C11 标准 §7.21.6.1/6

如果不存在 l 长度修饰符,则参数应是指向字符类型数组的初始元素的指针。

数组中的字符将写入(但不包括(终止空字符。如果指定了精度,则写入的字节数不超过该字节数。如果未指定精度或大于数组大小,则数组应包含空字符。

这需要访问数组(这将是 UB,因为指针对取消引用无效(

奖金

由于使用了strlen,您的示例代码实际上是在第二行引入 UB,原因与上述类似。

strlen定义为执行以下操作:

来自 C11 标准 §7.24.6.3/3:strlen函数

返回

strlen 函数返回终止空字符前面的字符数。

这是 UB,原因与使用printf相同。

很抱歉回答了您的"原始"问题(在您编辑之前(:

C呢?

在 C 中,您没有new.

然而:

strlen对数组中的字符进行计数,直到找到NUL字符。

printf(%s)将打印数组中的字符,直到找到NUL个字符。

如果您有本机编译器并且数组不包含NUL字符,则这两个命令将在数组结束后继续搜索NUL字符。

例:

char a[6]="Hello ";
char b[100]="world!";
char c[100]="John!";
printf("%sn",a);

如果编译器将数组b放在数组之后的内存中,则a此示例将打印"Hello world!

但是,如果编译器决定在a之后放置c,程序将打印"Hello John!

如果使用可以检测数组外部访问的编译器(例如 .NET 的C++编译器(,则当到达数组末尾并且没有NUL字符或数组末尾甚至会被视为NUL字符时,您将收到错误。

总而言之,你可以说:根据编译器的不同,当你将数组传递给printf(%s)时,当它不包含NUL字符时,你会有不同的行为。

这就是我所说的未定义行为...

我不知道C++中的new char[0]如何表现,但我认为与 C 没有区别......