当前位置:网站首页>[principe de stockage du type de données] stockage des données - analyse approfondie stockage des données en mémoire

[principe de stockage du type de données] stockage des données - analyse approfondie stockage des données en mémoire

2021-10-14 05:45:06 Aaronskr

Préface

Nous définissons toujours les variables lorsque nous tapons le Code , Stockage de diverses données ,Par exemple,int a = 10;Oui.10 Ces données sont stockées dans des variables aMoyenne,Et les variablesa, C'est l'espace qu'on a créé en mémoire. .
Comment créer de l'espace pour les variables en mémoire les blogueurs ont analysé le principe de désassemblage dans le cadre de la pile de fonctions ,Pour plus de détails, voir Cadre de pile de fonctions schématiques - Création et destruction de cadres de pile de fonctions .
Cet article analysera plus en détail le cas où une unit é de stockage a été ouverte. , Comment les différentes données sont stockées .


Avant de savoir comment les données sont stockées , Vous devriez d'abord comprendre nos types de données communs .

Résumé des types de données

InC99Dans la norme, Nous pouvons classer les types de données dans les catégories suivantes: .

  1. Famille entière
  2. Famille flottante( Famille solide )
  3. Type personnalisé(Type de construction)
  4. Type de pointeur
  5. Type vide

Les cinq types de situations de base sont décrits ci - dessous. .

Famille entière

char
		unsigned char
		signed char
short
		unsigned short [int]
		signed short [int]
int
		unsigned int
		signed int
long
		unsigned long [int]
		signed long [int]

Note::InC99 Normes ultérieures ,Oui.char Les données de type sont divisées en familles entières , Parce que les caractères en mémoire les convertissent en ASCII Valeur du Code à stocker .

Comme indiqué ci - dessus, Toutes les familles d'entiers sont divisées en entiers signés et non signés ,Etsigned Peut être omis ,En d'autres termes,,signed intExactement équivalent àint,Autres et ainsi de suite, Mais il y a une exception. : charType etsigned charPas l'équivalent, Écris - en un. char ch = 0; On ne pourra pas le dire. ch La variable est signée ou non signée , Il dépend entièrement du compilateur , Mais testé par les blogueurs , Sous la plupart des compilateurs char Les types sont traduits par le compilateur en symboles charType.

InC99Et introduitlong long - Long lifting,Utilisation etlongLes types sont cohérents,Mais...C Règles de grammaire linguistique ,sizeof(long)<= sizeof(long long),Etlong La taille de la mémoire occupée par le type est 4/8Octets,Alors...long long Le type doit avoir une taille de mémoire 8Octets.

Famille flottante

float
double

La famille flottante n'a que floatEtdoubleCes deux types,float La taille de l'espace occupé par le type est 4byte,double La taille de l'espace occupé par le type est 8byte.

La différence entre eux est la précision, en plus de la taille de l'espace qu'ils occupent. ,float Appelé point flottant de précision unique , Lorsque la précision effective est décimale 6Bits,Etdouble Le type est appelé double précision flottante ,Après la décimale15Bits, Mais son nombre effectif n'est que 11Position gauche et droite.

🦝Type personnalisé

> Type de tableau
> Type de structure struct
> Type d'énumération enum
> Type d'union union

Il y a peut - être beaucoup de gens ici qui ne peuvent pas comprendre pourquoi les types de tableaux sont également divisés en types personnalisés. , Voici quelques explications. .

Nous connaissons la définition des variables du type de tableau :Type de données+Nom du tableau+[Taille du tableau];

Par exemple::

int arr[10] = { 0 };

Il y a peut - être beaucoup d'erreurs ici. ,- Oui.arr Le type de tableau est intType, C'est ce que je veux dire. intType、Le tableau s'appellearr La taille du tableau pour 10Tableau de,En fait, non., Le nom du tableau pour ce tableau est vraiment arr, Mais son type de données est int [10], Ça pourrait être inacceptable pour la plupart des gens. ,

Un exemple simple peut expliquer :

Nous savons que,sizeof L'opérateur est utilisé pour calculer la taille de l'espace mémoire occupé , Son opérande peut être à la fois un nom de variable , Peut également être un type de variable .

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

int main()
{
    
	int a = 10;

	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));

	return 0;
}

Les deux sont corrects. ,Le résultat imprimé est:

Imprimer les résultats

Et pour les tableaux , L'opérande peut également être un nom de tableau ou un type de tableau :

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

int main()
{
    
	/*int a = 10; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(int));*/

	int arr[10] = {
     0 };

	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(int[10]));

	return 0;
}

Le résultat de l'impression est :

Imprimer les résultats
Et voilà., C'est vérifié. int [10]Est le type de tableau.

Je sais que, Il est plus clair d'expliquer pourquoi les types de tableaux sont personnalisés , Vous pouvez le savoir en utilisant les conclusions expliquées ci - dessus ,int arr[10]Etint arr[9] Différents types de tableaux pour , Pas tous. intType, La taille du tableau est spécifiée par nos programmeurs , Donc vous pouvez le diviser en types personnalisés .

D'autres types personnalisés sont évidents , Ce n'est pas tout. .

‍Type de pointeur.

Le type de pointeur est spécial .

Le pointeur dont nous parlons a deux significations :

  1. Adresse d'une variable , C'est le nombre en mémoire. , On pourrait appeler ça un pointeur. .
  2. Pour stocker l'adresse (No.)Variable de, Nous l'appelons la variable pointeur , Souvent appelé pointeur .

Le type de pointeur est défini comme suit: :

Type de données+*( Utilisé pour identifier le type de pointeur )+Nom de la variable pointeur

Les types de pointeurs courants sont: :

int* pi;
char* pc;
float* pf;
void* pv;

Voici un point important , La plupart des assignations de variables de pointeur prennent une adresse de variable et la stockent dans la variable de pointeur ,Par exemple:int pc = &c;

Mais il y a une exception. :

