Об использовании uint8_t в C при подсчете

Я хотел убедиться, что я понял, что происходит в следующем коде:

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

int main (void)
{
    uint8_t a = 255;

    a = a + 5;

    printf("%d\n", a);
}

Будет ли напечатанное значение равным 4, потому что, когда a достигает максимального значения, которое оно может считать до (255), оно сбрасывается обратно в 0? Итак, если я хочу продолжить отсчет после (255), я могу создать переменную int и добавить к ней? Например, int b = a + 5;, который напечатает 260.

Зачем вам нужен байтовый аккумулятор, если вы знаете, что он переполнится?

Yves Daoust 16.08.2024 15:27

@YvesDaoust Во многих ситуациях это полезно при программировании, связанном с аппаратным обеспечением. Предположим, например, что у меня есть 8-битный процессор, который предпочитает 8-битные переменные из соображений производительности. Затем каждый второй раз, когда я вызываю функцию, я хочу, чтобы что-то произошло: void func (void) { static uint8_t x; if (x & 1) { do_something(); } x++; } Здесь переменная в конечном итоге будет перенесена, но нам не нужно знать или заботиться о том, какое значение она содержит.

Lundin 16.08.2024 15:41

@Lundin: конечно, но здесь ОП явно упоминает счет выше 255.

Yves Daoust 16.08.2024 17:04

@YvesDaoust Речь идет больше о понимании того, что происходит, чем о том, что было бы разумнее сделать.

Guilherme Cintra 16.08.2024 21:00
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Во многом правильно. Вам просто нужно понимать, что в выражении a + 5unsigned char неявно преобразуется в тип 5, которым является int, благодаря правилу продвижения, называемому «обычные арифметические преобразования». Таким образом, сложение на самом деле выполняется по типу int, и в итоге вы получаете 260 во временном int. И поэтому вы действительно можете сделать int b = a + 5; и получить 260, потому что часть a + 5 равна 260.

Во время присвоения a в a = a + 5 вы принудительно выполняете еще одно преобразование обратно в unsigned char, и оно выполняется «как будто по модулю» на единицу больше, чем максимальное число, которое оно может содержать. 260 % ​​256 = 4, и в итоге у вас получится 4. Это то же самое, что происходит, когда вокруг появляется unsigned char.

(Переполнение/недополнение целого числа со знаком, что является неопределенным поведением. Целые числа без знака переносятся вокруг, что является четко определенным поведением.)

Если бы вы вместо этого написали a++; a++; a++; a++; a++;, вы бы получили зацикливание.

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

Eric Postpischil 16.08.2024 15:51

@lundin Подробности: unsigned char становится частью int. Тип + изначально не имеет значения для этого первого шага.

chux - Reinstate Monica 16.08.2024 17:21
Ответ принят как подходящий

Да на оба вопроса.

Учитывая, что a — это uint8_t, a + 5 эквивалентно (int)a + 5 и порождает int.

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

Целочисленное продвижение означает преобразование значения с типом меньшего ранга, чем int, в int (если весь их диапазон может уместиться в int) или unsigned int. uint8_t гарантированно будет типом меньшего ранга, чем int, и его диапазон гарантированно будет соответствовать int, поэтому значение этого типа будет повышено до int.

Итак, в результате обычных арифметических преобразований a + 5 преобразует a в int, добавляет к нему int5, создавая int260.

Пока перелива нет. Но затем мы присваиваем его uint8_t. Преобразование слишком большого значения в беззнаковый целочисленный тип четко определено. Для 8-битного беззнакового целого числа вы фактически получаете результат по модулю 256, то есть uint8_t4.

Не было бы переполнения, если бы вместо этого вы сохранили int260 в int, так что это действительно создаст int260.

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