Цифра после нулевого терминатора в C?

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 штука? Я не могу найти ничего об этом в Интернете. Как это работает?

Пожалуйста, прочитайте о «Escape-последовательности».

G.M. 11.04.2024 10:57

В двух словах \06\0\6!

Konrad Rudolph 11.04.2024 10:58

(или \0006/\x006, я думаю, вы это имели в виду.)

Ry- 11.04.2024 10:59

Также смутно stackoverflow.com/questions/4704440/escape-sequence-in-c, который конкретно касается C, а не C++.

tripleee 11.04.2024 11:18

Это означает, что ваш терминал использует старый набор символов DOS. en.m.wikipedia.org/wiki/Code_page_437

stark 11.04.2024 14:57

@Ry Шестнадцатеричные escape-последовательности не завершаются автоматически после двух шестнадцатеричных цифр, в отличие от восьмеричных escape-последовательностей, которые автоматически завершаются после трех восьмеричных цифр. "\0006" содержит нулевой терминатор, за которым следует цифра 6, но "\x006" содержит только символ ASCII ACK (код ASCII 6).

Ian Abbott 11.04.2024 17:17
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
107
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

\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.

Ian Abbott 11.04.2024 12:03
\0 и \<digits> неотличимы. В этом-то и дело. Это не значит, что «\0 на самом деле не имеет особого значения», что само по себе бессмысленно.
user207421 11.04.2024 12:04

@IanAbbott Ах да, похоже, это так, я, должно быть, путаю это с шестнадцатеричными escape-последовательностями.

Lundin 11.04.2024 12:31

Восьмеричный escape-код не только имеет не более трех цифр (C17 6.4.4.4/1), но также имеет самую длинную последовательность цифр, которая может составлять восьмеричный escape-код (C17 6.4.4.4/7). Итак, в конце концов, довольно четко определено. (Примечание: эти ссылки относятся к разделу о символьных константах, но раздел 6.4.5 о строковых литералах ссылается на те же положения, используя ту же самую последовательность escape-последовательностей в своей формальной грамматике).

John Bollinger 11.04.2024 19:03

\NNN (где N — целое число от 0 до 7) в строковом литерале представляет один байт, восьмеричное представление которого — NNN. После \ может быть 1, 2 или 3 цифры. \0 в строковом литерале интерпретируется компилятором как байт со всеми 0 битами, а \06 интерпретируется как байт с 2 установленными битами. (0000 0110). Символы, которые вы видите в выводе, — это всего лишь глифы, которые ваш терминал использует для представления этих байтов.

Было бы яснее сказать: «\0, за которым сразу не следует другая восьмеричная цифра, интерпретируется компилятором как байт со всеми нулевыми битами». Также может быть полезно заметить, что \00 и \000 означают одно и то же при одних и тех же обстоятельствах, и \000 даже тогда, когда за ним следует другая цифра.

John Bollinger 11.04.2024 20:18

Другие вопросы по теме

Похожие вопросы

Каков правильный ANSI-совместимый способ создания pthread из функции без параметров в C с использованием clang-10+?
Получение ошибки: переопределение структуры в программе .h
Как включить ss (еще одну утилиту для исследования сокетов) для просмотра IP-адреса присоединенной группы многоадресной рассылки на хосте Linux?
Понимание поведения рекурсивной функции C без оператора возврата
ISO_C_BINDING, вызов C из Фортрана
Компоновщик не находит реализацию, поскольку определение компилятора не оценивается в исходном коде
«Ошибка: ожидаемое выражение» при использовании логических коротких замыканий
Почему уже установленное значение сбрасывается на новое значение в условии цикла for в C?
«значение указателя используется там, где ожидалось число с плавающей запятой», но переменная имеет тип void *
Вычисление представления с фиксированной точкой (1 - SQRT(0,5)) с произвольным уровнем точности