C sprintf ломается с байтовыми параметрами (компилятор Keil)

У меня есть код, работающий в двух проектах / платформах. Он работает в одном, а не в другом. Код такой:

uint8_t val = 1;
uint8_t buff[16];
sprintf(buff, "%u", val);

Ожидаемый результат - «1» (gcc), но в одном компиляторе (Keil) он возвращает «511», что в шестнадцатеричном формате равно 0x1FF. Похоже, этот компилятор не продвигает байт в int. Это подтверждается, потому что все работает нормально, если я сделаю следующее:

sprintf(buff, "%u", (int)val);

У меня такой вопрос: почему один компилятор делает то, что я считаю «правильным», а другой - нет? Это мои неверные ожидания / предположения, настройка компилятора или что-то еще?


person radsdau    schedule 18.09.2015    source источник
comment
Попробуй sprintf(buf,"%hhu",val);, работает?   -  person Alex Lop.    schedule 18.09.2015
comment
ИМО, в компиляторе Keil есть ошибка. Вы должны проверить, известно ли это. Если нет или если вы не можете сказать, вам следует сообщить об этом. Аргумент val находится в вариативной части списка аргументов и должен быть повышен до int перед передачей. Я не думаю, что 0x1FF - правильный способ продвижения байта.   -  person Jonathan Leffler    schedule 18.09.2015
comment
@AlexLop .: Я чувствую, что (а) это, вероятно, не сработает, и (б) если это сработает, это, в лучшем случае, обходной путь, а не требуемое поведение. uint8_t должен быть повышен чисто до int в вызове. Даже если int является 16-битным типом, я не вижу возможности для 0x1FF быть допустимым продвижением значения 1.   -  person Jonathan Leffler    schedule 18.09.2015
comment
@JonathanLeffler да, это возможная работа, и, вероятно, это ошибка компилятора, НО это также может быть вызвано некоторым неопределенным поведением в других частях кода. Если мы увидим весь код, мы сможем лучше понять такое поведение.   -  person Alex Lop.    schedule 18.09.2015
comment
@AlexLop Не уверен, что сюда можно добавить какой-либо другой контекст. Я тоже думаю, что это ошибка компилятора. Не удалось попробовать этот вариант; может добраться до него на следующей неделе, когда проект будет запущен. Спасибо.   -  person radsdau    schedule 18.09.2015
comment
Давным-давно я использовал версию компилятора Keil C51, которая некорректно выполняла продвижение аргументов по умолчанию. Вы должны были написать sprintf(buff, "%u", (int)val);. Это, конечно, ошибка компилятора, и довольно вопиющая.   -  person M.M    schedule 18.11.2015


Ответы (2)


Ваше предположение может быть правильным или неправильным. Это зависит от реализации компилятора. Все современные (или, лучше сказать, умные) компиляторы будут делать то, что вы упомянули. Но Кейл, по вер. 9.02, вам нужно указать правильную переменную длину для printf.

Это способ Keil C обрабатывать все виды функций printf. Вам нужно точно указать, сколько это длится. Все обычные для 16-битных (беззнаковых) целых чисел, включая% d,% x и% u. Используйте модификатор «b» для 8-битного и «l» для 32-битного. Если вы указали неправильную длину, вы получите неправильный номер. Хуже того, все остальные переменные неверны. Например, чтобы использовать 8-битный char, вы используете% bd (% bu и% bx) и% ld,% lu и% lx для 32-битного long.

char c = 0xab;
printf("My char number is correctly displayed as '0x%02bx'\n", c);

Также обратите внимание, что для получения числовых данных из sscanf это то же самое. Следующий пример - получить 32-битную длинную переменную с помощью sscanf:

long var;
char *mynum = "12345678";
sscanf(mynum, "%ld", &var);

Переменная var содержит номер 12345678 после sscanf. Ниже показана длина переменных, используемых в семействе printf для Keil.

% bd,% bx,% bu - следует использовать для 8-битных переменных

% d,% x,% u - следует использовать для 16-битных переменных, и

% ld,% lx,% lu - следует использовать для 32-битных переменных

person Tim    schedule 06.11.2015
comment
Спасибо, Тим. Мне совсем не нравится план Кейла, но хорошо об этом знать. Полагаю, это имеет смысл для встраиваемых в младенцев микросхем с ограниченными ресурсами. - person radsdau; 11.11.2015
comment
Можете ли вы обосновать свою благодарность, отметив ответ? - person Tim; 12.11.2015
comment
Выполнено. Можете ли вы отредактировать sprintf, который должен быть sscanf? Я внес правку, но она была отклонена, не знаю почему. sprintf (mynum,% ld, var); должно быть sscanf (mynum,% ld, & var); и т.п. - person radsdau; 17.11.2015
comment
Спасибо за информацию. Не удалось с первого раза. Снова поменял и сделал. - person Tim; 18.11.2015

Для максимальной переносимости можно использовать эти макросы из inttypes.h: (есть и другие)

PRId8, PRIx8, PRIu8 PRId16, PRIx16, PRIu16 PRId32, PRIx32, PRIu32

Обычно (как я и ожидал):

#define PRIu8 "u"

Но для компилятора Keil в этом случае:

#define PRIu8 "bu"

e.g.,

printf("0x%"PRIx8" %"PRIu16"\n", byteValue, wordValue);

Хотя это довольно громоздко. Предлагаю более дружелюбные компиляторы.

Удивительно, что вы не знаете об этом даже после десятилетий.

person radsdau    schedule 17.11.2015