Printf выполняет неявное приведение

Почему в приведенном ниже фрагменте кода мы получаем результат 600? Два вопроса помогут мне понять поведение.

  1. Я указал тип переменной b как uint8_t, почему математическая операция не ограничивается одним и тем же типом?
  2. Я думал, что PRIu8 - правильный тип для печати uint8_t, почему это не работает?

Я надеялся получить ответ 88, который является результатом зацикливания за пределами диапазона uint8_t.

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

void main()
{

    uint8_t b = 200;
    printf("%" PRIu8 "\n",b+b+b);
    printf("%" PRIu8 "\n",3*b);

}

gcc версии 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)

Где-то наверняка есть обман, но, пожалуйста, прочитайте о неявных преобразованиях , в частности о целочисленном продвижении и обычных арифметических преобразованиях. Если вы хотите, чтобы результат был uint8_t, вам нужен слепок: godbolt.org/z/rjxejqrEb

Bob__ 15.04.2023 00:03

Это связано с продвижением аргументов по умолчанию.

Barmar 15.04.2023 00:03

Как упоминалось другими, существуют неявные преобразования (например,) ваш b будет преобразован в int или unsigned int. Вы можете получить ожидаемый результат с помощью: `printf("%u\n",(uint8_t) (b+b+b));

Craig Estey 15.04.2023 00:08

@Bob__ Я искал «целочисленное продвижение по умолчанию printf», но не смог найти для этого подходящего дубликата.

Barmar 15.04.2023 00:09

@ niil87 MS Visual Studio выводит 88.

Vlad from Moscow 15.04.2023 00:16

@Barmar Ну, есть это, которое близко.

Bob__ 15.04.2023 00:17

@Bob__ Это один из тех, что я видел. Я не думал, что подписанный/неподписанный был достаточно близок к 8-битному/32-битному.

Barmar 15.04.2023 00:18

Кстати, так и должно быть int main(void).

Bob__ 15.04.2023 00:24

@Bob__ Спасибо за отзыв... не знал, что это важно! stackoverflow.com/questions/8844915/…

niil87 15.04.2023 02:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
71
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Если мы посмотрим на inttypes.h, то обнаружим следующее:

# define PRIu8      "u"

Таким образом, этот спецификатор формата не содержит модификатора длины. Это имеет смысл в контексте соответствующего аргумента, поскольку невозможно передать uint8_t функции с переменным числом аргументов. Поскольку этот тип имеет меньший ранг, чем int, значение этого типа будет повышено до int. Это продвижение также происходит в выражениях b+b+b и 3*b.

Если вы явно используете модификатор длины для char, вы получите ожидаемый результат.

printf("%hhd\n",b+b+b);
printf("%hhd\n",3*b);

Похоже на ошибку в библиотеке

Vlad from Moscow 15.04.2023 00:20

@VladfromMoscow Для меня это тоже. Я ожидаю, что он будет напечатан правильно.

0___________ 15.04.2023 00:32
Ответ принят как подходящий

На мой взгляд, это ошибка библиотеки.

В MS Visual Studio макрос PRIu8 расширяется до hhu как и должно быть и вы получаете ожидаемый результат.

Интересно отметить, что если использовать clang, то если вы напишете, например,

printf("%" PRIu8 "\n",( uint8_t)(b+b+b));

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

В C всякий раз, когда вы вызываете функцию с аргументами с многоточием (...), например printf, все эти аргументы будут продвигаться по умолчанию. Это означает, что любой меньший целочисленный тип будет преобразован в int и передан таким образом. Таким образом, в printf префиксы типов h не имеют значения - они будут игнорироваться printf и не будут иметь никакого эффекта (поскольку аргумент в любом случае должен быть int).

Кроме того, такое же преобразование меньших целых типов будет происходить для любого арифметического оператора (например, + или *), и результирующая операция будет выполняться с int точностью, давая int результат.

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

printf("%u\n", (b+b+b) & 0xff);

Спасибо за ваш ответ, что такое префиксы типа h?

niil87 16.04.2023 15:33

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