Implicit conversions
Cuando una expresión se utiliza en un contexto donde se espera un valor de un tipo diferente, conversión puede ocurrir:
int n = 1L; // la expresión 1L tiene tipo long, se esperaba int n = 2.1; // la expresión 2.1 tiene tipo double, se esperaba int char* p = malloc(10); // la expresión malloc(10) tiene tipo void*, se esperaba char*
Las conversiones tienen lugar en las siguientes situaciones:
Conversión como si fuera por asignación
- En el operador de asignación , el valor del operando derecho se convierte al tipo no calificado del operando izquierdo.
- En la inicialización escalar , el valor de la expresión inicializadora se convierte al tipo no calificado del objeto que se está inicializando.
- En una expresión de llamada a función , para una función que tiene un prototipo, el valor de cada expresión de argumento se convierte al tipo de los tipos declarados no calificados del parámetro correspondiente.
- En una sentencia return , el valor del operando de return se convierte a un objeto que tiene el tipo de retorno de la función.
Tenga en cuenta que la asignación real, además de la conversión, también elimina el rango y precisión adicionales de los tipos de punto flotante y prohíbe superposiciones; esas características no se aplican a la conversión como si fuera por asignación.
Promociones predeterminadas de argumentos
En una expresión de llamada a función cuando la llamada se realiza a
Cada argumento de tipo entero sufre promoción de enteros , y cada argumento de tipo float se convierte implícitamente al tipo double .
int add_nums(int count, ...); int sum = add_nums(2, 'c', true); // add_nums se llama con tres ints: (2, 99, 1)
|
Nótese que float complex e float imaginary no son promovidos a double complex e double imaginary en este contexto. |
(desde C99) |
Conversiones aritméticas usuales
Los argumentos de los siguientes operadores aritméticos sufren conversiones implícitas con el propósito de obtener el tipo real común , que es el tipo en el que se realiza el cálculo:
- aritmética binaria * , / , % , + , - ,
- operadores relacionales < , > , <= , >= , == , ! = ,
- aritmética binaria a nivel de bits & , ^ , | ,
- el operador condicional ?: .
|
1)
Si un operando tiene tipo de coma flotante decimal, el otro operando no debe tener tipo de coma flotante estándar,
complejo o imaginario.
|
(desde C23) |
-
- tipo entero o de coma flotante real a long double
| (desde C99) |
-
- tipo entero o de coma flotante real a double
| (desde C99) |
-
- tipo entero a float (el único tipo real posible es float, que permanece como está)
| (desde C99) |
-
- Si los tipos son iguales, ese tipo es el tipo común.
-
De lo contrario, los tipos son diferentes:
- Si los tipos tienen la misma signatura (ambos con signo o ambos sin signo), el operando cuyo tipo tiene el menor rango de conversión [1] se convierte implícitamente [2] al otro tipo.
-
De lo contrario, los operandos tienen signatura diferente:
- Si el tipo sin signo tiene rango de conversión mayor o igual que el rango del tipo con signo, entonces el operando con tipo con signo se convierte implícitamente al tipo sin signo.
-
De lo contrario, el tipo sin signo tiene
rango de conversión
menor que el tipo con signo:
- Si el tipo con signo puede representar todos los valores del tipo sin signo, entonces el operando con tipo sin signo se convierte implícitamente al tipo con signo.
- De lo contrario, ambos operandos experimentan conversión implícita al tipo sin signo equivalente del tipo del operando con signo.
- ↑ Consulte promociones enteras a continuación para las reglas sobre rangos.
- ↑ Consulte "conversiones enteras" en semántica de conversión implícita a continuación.
1.f + 20000001; // int se convierte a float, dando 20000000.00 // la suma y luego el redondeo a float da 20000000.00 (char)'a' + 1L; // primero, char 'a', que es 97, se promueve a int // tipos diferentes: int y long // misma signatura: ambos con signo // rango diferente: long tiene mayor rango que int // por lo tanto, int 97 se convierte a long 97 // el resultado es 97 + 1 = 98 de tipo signed long 2u - 10; // tipos diferentes: unsigned int y signed int // signatura diferente // mismo rango // por lo tanto, signed int 10 se convierte a unsigned int 10 // dado que la operación aritmética se realiza para enteros sin signo // (ver el tema "Operadores aritméticos"), el cálculo realizado es (2 - 10) // módulo (2 elevado a n), donde n es el número de bits de valor de unsigned int // si unsigned int es de 32 bits y no hay bits de relleno en su representación // de objeto, entonces el resultado es (-8) módulo (2 elevado a 32) = 4294967288 // de tipo unsigned int 5UL - 2ULL; // tipos diferentes: unsigned long y unsigned long long // misma signatura // rango diferente: el rango de unsigned long long es mayor // por lo tanto, unsigned long 5 se convierte a unsigned long long 5 // dado que la operación aritmética se realiza para enteros sin signo // (ver el tema "Operadores aritméticos"), // si unsigned long long es de 64 bits, entonces // el resultado es (5 - 2) módulo (2 elevado a 64) = 3 de tipo // unsigned long long 0UL - 1LL; // tipos diferentes: unsigned long y signed long long // signatura diferente // rango diferente: el rango de signed long long es mayor. // si ULONG_MAX > LLONG_MAX, entonces signed long long no puede representar todos // los unsigned long por lo tanto, este es el último caso: ambos operandos se convierten // a unsigned long long unsigned long 0 se convierte a unsigned long long 0 // long long 1 se convierte a unsigned long long 1 dado que la operación // aritmética se realiza para enteros sin signo // (ver el tema "Operadores aritméticos"), // si unsigned long long es de 64 bits, entonces // el cálculo es (0 - 1) módulo (2 elevado a 64) // así, el resultado es 18446744073709551615 (ULLONG_MAX) de tipo // unsigned long long
|
El tipo de resultado se determina de la siguiente manera:
double complex z = 1 + 2*I; double f = 3.0; z + f; // z permanece como está, f se convierte a double, el resultado es double complex |
(desde C99) |
Como siempre, el resultado de un operador de punto flotante puede tener mayor rango y precisión de lo que indica su tipo (ver FLT_EVAL_METHOD ).
|
Nota: los operandos reales e imaginarios no se convierten implícitamente a complejos porque hacerlo requeriría cálculos adicionales, mientras que produciría resultados indeseables en ciertos casos que involucran infinitos, NaNs y ceros con signo. Por ejemplo, si los reales se convirtieran a complejos, 2.0×(3.0+i∞) se evaluaría como (2.0+i0.0)×(3.0+i∞) ⇒ (2.0×3.0–0.0×∞) + i(2.0×∞+0.0×3.0) ⇒ NaN+i∞ en lugar del correcto 6.0+i∞. Si los imaginarios se convirtieran a complejos, i2.0×(∞+i3.0) se evaluaría como (0.0+i2.0) × (∞+i3.0) ⇒ (0.0×∞ – 2.0×3.0) + i(0.0×3.0 + 2.0×∞) ⇒ NaN + i∞ en lugar de –6.0 + i∞. |
(desde C99) |
Nota: independientemente de las conversiones aritméticas habituales, el cálculo siempre puede realizarse en un tipo más estrecho que el especificado por estas reglas bajo la regla as-if .
Transformaciones de valores
Conversión de lvalue
Cualquier expresión lvalue de cualquier tipo no-array, cuando se utiliza en cualquier contexto excepto
- como el operando del operador de dirección de memoria (si está permitido),
- como el operando de los operadores de incremento y decremento (pre/post),
- como el operando izquierdo del operador de acceso a miembro (punto),
- como el operando izquierdo de los operadores de asignación y asignación compuesta ,
-
como el operando de
sizeof,
sufre
conversión de lvalue
: el tipo permanece igual, pero pierde
const
/
volatile
/
restrict
-calificadores y
atomic
propiedades, si las tiene. El valor permanece igual, pero pierde sus propiedades de lvalue (la dirección ya no puede ser tomada).
Si el lvalue tiene tipo incompleto, el comportamiento es indefinido.
Si el valor-l designa un objeto de duración de almacenamiento automático cuya dirección nunca fue tomada y si ese objeto no fue inicializado (no declarado con un inicializador y no se realizó ninguna asignación a él antes de su uso), el comportamiento es indefinido.
Esta conversión modela la carga en memoria del valor del objeto desde su ubicación.
volatile int n = 1; int x = n; // la conversión de lvalue en n lee el valor de n volatile int* p = &n; // no hay conversión de lvalue: no lee el valor de n
Conversión de array a puntero
Cualquier expresión lvalue (until C99) expresión (since C99) de tipo array , cuando se utiliza en cualquier contexto excepto
- como el operando del operador de dirección ,
-
como el operando de
sizeof, -
como el operando de
typeofytypeof_unqual(desde C23) , - como el literal de cadena usado para inicialización de arreglos ,
sufre una conversión al puntero no lvalue a su primer elemento.
Si el array fue declarado
register
, el comportamiento es indefinido.
Un array que no es un lvalue, o cualquiera de sus elementos , no es accesible (until C99) , tiene tiempo de vida temporal (since C99) .
int a[3], b[3][4]; int* p = a; /* conversión a &a[0] */ int (*q)[4] = b; /* conversión a &b[0] */ struct S { int a[1]; }; struct S f(void) { struct S result = {{0}}; /* {0} desde C99 */ return result; } void g(void) { int* p = f().a; /* error hasta C99; OK desde C99 */ int n = f().a[0]; /* error hasta C99; OK desde C99 */ f().a[0] = 13; /* error hasta C99; UB desde C99 */ (void)p, (void)n; } int main(void) { return 0; }
Conversión de función a puntero
Cualquier expresión designadora de función, cuando se utiliza en cualquier contexto que no sea
- como el operando del operador de dirección ,
-
como el operando de
sizeof, -
como el operando de
typeofytypeof_unqual(desde C23) ,
sufre una conversión al puntero no lvalue a la función designada por la expresión.
int f(int); int (*p)(int) = f; // conversión a &f (***p)(1); // desreferenciación repetida a f y conversión de vuelta a &f
Semántica de conversión implícita
La conversión implícita, ya sea como si fuera por asignación o una conversión aritmética usual , consiste en dos etapas:
Tipos compatibles
La conversión de un valor de cualquier tipo a cualquier tipo compatible es siempre una operación nula y no cambia la representación.
uint8_t (*a)[10]; // si uint8_t es un typedef para unsigned char unsigned char (*b)[] = a; // entonces estos tipos de puntero son compatibles
Promociones de enteros
La promoción de enteros es la conversión implícita de un valor de cualquier tipo entero con rango menor o igual al rango de int o de un campo de bits de tipo _Bool (until C23) bool (since C23) , int , signed int , unsigned int , al valor de tipo int o unsigned int .
Si int puede representar el rango completo de valores del tipo original (o el rango de valores del campo de bits original), el valor se convierte al tipo int . De lo contrario, el valor se convierte a unsigned int .
|
El valor de un campo de bits de un tipo entero de precisión de bits se convierte al tipo entero de precisión de bits correspondiente. De lo contrario, los tipos enteros de precisión de bits están exentos de las reglas de promoción de enteros. |
(desde C23) |
Las promociones enteras preservan el valor, incluido el signo:
int main(void) { void f(); // declaración de función al estilo antiguo // desde C23, void f(...) tiene el mismo comportamiento respecto a promociones char x = 'a'; // conversión de entero de int a char f(x); // promoción de entero de char de vuelta a int } void f(x) int x; {} // la función espera int
rank anterior es una propiedad de todo tipo entero y se define de la siguiente manera:
|
8)
el rango de un tipo entero con signo de precisión de bits será mayor que el rango de cualquier tipo entero estándar con menor anchura o cualquier tipo entero de precisión de bits con menor anchura.
9)
el rango de cualquier tipo entero de precisión de bits relativo a un tipo entero extendido de la misma anchura está definido por la implementación.
|
(desde C23) |
Nota: las promociones de enteros se aplican únicamente
- como parte de las conversiones aritméticas usuales (ver arriba),
- como parte de las promociones de argumentos por defecto (ver arriba),
- al operando de los operadores aritméticos unarios + y - ,
- al operando del operador unario de bits ~ ,
- a ambos operandos de los operadores de desplazamiento << y >> .
Conversión booleanaUn valor de cualquier tipo escalar puede convertirse implícitamente a _Bool (hasta C23) bool (desde C23) . Los valores que comparan iguales a una expresión constante entera de valor cero (hasta C23) son cero (para tipos aritméticos), nulos (para tipos puntero) o tienen tipo nullptr_t (desde C23) se convierten a 0 (hasta C23) false (desde C23) , todos los demás valores se convierten a 1 (hasta C23) true (desde C23) . bool b1 = 0.5; // b1 == 1 (0.5 converted to int would be zero) bool b2 = 2.0*_Imaginary_I; // b2 == 1 (but converted to int would be zero) bool b3 = 0.0 + 3.0*I; // b3 == 1 (but converted to int would be zero) bool b4 = 0.0 / 0.0; // b4 == 1 (NaN does not compare equal to zero) bool b5 = nullptr; // b5 == 0 (since C23: nullptr is converted to false) |
(desde C99) |
Conversiones de enteros
Un valor de cualquier tipo entero puede convertirse implícitamente a cualquier otro tipo entero. Excepto cuando esté cubierto por las promociones y conversiones booleanas mencionadas anteriormente, las reglas son:
- si el tipo destino puede representar el valor, el valor permanece sin cambios,
-
de lo contrario, si el tipo destino es sin signo, el valor
2
b
, donde b es el número de bits de valor en el tipo destino, se resta o suma repetidamente al valor origen hasta que el resultado quepa en el tipo destino. En otras palabras, los enteros sin signo implementan aritmética modular. - de lo contrario, si el tipo destino es con signo, el comportamiento está definido por la implementación (lo cual puede incluir la generación de una señal).
char x = 'a'; // int -> char, resultado sin cambios unsigned char n = -123456; // objetivo es unsigned, resultado es 192 (es decir, -123456+483*256) signed char m = 123456; // objetivo es signed, resultado está definido por la implementación assert(sizeof(int) > -1); // assert falla: // operador > solicita conversión de -1 a size_t, // objetivo es unsigned, resultado es SIZE_MAX
Conversiones de coma flotante a entero real
Un valor finito de cualquier tipo de punto flotante real puede convertirse implícitamente a cualquier tipo entero. Excepto cuando está cubierto por la conversión booleana anterior, las reglas son:
- La parte fraccionaria se descarta (truncada hacia cero).
-
- Si el valor resultante puede ser representado por el tipo de destino, se utiliza ese valor
- de lo contrario, el comportamiento no está definido.
int n = 3.14; // n == 3 int x = 1e10; // comportamiento indefinido para int de 32 bits
Un valor de cualquier tipo entero puede convertirse implícitamente a cualquier tipo de coma flotante real.
- si el valor puede representarse exactamente por el tipo de destino, permanece sin cambios.
- si el valor puede representarse, pero no de forma exacta, el resultado es una elección definida por la implementación del valor más cercano superior o inferior, aunque si se admite aritmética IEEE, el redondeo es al más cercano. No está especificado si FE_INEXACT se activa en este caso.
- si el valor no puede representarse, el comportamiento es indefinido, aunque si se admite aritmética IEEE, FE_INVALID se activa y el valor resultante no está especificado.
El resultado de esta conversión puede tener un rango y precisión mayores de lo que indica su tipo de destino (consulte FLT_EVAL_METHOD ).
Si se necesita control sobre FE_INEXACT en conversiones de punto flotante a entero, rint y nearbyint pueden utilizarse.
double d = 10; // d = 10.00 float f = 20000001; // f = 20000000.00 (FE_INEXACT) float x = 1 + (long long)FLT_MAX; // comportamiento indefinido
Conversiones de números reales en punto flotante
Un valor de cualquier tipo real de punto flotante puede convertirse implícitamente a cualquier otro tipo real de punto flotante.
- Si el valor puede ser representado exactamente por el tipo destino, permanece sin cambios.
- Si el valor puede ser representado, pero no de forma exacta, el resultado es el valor más cercano superior o el más cercano inferior (en otras palabras, la dirección del redondeo está definida por la implementación), aunque si se soporta aritmética IEEE, el redondeo es al más cercano.
-
Si el valor no puede ser representado, el comportamiento es indefinido
.Esta sección está incompleta
Razón: verificar IEEE si se requiere infinito con signo apropiado
El resultado de esta conversión puede tener mayor rango y precisión de lo que indica su tipo de destino (consulte FLT_EVAL_METHOD ).
double d = 0.1; // d = 0.1000000000000000055511151231257827021181583404541015625 float f = d; // f = 0.100000001490116119384765625 float x = 2 * (double)FLT_MAX; // indefinido
Conversiones de tipos complejosUn valor de cualquier tipo complejo puede convertirse implícitamente a cualquier otro tipo complejo. La parte real y la parte imaginaria siguen individualmente las reglas de conversión para los tipos de coma flotante reales. Conversiones de tipos imaginariosUn valor de cualquier tipo imaginario puede convertirse implícitamente a cualquier otro tipo imaginario. La parte imaginaria sigue las reglas de conversión para los tipos de coma flotante reales. double imaginary d = 0.1*_Imaginary_I; float imaginary f = d; // f es 0.100000001490116119384765625*I Conversiones real-complejoUn valor de cualquier tipo de coma flotante real puede convertirse implícitamente a cualquier tipo complejo.
Un valor de cualquier tipo complejo puede convertirse implícitamente a cualquier tipo de coma flotante real.
Nota: en la conversión complejo-real, un NaN en la parte imaginaria no se propagará al resultado real. double complex z = 0.5 + 3*I; float f = z; // la parte imaginaria se descarta, f se establece en 0.5 z = f; // establece z en 0.5 + 0*I Conversiones real-imaginarioUn valor de cualquier tipo imaginario puede convertirse implícitamente a cualquier tipo real (entero o coma flotante). El resultado es siempre un cero positivo (o sin signo), excepto cuando el tipo destino es _Bool (hasta C23) bool (desde C23) , en cuyo caso se aplican las reglas de conversión booleana. Un valor de cualquier tipo real puede convertirse implícitamente a cualquier tipo imaginario. El resultado es siempre un cero imaginario positivo. double imaginary z = 3*I; bool b = z; // Conversión booleana: establece b en true float f = z; // Conversión real-imaginario: establece f en 0.0 z = 3.14; // Conversión imaginario-real: establece z en 0*_Imaginary_I Conversiones complejo-imaginarioUn valor de cualquier tipo imaginario puede convertirse implícitamente a cualquier tipo complejo.
Un valor de cualquier tipo complejo puede convertirse implícitamente a cualquier tipo imaginario.
double imaginary z = I * (3*I); // el resultado complejo -3.0+0i pierde la parte real // establece z en 0*_Imaginary_I |
(desde C99) |
Conversiones de punteros
Un puntero a void puede convertirse implícitamente hacia y desde cualquier puntero a tipo objeto con la siguiente semántica:
- Si un puntero a objeto se convierte a un puntero a void y viceversa, su valor se compara igual al puntero original.
- No se ofrecen otras garantías.
int* p = malloc(10 * sizeof(int)); // malloc devuelve void*
Un puntero a un tipo no calificado puede convertirse implícitamente al puntero a la versión calificada de ese tipo (en otras palabras,
const
,
volatile
, y
restrict
pueden añadirse). El puntero original y el resultado se comparan iguales.
int n; const int* p = &n; // &n tiene tipo int*
Cualquier expresión constante entera constante con valor 0 así como cualquier expresión de puntero entero con valor cero convertida al tipo void * puede ser convertida implícitamente a cualquier tipo de puntero (tanto puntero a objeto como puntero a función). El resultado es el valor de puntero nulo de su tipo, garantizado que comparará desigual con cualquier valor de puntero no nulo de ese tipo. Esta expresión entera o void * se conoce como constante de puntero nulo y la biblioteca estándar proporciona una definición de esta constante como la macro NULL .
int* p = 0; double* q = NULL;
Notas
Aunque el desbordamiento de enteros con signo en cualquier operador aritmético es comportamiento indefinido, desbordar un tipo entero con signo en una conversión de enteros es meramente comportamiento no especificado.
Por otro lado, aunque el desbordamiento de enteros sin signo en cualquier operador aritmético (y en la conversión de enteros) es una operación bien definida y sigue las reglas de la aritmética modular, desbordar un entero sin signo en una conversión de punto flotante a entero es comportamiento indefinido: los valores de tipo de punto flotante real que pueden convertirse a entero sin signo son los valores del intervalo abierto
(
-1
,
Unnn_MAX
+ 1
)
.
unsigned int n = -1.0; // comportamiento indefinido
Las conversiones entre punteros y enteros (excepto desde puntero a _Bool (hasta C23) bool (desde C23) y (desde C99) desde una expresión constante entera con valor cero a puntero), entre punteros a objetos (excepto cuando uno de ellos es un puntero a void) y conversiones entre punteros a funciones (excepto cuando las funciones tienen tipos compatibles) nunca son implícitas y requieren un operador de cast .
No existen conversiones (implícitas o explícitas) entre punteros a funciones y punteros a objetos (incluyendo void * ) o enteros.
Referencias
- Estándar C23 (ISO/IEC 9899:2024):
-
- 6.3 Conversiones (p: 44-50)
- Estándar C17 (ISO/IEC 9899:2018):
-
- 6.3 Conversiones (p: 37-41)
- Estándar C11 (ISO/IEC 9899:2011):
-
- 6.3 Conversiones (p: 50-56)
- Estándar C99 (ISO/IEC 9899:1999):
-
- 6.3 Conversiones (p: 42-48)
- Estándar C89/C90 (ISO/IEC 9899:1990):
-
- 3.2 Conversiones
Véase también
|
Documentación de C++
para
Conversiones implícitas
|