c++ c memory types allocation

c++ - Pérdida de memoria? Si main() solo debe devolver 0 o 1, ¿por qué main se declara con int y no short int o incluso char?



memory types (7)

Es por una máquina que tiene medio siglo de antigüedad.

En el día en que se creó C, un int era una palabra de máquina en el PDP-11, dieciséis bits, y era natural y eficiente tener un retorno main .

La "palabra máquina" era el único tipo en el lenguaje B, que Ritchie y Thompson habían desarrollado anteriormente, y del que C surgió.
Cuando C agregó tipos, no especificar uno le dio una palabra de máquina: un int .
(Era muy importante en ese momento ahorrar espacio, por lo que no fue necesario explicar el tipo más común).

Entonces, desde que un programa B comenzó con

main()

y los programadores son generalmente conservadores del lenguaje, C hizo lo mismo y devolvió un int .

Por ejemplo:

#include <stdio.h> int main (void) /* Why int and not short int? - Waste of Memory */ { printf("Hello World!"); return 0; }

Por qué main() se define convencionalmente con el tipo int , que asigna 4 bytes en la memoria en 32 bits, si generalmente solo devuelve 0 o 1, mientras que otros tipos como short int (2 bytes, 32 bits) o incluso char ( 1 byte, 32 bits) ¿ahorraría más memoria?

Está desperdiciando espacio en la memoria.

NOTA: La pregunta no es un duplicado del hilo dado; sus respuestas solo corresponden al valor de retorno en sí, pero no a su tipo de datos en foco explícito.

La pregunta es para C y C ++. Si las respuestas entre esos se alteran, comparta su sabiduría con la mención del contexto en el que se centra el idioma en particular.


Estás trabajando o aprendiendo C, así que creo que es una muy buena idea que te preocupe la eficiencia. Sin embargo, parece que hay algunas cosas que parecen necesitar aclaración aquí.

Primero, el tipo de datos int no es un nunca fue destinado a significar "32 bits". La idea era que int sería el tipo entero binario más natural en la máquina de destino, generalmente el tamaño de un registro.

En segundo lugar, el valor de retorno de main () está destinado a acomodar una amplia gama de implementaciones en diferentes sistemas operativos. Un sistema POSIX utiliza un código de retorno de 8 bits sin firmar. Windows utiliza 32 bits que son interpretados por el shell CMD como complemento de 2 firmado. Otro sistema operativo podría elegir otra cosa.

Y finalmente, si le preocupa el "desperdicio" de memoria, ese es un problema de implementación que ni siquiera es un problema en este caso. Los códigos de retorno de main generalmente se devuelven en los registros de la máquina, no en la memoria, por lo que no hay costos ni ahorros involucrados. Incluso si lo hubiera, guardar 2 bytes en la ejecución de un programa no trivial no merece el tiempo de ningún desarrollador.


Hay dos razones por las que no consideraría esto un desperdicio:

1 uso práctico del código de salida de 4 bytes

Si desea devolver un código de salida que describa exactamente un error, desea más de 8 bits.

Como ejemplo, es posible que desee agrupar errores: el primer byte podría describir el tipo de error vago, el segundo byte podría describir la función que causó el error, el tercer byte podría dar información sobre la causa del error y el cuarto byte describe Información adicional de depuración.

2 acolchado

Si pasa un solo corto o char, todavía estarán alineados para caber en una palabra de máquina, que a menudo es de 4 Bytes / 32 bits dependiendo de la arquitectura. Esto se llama relleno y significa que lo más probable es que aún necesite 32 bits de memoria para devolver un solo corto o char.


La convención pasada de moda con la mayoría de los shells es usar los 8 bits menos significativos de int , no solo 0 o 1. 16 bits son cada vez más comunes debido a que ese es el tamaño mínimo de un int permitido por el estándar.

¿Y cuál sería el problema con el desperdicio de espacio? ¿Se desperdicia realmente el espacio? ¿Está su computadora tan llena de "cosas" que el sizeof(int) * CHAR_BIT - 8 restante de sizeof(int) * CHAR_BIT - 8 marcaría la diferencia? ¿Podría la arquitectura explotar eso y usar esos bits restantes para otra cosa? Lo dudo mucho.

Por lo tanto, no diría que la memoria está desperdiciada ya que la recupera del sistema operativo cuando finaliza el programa. Quizás extravagante ? ¿Un poco como usar una copa de vino grande para una pequeña bebida quizás?


La respuesta es "porque generalmente no devuelve solo 0 o 1." Encontré este hilo de la comunidad de ingeniería de software que al menos parcialmente responde a su pregunta. Aquí están los dos aspectos más destacados, primero de la respuesta aceptada:

Un número entero da más espacio que un byte para informar el error. Se puede enumerar (el retorno de 1 significa XYZ, el retorno de 2 significa ABC, el retorno de 3, significa DEF, etc.) o usarse como indicadores ( 0x0001 significa que falló, 0x0002 significa que falló, 0x0003 significa que esto y aquello fallaron ) Limitar esto a solo un byte podría quedarse sin banderas (solo 8), por lo que la decisión fue probablemente usar un número entero.