int main()
{
	char* pc = "hello world";

	printf("%c\n", *pc);

	return 0;
}

Une constante de chaîne est assignée à la variable pointeur entre pc,Nous savons que, Une constante de chaîne placée dans la zone constante , Sa valeur n'est pas modifiable , Et voici la chaîne plus la chaîne cachée ’\0’Au total,12Octets, Et nos variables de pointeur, selon la plate - forme, ne peuvent être que 4/8Octets, Il est impossible de mettre cette constante de chaîne , C'est donc mal compris. .

On l'imprime pour voir les résultats. :

Imprimer les résultats
Imprimer les résultats en une seule lettre h, Ça explique tout. , Assigner toute la chaîne constante à la variable pointeur , Ça ne met pas toute la chaîne dedans. , Au lieu de cela, assignez la première adresse de toute la chaîne à la variable pointeur , Le pointeur de comparaison contient l'adresse , Ceci est similaire à l'attribution d'un nom de tableau de caractères à une variable pointeur , Les adresses des premiers éléments sont stockées .

Type vide

void Utilisé pour représenter un type vide (Aucun type)
Type de retour généralement appliqué à une fonction、Paramètres de la fonction、Type de pointeur.

Voici quelques exemples de types vides pour aider à comprendre :

  • Type de retour:
void test(int x)
{
    
	printf("%d\n", x);
}

int main()
{
    
	int a = 10;
	test(a);

	return 0;
}

Imprimer les résultats

Ici.test Le type de retour de la fonction est void.

  • Paramètres de la fonction:
int test(void)
{
    
	return 1;
}

int main()
{
    
	int ret = test();

	printf("%d\n", ret);

	return 0;
}

Ce code définit les paramètres de la fonction à null , Indique que le paramètre de transfert de la fonction d'appel n'est pas autorisé , Si vous ne voulez pas passer le paramètre , Le compilateur vous avertira .

int test(void)
{
    
	return 1;
}

int main()
{
    
	int a = 10;
	int ret = test(a);

	printf("%d\n", ret);

	return 0;
}

Attention

  • Type de pointeur:
void* pc;

Représente la définition d'un pointeur pc, Mais il ne pointe vers rien. , Existe comme un pointeur vide .


Description de l'ordre des octets de fin de taille

Nous savons que peu importe le type de données , Est finalement compilé par le compilateur en code machine binaire pour le stockage , Et notre mémoire est stockée en octets comme Unit é de stockage minimale , Il y a un problème. , Lorsque les données sont stockées en octets , Dans quel ordre est - il stocké? ? Cela conduit au concept d'ordre des octets de taille .

🧠 Raison de l'ordre des octets de taille

Pourquoi y a - t - il un schéma d'ordre d'octets de taille? ?C'est parce que dans un système informatique,Nous sommes en octets,Chaque Unit é d'adresse correspond à un octet,Un octet est8bitBits.Mais dansCDans la langue8bitDecharEn dehors du type,Et16bitDeshortType,32bitDelongType(Voir le compilateur spécifique,64Plate - forme bitlongLe type est64Bits),En plus,Pour les chiffres supérieurs à8Processeur de bits,Par exemple16Bits ou32Processeur de bits, Parce que la largeur du registre est supérieure à un octet ,Il doit donc y avoir un problème avec la façon de programmer plusieurs octets.Il en résulte des modes de stockage à grande et à petite échelle.

Par exemple:Un16bitBitwiseshortVariable de typex ,L'adresse en mémoire est0x0010,Variablesx La valeur de0x1122 ,Alors0x11Est un octet élevé,0x22Faible octet.Pour le mode grand terminal,Juste... 0x11Placer à basse adresse,C'est - à - dire:0x0010Moyenne,0x22 Placer à haute adresse,C'est - à - dire:0x0011Moyenne.Mode petit terminal,Exactement le contraire..Que nous utilisonsX86(32Plate - forme bit)La structure est un modèle à petite extrémité,EtKEILC51Mode grand terminal.Beaucoup.ARM,DSPTous en mode petit bout.CertainsARMLe processeur peut également être sélectionné par le matériel en mode grand ou petit.

Concept d'ordre des octets

Ordre des octets, C'est - à - dire l'ordre des octets , Aussi appelé ordre final ou ordre de queue ,Dans le domaine de l'informatique,Ça veut dire「Mémoire」Moyen ou「 Liaison de communication numérique 」Moyenne, Ordre de tri des octets qui composent plusieurs octets . Sur presque toutes les machines , Les objets Multi - octets sont stockés dans une séquence d'octets continue .Par exemple, dansCDans la langue,Un intVariable de typexL'adresse est0x100, Alors son expression d'adresse correspondante &xLa valeur de0x100 Et x De4 Octets à stocker en mémoire 0x100, 0x101, 0x102, 0x103Emplacement. Les octets sont disposés comme suit: 2 Règles générales .

  1. Ordre - Ordre des grands octets
  2. Ordre inverse - Ordre des petits octets

La description textuelle ci - dessus peut être trop abstraite , Ensuite, les concepts d'ordre des octets de grande et de petite extrémité sont brièvement introduits d'une manière facile à comprendre. .

Ordre des octets de taille

Ordre des octets de fin de taille , C'est - à - dire que les bits haut et bas dans les données multi - octets sont stockés dans différentes adresses haut et bas en mémoire dans différents ordres. , équivalent à Shun (Inverse) Stockage séquentiel . Les blogueurs présenteront les concepts abstraits ci - dessus un par un :

  1. Comprendre d'abord ce qu'est une donnée Multi - octets .

Nous savons qu'une donnée est divisée en différents types de données en fonction de sa taille , Le nombre d'octets par type de données est différent , Nous les stockons dans différents types de données en fonction de la taille des octets de données .

Comme le type de caractère - Après son extension ASCIILa valeur du Code est0~255, Nous savons qu'un octet est 8Bits, En termes de caractères non signés, c'est - à - dire 00000000 ~ 11111111,Exactement.0 ~ 255, Le type de caractère est donc appelé données de type à caractère unique .

