¿Qué sucede cuando asigno long int a int en C?

9 minutos de lectura

avatar de usuario
Khaloymes

En una tarea reciente me dijeron que usara long variable para almacenar un resultado, ya que puede ser un número grande.

Decidí verificar si realmente me importa, en mi sistema (compilador Intel Core i5/64-bit Windows 7/gnu gcc) y descubrí que el siguiente código:

printf("sizeof(char) => %d\n", sizeof(char));
printf("sizeof(short) => %d\n", sizeof(short));
printf("sizeof(short int) => %d\n", sizeof(short int));
printf("sizeof(int) => %d\n", sizeof(int));
printf("sizeof(long) => %d\n", sizeof(long));
printf("sizeof(long int) => %d\n", sizeof(long int));
printf("sizeof(long long) => %d\n", sizeof(long long));
printf("sizeof(long long int) => %d\n", sizeof(long long int));

produce la siguiente salida:

sizeof(char) => 1
sizeof(short) => 2
sizeof(short int) => 2
sizeof(int) => 4
sizeof(long) => 4
sizeof(long int) => 4
sizeof(long long) => 8
sizeof(long long int) => 8

En otras palabras, en mi sistema, int y long son iguales, y lo que sea será demasiado grande para int para sostener, será demasiado grande para long para sostener también.

La tarea en sí no es el problema aquí. Me pregunto cómo, en un sistema donde int < long¿debo asignar un int mucho tiempo?

Soy consciente del hecho de que hay numerosas preguntas estrechamente relacionadas sobre este tema, pero siento que las respuestas dentro de ellas no me brindan una comprensión completa de lo que sucederá o puede suceder en el proceso.

Básicamente estoy tratando de averiguar lo siguiente:

  1. ¿Debería lanzar long para int antes de la asignación, o ya que long
    no es un tipo de datos diferente, sino simplemente un modificador,
    se considerará inofensivo ceder directamente?
  2. ¿Qué sucede en los sistemas donde long > int? ¿Será el resultado indefinido (o impredecible) o hará que se omitan las partes adicionales de la variable?
  3. ¿Cómo funciona el casting de long para int funciona en C?
  4. ¿Cómo funciona la tarea de long para int funciona en C cuando no uso el casting?

  • C tiene modificadores (volatile, const), pero short, long, signedy unsigned están NO modificadores Especifican tipos únicos.

    – Pato mugido

    30 de noviembre de 2012 a las 20:24


  • Cuidado ahí. Si tuviera que mirar, digamos, un sistema Linux AMD64. long es de 8 bytes donde como int en 4. Ver en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models para algunos casos comunes.

    usuario507577

    30 de noviembre de 2012 a las 20:24

  • @Rajesh: no creo que tenga nada que ver con el sistema operativo o hardware. El tamaño de los tipos es una decisión (en su mayoría) arbitraria del compilador.

    – Pato mugido

    30 de noviembre de 2012 a las 20:27

  • @MooingDuck y cuando todos los compiladores de una plataforma hacen lo mismo, está más o menos resuelto 🙂

    usuario507577

    30 de noviembre de 2012 a las 20:31

  • @MooingDuck: la decisión tomada por el compilador (más exactamente, por sus autores) está fuertemente influenciada por el sistema operativo y el hardware. Muchos sistemas tienen una ABI que especifica tamaños de tipos enteros; seguirlo hace posible mezclar código compilado por diferentes compiladores. En algunos casos, la compatibilidad con versiones anteriores es una gran influencia, lo que a veces da como resultado cosas como 32 bits long en un sistema de 64 bits.

    –Keith Thompson

    30 de noviembre de 2012 a las 20:36

avatar de usuario
keith thompson

El lenguaje garantiza que int es de al menos 16 bits, long es de al menos 32 bits, y long puede representar por lo menos todos los valores que int puede representar.

Si asignas un long valor a un int objeto, se convertirá implícitamente. No hay necesidad de un reparto explícito; simplemente especificaría la misma conversión que ocurrirá de todos modos.

En su sistema, donde int y long tiene el mismo tamaño y rango, la conversión es trivial; simplemente copia el valor.

en un sistema donde long es más ancho que intsi el valor no cabe en un int, entonces el resultado de la conversión está definido por la implementación. (O, a partir de C99, puede generar una señal definida por la implementación, pero no conozco ningún compilador que realmente haga eso). típicamente sucede es que los bits de orden superior se descartan, pero no debe depender de eso. (Las reglas son diferentes para los tipos sin signo; el resultado de convertir un entero con signo o sin signo en un tipo sin signo está bien definido).

Si lo necesitas sin peligro asignar un long valor a un int objeto, puedes comprobar que encajará antes de hacer la tarea:

#include <limits.h> /* for INT_MIN, INT_MAX */

/* ... */

int i;
long li = /* whatever */

if (li >= INT_MIN && li <= INT_MAX) {
    i = li;
}
else {
    /* do something else? */
}

Los detalles de “algo más” van a depender de lo que quieras hacer.

Una corrección: int y long están siempre tipos distintos, incluso si tienen el mismo tamaño y representación. Los tipos aritméticos se pueden convertir libremente, por lo que a menudo esto no hace ninguna diferencia, pero por ejemplo int* y long* son tipos distintos e incompatibles; no puedes asignar un long* a una int*o viceversa, sin un elenco explícito (y potencialmente peligroso).

