Скетч из статьи обещал управляемый ночник за десять минут. Я скопировал его в среду, нажал «Проверить» — и красная строка указала туда, где всё выглядит идеально: Serial.print("Ночник вкл");. Глаза видят ровную строку, а компилятор видит байт, которого не бывает в коде. Так я познакомился с самозванцами: символами, которые на экране неотличимы от честных, а байтом — совсем другие. Строка ниже собиралась трижды и трижды падала, пока я не перепечатал кавычки руками.
Проблема не в вашей невнимательности: текст между сайтом и вашим редактором проходит через вёрстку, автозамену телефона и мобильный буфер, и каждый слой подсовывает свои «красивые» знаки. Этот урок — про то, как опознать пятерых частых самозванцев, что именно печатает компилятор на каждый и как чистить чужой код за минуту, а не за вечер. Если же красная строка ругается на отсутствующий заголовок, а не на странный символ, — это другая история, библиотечная, её разбирает урок про библиотеки в IDE.
Пятеро самозванцев и что скажет компилятор
Первые двое — ёлочки-кавычки « и » вместо прямых двойных. В UTF-8 каждая — два байта, и компилятор отдаёт сообщение вида stray '\302' in program: слово stray, а число — код байта в восьмеричном виде; 302 — это первый байт ёлочки. Однажды я видел и парные апострофы вместо одинарных — тот же сюжет с тем же словом в ответе. Запомнить стоит одно: любое stray с числом — не про логику, а про лишний байт, который редактор честно показывает, а вы не видите.
Третий — длинное тире — вместо дефиса-минуса в формулах: a — b выглядит как вычитание, а собирается в строку про неожиданный символ, ведь на клавиатуре минус и тире живут на одной кнопке, но в Unicode — на разных. Четвёртый коварнее всех — неразрывный пробел из вёрстки: глазами он пробел, а байтом — 0xC2 0xA0, и он запросто разрывает имя переменной посередине. Пятый — многоточие … вместо трёх точек: один символ вместо трёх, и снова stray.
Инструменты опознания простые. Подсветка синтаксиса красит строковые литералы иначе, чем код, — самозванец внутри литерала виден по цвету. Ctrl+F с самозванцем, скопированным из статьи, показывает все его гнёзда за секунду. Мой же приём номер один — перепечатать проблемную строку руками, не глядя на оригинал: десять секунд, надёжнее любого сравнения на глаз. Автоформат тут бессилен — он двигает отступы и не трогает байты, границу его работы я разбирал в уроке про форматирование кода. Отдельно скажу: кириллица внутри кавычек легальна — печатать по-русски можно, а вот совпадать по кодировкам с монитором это всё равно обязывает, о том был урок про кириллицу и кодировки.
// Печатает каждый символ строки и его код в HEX и DEC.// Если вместо " вы скопировали ёлочку, коды скажут правду:// у " код 0x22, у « - пара байт 0xC2 0xAB.const char* STROKA = "Nocnik: ON"; void setup() { Serial.begin(9600); Serial.print("stroka: "); Serial.println(STROKA); Serial.println("simvol | hex | dec"); for (int i = 0; STROKA[i] != 0; i++) { Serial.print(" '"); Serial.print(STROKA[i]); Serial.print("' | "); Serial.print(STROKA[i], HEX); Serial.print(" | "); Serial.println(STROKA[i], DEC); }} void loop() {}Порядок чистки, который выработался у меня за десяток чужих скетчей: найти первую красную строку и читать лог по правилам из урока про ошибки компиляции, перепечатать её целиком руками, снова нажать «Проверить». Одна правка за раз, цикл в пять-семь секунд — самозванцы редко живут по одному, и штурм всех сразу путает следы. Фильтр первого прохода — вставка в блокнот без форматирования: чистый текстовый редактор сглатывает вёрстку, и половина самозванцев умирает ещё до среды.
Мнение, за которое меня ругают ленивые ученики: чужой код со статьи в проект перепечатывают руками, а не копируют. Копипаст экономит четыре минуты и возвращает их вечерними падениями на ровном месте; перепечатывание заодно заставляет прочитать строку — и половина вопросов «а что тут вообще происходит» снимается до первой сборки. В симуляторе ниже — модель: пять самозванцев, их байты и честная чистая версия строки.
| Самозванец | Как выглядит | Что на самом деле | Ответ компилятора |
|---|---|---|---|
| « и » | обычные двойные кавычки | два байта UTF-8 вместо одного | stray '\302' in program |
| — (тире) | минус в формуле | другой символ, не дефис | stray '\342' in program |
| неразрывный пробел | обычный пробел | пара байт 0xC2 0xA0 | неожиданный символ в выражении |
| … (многоточие) | три точки | один символ вместо трёх | stray с другим числом |
- 1. Что означает строка stray '\302' in program?
- 2. Что делает автоформат Ctrl+T с ёлочкой внутри строки?
- 3. Чем опасен неразрывный пробел из вёрстки?
- 4. Строка перепечатана, а сборка всё ещё падает. Где искать дальше?
- stray с числом — не про логику: компилятор называет чужой байт в восьмеричном виде.
- Пятёрка частых самозванцев: ёлочки, длинное тире, неразрывный пробел, многоточие, парные апострофы.
- Автоформат байты не трогает; опознанию помогают подсветка, Ctrl+F и перепечатывание строки руками.
- Красная строка — место огорчения компилятора: битый байт ищите строкой выше, включая комментарии.
- Кириллица в кавычках легальна; кодировки монитора — отдельная история со своей сверкой.
Частые вопросы по теме урока
- Что означает строка stray '\302' in program?
- Stray значит «чужой»: компилятор встретил байт вне языка. Число — код этого байта в восьмеричном виде, у ёлочки это первый байт пары. компилятор наткнулся на байт, которого не ждёт в коде
- Что делает автоформат Ctrl+T с ёлочкой внутри строки?
- Автоформат правит пробелы и отступы, а не содержимое литералов. Самозванца убирают перепечатыванием или заменой через Ctrl+F. ничего: он двигает отступы и не трогает байты
- Чем опасен неразрывный пробел из вёрстки?
- Байтом это 0xC2 0xA0, а не пробел 0x20. Компилятор видит разорванное имя и отвечает на неожиданный символ в выражении. он разрывает имя переменной, будучи на вид обычным пробелом
- Строка перепечатана, а сборка всё ещё падает. Где искать дальше?
- Красная строка показывает, где компилятор огорчился, а не где лежит битый байт. Самозванец нередко живёт строкой выше — даже в комментарии. в предыдущих двух-трёх строках, включая комментарии