Et le nombre hexadécimal ,Par exemple::0x11223344 Données Multi - octets ,Dont:4Octets,Respectivement.0x11、0x22、0x33、0x44, Ces données sont appelées données multi - octets .


  1. Comprendre ce qu'est un octet élevé de données multi - octets .

Dans une séquence binaire ,

Par exemple::01010110101001011010100101101001

Nous avons mis en évidence 0101 Appelé octet élevé , Ajouter une ligne de retrait à l'arrière 1001 Certains sont appelés bits de faible octet , Pour distinguer .

C'est très compréhensible., Parce que le dernier 1 Le poids de 20,C'est - à - dire2De0Secondaire,Et le premier0 Le poids de 231,C'est - à - dire2De31Secondaire, C'est un bon choix pour distinguer les bits haut et bas .


Ensuite, il décrit comment stocker l'ordre des octets de taille :

Ordre des grands octets

Ordre des grands octets , Est de stocker des données dans un octet élevé à une adresse mémoire basse , Stocker les données à faible octet à haute adresse en mémoire

Donne - moi les données. :0x11223344

Le stockage en mémoire est :

 Mode de stockage à grande échelle
Mode stocké sous cette forme , C'est ce qu'on appelle le mode de stockage à grande échelle. , Cette séquence de stockage , C'est ce qu'on appelle l'ordre des grands octets. .

Ordre des petits octets

Ordre des petits octets , Est de stocker des données en octets élevés à une adresse élevée en mémoire , Stocker les données à faible octet à faible adresse en mémoire

Voici les données. :0x11223344

Le stockage en mémoire est :

 Mode de stockage de petite taille
Mode stocké sous cette forme , C'est ce qu'on appelle le mode de stockage à petite échelle. , Cette séquence de stockage , C'est ce qu'on appelle l'ordre des petits octets. .

Utilisé par les blogueurs VS2019Sur le compilateur, L'ordre des petits octets est utilisé :

Exemple:

int main()
{
	int a = 0x0000ff40;

	return 0;
}

Mise en service - Fenêtre mémoire(&a):

 Stockage en mémoire
0x001DFEFC Dans ce code, aAdresse de la variable, Les conditions de stockage sont les suivantes: 40 ff 00 00.

C'est - à - dire le mode de stockage à petite échelle .

