Veámos, voy a intentar interpretarlo a ver si estoy en lo cierto:
#include <stdio.h>
// Incluye una librería con una serie funciones prefijadas entre la las cuales, imagino, está definido el significado de “int”, “ unsigned”, etc.
No.
El significado de "int", "unsigned", etc., viene establecido directamente en el compilador, no en las librerías.
La librería "stdio.h" sólo define funciones de entrada y salida de datos, como la función printf() y la función scanf(). También getchar(), el tipo de datos FILE* para manejar archivos, etc.
int main()
// Supongo que quiere decir que en el área local, o zona de la memoria principal donde se guardan las rutinas del programa, todas las variables o constantes que se definan serán entendidas por la máquina como valores enteros.
NO.
Lo que rodea al "main" para nosotros es irrelevante.
Ese "int" está ahí porque lo puso PabloN.
Lo único que hace eso es enviar un número con código de error al sistema operativo, una vez que el programa terminó.
{
unsigned int cero = 0;
// Esto no sé del todo cómo entenderlo, parece que define una constante llamada “cero” la cual es definida a su vez como entera, sin signo y de valor 0. Los entresijos del comando unsigned -en cuanto al funcionamiento de la máquina- se me escapa; yo sé que hasta el 127 decimal los entiende positivos , así, el -1 se expresa como 256-1, el -2 como 256-2... y cuando llega al 127 es positivo; esto depende en binario del séptimo bit, que pasa a valer 0 de 127 para abajo... Dado esto, no sé cómo la hace para considerar un número sin signo, pues el séptimo bit será cero ó 1; creo que nunca lo estudié o nunca leí sobre ello.
Bueno, no te compliques con eso, porque la representación binaria interna de los números no es algo que nos importe del todo.
Te explico por qué. El lenguaje C no es lenguaje de máquina, y entonces la manera en que se representan internamente los datos depende de cada compilador.
No es algo que te puedas aprender, porque no está fijado por las reglas del lenguaje C.
Un programador en C no tiene por qué saber o supone cómo se representan los números (o cualquier dato) internamente.
La declaración:
unsigned int cero = 0;
lo que hace es definir una variable, llamada cero, de tipo unsigned int.
Esto quiere decir que lo que fuere que hayamos guardado en esa variable, siempre se va a interpretar como un número entero positivo (o 0).
Eso quiere decir "sin signo".
____________
No se trata de si el -1 es un número positivo o negativo para el C.
De lo que se trata es de cómo lo interpreta, según el tipo de datos de la variable en que fue almacenado.
No es lo mismo el 0 de un "unsigned int" que el 0 de un "signed int".
Ambos se representan igual internamente, así: 00000000.......
Es muy fácil representar el 0.
Y aunque se representan de la misma forma, son datos de distinto tipo.
La distinción de tipos de datos es una convención "de alto nivel", no es algo que puedas ver en los bits de la máquina, sino que es una convención del lenguaje.
Un ejemplo más práctico quizá te lo muestre mejor.
El caracter '@' se representa en binario internamente: 01000000.
Eso también es el código binario del número decimal 64.
Ambos se escriben en memoria como 01000000, pero el '@' representa un dato de tipo "char", mientras que 64 representa un dato de tipo "int".
¿Cómo sabe el C que son datos de distinto tipo?
Bueno, lo que hace el C es armarse una tablita con todas las variables y las constantes, y memoriza el tipo de datos de cada dato.
Cuando declaramos una variable
int x;
el compilador de C no sólo reserva una posición de memoria para la x,
sino que además memoriza en alguna parte que esa posición de memoria es de cierto tipo, en esta caso "int".
Se trata de un atributo adicional de la variable, que suele ser invisible para nosotros.
printf("(-1 + cero)/2 = %d\n\n", (-1 + cero) / 2 );
// Aquí se imprime un par de números separados por una coma. Creo, por lo que he ido viendo, que la instrucción %d llama a un tipo de variables o cosntantes (alfanuméricas, numéricas enteras, etc., según la letra que va detrás del %); en este caso parece que guarda el resultado de (-1 + cero)/2, pero no estoy seguro del todo de cómo funciona, es una instrucción muy nueva para mí que no recuerdo haber manejado mucho; leí que cuando es %c se refiere a variables tipo char o algo así pero poco más. El otro número detrás de la coma no va entre comillas, por tanto no es un valor “alfanumérico” (que digo yo en plan antiguo, no sé si se usa así todavía) con lo que la cuestión puede estar también relacionada con este tema del tipo de variables que resultan incompatibles según las instrucciones
En una futura ocasión voy a explicar mejor cómo funciona el printf().
Pero en esencia, la opción %d dice que en ese lugar se va poner un número, que es el primer dato o expresión que aparece después de la coma.
Algo más fácil de entender sería poner esto:
printf("Resultado: %d ", (-1 + cero) /2);
Qué hace el printf acá no importa mucho.
Lo que importa es qué hace la simple y tonta suma.
El problema está en la cuenta (-1 + cero).
Eso es el meollo de todo.
Como cero lo hemos definido como 0, la cuenta (-1 + cero) tendría que darnos, según lo que sabemos tanto de matemática, como del lenguaje C, un simple valor -1.
Pero no nos da eso, sino cualquier otra cosa.
Ese otro número que da, "2 mil millones y pico", no importa cuánto es, ni cómo se forma, ni qué pasó con sus bits.
Eso no tiene importancia porque depende de cada compilador,
y no es algo que esté definido en el lenguaje C.
O sea que con otro compilador puede darnos cualquier otro número, equivocado o no.
El problema está en que hay unas ciertas reglas de conversión automática de tipos de datos que el lenguaje C exige, y ahí se arma el desastre.
El -1 es de tipo "signed int",
mientras que nuestra variable "cero" fue declarada de tipo "unsigned int".
No interesa acá si el -1 es negativo, positivo, 0, sino de qué tipo es.
Lo que hace el signo de suma +, antes de sumar, es convertir los tipos de datos de los sumandos.
Recién después efectúa la suma.
Son dos tareas las que hace el aparentemente inofensivo operador +.
Primero convierte ambos operandos al tipo "unsigned int".
No le importa qué le ocurre al -1 en esa conversión.
Luego los suma.
___________
Para distinguir mejor las cosas.
Supongamos que lo que hago es la suma (1 + cero).
La situación es la misma, sólo que en este caso no se ven los efectos.
Pero en este caso, en la suma (1 + cero),
el primer sumando, el 1, es considerado como "signed int", igual que el -1 de antes.
No importa que el 1 sea positivo. Se considera como un "entero con signo" de todos modos.
Como la variable "cero" es "unsigned", se aplica la misma regla de antes,
y así, primero se convierten ambos sumandos al tipo "unsigned int",
y recién después se suma.
_____________________________
En la matemática tenemos una situación similar al sumar enteros y naturales.
Si tenemos el número 1 del conjunto N de los naturales,
¿es lo mismo que el número 1 del conjunto Z de los enteros?
¿Se pueden sumar ambos?
La respuesta a esto es ambigua si no precisamos bien el contexto.
En principio, N y Z no tienen por qué ser compatibles,
o sea, no sabemos si N es un subconjunto de Z.
Sin embargo, sabemos que Z tiene un subconjunto Z+, que "funciona" igual que N.
Si "convertimos" los números de N (mediante una biyección) a números del subconjunto Z+ de Z, entonces ahora sí que podamos sumar ambos "1"s.
Lo que confunde es nuestro propio abuso de notación.
En la computadora pasa algo parecido: los tipos de datos distintos representan conjuntos de números distintos, y para poder mezclarlos, primero se tiene que hacer alguna clase de identificación, o conversión.
Si no se hiciera esto, directamente nos daría error por todas partes al compilar.
¿Qué significa todo esto?
Bueno, que el C tiene muchos tipos de datos numéricos, para distintas necesidades, y que el C intenta que todos esos tipos distintos sean compatibles entre sí, aún en malas situaciones.
El resultado es que pueden darse situaciones extrañas, difíciles de predecir, si no se programa con cuidado.