↓ Il semble qu'il soit populaire récemment de supprimer Python avec une erreur de segmentation. Segfo avec python en trois lignes Segfo avec python en 2 lignes Segfo avec python en une ligne Segfo avec 33 caractères en Python Segfo avec Python en une ligne sans utiliser de ctypes Segfo avec Rust en 5 lignes Le simple fait de changer le sujet (Python) en C le rend ennuyeux, mais c'est la même chose.
Opération confirmée avec gcc 10.1.0 (un avertissement apparaît)
*a;main(){*a=0;}
> gcc segf.c
segf.c:3:4:avertissement:La définition de données n'a pas de type ni de classe de stockage
3 | *a;main(){*a=0;}
| ^
segf.c:3:5:avertissement:Le type sera dans la déclaration de «a» à la valeur par défaut «int»[-Wimplicit-int]
3 | *a;main(){*a=0;}
| ^
segf.c:3:7:avertissement:Définir le type de retour comme "int" par défaut[-Wimplicit-int]
3 | *a;main(){*a=0;}
| ^~~~
> ./a.out
zsh: segmentation fault (core dumped) ./a.out
** Attention ** Actuellement, le nombre minimum de caractères pouvant être utilisés pour le langage C est de 5 caractères. (Voir les commentaires ci-dessous)
Cela seul ne répond pas aux exigences de l'article de Qiita, je vais donc l'expliquer brièvement. Le premier code à voir est ↓.
int main(void) {
int *a = 0;
*a = 0; //← Tomber ici
return 0;
}
Le langage C a un concept appelé pointeur, qui vous permet d'accéder à n'importe quelle adresse en mémoire.
Déclarez un pointeur nommé ʻa à ʻint * a = 0
et attribuez l'adresse mémoire 0
. Attribuer une valeur à «* a» dans la deuxième ligne accédera à une adresse [^ 1] qui n'existe pas réellement, et une exception CPU se produira.
Maintenant, raccourcissons le code ci-dessus. Premièrement, «return 0» peut être omis selon la convention du langage C. [^ 5] De plus, le type de retour ʻint peut être omis du point de vue de la compatibilité. (S'il est omis, il est implicitement ʻint
.) De plus, il fonctionne sans écrire l'argument void
.
Sur la base de ces derniers, c'est comme suit.
main() {
int *a = 0;
*a = 0; //← Tomber ici
}
Ici, la variable locale «a» peut être définie comme une variable globale. De plus, si vous la définissez comme une variable globale, elle sera initialisée avec «0» (sauf s'il s'agit d'un environnement très spécial), donc aucune affectation pour l'initialisation n'est requise [^ 6].
int *a;
main() {
*a = 0; //← Tomber ici
}
De plus, le spécificateur de type de variable globale ʻint` peut être omis. Ceci est le code au début.
Dans un ordinateur classique, la mémoire est un appareil qui peut stocker de nombreuses informations. Cependant, pour envoyer beaucoup d'informations à la mémoire ou les recevoir de la mémoire, intuitivement, il faut autant de fils que le nombre d'informations que vous souhaitez envoyer et recevoir. Cependant, il n'est pas possible en termes de coût de préparer beaucoup de fils fins pouvant supporter la vitesse de fonctionnement du CPU, nous allons donc introduire le concept d'adresse mémoire. En conséquence, bien que le nombre d'informations pouvant être lues et écrites en même temps (appelé 1 octet) [^ 2] soit petit, beaucoup d'informations peuvent être stockées dans la mémoire en modifiant l'adresse mémoire.
De cette manière, le concept d'adresse mémoire est indispensable à l'ère de la grande capacité mémoire, mais comme il est représenté par un entier positif commençant par "0", l'adresse "0" existe réellement. .. Ensuite, la raison pour laquelle l'erreur de segmentation s'est produite est due à la fonction CPU appelée pagination utilisée par le système d'exploitation.
Les adresses mémoire sont uniques sur un ordinateur. Par conséquent, sauf indication contraire, plusieurs processus posséderont le même espace d'adressage mémoire dans un système d'exploitation multitâche. Dans ce cas, lors de l'accès à la mémoire, vous devez faire attention à ne pas accéder accidentellement à la mémoire utilisée par d'autres processus. La pagination élimine cet inconvénient, permettant à chaque processus de posséder son propre espace d'adressage virtuel. À ce stade, le système d'exploitation gère une table qui convertit entre l'espace d'adressage virtuel [^ 3] et l'espace d'adressage physique (mémoire).
Par conséquent, le processus doit pouvoir accéder à toutes les adresses existantes. Cependant, ce n'est pas le cas dans la réalité, et le système d'exploitation tire également parti de l'espace d'adressage virtuel. Il limite la plage de mémoire disponible pour le programme. Ce type de division de l'espace d'adressage virtuel est appelé une carte mémoire. Comme indiqué dans cet article, la zone autour de l'adresse 0 n'est pas utilisée (l'adresse mémoire physique correspondante n'est pas attribuée), donc Un défaut de segmentation se produira. [^ 4]
Il m'a fallu environ 20 minutes pour l'écrire à la légère. J'aurais dû l'écrire.
[^ 1]: voir ci-dessous. [^ 2]: En fait, 64 bits peuvent être écrits dans chaque module de mémoire en même temps. De plus, la mémoire est accessible à grande vitesse en utilisant des technologies telles que DMA. [^ 3]: et espace d'adressage linéaire [^ 4]: Cela dépend de la façon dont le système d'exploitation est implémenté. En outre, selon le système d'exploitation, une autre exception de processeur se produira. [^ 5]: Beaucoup de gens pensent à tort qu'omettre «return 0;» est une erreur. [^ 6]: Ce n'est pas grave si vous ne l'initialisez pas car cela ne provoque que Segfo.
Recommended Posts