Язык C: неинициализированные локальные переменные (arm-none-eabi-gcc)

Частичная инициализация структур Init в сгенерированном коде CubeMX довольно распространена:

static void MX_GPIO_Init(void) 
{

GPIO_InitTypeDef GPIO_InitStruct;

...

/*Configure GPIO pin : MY_PIN_13_Pin */ 
GPIO_InitStruct.Pin = MY_PIN_13_Pin; 
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; 
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; 
HAL_GPIO_Init(MY_PIN_13_GPIO_Port, &GPIO_InitStruct);

GPIO_InitStruct.Pull не учтено В соответствии со стандартом языка начальное состояние локальных переменных не определено, поэтому неназначенные поля структуры Init должны содержать мусор, который не может быть допущен ни одной программой.

Означает ли это, что локальные переменные в конце концов инициализируются нулями (исследования не поддерживают эту гипотезу; то есть мое исследование)? В случае этого конкретного набора инструментов, по крайней мере.

2 ответа

Да, в приведенном вами примере структура имеет автоматическое хранение, и ее явная инициализация не означает, что ее значение может быть не определено.

От стандарта C99 (6.7.8.10 - Инициализация);

Если объект, имеющий автоматическую продолжительность хранения, не инициализируется явно, его значение является неопределенным. [...]

В этом случае эти структуры будут помещены в стек и рассчитывать на то, что он (стек) будет инициализирован нулями до того, как main все еще будет недостаточно. Прежде всего, такая нулевая инициализация может происходить или не происходить в зависимости от реализации. Во-вторых, если у вас есть функции инициализации, вызываемые в цепочке, как это:

initA();
initB();

и рассматриваемая структура используется таким образом в initB(), тогда его неинициализированные поля будут содержать значения в стеке, которые ранее использовались initA() и, скорее всего, больше не будет нулей. Наконец, даже если ваш стек инициализируется нулями, он может измениться, если вы, например, переместите свой код в задачу FreeRTOS, и в этом случае это будет новое значение инициализированного стека: 0xA5A5A5A5 а не нули. Это может привести к внезапному сбою кода инициализации, даже если вы не внесли в него никаких явных изменений.

Вы можете инициализировать структуру нулями, например:

memset(&GPIO_InitStruct, 0, sizeof(GPIO_InitStruct));

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

Лично я бы предположил, что если бы код был сгенерирован таким образом, неинициализированные поля были бы не использованы, по крайней мере, в этой комбинации значений. Однако, посмотрев на источник HAL_GPIO_Init это выглядит как упущение, так как все области, включая неинициализированные Pull поле - утверждается, что оно находится в диапазоне допустимых значений в самом начале функции.

Для дальнейшей проработки - назад в "дни StdPeriph", присутствовали функции, которые выполняли именно то, что вы просили - инициализировали все поля структуры значениями по умолчанию, которые казались наиболее интуитивно понятными для данного периферийного устройства.

Например, в случае GPIO:

void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct)
{
  /* Reset GPIO init structure parameters values */
  GPIO_InitStruct->GPIO_Pin  = GPIO_Pin_All;
  GPIO_InitStruct->GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStruct->GPIO_Speed = GPIO_Speed_2MHz;
  GPIO_InitStruct->GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct->GPIO_PuPd = GPIO_PuPd_NOPULL;
}

Насколько мне известно, такие функции отсутствуют в текущей версии библиотеки HAL, поэтому у вас остается либо нулевая инициализация структур (что может привести к поведению, которое вы не ожидаете), либо инициализация всех других полей вручную (что означает некоторую работу на вашей стороне, вероятно, написание функций, аналогичных GPIO_StructInit выше).

Если структура имеет автоматическое время хранения и инициализация не указана, начальное содержимое ее полей будет неопределенным значением. Для полей, которые не имеют символьного типа, любая попытка использовать значение может вести себя непредсказуемым образом, за исключением того, что копирование структуры, имеющей значение Indeterminate в некоторых из ее полей, не будет иметь никаких необычных эффектов, кроме сохранения значения Indeterminate в соответствующих полях пункт назначения.

Если вы хотите установить по умолчанию ноль по умолчанию для инициализации автоматической структуры, проще всего это добавить ={0} после декларации. Обратите внимание, что такая форма будет работать практически для любого типа структуры, независимо от содержащихся в ней типов, поскольку буквенный ноль является допустимым значением инициализации для любого целочисленного типа, типа с плавающей запятой или типа указателя, а так как 0 будет применяться к первому члену любых вложенных структур или типов объединения. Единственная потенциальная проблема будет, если один из членов структуры является профсоюзом, чей первый член не является крупнейшим. В этом случае не будет никакой гарантии относительно инициализации любого хранилища, которое не используется первым членом объединения, но используется другими. В противном случае, используя {0} инициализация со структурой автоматической длительности будет инициализироваться значением по умолчанию, которое статическая структура получит при запуске программы.

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