logicos - operaciones en c++
¿Por qué debe convertirse un short en int antes de operaciones aritméticas en C y C++? (4)
De las respuestas que obtuve de esta pregunta , parece que C ++ heredó este requisito para la conversión de short
en int
al realizar operaciones aritméticas desde C. ¿Puedo elegir sus cerebros sobre por qué esto se introdujo en C en primer lugar? ¿Por qué no simplemente hacer estas operaciones tan short
?
Por ejemplo ( tomado de la sugerencia de dyp en los comentarios ):
short s = 1, t = 2 ;
auto x = s + t ;
x
tendrá el tipo de int .
La pregunta vinculada parece cubrirlo bastante bien: la CPU simplemente no funciona. Una CPU de 32 bits tiene sus operaciones aritméticas nativas configuradas para registros de 32 bits. El procesador prefiere trabajar en su tamaño favorito, y para operaciones como esta, copiar un pequeño valor en un registro de tamaño nativo es barato. (Para la arquitectura x86, los registros de 32 bits se nombran como si fueran versiones extendidas de los registros de 16 bits ( eax
a ax
, ebx
a bx
, etc.); consulte las instrucciones de enteros x86 ).
Para algunas operaciones extremadamente comunes, particularmente la aritmética vectorial / flotante, puede haber instrucciones especializadas que operen en un tipo de registro o tamaño diferente. Para algo así como un corto, el relleno con (hasta) 16 bits de ceros tiene muy poco costo de rendimiento y la adición de instrucciones especializadas probablemente no valga la pena el tiempo o espacio en el dado (si quieres ser realmente físico sobre por qué; no estoy seguro de que ocupen espacio real, pero se vuelve mucho más complejo).
No es una característica del lenguaje tanto como una limitación de las arquitecturas de los procesadores físicos en los que se ejecuta el código. El int
typer en C suele ser del tamaño de su registro de CPU estándar. Más silicio consume más espacio y más potencia, por lo que en muchos casos la aritmética solo puede hacerse en los tipos de datos de "tamaño natural". Esto no es universalmente cierto, pero la mayoría de las arquitecturas todavía tienen esta limitación. En otras palabras, al agregar dos números de 8 bits, lo que realmente sucede en el procesador es algún tipo de aritmética de 32 bits seguida de una máscara de bits simple u otra conversión de tipo apropiada.
Si miramos los Fundamentos de las Lenguajes de Programación Estándar Internacional-C en la sección 6.3.1.8
Conversiones aritméticas habituales , dice ( énfasis mío en el futuro ):
Las reglas en el Estándar para estas conversiones son ligeras modificaciones de las de K & R: las modificaciones acomodan los tipos agregados y las reglas de preservación de valor. Se agregó una licencia explícita para realizar cálculos en un tipo "más amplio" que el absolutamente necesario, ya que a veces esto puede producir un código más pequeño y más rápido, sin mencionar la respuesta correcta más a menudo . Los cálculos también pueden realizarse en un tipo "más angosto" por la regla como si siempre se obtiene el mismo resultado final. La conversión explícita siempre se puede usar para obtener un valor en un tipo deseado
La sección 6.3.1.8
del borrador de la norma C99 cubre las conversiones aritméticas habituales que se aplican a los operandos de expresiones aritméticas, por ejemplo, la sección 6.5.6
operadores de aditivos dicen:
Si ambos operandos tienen un tipo aritmético, las conversiones aritméticas habituales se realizan sobre ellos.
También encontramos texto similar en la sección 6.5.5
Operadores multiplicativos . En el caso de un operando corto , primero las promociones enteras se aplican desde la sección 6.3.1.1
Boolean, caracteres y enteros, que dice:
Si un int puede representar todos los valores del tipo original, el valor se convierte en un int; de lo contrario, se convierte a unsigned int. Estas se llaman promociones enteras . 48) Los demás tipos no se modifican por las promociones enteras.
La discusión de la sección 6.3.1.1
de la Rationale o International Standard-Programming Languages-C sobre las promociones enteras es en realidad más interesante, voy a citar selectivamente b / c, es demasiado largo para citar por completo:
Las implementaciones se dividieron en dos grandes campos que pueden caracterizarse como preservación sin firma y preservación del valor .
[...]
El enfoque de preservación sin firmar requiere la promoción de los dos tipos más pequeños sin signo a unsigned int. Esta es una regla simple, y produce un tipo que es independiente del entorno de ejecución.
El enfoque de preservación del valor llama a promocionar esos tipos para que sean firmados int si ese tipo puede representar correctamente todos los valores del tipo original, y de lo contrario, para promocionar esos tipos a int sin firmar. Por lo tanto, si el entorno de ejecución representa corto como algo más pequeño que int, el corto sin signo se convierte en int; de lo contrario, se convierte en unsigned int.
Esto puede tener algunos resultados inesperados en algunos casos, como lo demuestra el comportamiento incoherente de la conversión implícita entre tipos firmados sin firmar y más grandes , hay muchos más ejemplos como ese. Aunque en la mayoría de los casos esto hace que las operaciones funcionen como se esperaba.
float
, short
y char
son considerados por el tipo estándar de "tipos de almacenamiento", es decir, subvaciamientos que puede usar para ahorrar espacio, pero que no le comprarán ninguna velocidad porque su tamaño es "antinatural" para la CPU.
En ciertas CPU esto no es cierto, pero los buenos compiladores son lo suficientemente inteligentes como para notar que si, por ejemplo, agregas una constante a un char sin signo y almacenas el resultado en un char sin firmar, entonces no hay necesidad de pasar por la conversión unsigned char -> int
. Por ejemplo, con g ++ el código generado para el bucle interno de
void incbuf(unsigned char *buf, int size) {
for (int i=0; i<size; i++) {
buf[i] = buf[i] + 1;
}
}
es solo
.L3:
addb $1, (%rdi,%rax)
addq $1, %rax
cmpl %eax, %esi
jg .L3
.L1:
donde puede ver que se usa una instrucción de suma de caracteres sin signo ( addb
).
Lo mismo ocurre si estás haciendo tus cálculos entre entradas cortas y almacenando el resultado en entradas cortas.