Baidu System Engineer written Examination ( Le compilateur est programmé pour déterminer s'il s'agit d'un stockage grand ou petit )

Baidu2015Examen écrit annuel de l'ingénieur des systèmes:

Veuillez décrire brièvement les concepts d'ordre des octets de grande et de petite extrémité.,Concevoir une applet pour déterminer l'ordre des octets de la machine actuelle.(10Points)

La première moitié de la question a été résolue ci - dessus. , Ici, le blogueur analysera le problème , Et mettre en œuvre le Code .

🧣Analyse des problèmes

Déterminer si le système de compilation est un stockage de grande ou de petite taille ,Ce n'est pas compliqué.

Par exemple:0x11223344

Si en mode grand stockage :
Le mode de stockage est :11 22 33 44

Si en mode de stockage à petite extrémité :
Le mode de stockage est :44 33 22 11

Donc tout ce qu'il faut vraiment savoir, c'est que le contenu du premier octet est 11Toujours44 On peut juger. .
Mais ces données sont trop complexes. , Autant changer les chiffres. ,Par exemple,1.

1 Le Haut octet de 00, Les bits de faible octet sont 01, C'est un bon jugement. .

Démonstration de code

int check_sys(int x)
{
    
	return *(char*)&x;
}

int main()
{
    
	int a = 1;

	//Accord:
	// Si c'est grand ,Retour0
	//Si c'est une petite extrémité,Retour1
	int ret = check_sys(a);
	if (ret)
	{
    
		printf(" Est un mode de stockage de petite taille \n");
	}
	else
	{
    
		printf(" Est le mode de stockage grand terminal \n");
	}

	return 0;
}

Résultats des opérations:
Résultats des opérations
J'ai déjà analysé ça. , Mon compilateur VS2019 Est un mode de stockage de petite taille , Donc le résultat du Code est correct , Analyser le code ci - dessous .

Analyse des codes

  1. J'aimerais4 Obtenir le premier octet des octets , Il suffit de convertir le type de force entier en type de caractère lors de la récupération de l'adresse , Après avoir obtenu l'adresse où le premier octet est stocké, vous pouvez obtenir les données du premier octet en le référençant. .

  2. Si c'est le cas, 01, Description la méthode de stockage est 01 00 00 00, C'est - à - dire le mode de stockage à petite échelle , Sinon, le mode de stockage à grande échelle .

S'il y a quelque chose de clair ici, , Bienvenue dans la section commentaires ou blogueurs privés. .


🧶 Stockage de données entières en mémoire

Le stockage des données en mémoire suit certaines règles , Les données entières et les données flottantes suivent des règles différentes en mémoire , Voici comment les données entières sont stockées en mémoire .

Introduction d'un concept pour le stockage de données entières :Original anti - complément.

Code source、Code inverse、Complément

Il existe trois façons de représenter le nombre signé dans un ordinateur ,C'est le code original.、Inversion et complément. Les trois représentations ont des bits symboliques et numériques ( Ou bit valide )Deux parties,Les bits de symbole sont utilisés0Représentation“Positif”,Avec1Représentation“Négatif”, Et les bits numériques , Les trois représentations sont différentes .Dans un système informatique,Les valeurs numériques sont toujours représentées et stockées par un complément.La raison en est que:Utiliser un complément,Les bits symboliques et les champs numériques peuvent être traités uniformément;En même temps,L'addition et la soustraction peuvent également être traitées uniformément.
Le complément est en fait défini pour le stockage négatif , Pour les nombres non signés , Son code inverse et son code complémentaire sont égaux au code original. .

Code source:

Le code original. , Est de traduire les données directement en séquences binaires .

Prends ça.32 Exemple de plate - forme bit , Le BIT le plus élevé comme bit de symbole ,Les chiffres positifs sont marqués par0,Le BIT de signe du nombre négatif est1,Derrière.31 Les bits sont appelés bits valides , Calculer différents nombres avec différents poids , Le poids le plus bas est 20,Le deuxième est21,Et ainsi de suite..

Par exemple::

13Le code original de:00000000000000000000000000001101

-3Le code original de:10000000000000000000000000000011

Code inverse:

Code inverse,Comme son nom l'indique, Est d'inverser la séquence binaire du code source , Mais il faut faire attention ici. , Tous les bits binaires ne sont pas inversés , Les bits symboliques sont spécialement indépendants , Il représente plus ou moins un nombre , Il peut y avoir des résultats inattendus. .

Donc le Code inverse devrait diviser les bits de symbole par le code original , Les autres bits sont obtenus par inversion de bits .
(Note:: Un nombre positif a le même code inverse que le code original .)

Par exemple::

13Le Code inverse de:00000000000000000000000000001101

-3Le Code inverse de:11111111111111111111111111111100

Complément:

Les entiers sont stockés en mémoire avec des compléments , Donc le complément doit être trouvé par le Code inverse ci - dessus , La règle d'acquisition du complément est que le code original est inversé par bit ( Diviser les bits de symbole )Encore un..
(Note:: Le complément et l'original d'un nombre positif sont égaux .)

Par exemple::

13Le complément est:00000000000000000000000000001101

-3Le complément est:11111111111111111111111111111101

Parce que les entiers sont stockés en mémoire sous forme de complément , Par conséquent, le but de l'extraction du complément inverse original est de trouver le complément , Et la formule de calcul du complément est :Complément = Code original inversé par bit ( Diviser les bits de symbole )Encore un.

Ici, on passe parVS2019 Le compilateur vérifie qu'un complément de données est stocké en mémoire :

int main()
{
    
	int a = 13;
	//Code source:00000000 00000000 00000000 00001101
	//Code inverse:01111111 11111111 11111111 11110010
	//Complément:01111111 11111111 11111111 11110011

	int b = -3;
	//Code source:10000000 00000000 00000000 00000011
	//Code inverse:11111111 11111111 11111111 11111100
	//Complément:11111111 11111111 11111111 11111101

	return 0;
}

Débogage sous le compilateur - Mémoire - &a:

&aLes résultats de
Ce qui est stocké en mémoire :0d 00 00 00

Mode de stockage pour petit terminal ,00001101 Convertir en hexadécimal est 0d.

Débogage sous le compilateur - Mémoire - &b:

&bLes résultats de
Ce qui est stocké en mémoire :fd ff ff ff

Mode de stockage pour petit terminal ,1111 1111 Convertir en hexadécimal est ff,1111 1101 Convertir en hexadécimal est fd.

C'est tout., Ce qui est vraiment stocké en mémoire est le complément , Donc, pour comprendre comment les données entières sont stockées en mémoire, , Le concept de complément original et de complément inverse doit être fermement maîtrisé .


Troncature et levage intégral

Nous savons queint La taille de la variable de type est 4Octets32- Oui.bitBits(32Sous la plate - forme bit),Etchar La taille de la variable de type est 1Octets8- Oui.bitBits, Comment puis - Je stocker des données entières dans un char Qu'en est - il des variables de type ? Voici une façon très utile d'apprendre , C'est impossible. ,32 Il est impossible de mettre un bit dans 8 Dans une petite grille , Donc ce qu'on appelle Troncature.

Nous savons que,UncharLe type ne peut être stocké que8Bits, Si je devais char Type de données à %dImprimer sous forme de, C'est comme ça. 32 Bitdata l'imprime , Que dois - je faire? ? Je vais vous montrer un autre moyen. , C'est toujours impossible. , Donc le compilateur ne peut que char Type de données Lifting.

Ensuite, Expliquez brièvement les principes de troncature et de levage intégral. .

Troncature

Disons que j'en ai un. 32Séquence binaire bit:
01010011001000110001000100100011

C'est un très grand nombre.:

Calcul de la calculatrice

Il y a uncharEspace de type:
charEspace de type

- Oui.32 Les chiffres ne peuvent pas être mis à l'intérieur. , La troncature se produit , Ne conserver que les huit chiffres inférieurs ,Autres24 Abandon direct des chiffres ,

 Processus de troncature

Le résultat final du stockage est: :

 Résultats de stockage de la calculatrice

C'est le processus de troncature. .

Lifting

Quand je vais char Type de données à %d Lors de l'impression ,Nous savons que,%d Est un entier signé imprimé ,C'est imprimé.32Bits0/1 Résultat final de la séquence ,Mais la nôtrechar Seulement stocké dans le type 8Bits, C'est là que se produit l'amélioration de la forme .

Règles de levage intégral :

  1. Si un nombre non signé est soulevé par un entier ,Avant24Bits0.
  2. Si un nombre signé est soulevé par un entier , Détermine que le nombre est dans le binaire courant 0/1 Premier élément de la séquence , équivalent à un bit de symbole .
    - Si oui0, - Oui. 0
    - Si oui1, - Oui. 1

Par exemple::

Il y en a un aujourd'hui. 8Nombre de bits non signés.

unsigned char a = 148;

D'abord, nous écrivons la séquence binaire du nombre .

10010100 - 148

À cause des variablesa Est de type non signé , Donc peu importe que le premier élément de la séquence binaire soit 0Toujours1, Tout sera réparé. 0

Obtenir:

00000000000000000000000010010100

Le résultat final de l'impression est 148

Exercice de stockage de données entières

Analyser les résultats de sortie pour le code suivant :

1.
//Produit quoi??
int main()
{
    
	char a = -1;
	signed char b = -1;
	
	unsigned char c = -1;

	printf("a=%d b=%d c=%d\n", a, b, c);

	return 0;
}

Tout d'abord,VS2019Paire de compilateurschar Le traitement de type est considéré comme signé par défaut char,Donc les variablesaEt variablesb Du même type .

Calculer d'abord-1Complément à.

int main()
{
    
	//-1
	//Code source:10000000000000000000000000000001
	//Code inverse:11111111111111111111111111111110
	//Complément:11111111111111111111111111111111
	
	char a = -1;
	signed char b = -1;
	

	unsigned char c = -1;
	

	printf("a=%d b=%d c=%d\n", a, b, c);

	return 0;
}

Les trois variables sont charType, Donc tout se passe pendant le stockage Troncature.

int main()
{
    
	//-1
	//Code source:10000000000000000000000000000001
	//Code inverse:11111111111111111111111111111110
	//Complément:11111111111111111111111111111111

	char a = -1;
	// Complément stocké :11111111
	signed char b = -1;
	// Complément stocké :11111111

	unsigned char c = -1;
	// Complément stocké :11111111

	printf("a=%d b=%d c=%d\n", a, b, c);

	return 0;
}

Maintenant, nous allons mettre les trois variables %dImpression formelle,Ça arrive.Lifting.

  • Et pour les variables aEt variablesbDis, Le stockage est signé char, Le nombre d'ascenseurs est déterminé par le premier bit binaire 1,Alors...

VariablesaEt variablesb Le résultat de la portance intégrale est :

11111111111111111111111111111111
  • Et pour les variables cDis, C'est non signé. char, Total direct 0,Alors...

Variablesc Le résultat de la portance intégrale est :

00000000000000000000000011111111

Parce qu'après l'Ascension c Le BIT du symbole est 0, Par conséquent, tous les anti - compléments originaux sont égaux .

Et appuyer %d L'impression formelle exige que le complément soit converti en code original puis en décimal pour l'impression. ,

Alors...:

int main()
{
    
	//-1
	//Code source:10000000000000000000000000000001
	//Code inverse:11111111111111111111111111111110
	//Complément:11111111111111111111111111111111

	char a = -1;
	// Complément stocké :11111111
	// Complément après levage :11111111111111111111111111111111
	// Code inverse après levage :10000000000000000000000000000000
	// Code original après levage :10000000000000000000000000000001
	signed char b = -1;
	// Complément stocké :11111111
	// Complément après levage :11111111111111111111111111111111
	// Code inverse après levage :10000000000000000000000000000000
	// Code original après levage :10000000000000000000000000000001

	unsigned char c = -1;
	// Complément stocké :11111111
	// Complément après levage :00000000000000000000000011111111
	// Code inverse après levage :00000000000000000000000011111111
	// Code original après levage :00000000000000000000000011111111

	printf("a=%d b=%d c=%d\n", a, b, c);

	return 0;
}

Et voilà., Le résultat imprimé devrait être -1 -1 255

Imprimer les résultats:

Imprimer les résultats

  1. Qu'est - ce que la procédure suivante produit ?
2.
int main()
{
    
	char a = -128;

	printf("%u\n", a);

	return 0;
}

Variables de la question aC'est signé.charType.

Calculer d'abord -128 Le complément original .

int main()
{
    
	char a = -128;
	//-128
	//Code source:10000000000000000000000010000000
	//Code inverse:11111111111111111111111101111111
	//Complément:11111111111111111111111110000000

	printf("%u\n", a);

	return 0;
}

Oui.01111111111111111111111110000000 Une telle séquence binaire est stockée dans a Ça va arriver. Troncature.

Après la troncature a Les résultats stockés dans :10000000

À ce moment - là,%uImprimer sous forme de, C'est - à - dire imprimer sous forme d'entier non signé , Pour le levage intégral ,Et les variablesa Est un symbole charType,Le premier élément est1, Donc le lifting 24- Oui.1.

int main()
{
    
	char a = -128;
	//-128
	//Code source:10000000000000000000000010000000
	//Code inverse:11111111111111111111111101111111
	//Complément:11111111111111111111111110000000

	// Résultats tronqués :10000000
	// Résultats après lifting :11111111111111111111111110000000

	printf("%u\n", a);

	return 0;
}

À ce stade, le complément promu doit être converti en code original et imprimé sous forme décimale. .

Et%u Les bits symboliques du complément seront considérés comme des bits valides , C'est la même chose. .

int main()
{
    
	char a = -128;
	//-128
	//Code source:10000000000000000000000010000000
	//Code inverse:11111111111111111111111101111111
	//Complément:11111111111111111111111110000000

	// Résultats tronqués :10000000
	// Résultats après lifting :11111111111111111111111110000000
	
	//Complément:11111111111111111111111110000000
	//Code inverse:11111111111111111111111110000000
	//Code source:11111111111111111111111110000000

	printf("%u\n", a);

	return 0;
}

Et11111111111111111111111110000000La valeur devrait être4,294,967,168

Calcul de la calculatrice

Donc la sortie :

Résultats obtenus

3.
int main()
{
    
	char a = 128;

	printf("%u\n", a);

	return 0;
}

C'est toujours pareil,Trouve d'abord.128Complément à,Parce que128Est un nombre positif, Par conséquent, les compléments originaux et inversés sont les mêmes :

00000000000000000000000010000000

Stockage des variables entrantes a Une troncature entière se produira :

10000000

Et les variablesaSignécharType, Donc l'entier est promu à

11111111111111111111111110000000

VariablesaPar%uImpression formelle, Les bits symboliques sont considérés comme des bits valides , À ce stade, le complément du code original et du Code inverse est le même. , Calcul direct ,11111111111111111111111110000000La forme décimale de4,294,967,168

Calculatrice

Donc le résultat de l'impression est:Imprimer les résultats

4.
int mian()
{
    
	int i = -20;
	unsigned int j = 10;

	//Effectuer le calcul sous forme de complément,Enfin formaté en entier signé
	printf("%d\n", i + j);
	
	return 0;
}

Ou commencer par-20Et10 Le complément est calculé ,Mais iciiEtj Sont des variables entières , Donc il n'y a pas de troncature et de levage intégral .

int mian()
{
    
	int i = -20;
	//-20
	//Code source:10000000000000000000000000010100
	//Code inverse:11111111111111111111111111101011
	//Complément:11111111111111111111111111101100
	unsigned int j = 10;
	//10
	//Complément:00000000000000000000000000001010


	//Effectuer le calcul sous forme de complément,Enfin formaté en entier signé
	printf("%d\n", i + j);
	
	return 0;
}

Les données sont calculées sous forme de compléments binaires , Le résultat final est ajusté et modifié en fonction des exigences d'impression ou de stockage. .

Résultats des calculs

int mian()
{
    
	int i = -20;
	//-20
	//Code source:10000000000000000000000000010100
	//Code inverse:11111111111111111111111111101011
	//Complément:11111111111111111111111111101100
	unsigned int j = 10;
	//10
	//Complément:00000000000000000000000000001010

	//Calcul:
	//11111111111111111111111111101100
	//00000000000000000000000000001010
	//11111111111111111111111111110110 -  Résultat de l'addition de compléments 

	//Effectuer le calcul sous forme de complément,Enfin formaté en entier signé
	printf("%d\n", i + j);
	
	return 0;
}

Selon les exigences%dImprimer sous forme de, Le résultat calculé est converti en code source et imprimé en nombre décimal signé .

Complément:11111111111111111111111111110110
Code inverse:10000000000000000000000000001001
Code source:10000000000000000000000000001010

Le résultat du calcul est-10

Imprimer les résultats

int main()
{
    
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
    
		printf("%u\n", i);
	}

	return 0;
}

