Je peux "C" - stockage de données

 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

2.3 Exercices  

3. Stockage des types à virgule flottante en mémoire 

3.1 Un exemple  

3.2 Règles de stockage des nombres à virgule flottante  


1. Introduction aux types de données

char         // Type de données caractère
      court // entier court
int         // mise en forme
long         // entier long
long long   // mise en forme plus longue
float       // nombre à virgule flottante simple précision
double       // nombre à virgule flottante double précision
// Existe-t-il un type chaîne en langage C ?
La signification du type :
1. Utilisez ce type pour ouvrir la taille de l'espace mémoire (la taille détermine le champ d'utilisation).
2. Comment considérer la perspective de l’espace mémoire.

1.1 Classification de base des types :

Famille plastique :
carboniser
        char non signé //ne met que des nombres positifs
        caractè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.
L'int que nous utilisons habituellement est équivalent à l'int signé, et signé peut être omis.
court
        court non signé [ int ]
        signé court [ int ]
int
        entier non signé
        signé en entier
long
        non signé long [ int ]
        signé long [ int ]

 Famille à virgule flottante :

flotter
double

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 ;
Nous savons que quatre octets d'espace sont alloués à un fichier .
Comment le stocker ?
Venez comprendre les concepts suivants :

2.1 Code original, code inverse, code complément

Il existe trois méthodes de représentation binaire des entiers dans les ordinateurs , à savoir le code original, le code complémentaire et le code complémentaire.
Les trois méthodes de représentation comportent toutes deux parties, le bit de signe et le bit de valeur . Le bit de signe utilise 0 pour représenter " positif " , 1 pour représenter " négatif " et le bit de valeur
L'original, l'inverse et le complément des nombres positifs sont les mêmes.
Il existe trois manières différentes de représenter des entiers négatifs.
code d'origine
Le code original peut être obtenu en traduisant directement la valeur en binaire sous forme de nombres positifs et négatifs.
code inverse
Gardez le bit de signe du code d'origine inchangé et inversez les autres bits tour à tour pour obtenir le code inverse.
Complément
Le 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 :

On peut voir que les compléments sont stockés respectivement pour a et b . Mais nous avons constaté que la commande était un peu erronée .
Pourquoi? Cela implique encore une fois des gros et des petits bouts, regardez en bas ~

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 2
int 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
//10000000000000000000000000000001
    caractè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êmes
    printf ( "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=255
Le résultat imprimera 225
La plage de valeurs du type de caractère est de -128 à 127.

7.
#include <stdio.h>
caractère non signé i = 0 ;
//0~255
int 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.14159
1E10
La 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 

Exemple de stockage en virgule flottante :
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 

num et *pFloat sont évidemment le même nombre en mémoire, pourquoi y a-t-il une si grande différence entre les résultats d'interprétation des nombres à virgule flottante et des entiers ?
Pour comprendre ce résultat, vous devez comprendre comment les nombres à virgule flottante sont représentés à l'intérieur de l'ordinateur.
Interprétation détaillée :
Selon la norme internationale IEEE (Institute of Electrical and Electronics Engineering) 754 , tout nombre binaire à virgule flottante V peut être exprimé sous la forme suivante :
  • (-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

 

Par exemple:
5,0 en décimal équivaut à 101,0 en binaire , ce qui équivaut à 1,01×2^2 .
Ensuite, selon le format de V ci-dessus , on peut conclure que S=0 , M=1.01 et E=2 .
La décimale -5.0 s'écrit -101.0 en binaire , ce qui équivaut à -1.01 × 2^2 . Alors, S=1 , M=1.01 , E=2 .
IEEE 754 déclare :
Pour les nombres à virgule flottante de 32 bits, le bit le plus élevé est le bit de signe s , les 8 bits suivants sont l'exposant E et les 23 bits restants sont le nombre effectif M.

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.

IEEE 754 a des réglementations spéciales sur le nombre significatif M et l'exposant E.
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.
Quant à l'indice E , la situation est plus compliquée. Vers le bas
Premièrement, E est un entier non signé ( unsigned int )
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 à 1
A 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 la
Ajoutez 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'est
1.0*2^(-1) , son code de commande est -1+127=126 , exprimé sous la forme
01111110 , et la mantisse 1.0 supprime la partie entière pour être 0 , remplit 0 à 23 chiffres 000000000000000000000000 , puis son binaire
Le 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 de
0 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.

Puis-je demander au nombre à virgule flottante 9.0 , comment utiliser la représentation binaire ? Quelle est la réduction en décimal ?
Premièrement, le nombre à virgule flottante 9.0 est égal à 1001.0 en binaire , soit 1.001×2^3 .
9.0 -> 1001.0 -> ( - 1 ) ^01 . 0012 ^3 -> s = 0 , M = 1,001 , E = 3 + 127 = 130
Alors, le premier bit de signe s=0 , le nombre effectif M est égal à 001 suivi de 20 0s , soit 23 bits, et l'exposant E est égal à 3+127=130 ,
Cela fait 1 000 0010 .
Par conséquent, écrit sous forme binaire, il devrait être s+E+M , c'est-à-dire
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

 

 

 

Je suppose que tu aimes

Origine blog.csdn.net/Yzl17841857589/article/details/130050024
conseillé
Classement