Keith Thompson también plantea un punto interesante:

Por ejemplo, en el dialecto de C utilizado en el sistema operativo Plan 9 , main se declara normalmente como una función void , pero el estado de salida se devuelve al entorno de llamada al pasar un puntero de cadena a la función exits() . La cadena vacía denota éxito, y cualquier cadena no vacía denota algún tipo de falla. Esto podría haberse implementado haciendo que main devuelva un resultado char* .

Aquí hay otra parte interesante de un foro de unix.com :

(Algunos de los siguientes pueden ser específicos para x86).

Volviendo a la pregunta original: ¿Dónde se almacena el estado de salida? Dentro del núcleo.

Cuando llama a exit (n), los 8 bits menos significativos del número entero n se escriben en un registro de la CPU. La implementación de la llamada al sistema kernel lo copiará a una estructura de datos relacionada con el proceso.

¿Qué pasa si su código no llama a exit ()? La biblioteca de tiempo de ejecución c responsable de invocar main () llamará a exit () (o alguna de sus variantes) en su nombre. El valor de retorno de main (), que se pasa al tiempo de ejecución c en un registro, se usa como argumento para la llamada de salida ().

En relación con la última cita, aquí hay otra de cppreference.com

5) La ejecución del retorno (o el retorno implícito al llegar al final de main) es equivalente a abandonar primero la función normalmente (que destruye los objetos con una duración de almacenamiento automática) y luego llamar a std :: exit con el mismo argumento que el argumento del regreso. (std :: exit luego destruye objetos estáticos y finaliza el programa)

Por último, encontré este ejemplo realmente genial here (aunque el autor de la publicación se equivoca al decir que el resultado devuelto es el valor devuelto módulo 512). Después de compilar y ejecutar lo siguiente:

int main() { return 42001; }

en un sistema my * compatible con POSIX , echo $? devuelve 17. Esto se debe a que 42001 % 256 == 17 que muestra que se utilizan realmente 8 bits de datos. Con eso en mente, elegir int asegura que haya suficiente almacenamiento disponible para pasar la información del estado de salida del programa, porque, según esta respuesta , el cumplimiento del estándar C ++ garantiza ese tamaño de int (en bits)

no puede ser inferior a 8. Eso se debe a que debe ser lo suficientemente grande como para contener "las unidades de código de ocho bits del formulario de codificación Unicode UTF-8".

EDITAR:

* Como Andrew Henle señaló en el comentario:

Un sistema totalmente compatible con POSIX hace que esté disponible todo el valor de retorno int , no solo 8 bits. Ver pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html : "Si si_code es igual a CLD_EXITED , si_status contiene el valor de salida del proceso; de lo contrario, es igual a la señal que causó que el proceso cambiar el estado. El valor de salida en si_status será igual al valor de salida completo (es decir, el valor pasado a _exit() , _Exit() , o exit() , o devuelto desde main() ); no estará limitado a los ocho bits menos significativos del valor ".

Creo que esto lo convierte en un argumento aún más fuerte para el uso de int sobre tipos de datos de tamaños más pequeños.


Por lo general, los ensambladores usan sus registros para devolver un valor (por ejemplo, el registro AX en procesadores Intel). El tipo int corresponde a la palabra máquina. Es decir, no es necesario convertir, por ejemplo, un byte que corresponda al tipo char a la palabra máquina.

Y, de hecho, main puede devolver cualquier valor entero.


Primero: solo su suposición / declaración if it usually returns only 0 or 1 es incorrecta.

Por lo general, se espera que el código de retorno sea 0 si no se produjo ningún error, pero de lo contrario, puede devolver cualquier número para representar diferentes errores. Y la mayoría (al menos los programas de línea de comandos) lo hacen. Muchos programas también generan números negativos.

Sin embargo, hay algunos códigos usados ​​comunes https://www.tldp.org/LDP/abs/html/exitcodes.html también aquí otro miembro de SO señala un encabezado de Unix que contiene algunos códigos https://.com/a/24121322/2331592

Entonces, después de todo, no es solo una C++ tipo C o C++ , sino que también tiene razones históricas sobre cómo funcionan la mayoría de los sistemas operativos y cómo se comportan los programas y, por lo tanto, los lenguajes tienen que soportar eso y, al menos, los C similares a C hacen usando int main(...) .

2º: su conclusión It is wasting memory space está mal.

  1. Usar un int en comparación con un tipo más corto no implica ningún desperdicio. La memoria generalmente se maneja en tamaño de palabra (eso significa que puede depender de su arquitectura) de todos modos
  2. trabajar con subtipos de palabras implica el cálculo general en alguna arquitectura (leer: cargar, palabra, enmascarar bits no relacionados; almacenar: cargar memoria, enmascarar bits variables, o ellos con el nuevo valor, escribir la palabra)
  3. la memoria no se desperdicia a menos que la use. si escribe return 0; en este punto nunca se usa memoria. si return myMemorySaving8bitVar; solo tiene 1 byte usado (lo más probable en la pila (si no está optimizado)