sscanf, похоже, ведет себя по-разному с переменными uint и переменными int в c

Я разрабатываю программу для микроконтроллера и хочу читать цифры из строки в целочисленные переменные, используя sscanf(). Проблема в том, что я получаю другое поведение при использовании объявлений int по сравнению с uint8_t или uint16_t.

Это использует int для объявления переменной:

 #include <stdio.h>
 #include <stdint.h>
 int main()
 {
     int power;
     int hr;
     int cadence;
     sscanf("204 67 94","%d %d %d",&power, &hr, &cadence);
     printf("power: %d, hr: %d, cadence: %d", power, hr, cadence);        
     return 0;
 }

и при вводе в https://www.onlinegdb.com/ возвращает:

power: 204, hr: 67, cadence: 94

...Program finished with exit code 0
Press ENTER to exit console.

Вот как я ожидаю, что он будет вести себя.


Принимая во внимание, что использование целых чисел uint8_t и uint16_t:

#include <stdio.h>
#include <stdint.h>
int main()
{
    uint16_t power;
    uint8_t hr;
    uint16_t cadence;
    sscanf("204 67 94","%d %d %d",&power, &hr, &cadence);
    printf("power: %d, hr: %d, cadence: %d", power, hr, cadence);
    return 0;
}

при вводе в https://www.onlinegdb.com/ выдает:

power: 0, hr: 67, cadence: 94

...Program finished with exit code 0
 Press ENTER to exit console.

Итак, по какой-то причине мощность (или, в более общем смысле, первая переменная) установлена ​​​​на 0. Может ли кто-нибудь указать мне правильное направление и объяснить, что я делаю неправильно?


person vbretsch    schedule 21.01.2018    source источник
comment
Настоящая проблема здесь заключается в том, что вы не запустили предупреждения компилятора достаточно высоко, чтобы он это уловил.   -  person o11c    schedule 21.01.2018
comment
Настоящая проблема заключается в том, что вы не читали справочную страницу — см. scanf   -  person Ed Heal    schedule 21.01.2018
comment
Функции XXXscanf() и XXXprintf() дают неопределенное поведение, если спецификатор формата не соответствует типу соответствующей переменной. %d задает целочисленный тип SIGNED, а предоставление беззнаковой переменной любого размера для соответствующего параметра приводит к неопределенному поведению.   -  person Peter    schedule 21.01.2018


Ответы (4)


Правильный спецификатор формата будет

scanf("%" SCNu16, &power);

Использование неправильного спецификатора формата является неопределенным поведением. Вы можете сделать то же самое и для sscanf.

sscanf("204","%"SCNu16 ,&power);

Точно так же для uint8_t это будет SCNu8. Точно так же для печати вы должны использовать правильный -

printf("%" PRIu16 "\n", power);

ТАКЖЕ скомпилируйте свой код с включенными предупреждениями -gcc -Wall -Werror progname.c. Предупреждения, которые вы видите - постарайтесь их устранить. Это поможет вам от больших проблем. Проверьте руководство для sscanf, чтобы узнать, что они что-то возвращают - проверьте его, чтобы узнать, был ли вызов sscanf успешным или нет.

person user2736738    schedule 21.01.2018

Во втором примере вы используете неправильные форматы для типов переменных.

Вы должны использовать макросы из <inttypes.h>:

 SCNu8   --> uint8_t
 SCNu16  --> uint16_t

Вы, вероятно, были бы в безопасности с %hhu для uint8_t и %hu для uint16_t.

У вас странное поведение, потому что вы солгали своему компилятору; вот как он получает обратно. Не лгите своему компилятору!

person Jonathan Leffler    schedule 21.01.2018

Спецификатор формата %d требует указателя на int. Если вы используете %u, для беззнакового значения вы должны использовать unsigned int. Вы не можете использовать другие типы, такие как uint16_t или uint8_t. Ну действуй:

unsigned int power;
unsigned int hr;
unsigned int cadence;
sscanf("204 67 94","%d %d %d",&power, &hr, &cadence);
person Greg Hewgill    schedule 21.01.2018
comment
Насколько важно использование %d вместо %u? Это не идеально; насколько это серьезно? - person Jonathan Leffler; 21.01.2018
comment
Вероятно, это не имеет большого значения, но, конечно, %d правильно просканирует отрицательное число и поместит значение в unsigned int, которое на самом деле не является отрицательным. - person Greg Hewgill; 21.01.2018
comment
Хммм… C11 7.21.6.2 fscanf() говорит: d — Соответствует необязательному десятичному целому числу со знаком, формат которого совпадает с ожидаемым для последовательности субъекта функции strtol со значением 10 для базового аргумента. Соответствующий аргумент должен быть указателем на целое число со знаком. Спецификация для u аналогична и определяет «указатель на целое число без знака». Строго говоря, вы на тонком льду. Практически, может быть, не так много. (Интересно, что за часть спецификации «при отсутствии модификатора размера»?) - person Jonathan Leffler; 21.01.2018

Решено! Спасибо.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main()
{
    uint16_t power;
    uint8_t hr;
    uint16_t cadence;
    sscanf("204 67 94","%"SCNu16 "%"SCNu8 "%"SCNu16,&power, &hr, &cadence);
    printf("power: %"PRIu16 " hr: %"PRIu8", cadence: %"PRIu16, power, hr, cadence);
    return 0;
}

Это сделало это и выдало результат, как и ожидалось. Дальнейшие поиски привели меня к https://stackoverflow.com/a/23748311/9246687, что дополнительно помогло.

person vbretsch    schedule 21.01.2018
comment
Добро пожаловать в Stackoverflow! Вы, конечно, можете сами ответить на свой вопрос, но это не обязательно. Если вы получили ответ, который решил проблему, вы обычно должны принять этот ответ, чтобы указать, что вопрос был решен для вашего удовлетворение. Вам также следует подумать о голосовании за ответы, которые вы сочли полезными, возможно, включая ответ, который вы приняли. - person rici; 21.01.2018