Analyse du programme:

VariablesiDe9 Début de la réduction à 0Heure, Les valeurs imprimées par le programme sont

9 8 7 6 5 4 3 2 1 0

Fin de l'impression 0Après,VariablesiEncore moins1,Devenir-1, Ça devrait sortir de la boucle. , Mais attention. ,Les variables iciiEst un entier non signé,Et-1Le complément est11111111111111111111111111111111, Donc il se résout à un entier positif particulièrement grand :4294967295.

 Résultats des calculs de la calculatrice
Il est donc admissible au contrôle de la circulation. (i >= 0), Donc la boucle continue 4294967295Une fois, Et a été réduit à 0Quand, Encore une fois, il s'est réduit et est redevenu -1, Ont été résolus à 4294967295, Donc le programme va tourner indéfiniment .

Ici, le blogueur coupe au hasard deux images imprimées pour votre référence .
Imprimer les résultats

Imprimer les résultats

6.
#include <string.h>

int main()
{
    
	char a[1000];
	int i;
	for (i = 0; i < 1000; i++)
	{
    
		a[i] = -1 - i;
	}
	printf("%d", strlen(a));

	return 0;
}

Analyse du programme:

Selon le Code, le premier nombre stocké dans le tableau devrait être -1,Le deuxième est-2,Et ainsi de suite..

 Stockage en mémoire

