TLDR: что-то вроде
char x[] = "some output \06 MORE OUTPUT";
puts(x);
дает неожиданные результаты.
Итак, у меня есть этот код:
#include <stdio.h>
int main(void)
{
char x[] = "some output \0 MORE OUTPUT";
puts(x);
return 0;
}
И он печатает: «какой-то вывод», что и ожидается, поскольку \0 означает конец строки. Но если я добавлю цифру сразу после \0, результат изменится. Итак, код
#include <stdio.h>
int main(void)
{
char x[] = "some output \06 MORE OUTPUT";
puts(x);
return 0;
}
отпечатки: немного вывода ♠ БОЛЬШЕ ВЫВОДА
Поэтому по какой-то причине, если я добавляю число после \0, то \0 больше не воспринимается как конец строки, и вместо этого печатается символ ♠.
Если я просто заменю \0 на \6, результат будет тот же. Если я сделаю это с другими цифрами, результат будет тот же, за исключением другого символа.
почему это? Что это за \6 штука? Я не могу найти ничего об этом в Интернете. Как это работает?
В двух словах \06 ≠ \0\6!
(или \0006/\x006, я думаю, вы это имели в виду.)
Также смутно stackoverflow.com/questions/4704440/escape-sequence-in-c, который конкретно касается C, а не C++.
Это означает, что ваш терминал использует старый набор символов DOS. en.m.wikipedia.org/wiki/Code_page_437
@Ry Шестнадцатеричные escape-последовательности не завершаются автоматически после двух шестнадцатеричных цифр, в отличие от восьмеричных escape-последовательностей, которые автоматически завершаются после трех восьмеричных цифр. "\0006" содержит нулевой терминатор, за которым следует цифра 6, но "\x006" содержит только символ ASCII ACK (код ASCII 6).





\0 на самом деле не имеет никакого особого значения, кроме нулевого символа. По соглашению мы пишем нулевые терминаторы, используя эту форму, поскольку это делает синтаксис похожим на другие escape-последовательности, такие как \n.
Но на самом деле \ <digits> — это так называемая восьмеричная escape-последовательность. Все, что начинается с \, вставляет в строку символ, соответствующий восьмеричному числу. 06 ACK или что-то в этом роде в ASCII, непечатаемый символ. \101 напечатает 'A' и так далее.
Там, где заканчивается escape-последовательность, на самом деле не так четко определено в C, компилятор может продолжать читать любые цифры, следующие за \, до тех пор, пока их можно использовать для формирования допустимого символа. Или до тех пор, пока не обнаружит другую escape-последовательность или конец строкового литерала. В случае восьмеричных escape-последовательностей допускается не более 3 цифр (но есть также шестнадцатеричные escape-последовательности \x, которые не содержат верхнего предела цифр).
Например, \08 приведет к нулевому терминатору, поскольку цифра 8 не может быть частью восьмеричного числа. И \101666 напечатает A666, поскольку допускается не более 3 допустимых восьмеричных цифр.
Чтобы вставить символ '6' после нулевого терминатора или другой восьмеричной escape-последовательности, проще всего просто завершить строковый литерал:
char x[] = "some output \0" "6 MORE OUTPUT"; // string literals will get concatenated into one
puts(x);
char* secret_message = x + 13;
puts(secret_message);
Выход:
some output
6 MORE OUTPUT
«Там, где заканчивается escape-последовательность, в C не очень четко определено». Восьмеричная escape-последовательность имеет не более 3 восьмеричных цифр, поэтому "\0112" будет таким же, как "\t2", а не "J" в ASCII.
\0 и \<digits> неотличимы. В этом-то и дело. Это не значит, что «\0 на самом деле не имеет особого значения», что само по себе бессмысленно.
@IanAbbott Ах да, похоже, это так, я, должно быть, путаю это с шестнадцатеричными escape-последовательностями.
Восьмеричный escape-код не только имеет не более трех цифр (C17 6.4.4.4/1), но также имеет самую длинную последовательность цифр, которая может составлять восьмеричный escape-код (C17 6.4.4.4/7). Итак, в конце концов, довольно четко определено. (Примечание: эти ссылки относятся к разделу о символьных константах, но раздел 6.4.5 о строковых литералах ссылается на те же положения, используя ту же самую последовательность escape-последовательностей в своей формальной грамматике).
\NNN (где N — целое число от 0 до 7) в строковом литерале представляет один байт, восьмеричное представление которого — NNN. После \ может быть 1, 2 или 3 цифры. \0 в строковом литерале интерпретируется компилятором как байт со всеми 0 битами, а \06 интерпретируется как байт с 2 установленными битами. (0000 0110). Символы, которые вы видите в выводе, — это всего лишь глифы, которые ваш терминал использует для представления этих байтов.
Было бы яснее сказать: «\0, за которым сразу не следует другая восьмеричная цифра, интерпретируется компилятором как байт со всеми нулевыми битами». Также может быть полезно заметить, что \00 и \000 означают одно и то же при одних и тех же обстоятельствах, и \000 даже тогда, когда за ним следует другая цифра.
Пожалуйста, прочитайте о «Escape-последовательности».