Я пытаюсь использовать va_arg для получения следующего аргумента в функции. Он хорошо работает для всех типов (включая char *), но char:

void test1(char t,...) {
  va_list args;
  va_start(args, t);
  if(t=='c') Serial.println(va_arg(args, char));
  else if(t=='n') Serial.println(va_arg(args, int));
  va_end(args);  
}

Контрольная работа:

int n = 42;
char c = '?';

test1('n', n); // prints 42
test1('c', c); // prints nothing!

Можете ли вы проверить / объяснить это? Код работает на Arduino Uno, 9600 бод.

1
Jan Turoň 12 Фев 2020 в 03:00

2 ответа

Лучший ответ

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

6
supercat 12 Фев 2020 в 00:06

Функции Variadic имеют специальное правило для неявного продвижения типов, известное как продвижение аргументов по умолчанию .

C17 6.5.2.2/7

Многоточие в объявлении прототипа функции останавливает преобразование типа аргумента после последнего объявленного параметра. Повышение аргументов по умолчанию выполняется на конечных аргументах.

Многоточие является ...

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

C17 6.5.2.2/6

Если выражение, обозначающее вызываемую функцию, имеет тип, который не содержит прототип, целочисленные преобразования выполняются для каждого аргумента, а аргументы с типом float повышаются до double. Они называются продвижения аргументов по умолчанию .

В вашем случае char считается целочисленным типом, а приведенное выше означает, что он получит целое число повышенных при переходе к функции с переменным значением.

Двоичный код означает, что ASCII ? = 0x3F повышен до int. AVR использует 16-битный порядок байтов, поэтому он сохраняется в памяти как 0x3F 0x00. Проблема не в этом.

Скорее, когда вы пытаетесь использовать va_arg для неправильного типа, вы вызываете неопределенное поведение. Это указано в документации для va_arg:

C17 7.16.1.1/2

Если фактический следующий аргумент отсутствует или тип не совместим с типом фактического следующего аргумента (как продвигается в соответствии с продвижением аргумента по умолчанию), поведение не определено

Таким образом, единственно возможное решение - изменить код на if(t=='c') Serial.println(va_arg(args, int));.


Независимо от вашего вопроса, использование функций variadic и stdio.h на 8-битном MCU - очень плохая практика. Эти функции не только опасны, они также потребляют много флэш-памяти и оперативной памяти.

Кроме того, для встроенных систем вы всегда должны использовать stdint.h вместо типов по умолчанию C.

1
Lundin 12 Фев 2020 в 11:33