Mais ce tableau est charType,Nous savons quechar Type la plage de données qui peut être stockée est -128~127, Donc ces données ont été réduites à -128Après, Si vous vous soustrayez encore, vous ne pouvez pas le laisser tomber. , Mais voici un petit point de connaissance .

Expliquez - le en dessinant. .

char Calcul du complément de type

  1. Ce graphique montre char Tous les cas du complément de type correspondant à la décimale , Bits binaires de 0 Début du complément plus 1, C'est - à - dire décimal à partir de 0Commencez.1Calcul, Calcul final à 127.

  2. 11111111Pour-1Complément à, Baisse vers le Haut 1Calculé-2,Encore moins.1C'est-3, Et ainsi de suite -127.

  3. Et10000000 Cette séquence binaire ne peut pas être calculée , Donc le système l'assigne directement à -128.

En combinant les trois points ci - dessus, nous pouvons voir ,char Le type de complément est en fait basé sur -1,-2,…,-127,-128,127,126,…,2,1 De cette façon continue .

La forme de l'image est :

char Type de complément continu
Comme le montrent les deux figures ci - dessus, , Les données en mémoire sont stockées sous :

Données en mémoire

En fait, c'est un cycle infini de stockage , Toujours plein 1000 Jusqu'aux données .

Et la longueur de la chaîne est imprimée ,UtiliséstrlenFonctions,strlen Fonction rencontrée \0Arrête de compter, Donc le résultat du calcul devrait être 128 + 127 = 255.

Résultats des opérations

7.
unsigned char i = 0;

int main()
{
    
	for (i = 0; i <= 255; i++)
	{
    
		printf("hello world\n");
	}

	return 0;
}

Analyse du programme:

Premièrement, une variable globale est définie :Entier non signéi.

Non signéchar La plage de type est 0~255, Donc le Code est imprimé avant 255- Oui."hello world\n", Ça doit être vrai. .

Et255 Le complément en mémoire en tant que nombre non signé est :

00000000000000000000000011111111

Auto - augmentation1Après ça, le résultat est:

00000000000000000000000100000000

Stocker ce nombre dans une variable i Il doit y avoir quelque chose. ,Ça arrive.Troncature.

Stockage de 8 bits bas seulement ,Donc les variablesi Ce qui est maintenant stocké 00000000,C'est - à - dire0, Est un nombre non signé ,Identique à l'original, Et satisfait aux conditions de circulation , Donc le cycle recommence. .

Après analyse ci - dessus, Le résultat de ce code devrait être un cycle mort d'impression infinie .

Résultats des opérations


Stockage de données flottantes en mémoire

Commençons par examiner les données flottantes communes ?

3.14159
1E10

Type de données flottantes :

  1. float
  2. double
  3. long double

long doubleOui.C99Introduction dans la norme, Les compilateurs plus anciens ne supportent pas cette écriture .

