Cuando aprendí el lenguaje C ++ por primera vez, aprendí que además de int, float, etc., existían versiones más pequeñas o más grandes de estos tipos de datos dentro del lenguaje. Por ejemplo, podría llamar a una variable x
int x;
or
short int x;
La principal diferencia es que short int toma 2 bytes de memoria, mientras que int toma 4 bytes, y short int tiene un valor menor, pero también podríamos llamar a esto para hacerlo aún más pequeño:
int x;
short int x;
unsigned short int x;
lo cual es aún más restrictivo.
Mi pregunta aquí es si es una buena práctica usar tipos de datos separados según los valores que tome su variable dentro del programa. ¿Es una buena idea declarar siempre las variables de acuerdo con estos tipos de datos?
c++
data-structures
Bugster
fuente
fuente
unsigned
alguna manera hace que un número entero ocupe menos espacio, lo que por supuesto es falso. Tendrá el mismo recuento de valores representables discretos (más o menos 1 dependiendo de cómo se represente el signo), pero se desplazó exclusivamente a positivo.Respuestas:
La mayoría de las veces el costo del espacio es insignificante y no debe preocuparse por ello, sin embargo, debe preocuparse por la información adicional que está dando al declarar un tipo. Por ejemplo, si usted:
Está dando información útil a otro desarrollador: el salario no puede ser negativo.
La diferencia entre short, int, long rara vez va a causar problemas de espacio en su aplicación. Es más probable que accidentalmente haga la suposición falsa de que un número siempre encajará en algún tipo de datos. Probablemente sea más seguro usar siempre int a menos que esté 100% seguro de que sus números siempre serán muy pequeños. Incluso entonces, es poco probable que le ahorre una cantidad notable de espacio.
fuente
unsigned
en este caso es una mala idea: no solo el salario no puede ser negativo, sino que la diferencia entre dos salarios tampoco puede ser negativa. (En general, la utilización sin firmar para nada más que de bits haciendo girar y tener un comportamiento definido en caso de desbordamiento es una mala idea.)El OP no dijo nada sobre el tipo de sistema para el que están escribiendo programas, pero supongo que el OP estaba pensando en una PC típica con GB de memoria desde que se menciona C ++. Como dice uno de los comentarios, incluso con ese tipo de memoria, si tiene varios millones de elementos de un tipo, como una matriz, el tamaño de la variable puede marcar la diferencia.
Si ingresa al mundo de los sistemas embebidos, que no está realmente fuera del alcance de la pregunta, ya que el OP no lo limita a las PC, entonces el tamaño de los tipos de datos es muy importante. Acabo de terminar un proyecto rápido en un microcontrolador de 8 bits que tiene solo 8K palabras de memoria de programa y 368 bytes de RAM. Ahí, obviamente, cada byte cuenta. Uno nunca usa una variable más grande de lo que necesita (tanto desde el punto de vista del espacio como del tamaño del código: los procesadores de 8 bits usan muchas instrucciones para manipular datos de 16 y 32 bits). ¿Por qué usar una CPU con recursos tan limitados? En grandes cantidades, pueden costar tan poco como una cuarta parte.
Actualmente estoy haciendo otro proyecto integrado con un microcontrolador basado en MIPS de 32 bits que tiene 512K bytes de flash y 128K bytes de RAM (y cuesta alrededor de $ 6 en cantidad). Al igual que con una PC, el tamaño de datos "natural" es de 32 bits. Ahora se vuelve más eficiente, en términos de código, usar ints para la mayoría de las variables en lugar de caracteres o cortos. Pero una vez más, cualquier tipo de matriz o estructura debe considerarse si se justifican los tipos de datos más pequeños. A diferencia de los compiladores para sistemas más grandes, es más probable que las variables en una estructura se empaqueten en un sistema embebido. Me aseguro de intentar siempre poner primero todas las variables de 32 bits, luego 16 bits, luego 8 bits para evitar cualquier "agujero".
fuente
La respuesta depende de su sistema. En general, aquí están las ventajas y desventajas de usar tipos más pequeños:
Ventajas
Desventajas
Mi consejo es que te guste esto:
Alternativamente, puede usar
int_leastn_t
oint_fastn_t
desde stdint.h, donde n es el número 8, 16, 32 o 64.int_leastn_t
tipo significa "Quiero que esto sea al menos n bytes pero no me importa si el compilador lo asigna como un tipo más grande para adaptarse a la alineación ".int_fastn_t
significa "Quiero que esto tenga una longitud de n bytes, pero si hace que mi código se ejecute más rápido, el compilador debe usar un tipo más grande que el especificado".En general, los diversos tipos stdint.h son una práctica mucho mejor que los simples
int
, etc., porque son portátiles. La intenciónint
era no darle un ancho específico únicamente para hacerlo portátil. Pero en realidad, es difícil de portar porque nunca se sabe qué tan grande será en un sistema específico.fuente
Dependiendo de cómo funcione el sistema operativo específico, generalmente espera que la memoria se asigne sin optimizar, de modo que cuando solicita un byte, una palabra u otro tipo de datos pequeños, el valor ocupa un registro completo, todo es muy propio. Sin embargo, cómo funciona su compilador o intérprete para interpretar esto es otra cosa, por lo que si compilara un programa en C #, por ejemplo, el valor podría ocupar físicamente un registro para sí mismo, sin embargo, el valor se verificará en el límite para asegurarse de que no intente almacenar un valor que exceda los límites del tipo de datos deseado.
En cuanto al rendimiento, y si es realmente pedante con respecto a estas cosas, es probable que sea más rápido simplemente usar el tipo de datos que más se aproxime al tamaño del registro objetivo, pero luego se pierde todo ese encantador azúcar sintáctico que hace que trabajar con variables sea tan fácil .
Cómo te ayuda esto? Bueno, realmente depende de ti decidir qué tipo de situación estás codificando. Para casi todos los programas que he escrito, es suficiente simplemente confiar en su compilador para optimizar las cosas y usar el tipo de datos que sea más útil para usted. Si necesita alta precisión, use los tipos de datos de punto flotante más grandes. Si trabaja solo con valores positivos, probablemente pueda usar un entero sin signo, pero en su mayor parte, simplemente usar el tipo de datos int es suficiente.
Sin embargo, si tiene algunos requisitos de datos muy estrictos, como escribir un protocolo de comunicaciones o algún tipo de algoritmo de cifrado, el uso de tipos de datos con control de rango puede ser muy útil, especialmente si está tratando de evitar problemas relacionados con desbordamientos / subestimaciones de datos o valores de datos no válidos.
La única otra razón por la que puedo pensar fuera de mi cabeza para usar tipos de datos específicos es cuando estás tratando de comunicar la intención dentro de tu código. Si usa una abreviatura, por ejemplo, le está diciendo a otros desarrolladores que está permitiendo números positivos y negativos dentro de un rango de valores muy pequeño.
fuente
Como comentó scarfridge , esta es una
Intentar optimizar el uso de la memoria podría afectar otras áreas de rendimiento, y las reglas de oro de la optimización son:
Para saber si ahora es el momento de optimizar, se requiere evaluación comparativa y pruebas. Necesita saber dónde su código está siendo ineficiente, para poder orientar sus optimizaciones.
Para determinar si la versión optimizada del código es realmente mejor que la implementación ingenua en un momento dado, debe compararlos lado a lado con los mismos datos.
Además, recuerde que el hecho de que una implementación dada sea más eficiente en la generación actual de CPU, no significa que siempre sea así. Mi respuesta a la pregunta ¿Es importante la microoptimización al codificar? detalla un ejemplo de la experiencia personal donde una optimización obsoleta resultó en una desaceleración del orden de magnitud.
En muchos procesadores, los accesos de memoria no alineados son significativamente más costosos que los accesos de memoria alineados. Empacar un par de cortos en su estructura puede significar que su programa tiene que realizar la operación de empaque / desempaque cada vez que toque cualquiera de los valores.
Por esta razón, los compiladores modernos ignoran sus sugerencias. Como Nikie comenta:
Segundo adivina tu compilador bajo tu propio riesgo.
Hay un lugar para tales optimizaciones, cuando se trabaja con conjuntos de datos de terabytes o microcontroladores integrados, pero para la mayoría de nosotros, no es realmente una preocupación.
fuente
Esto es incorrecto. No puede hacer suposiciones acerca de cuántos bytes contiene cada tipo, aparte de
char
ser un byte y al menos 8 bits por byte, junto con que el tamaño de cada tipo sea mayor o igual que el anterior.Los beneficios de rendimiento son increíblemente minúsculos para las variables de la pila: es probable que estén alineados / rellenados de todos modos.
Debido a esto,
short
ylong
prácticamente no tengo uso hoy en día, y casi siempre es mejor usarloint
.Por supuesto, también hay uno
stdint.h
que está perfectamente bien cuandoint
no se corta. Si alguna vez está asignando grandes matrices de enteros / estructuras, entoncesintX_t
tiene sentido ya que puede ser eficiente y confiar en el tamaño del tipo. Esto no es prematuro, ya que puede ahorrar megabytes de memoria.fuente
long
puede ser diferente aint
. Si su compilador es LP64,int
tiene 32 bits ylong
64 bits y encontrará queint
s todavía puede estar alineado a 4 bytes (mi compilador lo hace, por ejemplo).int64_t
int32_t
,int_fast32_t
ylong
son todas buenas opciones,long long
es un desperdicio yint
no es portátil.Esto será desde un punto de vista de OOP y / o empresarial / aplicación y podría no ser aplicable en ciertos campos / dominios, pero quiero plantear el concepto de obsesión primitiva .
Es una buena idea usar diferentes tipos de datos para diferentes tipos de información en su aplicación. Sin embargo, probablemente NO sea una buena idea usar los tipos integrados para esto, a menos que tenga algunos problemas de rendimiento graves (que se han medido y verificado, etc.).
Si queremos modelar temperaturas en Kelvin en nuestra aplicación, PODRÍAMOS usar una
ushort
ouint
algo similar para denotar que "la noción de grados negativos Kelvin es absurda y un error de lógica de dominio". La idea detrás de esto es sólida, pero no vas hasta el final. Lo que nos dimos cuenta es que no podemos tener valores negativos, por lo que es útil si podemos obtener el compilador para asegurarnos de que nadie asigne un valor negativo a una temperatura Kelvin. También es cierto que no puede realizar operaciones bit a bit en temperaturas. Y no puede agregar una medida de peso (kg) a una temperatura (K). Pero si modela la temperatura y la masa comouint
s, podemos hacer exactamente eso.El uso de tipos incorporados para modelar nuestras entidades DOMINIO está destinado a generar un código desordenado y algunas comprobaciones perdidas e invariantes rotos. Incluso si un tipo captura ALGUNA parte de la entidad (no puede ser negativa), seguramente perderá otras (no se puede usar en expresiones aritméticas arbitrarias, no se puede tratar como una matriz de bits, etc.)
La solución es definir nuevos tipos que encapsulan los invariantes. De esta manera, puede asegurarse de que el dinero es dinero y las distancias son distancias, y no puede sumarlos, y no puede crear una distancia negativa, pero PUEDE crear una cantidad negativa de dinero (o una deuda). Por supuesto, estos tipos utilizarán los tipos integrados internamente, pero esto está oculto para los clientes. En relación con su pregunta sobre el rendimiento / consumo de memoria, este tipo de cosas puede permitirle cambiar la forma en que las cosas se almacenan internamente sin cambiar la interfaz de sus funciones que operan en las entidades de su dominio, en caso de que se dé cuenta, a
short
es demasiado maldito grande.fuente
Sí, por supuesto. Es una buena idea usarlo
uint_least8_t
para diccionarios, matrices de constantes enormes, buffers, etc. Es mejor usarlouint_fast8_t
para fines de procesamiento.uint8_least_t
(almacenamiento) ->uint8_fast_t
(procesamiento) ->uint8_least_t
(almacenamiento).Por ejemplo, está tomando un símbolo de 8 bits
source
, códigos de 16 bitsdictionaries
y unos 32 bitsconstants
. Entonces estás procesando operaciones de 10-15 bits con ellos y salidas de 8 bitsdestination
.Imaginemos que tiene que procesar 2 gigabytes de
source
. La cantidad de operaciones de bits es enorme. Recibirá una gran bonificación de rendimiento si cambia a tipos rápidos durante el procesamiento. Los tipos rápidos pueden ser diferentes para cada familia de CPU. Puede incluirstdint.h
y el usouint_fast8_t
,uint_fast16_t
,uint_fast32_t
, etc.Podrías usar en
uint_least8_t
lugar deuint8_t
portabilidad. Pero nadie sabe realmente qué CPU moderna utilizará esta función. La máquina VAC es una pieza de museo. Entonces quizás sea una exageración.fuente