Tabla de contenido
1. Introducción a los tipos de datos
1.1 Clasificación básica de tipos:
2. La forma se almacena en la memoria.
2.1 Código original, código inverso, código complementario
2.2 Introducción al endian grande y pequeño
3. Almacenamiento de tipos de punto flotante en la memoria.
3.2 Reglas de almacenamiento de números de punto flotante
1. Introducción al tipo de datos
char // tipo de datos de caráctercorto // entero cortoint // dando formalargo // entero largolargo largo // forma más largafloat // número de punto flotante de precisión simpledoble // número de punto flotante de doble precisión// ¿ Existe un tipo de cadena en lenguaje C ?
1.1 Clasificación básica de tipos:
carbonizarsechar unsigned //sólo poner números positivoscarácter firmado //poner números positivos y negativos// Los valores ASCII se utilizan esencialmente al almacenar y representar caracteres. Los valores ASCII son números enteros y los tipos de caracteres también se clasifican en familias de números enteros.
cortocorto sin firmar [ int ]firmado corto [ int ]En tentero sin firmarfirmado enterolargolargo sin firmar [ int ]firmado largo [ int ]
Familia de coma flotante:
flotardoble
Tipo construido (tipo personalizado):
> tipo de matriz> tipo de estructura estructura> enumeración tipo enumeración> unión tipo unión
tipo de puntero
int* pi ;carbón * ordenador personal ;flotar* pf ;vacío* pv ;
tipo vacío:
void representa un tipo vacío (sin tipo)Generalmente se aplica al tipo de retorno de la función, al parámetro de la función, al tipo de puntero.void test(...)//La función no necesita devolver un valor{}prueba nula (void) // La función no requiere parámetros{}void* p;//Sin puntero de tipo específico
2. Almacenamiento de modelado en la memoria.
Dijimos antes que la creación de una variable es para abrir espacio en la memoria. El tamaño del espacio se determina según diferentes tipos. Entonces, hablemos de cómo se almacenan los datos en la memoria asignada.
Por ejemplo:
int a = 20 ;int b = - 10 ;
2.1 Código original, código inverso, código complementario
código originalEl código original se puede obtener traduciendo directamente el valor al binario en forma de números positivos y negativos.código inversoMantenga el bit de signo del código original sin cambios e invierta los otros bits uno por uno para obtener el código inverso.ComplementarEl código inverso + 1 obtendrá el código complementario.
Para dar forma: los datos almacenados en la memoria son en realidad el código complementario.
¿por qué?
En los sistemas informáticos, los valores siempre se expresan y almacenan en complemento a dos. La razón es que, usando el código de complemento, el bit de signo y el campo de valor se pueden procesar de manera uniforme, al mismo tiempo, la suma y la resta también se pueden procesar de manera uniforme (la CPU solo tiene un sumador). Además, el proceso de operación El código del complemento y el código original son iguales, no se requieren circuitos de hardware adicionales.
Veamos el almacenamiento en la memoria:
2.2 Introducción al endian grande y pequeño
Qué big endian, little endian:
El modo big-endian (almacenamiento) significa que los bits bajos de los datos se almacenan en las direcciones altas de la memoria, mientras que los bits altos de los datos se almacenan en las direcciones bajas de la memoria.medio;El modo little-endian (almacenamiento) significa que los bits bajos de datos se almacenan en las direcciones bajas de la memoria, mientras que los bits altos de datos se almacenan en los lugares altos de la memoria.DIRECCIÓN.
Por qué existe big endian y little endian:
¿Por qué hay una diferencia entre los modos endian grandes y pequeños? Esto se debe a que en el sistema informático utilizamos bytes como unidad, y cada unidad de dirección corresponde a un byte, y un byte tiene 8 bits . Pero en el lenguaje C , además del char de 8 bits , también hay tipos cortos de 16 bits y tipos largos de 32 bits (dependiendo del compilador específico), además, para procesadores con más de 8 bits, como 16 bits O para un procesador de 32 bits, dado que el ancho del registro es mayor que un byte, debe haber un problema de cómo organizar varios bytes. Por lo tanto, conduce al modo de almacenamiento big-endian y al modo de almacenamiento little-endian.Por ejemplo: un tipo corto de 16 bits x , la dirección en la memoria es 0x0010 , el valor de x es 0x1122 , luego 0x11 es el byte alto y 0x22 es el byte bajo. Para el modo big-endian, coloque 0x11 en la dirección baja, es decir, 0x0010 , y coloque 0x22 en la dirección alta, es decir, 0x0011 . Modo little endian, todo lo contrario. Nuestra estructura X86 comúnmente utilizada es el modo little-endian, mientras que KEIL C51 es el modo big-endian. Muchos ARM y DSP están en modo little-endian. Algunos procesadores ARM también pueden elegir el modo big-endian o el modo little-endian por hardware.
Preguntas de la prueba escrita para ingenieros de sistemas de Baidu 2015 :
Describa brevemente los conceptos de big-endian y little-endian y diseñe un pequeño programa para determinar el orden de bytes de la máquina actual. ( 10 puntos)
// código 1#incluir <stdio.h>int check_sys (){int yo = 1 ;retorno ( * ( char * ) & i );}int principal (){int ret = check_sys ();si ( ret == 1 ){printf ( " little endian \n" );}demás{printf ( " big endian \n" );}devolver 0 ;}// Código 2int check_sys (){Unión{int yo ;carácter c ;} y ;un . i = 1 ;volver ONU . c ;}
2.3 Ejercicios
1.// ¿ generar qué?#incluir <stdio.h>int principal (){carácter a = - 1 ;//100000000000000000000000000000001//111111111111111111111111111111110//11111111111111111111111111111111//11111111 - truncado//Promoción de enteros - promoción según el bit de signo//11111111111111111111111111111111//1111111111111111111111111111110 - menos 1//100000000000000000000000000000001carácter firmado b = - 1 ;carácter sin firmar c =- 1 ;//10000000000000000000000000000001//111111111111111111111111111111110//11111111111111111111111111111111//11111111 - Si es un número sin signo, suma directamente 0 al bit alto//00000000000000000000000011111111 se convierte a 255 en decimal// El bit de signo del bit más alto después de completar 0 es 0, por lo que los códigos original, inverso y complemento son los mismosprintf ( "a=%d,b=%d,c=%d" , a , b , c );devolver 0 ;}
La estructura es a = -1 b = -1 c = 255
¿Qué genera el siguiente programa?
2.#incluir <stdio.h>int principal (){carácter a = - 128 ;printf ( "%u\n" , a );devolver 0 ;}//10000000000000000000000010000000 - código original
//1111111111111111111111101111111 - inversa
//1111111111111111111111110000000 - complemento
//10000000 - un truncamiento
——> Complemento de refuerzo entero 1
//11111111111111111111111110000000
//%u print piensa que el complemento impreso es el mismo que el complemento invertido original para números sin signo
Así, la impresión directa (convertida a decimal) imprime más de 4.200 millones
3.#incluir <stdio.h>int principal (){carácter a = 128 ;printf ( "%u\n" , a );devolver 0 ;}Es exactamente igual que el último, porque el truncamiento es lo mismo que llenar 1
4.int yo = - 20 ;int sin signo j = 10 ;printf ( "%d\n" , i + j );// Operar en forma de complemento a dos y finalmente formatearlo como un entero con signoExplicación detallada://10000000 00000000 00000000 00010100 El código original del negativo 20
//11111111 11111111 11111111 11101011 El código negativo del negativo 20 (el bit de signo permanece sin cambios y los demás se invierten) //11111111 11111111 1111111 1 11101100 Complemento a 20 negativo (suma
1 al complemento para obtener el código del complemento)
//00000000 00000000 00000000 00001010 El código original de 10 (el original, el inverso y el complemento de los números positivos son iguales) //11111111 11111111
11111111 11110110 La suma de -20 y el complemento a 10
/ / (el resultado de la computadora se almacena en la memoria. Es el código complementario)
//11111111 11111111 11111111 11110101 (menos 1)
//10000000 00000000 00000000 00001010 (inverso) para obtener -10
5.int sin firmar yo ;para ( yo = 9 ; yo >= 0 ; yo - ){printf ( "%u\n" , i );}//-1 del suplemento original
// 100000000000000000000000000000000001 Original
// 1111111111111111111111111111111110 Anti-anti
// 11111111111111111111111111111 llenar
// Cuando el ciclo I-0, la reducción es -1 nuevamente, y -1 de -1 El suplemento es 1111111111111111111111111111111111, a oportunidad informática Piensa que es un número muy grande, por lo que sigue repitiendo.%u imprime un número sin firmar
Pero si cambio a %d, puedo imprimir -1
6.int principal (){carácter a [ 1000 ];int yo ;para ( yo = 0 ; yo < 1000 ; yo ++ ){a [ yo ] = -1 - yo ;}printf ( "%d" , strlen ( a ));devolver 0 ;}//-1 -2 -3 -4...-127...-998 -999 -1000
//char -1 -2 -3...-128 127 126...3 2 1...0 -1 -2...-128 127// strlen encuentra la longitud de la cadena y busca \0. El valor del código ASCII de \0 es 0, por lo que el cálculo de la longitud de char se detendrá cuando llegue a 0, por lo que 128+127=255El resultado imprimirá 225El rango de valores del tipo char es -128~127
7.#incluir <stdio.h>carácter sin firmar i = 0 ;//0~255int principal (){para ( yo = 0 ; yo <= 255 ; yo ++ ){printf ( "hola mundo\n" );//La conversión de 256 en decimal a binario es 1 00000000. Los siguientes ocho dígitos no se convierten en 0, por lo que la condición de i<=255 siempre es verdadera, por lo que el bucle infinito}devolver 0 ;}Entonces el resultado es un bucle infinito que imprime hola mundo.
3. Almacenamiento de punto flotante en la memoria
Números de coma flotante comunes:
3.141591E10La familia de coma flotante incluye: tipos flotante , doble y doble largo .El rango de números de punto flotante: definido en float.h
3.1 Un ejemplo
int principal (){int norte = 9 ;flotador * pFloat = ( flotante * ) & n ;printf ( "El valor de n es: %d\n" , n );printf ( "El valor de *pFloat es: %f\n" , * pFloat );* pFlotación = 9,0 ;printf ( "El valor de num es: %d\n" , n );printf ( "El valor de *pFloat es: %f\n" , * pFloat );devolver 0 ;}El valor de n es: 9 --> se imprime en forma de número entero
El valor de *pFloat es: 0.000000 --> el resultado obtenido en forma de número de punto flotante no es 9.0, lo que indica que la forma de almacenamiento de un número entero es diferente a la del número de punto flotante
Las dos similitudes siguientes verifican nuevamente que la forma de almacenamiento de números enteros y números de punto flotante son diferentes.
¿Cuál es el resultado de la salida?
3.2 Reglas de almacenamiento de números de punto flotante
- (-1)^S*M*2^E
- (-1)^S representa el bit de signo, cuando S=0 , V es un número positivo; cuando S=1 , V es un número negativo.
- M representa un número válido, mayor o igual a 1 y menor que 2 .
- 2^E significa bits exponentes.
- Ejemplo: 5.5 en forma binaria -->
Como se muestra en el principio 5.5, convierta a 101.1 notación científica 1.011*2^2
Para números de punto flotante de 64 bits, el bit más alto es el bit de signo, los siguientes 11 bits son el exponente E y los 23 bits restantes son el significado M.
Como se mencionó anteriormente, 1≤M<2 , es decir, M se puede escribir en la forma 1.xxxxxx , donde xxxxxx representa la parte decimal.IEEE 754 estipula que cuando M se guarda dentro de la computadora , el primer dígito de este número siempre es 1 por defecto , por lo que se puede descartar y solo se guarda la parte xxxxxx que sigue.Por ejemplo, al guardar 1.01 , solo guarde 01 y luego agregue el primer 1 al leer. El propósito de hacer esto es ahorrar 1 cifra significativa. Tome el número de punto flotante de 32 bits como ejemplo, solo quedan 23 bits para M , y después de descartar el primer 1, equivale a guardar 24 cifras significativas.
Eso significa que si E tiene 8 bits, su rango de valores es 0~255 ; si E tiene 11 bits, su rango de valores es 0~2047 . Sin embargo, sabemos que E en notación científica puede tener números negativos, por lo que IEEE 754 estipula que se debe sumar un número intermedio al valor real de E cuando se almacena en la memoria . Para E de 8 bits , este número intermedio es 127 ; para E de 11 bits , este número intermedio es 1023 . Por ejemplo, la E de 2^10 es 10 , por lo que al guardarlo como un número de punto flotante de 32 bits, se debe guardar como 10+127=137 , es decir, 10001001.
Luego, el índice E se recupera de la memoria y se puede dividir en tres casos:E no es todo 0 o no todo 1En este momento, el número de coma flotante está representado por las siguientes reglas, es decir, al valor calculado del exponente E se le resta 127 (o 1023 ) para obtener el valor real, y luego elAgregue el primer dígito 1 antes del dígito significativo M.Por ejemplo:La forma binaria de 0.5 ( 1/2 ) es 0.1 , ya que se estipula que la parte positiva debe ser 1 , es decir, se desplaza la coma decimal 1 hacia la derecha , entonces es1.0*2^(-1) , su código de pedido es -1+127=126 , expresado como01111110 , y la mantisa 1.0 elimina la parte entera para que sea 0 , llena de 0 a 23 dígitos 00000000000000000000000 , luego es binarioLa forma de representación es :
0 01111110 000000000000000000000000
E es todo 0
En este momento, el exponente E del número de coma flotante es igual a 1-127 (o 1-1023 ), que es el valor real.El número significativo M ya no suma el primer 1 , sino que restablece el decimal 0.xxxxxx . Esto se hace para representar ±0 , así como cerca de0 es un número muy pequeño.
E es todo 1
En este momento, si el dígito significativo M es todo 0 , significa ± infinito (positivo o negativo depende del bit de signo s );
Bueno, eso es todo en cuanto a las reglas de representación de números de coma flotante.
Para explicar la pregunta anterior:
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
A continuación, volvamos a la pregunta original: ¿Por qué 0x00000009 se convierte en 0,000000 cuando se restaura a un número de punto flotante ?Primero, divida 0x00000009 para obtener el primer bit de signo s=0 y el exponente de los siguientes 8 bits E=00000000 .Los últimos 23 dígitos significativos M=000 0000 0000 0000 0000 1001 .
9 -> 0000 0000 0000 0000 0000 0000 0000 1001
Dado que el exponente E es todo 0 , cumple con el segundo caso de la sección anterior. Por tanto, el número de coma flotante V se escribe como:
V=( - 1)^0 × 0,00000000000000000001001×2^( - 126)=1,001×2^( - 146)
Obviamente, V es un número positivo muy pequeño cercano a 0 , por lo que es 0,000000 en notación decimal .
Mire la segunda parte del ejemplo.
9.0 -> 1001.0 -> ( -1 ) ^ 01 . 0012 ^3 -> s = 0 , M = 1.001 , E = 3 + 127 = 130
0 10000010 001 0000 0000 0000 0000 0000
Este número binario de 32 bits, restaurado a decimal, es exactamente 1091567616 .
EL FIN
Esto es algo que compartimos hoy sobre el almacenamiento de datos. ¡Espero que pueda ayudar a todos! Si hay alguna deficiencia, por favor, los miembros de la familia le dan a Xiaoye algunas buenas sugerencias. ¡Continuaré optimizando el artículo! ¡Entonces trabajemos juntos! Jajajajaja