Formation Python
Le but de ce didacticiel est de familiariser un programmeur Python expérimenté avec les bases du langage C et son utilisation dans le code source CPython. Cela suppose que vous avez déjà une compréhension intermédiaire de la syntaxe Python.
Cela dit, C est un langage assez limité et la plupart de son utilisation dans CPython relève d'un petit ensemble de règles de syntaxe. Arriver au point où vous comprenez le code est une étape beaucoup plus petite que d'être capable d'écrire C efficacement. Ce tutoriel vise le premier objectif mais pas le second.
L'une des premières choses qui se démarque comme une grande différence entre Python et C est le préprocesseur C. Vous examinerez cela en premier.
Le préprocesseur C
Le préprocesseur, comme son nom l'indique, est exécuté sur vos fichiers source avant l'exécution du compilateur. Il a des capacités très limitées, mais vous pouvez les utiliser à grand avantage dans la construction de programmes C.
Le préprocesseur produit un nouveau fichier, qui est ce que le compilateur va réellement traiter. Toutes les commandes du préprocesseur démarrent au début d'une ligne, avec un #
symbole comme premier caractère non blanc.
Le but principal du préprocesseur est de faire une substitution de texte dans le fichier source, mais il fera également du code conditionnel de base avec #si
ou des déclarations similaires.
Vous allez commencer par la directive de préprocesseur la plus fréquente: #comprendre
.
#comprendre
#comprendre
est utilisé pour extraire le contenu d'un fichier dans le fichier source actuel. Il n'y a rien de sophistiqué dans #comprendre
. Il lit un fichier à partir du système de fichiers, exécute le préprocesseur sur ce fichier et place les résultats dans le fichier de sortie. Ceci est fait récursivement pour chaque #comprendre
directif.
Par exemple, si vous regardez les CPython Modules / _multiprocessing / semaphore.c
fichier, puis vers le haut, vous verrez la ligne suivante:
#comprendre "multiprocessing.h"
Cela indique au préprocesseur d'extraire tout le contenu de multiprocessing.h
et placez-les dans le fichier de sortie à cette position.
Vous remarquerez deux formes différentes pour le #comprendre
déclaration. L'un d'eux utilise des guillemets (""
) pour spécifier le nom du fichier d'inclusion, et l'autre utilise des chevrons (<>
). La différence vient des chemins à rechercher lors de la recherche du fichier sur le système de fichiers.
Si tu utilises <>
pour le nom de fichier, le préprocesseur examinera uniquement les fichiers d'inclusion système. L'utilisation de guillemets autour du nom de fichier forcera le préprocesseur à chercher d'abord dans le répertoire local, puis à revenir aux répertoires système.
#définir
#définir
vous permet de faire une substitution de texte simple et joue également dans le #si
directives que vous verrez ci-dessous.
Dans sa forme la plus élémentaire, #définir
vous permet de définir un nouveau symbole qui est remplacé par une chaîne de texte dans la sortie du préprocesseur.
Continuer dans semphore.c
, vous trouverez cette ligne:
Cela indique au préprocesseur de remplacer chaque instance de SEM_FAILED
en dessous de ce point avec la chaîne littérale NUL
avant que le code ne soit envoyé au compilateur.
#définir
les éléments peuvent également prendre des paramètres comme dans cette version spécifique à Windows de SEM_CREATE
:
#define SEM_CREATE (nom, val, max) CreateSemaphore (NULL, val, max, NULL)
Dans ce cas, le préprocesseur attendra SEM_CREATE ()
pour ressembler à un appel de fonction et avoir trois paramètres. Ceci est généralement appelé un macro. Il remplacera directement le texte des trois paramètres dans le code de sortie.
Par exemple, à la ligne 460 de semphore.c
, la SEM_CREATE
la macro est utilisée comme ceci:
manipuler = SEM_CREATE(Nom, valeur, max);
Lorsque vous compilez pour Windows, cette macro sera développée pour que la ligne ressemble à ceci:
manipuler = CréerSémaphore(NUL, valeur, max, NUL);
Dans une section ultérieure, vous verrez comment cette macro est définie différemment sur Windows et d'autres systèmes d'exploitation.
#undef
Cette directive efface toute définition de préprocesseur précédente de #définir
. Cela permet d'avoir un #définir
en vigueur pour une partie seulement d'un fichier.
#si
Le préprocesseur autorise également les instructions conditionnelles, vous permettant d'inclure ou d'exclure des sections de texte en fonction de certaines conditions. Les instructions conditionnelles sont fermées avec le #fin si
directive et peut également utiliser #elif
et #autre
pour des réglages précis.
Il existe trois formes de base de #si
que vous verrez dans la source CPython:
#ifdef
inclut le bloc de texte suivant si la macro spécifiée est définie. Vous pouvez également le voir écrit comme#if défini (
.) #ifndef
inclut le bloc de texte suivant si la macro spécifiée est ne pas défini.#si
inclut le bloc de texte suivant si la macro est définie et il évalue àVrai
.
Notez l'utilisation de "texte" au lieu de "code" pour décrire ce qui est inclus ou exclu du fichier. Le préprocesseur ne sait rien de la syntaxe C et ne se soucie pas du texte spécifié.
#pragma
Les pragmas sont des instructions ou des astuces pour le compilateur. En général, vous pouvez les ignorer lors de la lecture du code car ils traitent généralement de la façon dont le code est compilé, et non de la façon dont le code s'exécute.
#Erreur
Finalement, #Erreur
affiche un message et provoque l'arrêt du préprocesseur. Encore une fois, vous pouvez les ignorer en toute sécurité pour lire le code source CPython.
Syntaxe C de base pour les programmeurs Python
Cette section ne couvrira pas tout aspects de C, il ne vise pas non plus à vous apprendre à écrire C. Il se concentrera sur les aspects de C qui sont différents ou déroutants pour les développeurs Python la première fois qu'ils les voient.
Général
Contrairement à Python, les espaces n’est pas important pour le compilateur C. Le compilateur ne se soucie pas si vous divisez des instructions sur plusieurs lignes ou si vous bloquez tout votre programme en une seule très longue ligne. En effet, il utilise des délimiteurs pour toutes les instructions et tous les blocs.
Il existe bien sûr des règles très spécifiques pour l'analyseur, mais en général, vous serez en mesure de comprendre la source CPython en sachant simplement que chaque instruction se termine par un point-virgule (;
), et tous les blocs de code sont entourés d'accolades ().
L'exception à cette règle est que si un bloc n'a qu'une seule instruction, les accolades peuvent être omises.
Toutes les variables en C doivent être déclaré, ce qui signifie qu'il doit y avoir une seule déclaration indiquant le type de cette variable. Notez que, contrairement à Python, le type de données qu'une seule variable peut contenir ne peut pas changer.
Voici quelques exemples:
/ * Les commentaires sont inclus entre slash-astérisque et astérisque-slash * /
/ * Ce style de commentaire peut s'étendre sur plusieurs lignes -
donc cette partie est encore un commentaire. * /
// Les commentaires peuvent également venir après deux barres obliques
// Ce type de commentaire ne va que jusqu'à la fin de la ligne, donc nouveau
// les lignes doivent commencer par des doubles barres obliques (//).
int X = 0; // Déclare x comme étant de type 'int' et l'initialise à 0
si (X == 0)
// Ceci est un bloc de code
int y = 1; // y n'est qu'un nom de variable valide jusqu'à la fermeture
// Plus de déclarations ici
printf("x est% d y est% d\n ", X, y);
}
// Les blocs sur une seule ligne ne nécessitent pas de parenthèses
si (X == 13)
printf("x est 13!\n ");
printf("passé le bloc if\n ");
En général, vous verrez que le code CPython est formaté de manière très claire et reste généralement fidèle à un seul style dans un module donné.
si
Déclarations
En C, si
fonctionne généralement comme il le fait en Python. Si la condition est vraie, le bloc suivant est exécuté. le autre
et sinon si
la syntaxe doit être suffisamment familière aux programmeurs Python. Notez que C si
les déclarations n’ont pas besoin fin si
car les blocs sont délimités par .
Il y a un raccourci en C pour faire court si
… autre
déclarations appelées opérateur ternaire:
état ? true_result : false_result
Vous pouvez le trouver dans sémaphore.c
où, pour Windows, il définit une macro pour SEM_CLOSE ()
:
#define SEM_CLOSE (sem) (CloseHandle (sem)? 0: -1)
La valeur de retour de cette macro sera 0
si la fonction Fermer la poignée ()
Retour vrai
et -1
autrement.
Remarque: Les types de variables booléennes sont pris en charge et utilisés dans certaines parties de la source CPython, mais ils ne font pas partie du langage d'origine. C interprète les conditions binaires en utilisant une règle simple: 0
ou NUL
est faux, et tout le reste est vrai.
commutateur
Déclarations
Contrairement à Python, C prend également en charge commutateur
. En utilisant commutateur
peut être considéré comme un raccourci pour si
… sinon si
Chaînes. Cet exemple est de sémaphore.c
:
commutateur (WaitForSingleObjectEx(manipuler, 0, FAUX))
Cas WAIT_OBJECT_0:
si (!LibérationSémaphore(manipuler, 1, &précédent))
revenir MP_STANDARD_ERROR;
*valeur = précédent + 1;
revenir 0;
Cas WAIT_TIMEOUT:
*valeur = 0;
revenir 0;
défaut:
revenir MP_STANDARD_ERROR;
Cela effectue un basculement sur la valeur de retour de WaitForSingleObjectEx ()
. Si la valeur est WAIT_OBJECT_0
, puis le premier bloc est exécuté. le WAIT_TIMEOUT
value aboutit au second bloc, et tout le reste correspond au défaut
bloquer.
Notez que la valeur testée, dans ce cas la valeur de retour de WaitForSingleObjectEx ()
, doit être une valeur intégrale ou un type énuméré, et chaque Cas
doit être une valeur constante.
Boucles
Il existe trois structures en boucle en C:
pour
bouclestandis que
bouclesfaire
…tandis que
boucles
pour
les boucles ont une syntaxe assez différente de Python:
pour ( <initialisation>; <état>; <incrément>)
<code à être bouclé plus de>
En plus du code à exécuter dans la boucle, il existe trois blocs de code qui contrôlent le pour
boucle:
-
le
section s'exécute exactement une fois au démarrage de la boucle. Il est généralement utilisé pour définir un compteur de boucle sur une valeur initiale (et éventuellement pour déclarer le compteur de boucle). -
le
le code s'exécute immédiatement après chaque passage dans le bloc principal de la boucle. Traditionnellement, cela incrémentera le compteur de boucle. -
Finalement, le
court après le
. La valeur de retour de ce code sera évaluée et la boucle se rompt lorsque cette condition retourne false.
Voici un exemple de Modules / sha512module.c
:
pour (je = 0; je < 8; ++je)
S[[[[je] = sha_info->digérer[[[[je];
Cette boucle fonctionnera 8
fois, avec je
incrémentation de 0
à sept
, et se terminera lorsque la condition est vérifiée et je
est 8
.
tandis que
les boucles sont pratiquement identiques à leurs homologues Python. le faire
… tandis que
la syntaxe est cependant un peu différente. La condition sur un faire
… tandis que
la boucle n’est pas vérifiée tant que après le corps de la boucle est exécuté pour la première fois.
Il existe de nombreux exemples de pour
boucles et tandis que
boucles dans la base de code CPython, mais faire
… tandis que
est inutilisé.
Les fonctions
La syntaxe des fonctions en C est similaire à celle de Python, avec l'ajout que le type de retour et les types de paramètres doivent être spécifiés. La syntaxe C ressemble à ceci:
<return_type> nom_fonction(<paramètres>)
<corps_fonction>
Le type de retour peut être n'importe quel type valide en C, y compris des types intégrés comme int
et double
ainsi que des types personnalisés comme PyObject
, comme dans cet exemple de sémaphore.c
:
statique PyObject *
semlock_release(SemLockObject *soi, PyObject *args)
<déclarations de fonction corps ici>
Ici, vous voyez quelques fonctionnalités spécifiques à C en jeu. Tout d’abord, rappelez-vous que les espaces n’ont pas d’importance. Une grande partie du code source CPython place le type de retour d'une fonction sur la ligne au-dessus du reste de la déclaration de fonction. C'est le PyObject *
partie. Vous examinerez de plus près l’utilisation de *
un peu plus tard, mais pour l’instant, il est important de savoir qu’il existe plusieurs modificateurs que vous pouvez placer sur les fonctions et les variables.
statique
est l'un de ces modificateurs. Il existe des règles complexes régissant le fonctionnement des modificateurs. Par exemple, le statique
modificateur ici signifie quelque chose de très différent de celui que vous aviez placé devant une déclaration de variable.
Heureusement, vous pouvez généralement ignorer ces modificateurs tout en essayant de lire et de comprendre le code source CPython.
La liste des paramètres des fonctions est une liste de variables séparées par des virgules, similaire à ce que vous utilisez en Python. Encore une fois, C nécessite des types spécifiques pour chaque paramètre, donc SemLockObject * self
dit que le premier paramètre est un pointeur vers un SemLockObject
et s'appelle soi
. Notez que tous les paramètres en C sont positionnels.
Voyons ce que signifie la partie "pointeur" de cette déclaration.
Pour donner un peu de contexte, les paramètres qui sont passés aux fonctions C sont tous passé par valeur, ce qui signifie que la fonction opère sur une copie de la valeur et non sur la valeur d'origine dans la fonction appelante. Pour contourner ce problème, les fonctions transmettront fréquemment l'adresse de certaines données que la fonction peut modifier.
Ces adresses sont appelées pointeurs et avoir des types, donc int *
est un pointeur vers une valeur entière et est d'un type différent de double *
, qui est un pointeur vers un nombre à virgule flottante double précision.
Pointeurs
Comme mentionné ci-dessus, les pointeurs sont des variables qui contiennent l'adresse d'une valeur. Ceux-ci sont fréquemment utilisés en C, comme le montre cet exemple:
statique PyObject *
semlock_release(SemLockObject *soi, PyObject *args)
<déclarations de fonction corps ici>
Ici le soi
le paramètre contiendra l'adresse de, ou un pointeur vers, une SemLockObject
valeur. Notez également que la fonction renverra un pointeur vers un PyObject
valeur.
Il y a une valeur spéciale en C appelée NUL
cela indique qu'un pointeur ne pointe vers rien. Vous verrez des pointeurs attribués à NUL
et vérifié contre NUL
dans toute la source CPython. Ceci est important car il y a très peu de limitations quant aux valeurs qu'un pointeur peut avoir, et l'accès à un emplacement mémoire qui ne fait pas partie de votre programme peut provoquer un comportement très étrange.
En revanche, si vous essayez d'accéder à la mémoire à NUL
, alors votre programme se fermera immédiatement. Cela peut ne pas sembler mieux, mais il est généralement plus facile de détecter un bogue de mémoire si NUL
est accessible que si une adresse mémoire aléatoire est modifiée.
Cordes
C n'a pas de type chaîne. Il existe une convention autour de laquelle de nombreuses fonctions de bibliothèque standard sont écrites, mais il n'y a pas de type réel. Au contraire, les chaînes en C sont stockées sous forme de tableaux de carboniser
(pour ASCII) ou wchar
(pour Unicode), dont chacune contient un seul caractère. Les chaînes sont marquées d'un terminateur nul, qui a une valeur 0
et est généralement affiché dans le code comme \ 0
.
Opérations de base sur les chaînes comme strlen ()
comptez sur ce terminateur nul pour marquer la fin de la chaîne.
Étant donné que les chaînes ne sont que des tableaux de valeurs, elles ne peuvent pas être directement copiées ou comparées. La bibliothèque standard a le strcpy ()
et strcmp ()
fonctions (et leurs wchar
cousins) pour avoir effectué ces opérations et plus encore.
Structs
Votre dernière étape de cette mini-visite de C est de savoir comment créer de nouveaux types en C: structs. le struct
mot-clé vous permet de regrouper un ensemble de types de données différents dans un nouveau type de données personnalisé:
struct <nom_struct>
<type> <nom de membre>;
<type> <nom de membre>;
...
;
Cet exemple partiel de Modules / arraymodule.c
montre un struct
déclaration:
struct arraydescr
carboniser code de type;
int taille de l'article;
...
;
Cela crée un nouveau type de données appelé arraydescr
qui compte de nombreux membres, dont les deux premiers sont code de type char
Et un int itemsize
.
Les structures seront fréquemment utilisées dans le cadre d'un typedef
, qui fournit un simple alias pour le nom. Dans l'exemple ci-dessus, toutes les variables du nouveau type doivent être déclarées avec le nom complet struct arraydescr x;
.
Vous verrez fréquemment une syntaxe comme celle-ci:
typedef struct
PyObject_HEAD
SEM_HANDLE manipuler;
non signé longue last_tid;
int compter;
int Valeur max;
int gentil;
carboniser *Nom;
SemLockObject;
Cela crée un nouveau type de structure personnalisé et lui donne le nom SemLockObject
. Pour déclarer une variable de ce type, vous pouvez simplement utiliser l'alias SemLockObject x;
.
Conclusion
Cela conclut votre visite rapide de la syntaxe C. Bien que cette description efface à peine la surface du langage C, vous avez maintenant des connaissances suffisantes pour lire et comprendre le code source CPython.
Dans ce didacticiel, vous avez appris:
- Qu'est-ce que Préprocesseur C est et quel rôle il joue dans la construction de programmes C
- Comment vous pouvez utiliser Directives du préprocesseur pour manipuler les fichiers source
- Comment Syntaxe C se compare à Syntaxe Python
- Comment créer boucles, les fonctions, cordeset d'autres fonctionnalités de C
Maintenant que vous êtes familiarisé avec C, vous pouvez approfondir vos connaissances sur le fonctionnement interne de Python en explorant le code source de CPython. Joyeux Python!
[ad_2]