不同操作系统/编译器之间的C风格字符串输出不一致
Inconsistent C style string output between different operating systems / compilers
我有一个C++程序:
#include <iostream>
char * foo (char * bar, const char * baz) {
int i = -1;
do {
i++;
*(bar + i) = *(baz + i);
} while (*(baz + i));
return bar;
}
int main (int argc, char *argv[]) {
char bar[] = "";
char baz[] = "Hello";
foo(bar, baz);
std::cout << "bar: " << bar << std::endl;
std::cout << "baz: " << baz << std::endl;
}
这并不是重要的部分,但这个程序的要求是使用指针将一个C风格的字符串复制到另一个。
当我在Ubuntu 16.04桌面上编译和执行二进制文件时,我看到的是:
$ g++ -std=c++11 test.cpp -o test && ./test
bar: Hello
baz: ello
Egad!baz
的初始'H'
已经被删除,但我根本看不到我的foo
函数是如何更改baz
的。嗯…
我的Ubuntu桌面上的g++版本是这样的:
$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
我认为这是我的代码中的一个错误或bug(现在可能仍然如此(,但我发现当我在任何其他操作系统上编译和运行时,我会得到不同的行为。
以下是macOS上的输出:
$ g++ -std=c++11 test.cpp -o test && ./test
bar: Hello
baz: Hello
这是macOS笔记本电脑上的g++版本:
$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.0 (clang-1200.0.32.2)
Target: x86_64-apple-darwin19.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
当在其他Linux盒子、Windows等上测试时,它具有正确的、预期的bar
和baz
输出,两者都是Hello
。
发生了什么事!?
tl;drC++程序在我的桌面上输出的C风格字符串与其他任何计算机都不同。为什么?
char bar[] = "";
这保证了创建一个一字节长的内存区域(基本上刚好足够容纳' '
(。实现可能会给你更多,但你不能依赖它。
因此,它不够大,无法存储需要六个字节的字符串"Hello"
。例如,C++20 [expr.add]
中介绍了这一点,我还强调了一点:
如果表达式
P
指向具有n
元素的数组对象x
的元素x[i]
,则表达式P + J
和J + P
(其中J
具有值j
(如果0 <= i + j <= n
则指向(可能假设的(元素x[i + j]
否则,行为未定义
如果您想确保这个代码段中有足够的空间,您可以将声明更改为:
char baz[] = "Hello";
char bar[sizeof(baz)]; // bar will be same size as baz
对于其他场景,有不同的方法来保证这个大小,但一般规则仍然相同:确保目标数组足够大,这样你就不会写超出它的末尾。
尽管未定义的行为意味着任何事情都可能发生,但在错误情况下最有可能发生的是堆栈上的以下内存布局。将字符从baz
一个接一个地复制到bar
(其中$
表示