ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Вторник
27 января
1567449 Топик полностью
VLLV (Сегодня, 18:05, просмотров: 55) ответил SciFi на "Насколько эти предупреждения несут гарантированные проблемы?" Буквальный ответ: нет гарантированных проблем. Просто с функциями без объявлений компилятор не отслеживает правильность типов аргументов и возвращаемых значений. Потенциальные указатели без нужного выравнивания, даже если среди них окажется реально неровный указатель, не на всех процах вызывают проблемы. Армянское радио ответит лучше:
Румынское радио подтянулось... По рекомендации включил Require prototypes = сотни ошибок, намного больше, чем было предупреждений. 

В среде IAR Embedded Workbench для ARM (как и в любом стандартном C компиляторе), отсутствие прототипов функций — это очень плохая практика, которая может привести к трудноуловимым ошибкам.

Исторически в стандарте C89/C90, если компилятор встречал вызов функции без предварительного объявления (прототипа), он делал неявное объявление (implicit declaration).

Вот список конкретных проблем, которые возникают из-за этого механизма в контексте ARM и IAR:

1. Предположение о возвращаемом типе int

Если прототипа нет, компилятор считает, что функция возвращает тип int.

Проблема: Если ваша функция на самом деле возвращает float, double, структуру или void, то вызывающий код будет интерпретировать результат неправильно.

Пример: Функция возвращает float (значение 3.14). Компилятор думает, что это int. В памяти представление float (IEEE 754) и int совершенно разное. Вы получите "мусорное" огромное целое число вместо 3.14.

2. Неправильная передача аргументов (ABI и регистры)

ARM использует соглашение о вызовах AAPCS (Procedure Call Standard for the Arm Architecture). Без прототипа компилятор не знает, какие типы данных ожидает функция, и применяет стандартные правила продвижения типов (default argument promotions):

float преобразуется в double.

char и short преобразуются в int.

Сценарий катастрофы:

Допустим, у вас есть функция: void motor_set_speed(float speed);

Вы вызываете её без прототипа: motor_set_speed(10.5f);

Вызывающий код (без прототипа): Видит аргумент float. По правилам varargs (так как типы неизвестны) продвигает его до double (8 байт). Кладет это значение в пару регистров (например, R0 и R1) или в регистры FPU для double (D0).

Вызываемая функция (реализация): Ожидает float (4 байта). Она будет читать значение из регистра для float (S0) или, если FPU выключен, только из R0.

Итог: Функция получит совершенно неверные данные, мотор может включиться на полную мощность или вести себя непредсказуемо.

3. Проблемы с использованием FPU (Floating Point Unit)

На процессорах Cortex-M4F, M7 и т.д. (где есть Hard FPU), передача чисел с плавающей точкой зависит от того, знает ли компилятор типы аргументов.

С прототипом: float передается через специальные регистры FPU (S0, S1...).

Без прототипа: Компилятор может передать значения через регистры общего назначения (Core registers R0-R3), так как он предполагает совместимость со старым кодом или variadic функциями.

Результат: Функция ищет данные в регистре FPU (S0), а вы положили их в обычный регистр (R0). Программа не упадет, но вычисления будут с мусором.

4. Несовпадение количества аргументов

Без прототипа компилятор не может проверить, передали ли вы правильное количество аргументов.

Функция: void update_lcd(int x, int y, int color);

Вызов: update_lcd(10, 20); // Забыли цвет

Что произойдет: Функция возьмет значение для color из регистра R2 (по стандарту ARM). В R2 в этот момент лежит случайное значение, оставшееся от предыдущих операций. Экран зальется случайным цветом или программа уйдет в HardFault, если этот мусор будет использован как указатель.

5. Риск при переходе на 64-битные архитектуры (ARM64)

Хотя Cortex-M обычно 32-битные, если вы пишете под ARM64 (Aarch64), проблема усугубляется.

Если функция возвращает указатель (void*), а компилятор думает, что она возвращает int (обычно 32 бита), то старшие 32 бита адреса будут отсечены. Это гарантированный Segmentation Fault (или HardFault) при попытке разыменования такого указателя.

6. C99 и новее

Начиная со стандарта C99, неявные объявления функций запрещены.

Если в настройках IAR стоит галочка "Require prototypes" (или выбран строгий режим C99/C11/C18), код просто не скомпилируется.

Если настройки "расслабленные" (по умолчанию IAR часто допускает расширения), вы получите только Warning. Игнорирование этого варнинга — частая причина багов в Embedded.

Резюме

В Embedded разработке (IAR/ARM) отсутствие прототипов приводит к рассинхронизации того, куда вызывающий кладет данные (какие регистры R0-R3, S0-S31, стек), и откуда вызываемый их забирает.

Рекомендация:


Всегда используйте заголовочные файлы (.h) с прототипами.

В настройках IAR включите опцию:

Project -> Options -> C/C++ Compiler -> Language 1 -> Require prototypes (Требовать прототипы). Это превратит потенциальные баги времени выполнения в ошибки компиляции.