Результатом присваивания C и передачи параметров является неявное приведение, или мы должны приводить явное приведение? - PullRequest
0 голосов
/ 20 апреля 2019

Есть ли разница между двумя следующими частями кода?

A:

int x = get_some_int();
uint8_t y = x;

B

int x = get_some_int();
uint8_t y = (uint8_t) x;

Кроме того, что, если y был аргументом uint8_t для функции и x был передан в нее (с явным приведением и без него)?

РЕДАКТИРОВАТЬ: Пожалуйста, позвольте мне перефразировать вопрос: является ли хорошей практикой в ​​C всегда приводить переменные в явном виде при присваивании или передаче другому типу? Или это хорошая практика оставить это компилятору? Что должен делать программист по умолчанию?

Ответы [ 2 ]

1 голос
/ 20 апреля 2019

Стандарт C 2018 гласит, в пункте 6.5.16.1, абзац 2:

В простое присвоение (=), значение правого операнда преобразуетсятипу выражения присваивания и заменяет значение, хранящееся в объекте, обозначенном левым операндом.

Итак, да, преобразование происходит неявно.Стандарт C не требует, чтобы вы использовали приведение.

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

Это дает нам следующие случаи:

  • Левый и правый операндыразрешено стандартом C, и компилятор принимает их без жалоб: приведение не требуется.Вы можете использовать его, если хотите передать какое-то намерение человеку, читающему код, но приведение обычно не рекомендуется, так как оно может скрывать предупреждения и сообщения об ошибках, которые могут помочь найти ошибки.
  • Левый и правый операндыразрешены стандартом C, но используемый вами компилятор (-ы) выдает предупреждающее сообщение: приведение необходимо, если вы хотите подавить предупреждение.Однако компилятор пытается сообщить вам что-то с предупреждением, например, что приведение может скрыть переполнение.Вы должны быть уверены, что понимаете причину предупреждения, прежде чем вставлять приведение.
  • Левый и правый операнды не допускаются стандартом C: приведение необходимо.Опять же, вы должны полностью понять семантику, прежде чем вставлять приведение.

Сноска

1 Полный текст пункта 1:

Должно быть выполнено одно из следующих действий:

- левый операнд имеет атомарный, квалифицированный или неквалифицированный арифметический тип, а правый имеет арифметический тип;

- левый операнд имеетатомарный, квалифицированный или неквалифицированный вариант структуры или типа объединения, совместимый с типом правого;

- левый операнд имеет атомный, квалифицированный или неквалифицированный тип указателя, и (учитывая тип, левый операнд будетиметь после преобразования в lvalue) оба операнда являются указателями на квалифицированные или неквалифицированные версии совместимых типов, а тип, на который указывает левый, имеет все квалификаторы типа, на который указывает правый;

- левый операнд имеетатомарный, квалифицированный или неквалифицированный тип указателя и (учитывая тип, который левый операнд будет иметь после преобразования lvalue)один операнд является указателем на тип объекта, а другой - указателем на квалифицированную или неквалифицированную версию void, а тип, на который указывает левый, имеет все квалификаторы типа, на который указывает правый;

- левый операнд является атомарным, квалифицированным или неквалифицированным указателем, а правый - константой нулевого указателя;или

- левый операнд имеет тип атомарный, квалифицированный или неквалифицированный _Bool, а правый - указатель.

1 голос
/ 20 апреля 2019

Проблема не в кастинге (не требуется, чтобы присвоение было прямым преобразованием в uint8_t C11 Standard - 6.5.16.1 Простое присвоение (p2) ), проблема - в диапазоне. int x; - это 32-разрядное целое число со знаком, способное хранить значения в диапазоне -2147483648 to 2147483647. В отличие от этого uint8_t y; является 8-разрядным значением без знака, способным содержать значения 0 - 255.

Вы можете легко присвоить значение x, которое превышает диапазон y. Так что же тогда происходит? Если значение x превышает диапазон y, значение x уменьшается по модулю 1 + max_for_type для соответствия y C11 Standard - 6.2.5 Типы (p9) Так присвоение эквивалентно y = x % 256; для значений x, которые превышают диапазон uint8_t.

Так, например, x = 25306708;, тогда когда x преобразуется в uint8_t с y = x; x, уменьшается по модулю 256, чтобы он соответствовал y. (например, y == 84)

Если вы хотите избежать бесшумного уменьшения значения до значения y, то вы должны проверить, что значение x будет соответствовать y без уменьшения. Вы можете сделать это с помощью простой условной переменной и константы, определенной в stdint.h, например,

    y = x;  /* direct assignment, no cast required */

    if (0 <= x && x <= UINT8_MAX)   /* validate x within the range of uint8_t */
        printf ("int x: %d is in range of uint8_t y: %" PRIu8 "\n", x, y);
    else
        /* handle the error as required */

Составив короткий пример, показывающий поведение, вы можете сделать:

#include <stdio.h>
#include <inttypes.h>  /* includes stdint.h */

int main (void) {

    int x;
    uint8_t y;

    fputs ("enter a value: ", stdout);
    if (scanf ("%d", &x) != 1) {
        fputs ("error: invalid integer value.\n", stderr);
        return 1;
    }
    y = x;  /* direct assignment, no cast required */

    if (0 <= x && x <= UINT8_MAX)   /* validate x within the range of uint8_t */
        printf ("int x: %d is in range of uint8_t y: %" PRIu8 "\n", x, y);
    else 
        printf ("x: %d exceeds range of uint8_t, reduced modulo to y: %"
                PRIu8 "\n", x, y);
}

Пример использования / Вывод

$ ./bin/intuint8_t
enter a value: 254
int x: 254 is in range of uint8_t y: 254

$ ./bin/intuint8_t
.enter a value: 256
x: 256 exceeds range of uint8_t, reduced modulo to y: 0

$ ./bin/intuint8_t
enter a value: 25306708
x: 25306708 exceeds range of uint8_t, reduced modulo to y: 84

В ответ на:

«Является ли хорошей практикой в ​​C всегда явное приведение переменных»

Нет. Приведение будет маскировать предупреждения, которые сгенерирует компилятор, если то, что вы делаете, включает несовместимые типы. Вы должны приводить только редко и только там, где это явно необходимо, как того требует библиотечный API и т. Д.

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...