static volatile unsigned char PORTB @ 0x06;
Il s'agit d'une ligne de code dans un fichier d'en-tête de microcontrôleur PIC. L' @
opérateur est utilisé pour stocker la valeur PORTB à l'intérieur de l'adresse 0x06
, qui est un registre à l'intérieur du contrôleur PIC qui représente PORTB. Jusqu'à ce point, j'ai une idée claire.
Cette ligne est déclarée comme une variable globale dans un fichier d'en-tête ( .h
). Donc, d'après ce que j'ai appris sur le langage C, une "variable globale statique" n'est visible par aucun autre fichier - ou, simplement, les variables / fonctions globales statiques ne peuvent pas être utilisées en dehors du fichier actuel.
Ensuite, comment ce mot clé peut-il PORTB
être visible dans mon fichier source principal et dans de nombreux autres fichiers d'en-tête que j'ai créés manuellement?
Sur mon fichier source principal, j'ai seulement ajouté le fichier d'en #include pic.h
- tête Cela a-t-il quelque chose à voir avec ma question?
static
les globaux sont visibles à l'intérieur de l'unité de compilation unique et ne sont pas exportés au-delà. Ils ressemblent beaucoup auxprivate
membres d'une classe de POO. C'est-à-dire toutes les variables qui doivent être partagées entre différentes fonctions à l'intérieur d'une unité de compilation mais qui ne sont pas censées être visibles en dehors de ce que cu devrait êtrestatic
. Cela réduit également le "clobbering" de l'espace de noms global du programme.Réponses:
Le mot-clé «statique» en C a deux significations fondamentalement différentes.
Limiter la portée
Dans ce contexte, «statique» s'associe à «extern» pour contrôler la portée d'un nom de variable ou de fonction. Statique fait que le nom de la variable ou de la fonction n'est disponible que dans une seule unité de compilation et uniquement disponible pour le code qui existe après la déclaration / définition dans le texte de l'unité de compilation.
Cette limitation elle-même ne signifie vraiment que si et seulement si vous avez plusieurs unités de compilation dans votre projet. Si vous n'avez qu'une seule unité de compilation, cela fait toujours des choses, mais ces effets sont pour la plupart inutiles (sauf si vous aimez creuser dans des fichiers objets pour lire ce que le compilateur a généré.)
Comme indiqué, ce mot-clé dans ce contexte s'apparie avec le mot-clé 'extern', qui fait le contraire - en rendant le nom de variable ou de fonction pouvant être lié avec le même nom que celui trouvé dans d'autres unités de compilation. Ainsi, vous pouvez considérer «statique» comme nécessitant que la variable ou le nom soit trouvé dans l'unité de compilation actuelle, tandis que «externe» permet le lien entre les unités de compilation croisée.
Durée de vie statique
La durée de vie statique signifie que la variable existe pendant toute la durée du programme (aussi longue soit-elle). Lorsque vous utilisez 'statique' pour déclarer / définir une variable dans une fonction, cela signifie que la variable est créée quelque temps avant sa première utilisation ( ce qui signifie, chaque fois que je l'ai vécu, que la variable est créée avant le début de main ()) et n'est pas détruite par la suite. Pas même lorsque l'exécution de la fonction est terminée et qu'elle revient à son appelant. Et tout comme les variables de durée de vie statiques déclarées en dehors des fonctions, elles sont initialisées au même moment - avant le début de main () - à un zéro sémantique (si aucune initialisation n'est fournie) ou à une valeur explicite spécifiée, si elle est donnée.
Ceci est différent des variables de fonction de type `` auto '', qui sont créées nouvelles (ou comme si elles étaient nouvelles) chaque fois que la fonction est entrée, puis sont détruites (ou comme si elles étaient détruites) lorsque la fonction se ferme.
Contrairement à l'impact de l'application de «statique» sur une définition de variable en dehors d'une fonction, qui a un impact direct sur sa portée, déclarer une variable de fonction (dans un corps de fonction, évidemment) comme «statique» n'a pas impact sur sa portée. La portée est déterminée par le fait qu'elle a été définie au sein d'un corps de fonction. Les variables de durée de vie statiques définies dans les fonctions ont la même étendue que les autres variables «auto» définies dans les corps de fonction - étendue de la fonction.
Sommaire
Ainsi, le mot clé «statique» a des contextes différents avec ce qui équivaut à «des significations très différentes». La raison pour laquelle il a été utilisé de deux manières, comme celle-ci, était d'éviter d'utiliser un autre mot clé. (Il y a eu une longue discussion à ce sujet.) Il a été estimé que les programmeurs pouvaient tolérer l'utilisation et la valeur d'éviter encore un autre mot-clé dans la langue était plus importante (que les arguments autrement).
(Toutes les variables déclarées en dehors des fonctions ont une durée de vie statique et n'ont pas besoin du mot clé 'statique' pour que cela soit vrai. Donc, ce type de mot clé libéré pour être utilisé ici signifie quelque chose de complètement différent: 'visible uniquement dans une seule compilation "C'est un hack, en quelque sorte.)
Note spécifique
Le mot «statique» ici doit être interprété comme signifiant que l'éditeur de liens ne tentera pas de faire correspondre plusieurs occurrences de PORTB qui peuvent être trouvées dans plusieurs unités de compilation (en supposant que votre code en a plusieurs).
Il utilise une syntaxe spéciale (non portable) pour spécifier «l'emplacement» (ou la valeur numérique de l'étiquette qui est généralement une adresse) de PORTB. L'éditeur de liens reçoit donc l'adresse et n'a pas besoin d'en trouver une. Si vous aviez deux unités de compilation utilisant cette ligne, elles finiraient chacune par pointer vers le même endroit, de toute façon. Il n'est donc pas nécessaire de l'étiqueter «extern», ici.
S'ils avaient utilisé «extern», cela pourrait poser un problème. L'éditeur de liens pourrait alors voir (et tenter de faire correspondre) plusieurs références à PORTB trouvées dans plusieurs compilations. Si tous spécifient une adresse comme celle-ci, et que les adresses ne sont PAS les mêmes pour une raison quelconque [erreur?], Alors qu'est-ce que c'est censé faire? Se plaindre? Ou? (Techniquement, avec 'extern' la règle de base serait que seulement UN unité de compilation spécifierait la valeur et les autres ne devraient pas.)
Il est simplement plus facile de l'étiqueter comme `` statique '', en évitant de faire en sorte que l'éditeur de liens s'inquiète des conflits, et il suffit de blâmer toute erreur pour les adresses mal appariées sur quiconque a changé l'adresse en quelque chose qu'elle ne devrait pas être.
Dans les deux cas, la variable est traitée comme ayant une «durée de vie statique». (Et «volatile».)
Une déclaration n'est pas une définition , mais toutes les définitions sont des déclarations
En C, une définition crée un objet. Il le déclare également. Mais une déclaration ne crée généralement pas (voir la puce ci-dessous) un objet.
Voici les définitions et déclarations:
Les éléments suivants ne sont pas des définitions, mais uniquement des déclarations:
Notez que les déclarations ne créent pas d'objet réel. Ils déclarent uniquement les détails à ce sujet, que le compilateur peut ensuite utiliser pour aider à générer le code correct et à fournir des messages d'avertissement et d'erreur, le cas échéant.
Ci-dessus, je dis «habituellement» à bon escient. Dans certains cas, une déclaration peut créer un objet et est donc promue à une définition par l'éditeur de liens (jamais par le compilateur). Ainsi, même dans ce cas rare, le compilateur C pense toujours que la déclaration n'est qu'une déclaration. C'est la phase de l'éditeur de liens qui fait les promotions nécessaires d'une déclaration. Gardez cela à l'esprit.
Dans les exemples ci-dessus, s'il s'avère qu'il n'y a que des déclarations pour un "extern int b;" dans toutes les unités de compilation liées, l'éditeur de liens est chargé de créer une définition. Sachez qu'il s'agit d'un événement de liaison. Le compilateur est complètement ignorant, lors de la compilation. Elle ne peut être déterminée au moment de la liaison que si une déclaration de ce type est la plus promue.
Le compilateur sait que "static int a;" ne peut pas être promu par l'éditeur de liens au moment du lien, il s'agit donc en fait d'une définition au moment de la compilation .
la source
extern
, et ce serait la façon la plus appropriée de le faire en C: déclarer la variableextern
dans un fichier d'en-tête à inclure plusieurs fois dans le programme et la définir dans un fichier non en-tête à compiler et lié exactement une fois. Après tout,PORTB
est censé être exactement une instance de la variable à laquelle différents cu peuvent se référer. Donc, l'utilisationstatic
ici est une sorte de raccourci qu'ils ont pris pour éviter d'avoir besoin d'un autre fichier .c en plus du fichier d'en-tête.static
les s ne sont pas visibles en dehors de l' unité de compilation actuelle ou "unité de traduction". Ce n'est pas la même chose que le même fichier .Notez que vous incluez le fichier d'en-tête dans n'importe quel fichier source où vous pouvez avoir besoin des variables déclarées dans l'en-tête. Cette inclusion fait du fichier d'en-tête une partie de l'unité de traduction actuelle et (une instance de) la variable visible à l'intérieur.
la source
Files included by using the #include preprocessor directive become part of the compilation unit.
lorsque vous incluez votre fichier d'en-tête (.h) dans un fichier .c, pensez-y comme l'insertion du contenu de l'en-tête dans le fichier source, et maintenant, c'est votre unité de compilation. Si vous déclarez cette variable statique ou cette fonction dans un fichier .c, vous ne pouvez les utiliser que dans le même fichier qui, à la fin, sera une autre unité de compilation.Je vais essayer de résumer les commentaires et la réponse de @ JimmyB avec un exemple explicatif:
Supposons que cet ensemble de fichiers:
static_test.c:
static.h:
no_static.h:
static_src.c:
Vous pouvez compiler et exécuter le code en utilisant
gcc -o static_test static_src.c static_test.c -DUSE_STATIC=1; ./static_test
pour utiliser l'en-tête statique ougcc -o static_test static_src.c static_test.c -DUSE_STATIC=0; ./static_test
pour utiliser l'en-tête non statique.Notez que deux unités de compilation sont présentes ici: static_src et static_test. Lorsque vous utilisez la version statique de l'en-tête (
-DUSE_STATIC=1
), une version devar
etsay_hello
sera disponible pour chaque unité de compilation, c'est-à-dire que les deux unités peuvent les utiliser, mais vérifiez que même si lavar_add_one()
fonction incrémente savar
variable, lorsque la fonction principale imprime savar
variable , c'est toujours 64:Maintenant, si vous essayez de compiler et d'exécuter le code, en utilisant la version non statique (
-DUSE_STATIC=0
), il générera une erreur de liaison en raison de la définition de variable dupliquée:J'espère que cela pourrait vous aider à clarifier cette question.
la source
#include pic.h
signifie à peu près "copier le contenu de pic.h dans le fichier actuel". Par conséquent, chaque fichier qui inclutpic.h
obtient sa propre définition locale dePORTB
.Vous vous demandez peut-être pourquoi il n’existe pas de définition globale unique de
PORTB
. La raison est assez simple: vous ne pouvez définir une variable globale que dans un seul fichier C, donc si vous souhaitez utiliserPORTB
plusieurs fichiers dans votre projet, vous auriez besoinpic.h
d'une déclaration dePORTB
etpic.c
avec sa définition . Laisser chaque fichier C définir sa propre copie dePORTB
facilite la construction de code, car vous n'avez pas à inclure dans vos fichiers de projet que vous n'avez pas écrits.Un avantage supplémentaire des variables statiques par rapport aux globales est que vous obtenez moins de conflits de nommage. Le fichier AC qui n'utilise aucune fonctionnalité matérielle MCU (et ne comprend donc pas
pic.h
) peut utiliser le nomPORTB
à ses propres fins. Ce n'est pas une bonne idée de le faire exprès, mais lorsque vous développez, par exemple, une bibliothèque mathématique agnostique MCU, vous seriez surpris de la facilité avec laquelle il est facile de réutiliser accidentellement un nom qui est utilisé par l'un des MCU.la source
Il existe déjà de bonnes réponses, mais je pense que la cause de la confusion doit être abordée simplement et directement:
La déclaration PORTB n'est pas la norme C. C'est une extension du langage de programmation C qui ne fonctionne qu'avec le compilateur PIC. L'extension est nécessaire car les PIC n'ont pas été conçus pour prendre en charge C.
L'utilisation du
static
mot - clé ici est déroutante car vous n'utiliseriez jamais destatic
cette façon dans du code normal. Pour une variable globale, vous utiliseriezextern
dans l'en-tête, nonstatic
. Mais PORTB n'est pas une variable normale . C'est un hack qui indique au compilateur d'utiliser des instructions d'assemblage spéciales pour le registre IO. Déclarer PORTBstatic
aide le compilateur à faire ce qu'il faut.Lorsqu'il est utilisé à la portée du fichier,
static
limite la portée de la variable ou de la fonction à ce fichier. "Fichier" signifie le fichier C et tout ce qui y est copié par le préprocesseur. Lorsque vous utilisez #include, vous copiez du code dans votre fichier C. C'est pourquoi l'utilisationstatic
dans un en-tête n'a aucun sens - au lieu d'une variable globale, chaque fichier qui #inclut l'en-tête obtiendrait une copie distincte de la variable.Contrairement à la croyance populaire,
static
signifie toujours la même chose: allocation statique avec une portée limitée. Voici ce qui arrive aux variables avant et après leur déclarationstatic
:Ce qui le rend confus, c'est que le comportement par défaut des variables dépend de l'endroit où elles sont définies.
la source
La raison pour laquelle le fichier principal peut voir la définition de port "statique" est à cause de la directive #include. Cette directive équivaut à insérer tout le fichier d'en-tête dans votre code source sur la même ligne que la directive elle-même.
Le compilateur de micropuces XC8 traite les fichiers .c et .h exactement de la même manière afin que vous puissiez mettre vos définitions de variables dans l'une ou l'autre.
Normalement, un fichier d'en-tête contient une référence "externe" à des variables définies ailleurs (généralement un fichier .c).
Les variables de port devaient être spécifiées à des adresses mémoire spécifiques qui correspondent au matériel réel. Une définition réelle (non externe) devait donc exister quelque part.
Je ne peux que deviner pourquoi Microchip Corporation a choisi de mettre les définitions réelles dans le fichier .h. Une supposition probable est qu'ils voulaient juste un fichier (.h) au lieu de 2 (.h et .c) (pour faciliter les choses pour l'utilisateur).
Mais si vous placez les définitions de variables réelles dans un fichier d'en-tête et que vous incluez ensuite cet en-tête dans plusieurs fichiers source, l'éditeur de liens se plaindra que les variables sont définies plusieurs fois.
La solution consiste à déclarer les variables comme statiques, puis chaque définition est traitée comme locale pour ce fichier objet et l'éditeur de liens ne se plaindra pas.
la source