Conversión de enteros (conversión explícita e implícita)

Miré a mi alrededor y solo encontré publicaciones más complicadas con punteros. Estoy aprendiendo C, y solo quería confirmar mi comprensión de algunos ejemplos. (Estos ejemplos asumen tamaños int y short int de 32 y 16 bits, respectivamente.)

Código inicial:

int int1 = 70000; int int2; short int shortInt1 = -70; short int shortInt2, shortInt3; 

Y algunas conversiones de muestra:

 shortInt2 = int1 / shortInt1; 

Mi punto de vista:
yo. La división se lleva a cabo (70000 / -70), dando un valor de -1000
ii. Debido a que int tiene mayor prioridad que short int, el resultado se asigna a un int anónimo firmado
iii. Anónimo firmado int se convierte en un anónimo firmado corto int
iv. El valor del int corto anónimo firmado se asigna a shortInt2
v. El int firmado anónimo y el int corto anónimo firmado son basura recolectada
vi. Resultado: -1000

 shortInt3 = (short) int1 / shortInt1; 

Mi punto de vista:
yo. Debido a que la conversión tiene prioridad sobre la aritmética, int1 se convierte en un int corto anónimo firmado. Se produce un desbordamiento, que le da un valor de 4464.
ii. La división se lleva a cabo y el resultado de -63 se asigna a un segundo int corto anónimo firmado.
iii. El valor del segundo int corto anónimo firmado se asigna a shortInt3
iv. Ambas entradas cortas anónimas firmadas son recolectadas basura
v. Resultado: -63

 int2 = (short)(int1 / shortInt2); 

Este es el ejemplo por el que estoy más confundido. Entiendo que la conversión tiene prioridad sobre la aritmética, pero aquí parece que los paréntesis alrededor de la aritmética están dando la prioridad aritmética sobre la conversión. (Lo que supongo que tiene sentido, ya que la operación de lanzamiento necesita algo de valor para lanzar).

Por lo tanto, mi entendimiento:
yo. La división se lleva a cabo y, como int tiene una prioridad más alta, el valor de la división se asigna a un int firmado anónimo.
ii. El int anónimo firmado se convierte en un int anónimo firmado
iii. El cambio a la derecha aritmética se realiza en el int corto anónimo firmado, expandiéndolo en otro int anónimo firmado
iv. El valor del segundo int anónimo firmado se asigna a int2
v. El int firmado anónimo (el primero), el int corto anónimo firmado y el int firmado anónimo (el segundo) se recolectan como basura.
vi. Valor: -1000

Su pregunta hace una serie de suposiciones incorrectas y utiliza una terminología incorrecta. Comenzaré explicando lo que dice el estándar C sobre su código de muestra.

Puede obtener una copia del último borrador del estándar C, N1570 . Las “conversiones aritméticas habituales” se describen en la sección 6.3.1.8; Las promociones enteras se describen en 6.3.1.1.

 int int1 = 70000; 

Dados sus supuestos, int es de 32 bits, por lo que es lo suficientemente grande para mantener el valor 70000 . (Hay sistemas donde int es de solo 16 bits; en tales sistemas, esto daría un resultado definido por la implementación).

 int int2; short int shortInt1 = -70; 

70 es una constante de tipo int . El operador unario se aplica al valor resultante, lo que arroja un valor int de -70. La inicialización hace que ese valor int se convierta al tipo del objeto de destino. Las conversiones conservan los valores siempre que sea posible, y como se garantiza que short tiene un ancho de al menos 16 bits, shortInt1 se establece en el valor obvio.

 short int shortInt2, shortInt3; shortInt2 = int1 / shortInt1; 

El operador de división aplica las conversiones aritméticas habituales a sus dos operandos. Este es un conjunto de reglas moderadamente complicado, diseñado para garantizar que ambos operandos sean del mismo tipo (en su mayor parte, C no tiene operadores aritméticos de tipo mixto). Este caso es bastante simple: el operando short int se convierte a int , y el / luego se aplica a dos operandos int y produce un resultado int . El valor es -1000 .

La asignación hace que ese resultado se convierta de int a short . Nuevamente, short es lo suficientemente grande para mantener ese valor, por lo que shortInt2 obtiene el valor -1000 (de tipo short , no int ).

La mayoría de las expresiones en C se evalúan sin tener en cuenta el contexto en el que aparecen. Una división en el lado derecho de una asignación no se ve afectada por el tipo del lado izquierdo. Se evalúa de forma aislada y luego se convierte al tipo de destino si es necesario.

 shortInt3 = (short) int1 / shortInt1; 

La int1 convierte el valor de int1 al tipo short , por lo que el operador / tiene dos operandos de tipo short . Pero las conversiones aritméticas habituales en operandos enteros incluyen las promociones enteras , que convierten ambos operandos short al tipo int . La división int -by- int produce un resultado int , que luego se convierte en short y se asigna a shortInt3 .

 int2 = (short)(int1 / shortInt2); 

La división int1 / shortInt2 aplica las conversiones aritméticas habituales , que convierten el operando correcto de short a int . El resultado de la conversión int -by- int se convierte luego en short por el lanzamiento. El resultado short se convierte a int porque se está asignando a un objeto de destino int .

Ahora para aclarar algunas cosas que escribiste:

Debido a que int tiene mayor prioridad que short int, el resultado se asigna a un int anónimo firmado

Los tipos no tienen precedencia. Los operadores hacen. (Los tipos tienen rango , que se utiliza para determinar las * conversiones aritméticas habituales “.)

Parece que estás asumiendo que el resultado de evaluar una expresión debe asignarse a algún objeto (variable). Ese no es el caso. La evaluación de una expresión produce un resultado . Ese resultado es un valor particular, y es de algún tipo en particular, pero no necesita ser almacenado en ningún lugar. (Quizás se almacenará temporalmente en un registro, o incluso en alguna ubicación de la memoria, pero ese es un detalle de la implementación que podemos ignorar de manera segura). No hay necesidad de inventar nada “anónimo” para contener el resultado de una expresión. El resultado simplemente es . Eventualmente, podría almacenarse en un objeto, pasarse a una función o usarse como operando de otro operador. La forma en que se hace no está definida por el estándar C.