Y si necesita convertir un long valor a int, lo primero que debe hacer es reconsiderar el diseño de su código. A veces, tales conversiones son necesarias, pero más a menudo son una señal de que el int al que está asignando debería haber sido definido como un long en primer lugar.

  • Antes que nada, gracias por tu respuesta! Supongo (de googlear un poco) que para usar INT_MIN e INT_MAX debo incluir limites.h. ¿Hay alguna manera de averiguar sus tamaños sin incluir este encabezado? De alguna manera solo por el tamaño de tal vez?

    – Khaloymes

    1 de diciembre de 2012 a las 9:35


  • @Khaloymes: Sí, necesitas #include <limits.h>; Debería haber mencionado eso. Tenga en cuenta que estos son límites, no tamaños. Probablemente hay formas de calcularlos sin usar <limits.h>, pero ¿por qué molestarse? eso es lo que <limits.h> es para, después de todo.

    –Keith Thompson

    1 de diciembre de 2012 a las 9:57

  • Esa señal definida por la implementación es interesante. En particular, no pude encontrar nada en el estándar que diga que solo se aplica a implícito conversiones; si una implementación sigue esta ruta, el uso habitual de un molde como “no me avise, sé lo que estoy haciendo” no evitará el comportamiento, como uno esperaría intuitivamente.

    – Leushenko

    10 de junio de 2015 a las 18:08

  • @Leushenko: Correcto, se aplica a las conversiones, ya sean explícitas (casts) o implícitas.

    –Keith Thompson

    10 de junio de 2015 a las 18:11

  • @Lakey: Hubiera dicho que no, pero el estándar C se refiere a esto como “truncar”. (Y da la casualidad de que su pregunta me llevó a lo que probablemente sea un error en el estándar. N1570 5.1.2.3p11, Ejemplo 2, dice que char c1, c2; /* ... */ c1 = c1 + c2; “truncará la suma”, pero si char está firmado, el resultado está definido por la implementación. Supongo que todavía podrías llamarlo un “truncamiento”. En cualquier caso, los ejemplos no son normativos.)

    –Keith Thompson

    19 de marzo de 2019 a las 19:37

A long siempre puede representar todos los valores de int. Si el valor en cuestión se puede representar mediante el tipo de variable que asigna, el valor se conserva.

Si no se puede representar, entonces para el tipo de destino firmado, el resultado no se especifica formalmente, mientras que para el tipo de destino no firmado se especifica como el valor original módulo 2nortedonde norte es el número de bits en la representación del valor (que no es necesariamente todos los bits en el destino).

En la práctica, en las máquinas modernas también se envuelven los tipos firmados.

Eso es porque las máquinas modernas usan forma de complemento a dos para representar enteros con signo, sin ningún bit utilizado para indicar “valor no válido” o similar, es decir, todos los bits utilizados para la representación del valor.

Con norte representación del valor de los bits cualquier valor entero es X está asignado a X+K*2norte con la constante entera K elegida de manera que el resultado esté en el rango donde la mitad de los valores posibles son negativos.

Así, por ejemplo, con 32 bits int el valor -7 se representa como número de patrón de bits -7+232 = 232-7, de modo que si muestra el número que representa el patrón de bits como un entero sin signo, obtiene un número bastante grande.

La razón por la que esto se llama complemento a dos es porque tiene sentido para el sistema numérico binario, el sistema numérico de base dos. Para el sistema numérico binario también hay un complemento de unos (nótese la ubicación del apóstrofo). De manera similar, para el sistema numérico decimal existe el complemento de diez y el complemento de nueve. Con la representación del complemento de diez de 4 dígitos representarías -7 como 10000-7 = 9993. Eso es todo, de verdad.

  • C, que comienza con C99, requiere que los enteros con signo se representen como complemento a dos, complemento a uno o signo y magnitud. Casi todos los sistemas modernos utilizan el complemento a dos.

    –Keith Thompson

    30 de noviembre de 2012 a las 20:38

  • @KeithThompson ¿Cómo decide C99 exigir que los enteros con signo se representen como complemento a dos o como complemento a uno? Quiero decir, ¿hay alguna manera de que pueda saber de antemano cómo se representará mi entero con signo? ¿Hay alguna manera de cambiar la representación después de que se asignó un valor? Además, avíseme si este comentario es demasiado amplio y debe transformarse en una nueva pregunta.

    – Khaloymes

    1 de diciembre de 2012 a las 9:45

  • @Khaloymes: No estoy seguro de lo que quieres decir. Es un requisito explícito en la norma; ver sección 6.2.6.2. No puede cambiar la forma en que su sistema opera en enteros con signo; eso generalmente está determinado por el hardware. Por supuesto, puede hacer cualquier manipulación de bits que desee, pero si su sistema usa complemento a dos, hay muy pocas razones para usar alguna otra representación (a menos que esté tratando con algún requisito de datos impuesto externamente).

    –Keith Thompson

    1 de diciembre de 2012 a las 10:05

  • @KeithThompson Esto es lo que quería saber, si tengo algo que ver para decidir cómo se representará o no el entero con signo. Concluyo de su comentario que se basa en el hardware.

    – Khaloymes

    1 de diciembre de 2012 a las 10:18

  • @Khaloymes: Depende del implementador del compilador, quien debe documentar la elección. Es casi seguro que esa decisión se base en lo que admite el hardware (que, en estos días, es casi seguro que sea complemento de dos).

    –Keith Thompson

    1 de diciembre de 2012 a las 10:22

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad