Неявно преобразуване по време на присвояване в 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
@Jens Уау. Никога не съм виждал това преди; Имах ужасно време да отпечатвам типове с фиксирана ширина. Трябва да тръгвам сега, но ще го разгледам отблизо по-късно.   -  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 има използваема или полезна стойност).

Друга употреба на явни преобразувания е, когато променя значението на стойност - типичен пример е, когато искате да третирате байт I/O като неподписана стойност. Тъй като 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