Plage représentée par un nombre flottant:In"float.h"Disponible dans le fichier.

 Ouvrir le document comme

float.hDocumentation

La taille de la plage des données flottantes peut être visualisée dans ce document .

Ensuite, la façon dont les données flottantes sont stockées en mémoire est introduite. .

Prouver que les méthodes d'accès des entiers et des points flottants sont différentes

Exemple de stockage en virgule flottante:

int main()
{
    
	int n = 9;
	float* pFloat = (float*)&n;

	printf("nLa valeur de:%d\n", n);
	printf("*pFloatLa valeur de:%f\n", *pFloat);

	*pFloat = 9.0;

	printf("numLa valeur de:%d\n", n);
	printf("*pFloatLa valeur de:%f\n", *pFloat);

	return 0;
}

Le résultat imprimé est:

Imprimer les résultats

  • Oui.9StockageintVariable de typeiMoyenne, Donc la première impression est 9,C'est facile à comprendre.,Et utiliserfloat Le pointeur de type n'est pas référencé pour obtenir une valeur de 0;

  • Avecfloat Le type de pointeur écrase les valeurs en mémoire comme suit: 9.0, Imprimer en entier est une valeur que nous ne connaissons pas ,Et utiliserfloat Le pointeur de type n'est pas référencé 9.0.

Les exemples ci - dessus montrent que les données entières et les données flottantes sont stockées différemment. ,Et ensuite
Commencer à étudier comment les données flottantes sont stockées en mémoire .


IEEEFormulaire standard

Conformément aux normes internationalesIEEE(Institute of Electrical and Electronic Engineering)754,N'importe quel nombre binaire de points flottantsV Peut être exprimé sous la forme suivante: :

  • (-1)S M 2E
  • (-1)sReprésente un bit de symbole,Quands = 0Heure,VEst un nombre positif;Quands = 1Heure,VEst négatif.
  • MIndique un nombre valide,MDoit être supérieur ou égal à1,Et moins de2.
  • 2EReprésente un bit exponentiel.

Deux exemples::

  1. Nombre décimal3.75, Convertir d'abord en nombre binaire 011.11

Nombre binaire011 C'est un nombre décimal. 3, Première décimale 1Représentation1.0 / 21,Deuxième1Représentation1.0 / 22
Convertir enIEEELa forme standard est(-1)0 1.111 21
En ce momentS = 0,M = 1.111,E = 1

  1. Nombre décimal-0.5, Il est maintenant converti en nombre binaire -0.1

Nombre binaire0 C'est un nombre décimal. 0, Après le point décimal 1Représentation1.0 / 21.
Convertir enIEEELa forme standard est(-1)1 1.0 2-1.
En ce momentS = 1,M = 1.0,E = -1

==Attention!:== Les chiffres après la décimale sont basés sur 1.0 / 2n De la forme , Donc beaucoup de nombres n'ont pas de valeur exacte .

IEEE Dispositions relatives aux normes de stockage

  1. IEEE 754Le règlement:
  1. Pour32Nombre de points flottants de bits,Le plus élevé1Les bits sont des bits symboliquess,C'est parti.8Les bits sont des indicesE,Le reste23Bit est un nombre valideM.
  2. Pour64Nombre de points flottants de bits,Le plus élevé1Les bits sont des bits symboliquesS,C'est parti.11Les bits sont des indicesE,Le reste52Bit est un nombre valideM.

Description du dessin

  1. Pour un seul flotteur de précision :

 Mode de stockage à point flottant de précision unique

  1. Pour le nombre de points flottants de double précision :

 Mode de stockage à virgule flottante de double précision

  1. IEEE 754Pour les chiffres validesMEt indexE,Il existe également des dispositions spéciales.

PourM(Nombre effectif)Dispositions:

Comme je l'ai dit,1 ≤ M < 2 ,C'est - à - dire,MPeut être écrit comme1.xxxxxxForme,Parmi euxxxxxxxReprésente la partie décimale.
IEEE 754Le règlement,Enregistrer à l'intérieur de l'ordinateurMHeure,Par défaut, le premier chiffre de ce nombre est toujours1,Pour qu'il puisse être abandonné.,Enregistrer uniquement les suivantsxxxxxxSection.Comme la préservation1.01Quand,Enregistrer seulement01,Attendez la lecture., Et automatiquement mettre le premier 1 Mets ça. . Le but est d'économiser 1Nombre effectif de bits,Pour augmenterMPrécision.
Par32Exemple de nombre de points flottants,Laisse - le.M L'espace de 23Bits,Le premier1Après le départ,égal à peut être sauvegardé24Nombre effectif de bits.

PourE(Section des indices)Dispositions:

En ce qui concerne les indicesE,C'est compliqué..

Tout d'abord,,EEst un entier non signé(unsigned int),Cela signifie,SiEPour8Bits,Sa plage de valeurs est0 ~ 255;SiEPour11Bits,Sa plage de valeurs est0~2047.Mais,Nous savons que,Dans le comptage scientifiqueEPeut être négatif,Alors...IEEE 754Le règlement,En mémoireELa valeur réelle de doit être ajoutée à un nombre intermédiaire,Pour8BitwiseE,Ce nombre moyen est127;Pour11BitwiseE,Ce nombre moyen est1023.Par exemple,,210DeE- Oui.10, Enregistrer sous 32Bit float, Doit être sauvegardé sous 10 + 127 = 137,C'est - à - dire:10001001.

Plus127Ou1023 La raison du stockage est que vous devez soustraire le nombre lorsque vous le retirez. 127Ou1023,Voilà.E Pour obtenir un nombre négatif .


IEEE Lire les spécifications standard

