char 数组中的终止符
Termination character in char array
我正在尝试在屏幕上打印一些字符。我使用了很多错误的实现方式。例如:
示例 1:
#include <stdio.h>
int main(int argc, char const *argv[]) {
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
char bye[5] = {'b', 'y', 'e', '\n', '[=10=]'};
char end[] = "end";
char dot = '.';
char oops[] = {'o', 'o', 'p', 's'};
printf("%s\n", hello);
printf("%s\n", bye);
printf("%s\n", end);
printf("%s\n", dot);
printf("%s\n", oops);
return 0;
}
输出 1:
问题:
为什么在bye
之前打印了dot
字符?
dot
字符后面的这些垃圾是什么?
例2(去掉dot
declaration/definition,打印):
#include <stdio.h>
int main(int argc, char const *argv[]) {
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
char bye[5] = {'b', 'y', 'e', '\n', '[=11=]'};
char end[] = "end";
char oops[] = {'o', 'o', 'p', 's'};
printf("%s\n", hello);
printf("%s\n", bye);
printf("%s\n", end);
printf("%s\n", oops);
return 0;
}
输出 2:
问题
垃圾还在!我对他们感觉很好,但为什么 end
被打印了两次?
我设法在应该使用终止符的时候正确地做到了,但是为什么我在打印时会出现这种不一致的情况?我来自 Java 背景,我已经感觉很奇怪了!
你得到奇怪输出的原因是你有未定义的行为。处理字符串的函数(包括使用 %s
格式说明符和 printf
打印它们)期望它们以 null 终止,否则你会得到未定义的行为。如果发生这种情况,则无法保证特定的输出。它可以按预期工作,它可以提供奇怪的输出,它可以使程序崩溃或做一些不同的事情。通常对于打印字符串,它会继续打印直到出现访问冲突并崩溃,或者直到遇到下一个空字节(这可能会发生得早而不是晚,并且直到它发生,它会打印 "rubbish").
这也是为什么改变看似无关的事物可以改变UB以不同方式表现出来的原因。由于Example 2的变化,栈看起来不一样了,当因为UB错误访问的时候,会访问到不同的东西。打印 hello
时示例 1 中打印的点可能实际上是 char dot = '.';
.
中的点
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
打印这是未定义的行为,因为它不是空终止的。
char bye[5] = {'b', 'y', 'e', '\n', '[=11=]'};
您可以将其打印为字符串,因为它以 null 结尾。
char end[] = "end";
由于您是使用字符串文字创建的,因此会自动添加空终止符;你可以打印出来。 char end[]
是 四个 个字符的数组:'e'
、'n'
、'd'
和 '[=23=]'
.
char dot = '.';
这是一个 char
,不是字符串。使用 %s
打印是未定义的行为,您的编译器可能会警告您。改为使用 %c
打印它。
char oops[] = {'o', 'o', 'p', 's'};
与第一个相同,缺少空终止符,因此用 %s
打印它是未定义的行为。
The rubbish are still there! I am feeling good for them but why the
end was printed twice?
编译器通常将程序的二进制文件中的所有字符串并排放置。因此,您的 end
字符串可能会紧跟在非空终止 oops
之后。所以在内存中它看起来像 oopsend[=30=]
,这就是为什么它可以打印 "oopsend"
(或者做一些完全不同的事情,因为它仍然是未定义的行为)。
一般来说,想知道为什么 UB 会做它所做的事情并没有什么好处,因为它不一致(每次你 运行 它可能会做一些不同的事情,在不同的 compiler/machine 上,或者工作直到你向其他人演示它然后突然使程序崩溃)。就当是不该做的错事吧
您的第一个字符串数组不是空终止的。
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
将非空终止的字符串传递给 printf(以及其他字符串相关函数,如 strlen、strdup、strcat 等)时,行为未定义。
它可能会在您的数组之后打印更多字节,直到它到达空(零)字节(就像您的情况一样,显示为垃圾),或者它可能会使程序崩溃。
要修复该行为,您需要所有字符串数组都以 null 结尾,即末尾有“\0”字符。
如果你像这样初始化一个字符串
char end[] = "end";
它自动以 null 终止并为声明的 char 数组分配正确的长度。
我正在尝试在屏幕上打印一些字符。我使用了很多错误的实现方式。例如:
示例 1:
#include <stdio.h>
int main(int argc, char const *argv[]) {
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
char bye[5] = {'b', 'y', 'e', '\n', '[=10=]'};
char end[] = "end";
char dot = '.';
char oops[] = {'o', 'o', 'p', 's'};
printf("%s\n", hello);
printf("%s\n", bye);
printf("%s\n", end);
printf("%s\n", dot);
printf("%s\n", oops);
return 0;
}
输出 1:
问题:
为什么在bye
之前打印了dot
字符?
dot
字符后面的这些垃圾是什么?
例2(去掉dot
declaration/definition,打印):
#include <stdio.h>
int main(int argc, char const *argv[]) {
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
char bye[5] = {'b', 'y', 'e', '\n', '[=11=]'};
char end[] = "end";
char oops[] = {'o', 'o', 'p', 's'};
printf("%s\n", hello);
printf("%s\n", bye);
printf("%s\n", end);
printf("%s\n", oops);
return 0;
}
输出 2:
问题
垃圾还在!我对他们感觉很好,但为什么 end
被打印了两次?
我设法在应该使用终止符的时候正确地做到了,但是为什么我在打印时会出现这种不一致的情况?我来自 Java 背景,我已经感觉很奇怪了!
你得到奇怪输出的原因是你有未定义的行为。处理字符串的函数(包括使用 %s
格式说明符和 printf
打印它们)期望它们以 null 终止,否则你会得到未定义的行为。如果发生这种情况,则无法保证特定的输出。它可以按预期工作,它可以提供奇怪的输出,它可以使程序崩溃或做一些不同的事情。通常对于打印字符串,它会继续打印直到出现访问冲突并崩溃,或者直到遇到下一个空字节(这可能会发生得早而不是晚,并且直到它发生,它会打印 "rubbish").
这也是为什么改变看似无关的事物可以改变UB以不同方式表现出来的原因。由于Example 2的变化,栈看起来不一样了,当因为UB错误访问的时候,会访问到不同的东西。打印 hello
时示例 1 中打印的点可能实际上是 char dot = '.';
.
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
打印这是未定义的行为,因为它不是空终止的。
char bye[5] = {'b', 'y', 'e', '\n', '[=11=]'};
您可以将其打印为字符串,因为它以 null 结尾。
char end[] = "end";
由于您是使用字符串文字创建的,因此会自动添加空终止符;你可以打印出来。 char end[]
是 四个 个字符的数组:'e'
、'n'
、'd'
和 '[=23=]'
.
char dot = '.';
这是一个 char
,不是字符串。使用 %s
打印是未定义的行为,您的编译器可能会警告您。改为使用 %c
打印它。
char oops[] = {'o', 'o', 'p', 's'};
与第一个相同,缺少空终止符,因此用 %s
打印它是未定义的行为。
The rubbish are still there! I am feeling good for them but why the end was printed twice?
编译器通常将程序的二进制文件中的所有字符串并排放置。因此,您的 end
字符串可能会紧跟在非空终止 oops
之后。所以在内存中它看起来像 oopsend[=30=]
,这就是为什么它可以打印 "oopsend"
(或者做一些完全不同的事情,因为它仍然是未定义的行为)。
一般来说,想知道为什么 UB 会做它所做的事情并没有什么好处,因为它不一致(每次你 运行 它可能会做一些不同的事情,在不同的 compiler/machine 上,或者工作直到你向其他人演示它然后突然使程序崩溃)。就当是不该做的错事吧
您的第一个字符串数组不是空终止的。
char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
将非空终止的字符串传递给 printf(以及其他字符串相关函数,如 strlen、strdup、strcat 等)时,行为未定义。 它可能会在您的数组之后打印更多字节,直到它到达空(零)字节(就像您的情况一样,显示为垃圾),或者它可能会使程序崩溃。
要修复该行为,您需要所有字符串数组都以 null 结尾,即末尾有“\0”字符。
如果你像这样初始化一个字符串
char end[] = "end";
它自动以 null 终止并为声明的 char 数组分配正确的长度。