Неявное преобразование во время присваивания в C?

Нужен ли нам бросок в этой ситуации?:

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

int main(){

  // Say we're working with 32-bit variables.
  uint32_t a = 123456789;
  uint32_t b = 5123412;
  uint32_t c = 123049811;

  // We want to use 64-bit arithmetic at some intermediate stage, 
  // e.g. the a*b product here. After dividing by 'c', the result
  // again fits into a 32-bit unsigned, 'result'.
  uint32_t result = (uint64_t)a*b/c;

  // QUESTION HERE: Should we cast before the assignment? I.e.:
  //uint32_t result = (uint32_t)( (uint64_t)a*b/c );

  // Either way the result turns out OK on my system.
  printf("%u\n", (unsigned)result);

}

person Douglas B. Staple    schedule 27.10.2013    source источник
comment
Это очень спорный вопрос, я бы предпочел иметь слепки только в случае крайней необходимости. Приведения типов синтаксически трудно найти и могут скрывать гораздо более серьезные проблемы, поскольку теперь компилятор предполагает, что вы знаете, что делаете. Отливка в printf является хорошим примером: вы можете отбрасывать значительные части. (Подумайте о системе с 16-битным int.) Здесь правильно использовать printf("%" PRIu32 "\n", result).   -  person Jens Gustedt    schedule 28.10.2013
comment
@ Дженс Вау. Я никогда не видел этого раньше; У меня было ужасное время, когда я печатал шрифты с фиксированной шириной. Мне нужно идти сейчас, но я собираюсь внимательно посмотреть на это позже.   -  person Douglas B. Staple    schedule 28.10.2013
comment
Я могу только порекомендовать прочитать стандарт, он, наконец, не так сложно читается, и в нем много таких перлов.   -  person Jens Gustedt    schedule 28.10.2013


Ответы (3)


// QUESTION HERE: Should we cast before the assignment? I.e.:
//uint32_t result = (uint32_t)( (uint64_t)a*b/c );

Актерский состав здесь не нужен.

C никогда не требует приведения при присвоении значения одного арифметического типа объекту другого арифметического типа. Существует неявное преобразование между всеми арифметическими типами.

Теперь имейте в виду, что некоторые компиляторы могут предупреждать. Например, если правый операнд является константным выражением и не может быть представлен в типе левого операнда, выдается предупреждение gcc.

person ouah    schedule 27.10.2013
comment
Именно такое предупреждение и стало причиной вопроса. Как вы говорите, в этой ситуации нет предупреждения (и нет проблемы). - person Douglas B. Staple; 28.10.2013
comment
@DouglasB.Staple, и для полноты моего ответа MISRA-C потребует здесь приведения (чтобы сделать преобразование в более узкий тип явным для читателя). - person ouah; 28.10.2013
comment
Что ж, для меня это означало бы, что это хорошая практика — указывать явное приведение, даже если оно избыточно? - person Douglas B. Staple; 28.10.2013
comment
@DouglasB.Staple не обязательно, многие правила MISRA оспариваются. Лично я не люблю лишнего в программировании и литье не стал бы ставить. - person ouah; 28.10.2013
comment
Все, что является требованием MISRA, требует серьезного скептицизма. - person R.. GitHub STOP HELPING ICE; 28.10.2013

Для любого присвоения a = b; значение b преобразуется в значение типа a, если это возможно, и это преобразованное значение присваивается a. Таким образом, самый внешний состав, который вы предлагаете, является излишним.

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

foo x;

bar * y = (void *)&x;

Неявного преобразования из foo * в bar * не существует, но существует существует неявное преобразование из любого типа указателя объекта в void * и из него (хотя не указано, имеет ли y пригодное или полезное значение).

Еще одно использование явного приведения — когда оно изменяет значение значения — типичный пример — когда вы хотите обработать байт ввода-вывода как беззнаковое значение. Поскольку char может быть как со знаком, так и без знака, преобразование char непосредственно в unsigned int может дать неправильный ответ. Например, мы хотим, чтобы -1 было значением 255, а не -1. Итак, вам нужно:

char b = get_input();

unsigned int value = (unsigned char)b;

Преобразование из char в unsigned char всегда дает "ожидаемое" байтовое значение без знака, и тогда его можно преобразовать в целое число без знака.

person Kerrek SB    schedule 27.10.2013
comment
Да, я так и думал. Я почему-то поймал себя на том, что пишу актерский состав. Я всегда пишу код один, поэтому мне не с кем перепроверить такие базовые вещи. - person Douglas B. Staple; 28.10.2013
comment
@KerrekSB Из-за отсутствия PM я просто опубликую комментарий здесь. Что касается вопроса о декартовых произведениях, я предложил (довольно короткое) исправление в связанном живом примере (части без комментариев) - я нашел ваше решение довольно компактным и простым. - person dyp; 28.10.2013
comment
@DyP: Хочешь сам починить? Я могу восстановить это. - person Kerrek SB; 28.10.2013

6.3 Преобразования:

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

6.5.16.1 Простое назначение:

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

person haccks    schedule 27.10.2013