IndexE De la mémoire Enlevez - le. Il y a trois autres situations. :

  1. EPas tout à fait.0Ou pas tous1
    À ce moment - là,,Le nombre de points flottants est représenté par les règles suivantes::
    C'est - à - dire l'indiceEValeur calculée moins127(Ou1023),Obtenir la valeur réelle,Ensuite, les chiffres validesMAjouter le premier1.
    Par exemple,:
    Nombre décimal0.5La Forme binaire de0.1, Parce que la partie entière spécifiée doit être 1,Déplacer le point décimal à droite1Bits,Oui.(-1)0 1.0 2(-1), Son ordre (Section des indices)Pour-1 + 127 = 126,Exprimé en01111110, Et la partie bit valide 1.0Supprimer la partie entière comme suit:0,Compléter0À23
    Bits00000000000000000000000,Sa représentation binaire est:
    0 01111110 00000000000000000000000
  1. ETout est0
    Peut être compris commeETout.0Heure, Le nombre est résolu à 0.
    Parce que,QuandETout.0Heure, Description IEEE La partie exponentielle de l'équation écrite sous forme standard est -127Ou-1023, C'est - à - dire que les bits de symbole et les bits valides doivent être multipliés par 1.0 / 2127 Ou multiplié par 1.0 / 21023Nombre de, Et c'est très petit. , Presque 0, Donc quand vous récupérez ce nombre en mémoire, vous pouvez le traduire directement 0.
  1. ETout est1
    À ce moment - là,,Représentation±Infini(Plus ou moins dépend du BIT de symboleS);
    La raison en est que,SiETout est1, Qui est calculé numériquement 128,2128 La puissance est un très grand nombre , Donc ici nous pouvons penser qu'il est plus négatif infini .

Un exemple simple, Comme le nombre décimal -12.75, Convertir en nombre binaire :-1100.11,Convertir enIEEELa forme standard est(-1)1 1.10011 2 3,En ce momentS = -1, M = 1.10011,E = 3

Un exemple de point flottant de précision unique , Il est stocké en mémoire :Prends ça.S Placer le premier bit comme symbole ,EPlus127,C'est - à - dire:3 + 127 = 130 Convertir en nombre binaire 10000010,Prends ça.M Supprimer la partie entière de , Stocker la partie décimale ,EEtM Tous les postes ne suffisent pas 0.

C'est - à - dire:

1 10000010 10011000000000000000000

InVS2019 Test sur le compilateur :

int main()
{
    
	float f = -12.75;

	return 0;
}

Mise en service - Mémoire - &f:

 Résultats de la mise en service

Le compilateur prend la forme hexadécimale

Traduire en binaire :

00000000 00000000 01001100 11000001

Et ce que nous avons calculé tout à l'heure :

11000001 01001100 00000000 00000000

On peut le découvrir., Contrairement au signe positif que nous avons écrit , Cela signifie que les données flottantes stockées en mémoire suivent également les règles d'ordre des octets de taille , Et voici l'ordre des petits octets .

Enfin, Nous regardons l'exemple que nous avons donné au début :

int main()
{
    
	int n = 9;
	float* pFloat = (float*)&n;

	printf("nLa valeur de:%d\n", n);
	printf("*pFloatLa valeur de:%f\n", *pFloat);

	*pFloat = 9.0;

	printf("numLa valeur de:%d\n", n);
	printf("*pFloatLa valeur de:%f\n", *pFloat);

	return 0;
}

Analyse du programme:

  1. Lors de la première affectation,Oui.9Assigner une valeur àn, Appartient au stockage de données entier .

Sa séquence binaire est :

000000000000000000000000000001001

Première impression en entier ,Les résultats sont les suivants:9
La deuxième impression est une impression en virgule flottante , C'est une sorte de données flottantes. :

0 00000000 000000000000000000001001

La première partie est la suivante:S(Bits de symbole),La deuxième partie est la suivante:E(Position exponentielle(À déduire127/1023)), La troisième partie est la suivante: M( Bit valide (Partie décimale))

Le BIT du symbole est0, La description est un nombre positif , Tous les indices 0,Moins127Plus tard.-127, La partie exponentielle est 2-127,C'est - à - dire:1.0 / 2127, Est un très petit nombre ,Peu importe.M( Bit valide )Combien?, Tout sera traduit ici 0, Donc la deuxième impression est 0.0.

  1. Lors de la deuxième affectation , Est attribué en tant que stockage flottant .

Nombre décimal9.0, Convertir en nombre binaire 1001.0,Convertir enIEEELe format standard est(-1)0 1.001 23.
Parmi euxS = 0,M = 1.001,E = 3
Lors du stockage binaire , Premier bit de symbole ,Après8 Position. E+127Séquence binaire de, Position restante MLa partie décimale de.

C'est - à - dire:

0 10000010 00100000000000000000000

Le troisième résultat d'impression est la traduction de ce nombre binaire en décimal .

C'est - à - dire:1091567616‬
Calcul de la calculatrice

Alors..., Le résultat de la troisième impression est 1091567616‬

La quatrième impression est en virgule flottante , C'est - à - dire extraire les données sous forme de points flottants , Donc la quatrième fois que vous imprimez 9.0.

Imprimer les résultats:

Imprimer les résultats

Résumé

Il y a beaucoup de contenu dans cet article , Tout d'abord, les types de données sont introduits , Introduit également le mode de stockage côté taille dans le compilateur , C'est tout. 2015 Un test écrit pour l'Ingénieur du système Baidu , Le complément inverse original est introduit dans la mémoire de données entière 、 Concepts de troncature et de levage intégral ,Et l'a fait.7 Formation au Tao ,Enfin, selonIEEE L'Association parle des données flottantes en mémoire , C'est sec. , Je vous suggère de vous asseoir et de regarder lentement. .

Enfin, je suis Aaron, J'espère que le blog d'aujourd'hui vous aidera. , N'oublie pas le triple soutien. ~

- Oui. + Attention + Collection

S'il y a quelque chose que vous ne comprenez pas, n'hésitez pas à laisser un commentaire ou à envoyer un message privé à un blogueur. ~

版权声明
本文为[Aaronskr]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/10/20211013211740527z.html

随机推荐