Table des matières
1. Introduction aux types de données
1.1 Classification de base des types :
2. La mise en forme est stockée en mémoire
2.1 Code original, code inverse, code complément
2.2 Introduction au grand et au petit endian
3. Stockage des types à virgule flottante en mémoire
3.2 Règles de stockage des nombres à virgule flottante
1. Introduction aux types de données
char // Type de données caractèrecourt // entier courtint // mise en formelong // entier longlong long // mise en forme plus longuefloat // nombre à virgule flottante simple précisiondouble // nombre à virgule flottante double précision// Existe-t-il un type chaîne en langage C ?
1.1 Classification de base des types :
carboniserchar non signé //ne met que des nombres positifscaractère signé //mettre des nombres positifs et négatifs//Les valeurs ASCII sont essentiellement utilisées lors du stockage et de la représentation de caractères. Les valeurs ASCII sont des entiers et les types de caractères sont également classés en familles d'entiers.
courtcourt non signé [ int ]signé court [ int ]intentier non signésigné en entierlongnon signé long [ int ]signé long [ int ]
Famille à virgule flottante :
flotterdouble
Type construit (type personnalisé) :
> type de tableau> type de structure structure> type d'énumération énumération> syndicat de type syndicat
type de pointeur
int * pi ;char * pc ;float* pf ;vide* pv ;
type vide :
void représente un type vide (pas de type)Généralement appliqué au type de retour de la fonction, au paramètre de la fonction, au type de pointeur.void test(...)//La fonction n'a pas besoin de renvoyer de valeur{}void test(void)//La fonction ne nécessite pas de paramètres{}void* p;//Aucun pointeur de type spécifique
2. Stockage du façonnage en mémoire
Nous avons dit précédemment que la création d'une variable consiste à ouvrir de l'espace en mémoire. La taille de l’espace est déterminée selon différents types. Parlons ensuite de la façon dont les données sont stockées dans la mémoire allouée ?
Par exemple:
int a = 20 ;int b = - 10 ;
2.1 Code original, code inverse, code complément
code d'origineLe code original peut être obtenu en traduisant directement la valeur en binaire sous forme de nombres positifs et négatifs.code inverseGardez le bit de signe du code d'origine inchangé et inversez les autres bits tour à tour pour obtenir le code inverse.ComplémentLe code inverse + 1 obtiendra le code complément.
Pour le façonnage : les données stockées en mémoire sont en fait le code complément.
pourquoi ?
Dans les systèmes informatiques, les valeurs sont toujours exprimées et stockées en complément à deux. La raison en est qu'en utilisant le code complémentaire, le bit de signe et le champ de valeur peuvent être traités de manière uniforme, et en même temps, l'addition et la soustraction peuvent également être traitées de manière uniforme (la CPU n'a qu'un additionneur ) . du code complémentaire et du code original sont les mêmes, aucun circuit matériel supplémentaire n'est requis.
Regardons le stockage en mémoire :
2.2 Introduction au grand et au petit endian
Quel gros-boutiste petit-boutiste :
Le mode big-endian (stockage) signifie que les bits faibles des données sont stockés dans les adresses hautes de la mémoire, tandis que les bits forts des données sont stockés dans les adresses basses de la mémoire.milieu;Le mode petit-boutiste (stockage) signifie que les bits de données faibles sont stockés dans les adresses basses de la mémoire, tandis que les bits de données hauts sont stockés dans les emplacements hauts de la mémoire.adresse.
Pourquoi il y a big endian et little endian :
Pourquoi y a-t-il une différence entre les modes grand et petit endian ? En effet, dans le système informatique, nous utilisons les octets comme unité, et chaque unité d'adresse correspond à un octet, et un octet fait 8 bits . Mais dans le langage C , en plus du char 8 bits , il existe également des types courts de 16 bits et des types longs de 32 bits (selon le compilateur spécifique). De plus, pour les processeurs de plus de 8 bits, comme 16 bits Ou pour un processeur 32 bits, puisque la largeur du registre est supérieure à un octet, il doit y avoir un problème sur la façon d'organiser plusieurs octets. Par conséquent, cela conduit au mode de stockage big-endian et au mode de stockage small-endian.Par exemple : un type court de 16 bits x , l'adresse dans la mémoire est 0x0010 , la valeur de x est 0x1122 , puis 0x11 est l'octet de poids fort et 0x22 est l'octet de poids faible. Pour le mode big-endian, mettez 0x11 dans l'adresse basse, c'est-à-dire 0x0010 , et mettez 0x22 dans l'adresse haute, c'est-à-dire 0x0011 . Le mode Little Endian, c'est tout le contraire. Notre structure X86 couramment utilisée est le mode petit-boutiste, tandis que KEIL C51 est le mode gros-boutiste. De nombreux ARM et DSP sont en mode petit-boutiste. Certains processeurs ARM peuvent également choisir le mode big-endian ou le mode small-endian par matériel.
Questions du test écrit d'ingénieur système Baidu 2015 :
Veuillez décrire brièvement les concepts de big-endian et little-endian, et concevoir un petit programme pour déterminer l'ordre des octets de la machine actuelle. ( 10 points)
// code 1#include <stdio.h>int check_sys (){int je = 1 ;return ( * ( char * ) & i );}int principal (){int ret = check_sys ();si ( ret == 1 ){printf ( " petit endian \n" );}autre{printf ( " gros endian \n" );}renvoie 0 ;}// Code 2int check_sys (){syndicat{int je ;char c ;} et ;un _ et = 1 ;retourner un . c ;}
2.3 Exercices
1.// affiche quoi ?#include <stdio.h>int principal (){caractère a = - 1 ;//10000000000000000000000000000001//1111111111111111111111111111110//1111111111111111111111111111111//11111111 - tronqué// Promotion entière - promotion en fonction du bit de signe//1111111111111111111111111111111//111111111111111111111111111110 - moins 1//10000000000000000000000000000001caractère signé b =- 1 ;caractère non signé c =- 1 ;//1000000000000000000000000000001//1111111111111111111111111111110//1111111111111111111111111111111//11111111 - S'il s'agit d'un nombre non signé, ajoutez directement 0 au bit haut//00000000000000000000000011111111 est converti en 255 en décimal//Le bit de signe du bit le plus élevé après la fin de 0 est 0, donc les codes d'origine, inverse et complémentaire sont les mêmesprintf ( "a=%d,b=%d,c=%d" , a , b , c );renvoie 0 ;}
La structure est a = -1 b = -1 c = 255
Que produit le programme suivant ?
2.#include <stdio.h>int principal (){caractère a = - 128 ;printf ( "%u\n" , a );renvoie 0 ;}//10000000000000000000000010000000 - code d'origine
//111111111111111111111101111111 - inverse
//111111111111111111111110000000 - complément
//10000000 - une troncature
——>Complément de boost entier 1
//1111111111111111111111110000000
//%u l'impression pense que le complément imprimé est le même que le complément inversé d'origine pour les nombres non signés
Ainsi, l'impression directe (convertie en décimal) imprime plus de 4,2 milliards
3.#include <stdio.h>int principal (){caractère a = 128 ;printf ( "%u\n" , a );renvoie 0 ;}![]()
C'est exactement la même chose que la précédente, car la troncature revient à remplir 1
4.int je = - 20 ;unsigned int j = 10 ;printf ( "%d\n" , i + j );// Fonctionne sous la forme d'un complément à deux, et enfin le formate en un entier signéExplication détaillée://10000000 00000000 00000000 00010100 Le code original du négatif 20
//11111111 11111111 11111111 11101011 Le code négatif du négatif 20 (le bit de signe reste inchangé et les autres sont inversés) //11111111 11111111 1 111111 1 11101100 Complément négatif de 20 (ajouter
1 au complément pour obtenir le complément Code)
//00000000 00000000 00000000 00001010 Le code original de 10 (l'original, l'inverse et le complément des nombres positifs sont les mêmes) //11111111
11111111 11111111 11110110 L'ajout de -20 et du complément à 10
/ / (le résultat de l'ordinateur est stocké en mémoire C'est le code complément)
//11111111 11111111 11111111 11110101 (moins 1)
//10000000 00000000 00000000 00001010 (inverse) pour obtenir -10
5.int non signé je ;pour ( je = 9 ; je >= 0 ; je -- ){printf ( "%u\n" , je );}//-1 du supplément d'origine
// 10000000000000000000000000000000001 Original
// 11111111111111111111111111111110 Anti-anti
// 11111111111111111111111111111 remplissage
// Lorsque le cycle I-0, la réduction est à nouveau -1, et -1 de -1 Le supplément est 111111111111111111111111111111111, un opportunité informatique Je pense que c'est un très grand nombre, donc il continue de tourner en boucle.
%u imprime un numéro non signé
Mais si je passe à %d, je peux imprimer -1
6.int principal (){char a [ 1000 ];int je ;pour ( je = 0 ; je < 1000 ; je ++ ){une [ je ] = - 1 - je ;}printf ( "%d" , strlen ( a ));renvoie 0 ;}//-1 -2 -3 -4...-127...-998 -999 -1000
//caractère -1 -2 -3...-128 127 126...3 2 1...0 -1 -2...-128 127//strlen trouve la longueur de la chaîne, en recherchant \0, la valeur du code ASCII de \0 est 0, donc le calcul de la longueur du caractère s'arrêtera lorsqu'il atteindra 0, donc 128+127=255Le résultat imprimera 225La plage de valeurs du type de caractère est de -128 à 127.![]()
7.#include <stdio.h>caractère non signé i = 0 ;//0~255int principal (){pour ( je = 0 ; je <= 255 ; je ++ ){printf ( "bonjour tout le monde\n" );//La conversion de 256 en décimal en binaire est 1 00000000. Les huit chiffres suivants ne deviennent-ils pas 0, donc la condition de i<=255 est toujours vraie, donc la boucle infinie}renvoie 0 ;}Le résultat est donc une impression en boucle infinie bonjour tout le monde
3. Stockage en virgule flottante en mémoire
Nombres à virgule flottante courants :
3.141591E10La famille à virgule flottante comprend : les types float , double , long double .La plage de nombres à virgule flottante : définie dans float.h
3.1 Un exemple
int principal (){int n = 9 ;float * pFloat = ( float * ) & n ;printf ( "La valeur de n est : %d\n" , n );printf ( "La valeur de *pFloat est : %f\n" , * pFloat );* pFlottant = 9,0 ;printf ( "La valeur de num est : %d\n" , n );printf ( "La valeur de *pFloat est : %f\n" , * pFloat );renvoie 0 ;}![]()
La valeur de n est : 9 --> est imprimé sous forme d'entier
La valeur de *pFloat est : 0,000000 --> le résultat obtenu sous forme de nombre à virgule flottante n'est pas 9,0, indiquant que la forme de stockage de l'entier est différente de celle du nombre à virgule flottante
Les deux similitudes suivantes vérifient à nouveau que la forme de stockage des nombres entiers et des nombres à virgule flottante est différente.
Quel est le résultat de la sortie ?
3.2 Règles de stockage des nombres à virgule flottante
- (-1)^S * M * 2^E
- (-1)^S représente le bit de signe, lorsque S=0 , V est un nombre positif ; lorsque S=1 , V est un nombre négatif.
- M représente un nombre valide, supérieur ou égal à 1 et inférieur à 2 .
- 2 ^ E signifie bits d'exposant.
- Exemple : 5,5 sous forme binaire -->
Comme indiqué dans le principe 5.5, convertir en notation scientifique 101.1 1.011*2^2
![](https://img-blog.csdnimg.cn/35b0872e8e5047cfa009a5c99741dee9.png)
Pour les nombres à virgule flottante de 64 bits, le bit le plus élevé est le bit de signe, les 11 bits suivants sont l'exposant E et les 23 bits restants sont la mantisse M.
Comme mentionné précédemment, 1≤M<2 , c'est-à-dire que M peut s'écrire sous la forme 1.xxxxxx , où xxxxxx représente la partie décimale.IEEE 754 stipule que lorsque M est enregistré dans l'ordinateur , le premier chiffre de ce numéro est toujours 1 par défaut , il peut donc être supprimé et seule la partie xxxxxx qui suit est enregistrée.Par exemple, lors de la sauvegarde de 1.01 , enregistrez uniquement 01, puis ajoutez le premier 1 lors de la lecture. Le but de cette opération est d’économiser 1 chiffre significatif. Prenons l'exemple du nombre à virgule flottante de 32 bits, il ne reste que 23 bits pour M , et une fois le premier 1 supprimé, cela équivaut à enregistrer 24 chiffres significatifs.
Cela signifie que si E est de 8 bits, sa plage de valeurs est de 0~255 ; si E est de 11 bits, sa plage de valeurs est de 0~2047 . Cependant, nous savons que E en notation scientifique peut avoir des nombres négatifs, donc IEEE 754 stipule qu'un nombre intermédiaire doit être ajouté à la valeur réelle de E lorsqu'il est stocké en mémoire . Pour E 8 bits , ce nombre intermédiaire est 127 ; pour E de 11 bits , ce nombre intermédiaire est 1023 . Par exemple, le E de 2^10 est 10 , donc lorsque vous l'enregistrez sous forme de nombre à virgule flottante 32 bits, il doit être enregistré sous 10+127=137 , c'est-à-dire 10001001.
Ensuite, l'index E est extrait de la mémoire et peut être divisé en trois cas :E n'est pas tout à 0 ou tout à fait à 1A ce moment, le nombre à virgule flottante est représenté par les règles suivantes, c'est-à-dire que la valeur calculée de l'exposant E est soustraite de 127 (ou 1023 ) pour obtenir la valeur réelle, puis laAjoutez le premier chiffre 1 avant le chiffre significatif M.Par exemple:La forme binaire de 0,5 ( 1/2 ) est 0,1 , puisqu'il est stipulé que la partie positive doit être 1 , c'est-à-dire que la virgule décimale est décalée de 1 vers la droite , alors c'est1.0*2^(-1) , son code de commande est -1+127=126 , exprimé sous la forme01111110 , et la mantisse 1.0 supprime la partie entière pour être 0 , remplit 0 à 23 chiffres 000000000000000000000000 , puis son binaireLe formulaire de représentation est :
0 01111110 00000000000000000000000
E est tout 0
A ce moment, l'exposant E du nombre à virgule flottante est égal à 1-127 (ou 1-1023 ) qui est la valeur réelle,Le nombre significatif M n'ajoute plus le premier 1 , mais revient à la décimale de 0.xxxxxx . Ceci est fait pour représenter ±0 , ainsi que proche de0 est un très petit nombre.
E est tout 1
A ce moment, si le chiffre significatif M est entièrement 0 , cela signifie ± l'infini (positif ou négatif dépend du bit de signe s ) ;
Eh bien, c'est tout pour les règles de représentation des nombres à virgule flottante.
Pour expliquer la question précédente :
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;
}
Revenons ensuite à la question initiale : pourquoi 0x00000009 devient- il 0,000000 lorsqu'il est restauré en nombre à virgule flottante ?Tout d'abord, divisez 0x00000009 pour obtenir le premier bit de signe s=0 et l'exposant des 8 bits suivants E=00000000 ,Les 23 derniers chiffres significatifs M=000 0000 0000 0000 0000 1001 .
9 -> 0000 0000 0000 0000 0000 0000 0000 1001
Puisque l’exposant E est entièrement 0 , il répond au deuxième cas de la section précédente. Par conséquent, le nombre à virgule flottante V s’écrit :
V=( - 1)^0 × 0,000000000000000000001001×2^( - 126)=1,001×2^( - 146)
Évidemment, V est un très petit nombre positif proche de 0 , il vaut donc 0,000000 en notation décimale .
Regardez la deuxième partie de l'exemple.
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
Ce nombre binaire de 32 bits, restauré en décimal, est exactement 1091567616 .
LA FIN
C'est un partage sur le stockage de données aujourd'hui, j'espère que cela pourra aider tout le monde ! S'il y a des lacunes, s'il vous plaît, les membres de la famille donnent à Xiaoye de bonnes suggestions, je continuerai à optimiser l'article ! Alors travaillons ensemble ! Hahahahaha