¿32 bits sin signo se multiplican en 64 bits causando un comportamiento indefinido?

Entonces tengo sobre este código:

uint32_t s1 = 0xFFFFFFFFU; uint32_t s2 = 0xFFFFFFFFU; uint32_t v; ... v = s1 * s2; /* Only need the low 32 bits of the result */ 

En todo lo que sigue, supongo que el comstackdor no podría tener ninguna idea preconcebida sobre el rango de s1 o s2 , los inicializadores solo sirven para un ejemplo anterior.

Si compilé esto en un comstackdor con un tamaño entero de 32 bits (como al comstackr para x86), no hay problema. El comstackdor simplemente usaría s1 y s2 como uint32_t valores tipeados (no pudiendo promoverlos más), y la multiplicación simplemente daría el resultado como dice el comentario (módulo UINT_MAX + 1 que es 0x100000000 en este caso).

Sin embargo, si compilé esto en un comstackdor con un tamaño entero de 64 bits (como para x86-64), podría haber un comportamiento indefinido a partir de lo que puedo deducir del estándar C. La promoción de enteros vería que uint32_t puede promocionarse a int (64 bit signed), la multiplicación intentaría multiplicar dos int , que, si tienen los valores mostrados en el ejemplo, causarían un desbordamiento de enteros, que es comportamiento indefinido

¿Estoy en lo correcto con esto y de ser así cómo lo evitarías de una manera sensata?

Vi esta pregunta que es similar, pero cubre C ++: ¿Cuál es la mejor forma de C ++ para multiplicar enteros sin signo de manera modular y segura? . Aquí me gustaría obtener una respuesta aplicable a C (preferiblemente compatible con C89). No consideraría hacer una pobre máquina de 32 bits ejecutando potencialmente una multiplicación de 64 bits, una respuesta aceptable (generalmente en el código donde esto sería preocupante, el rendimiento de 32 bits podría ser más crítico ya que típicamente esas son las máquinas más lentas).

Tenga en cuenta que el mismo problema puede aplicarse a ints sin firmar de 16 bits cuando se comstack con un comstackdor con un tamaño int de 32 bits o caracteres sin signo cuando se comstack con un comstackdor que tiene un tamaño int de 16 bits (este último puede ser común con comstackdores para CPU de 8 bits : el estándar C requiere que los enteros sean al menos 16 bits, por lo que un comstackdor compatible probablemente se vea afectado).

La forma más sencilla de obtener la multiplicación en un tipo sin firmar que sea al menos uint32_t , y también al menos unsigned int , es involucrar una expresión de tipo unsigned int .

 v = 1U * s1 * s2; 

Esto convierte 1U en uint32_t , o s1 y s2 en unsigned int , dependiendo de lo que sea apropiado para su plataforma en particular.

@Dupuplicator comenta que algunos comstackdores, donde uint32_t es más estrecho que unsigned int , pueden advertir acerca de la conversión implícita en la asignación, y señala que tales advertencias son probablemente suprimibles haciendo que la conversión sea explícita:

 v = (uint32_t) (1U * s1 * S2); 

Sin embargo, parece un poco menos elegante, en mi opinión.

Felicitaciones por encontrar un punto de fricción.

Una posible forma:

 v = (uint32_t) (UINT_MAX<=0xffffffff ? s1 * s2 : (unsigned)s1 * (unsigned)s2); 

De todos modos, parece agregar algunos typedefs a para tipos garantizados que no sean más pequeños que int estarían en orden ;-).