Konzept des Schlüsselworts static aus Sicht von Embedded C

static volatile unsigned char   PORTB   @ 0x06;

Dies ist eine Codezeile in einer PIC-Mikrocontroller-Header-Datei. Der @Operator wird verwendet, um den PORTB-Wert innerhalb der Adresse zu speichern 0x06, die ein Register innerhalb des PIC-Controllers ist, der PORTB darstellt. Bis hierhin habe ich eine klare Vorstellung.

Diese Zeile wird als globale Variable in einer Header-Datei ( .h) deklariert. Nach dem, was ich über die C-Sprache erfahren habe, ist eine "statische globale Variable" für keine andere Datei sichtbar - oder einfach statische globale Variablen / Funktionen können nicht außerhalb der aktuellen Datei verwendet werden.

Wie kann dieses Schlüsselwort dann PORTBfür meine Hauptquelldatei und viele andere Header-Dateien sichtbar sein, die ich manuell erstellt habe?

In meiner Hauptquelldatei habe ich nur die Header-Datei hinzugefügt. #include pic.hHat das etwas mit meiner Frage zu tun?

Kein Problem mit der Frage, aber ich fürchte, falscher SE-Abschnitt
static wird normalerweise innerhalb einer Funktion verwendet, um anzugeben, dass die Variable einmal erstellt wird und ihren Wert von einer Ausführung einer Funktion zur nächsten behält. Eine globale Variable wird außerhalb einer Funktion erstellt, sodass sie überall sichtbar ist. static global macht keinen Sinn.
@Finbarr Falsch. staticGlobals sind innerhalb der gesamten einzelnen Kompilationseinheit sichtbar und werden nicht darüber hinaus exportiert. Sie sind ähnlich wie privateMitglieder einer Klasse in OOP. Dh jede Variable, die zwischen verschiedenen Funktionen innerhalb einer Kompilationseinheit geteilt werden muss, aber nicht außerhalb dieser cu sichtbar sein soll, sollte es wirklich sein static. Dadurch wird auch das „Clobbering“ des globalen Namensraums des Programms reduziert.
Zu "Der @-Operator wird verwendet, um den PORTB-Wert in der Adresse 0x06 zu speichern" . Wirklich? Ist das nicht eher "Der @-Operator wird verwendet, um die Variable "PORTB" an der absoluten Speicheradresse 0x06 zu speichern" ?

Antworten (6)

Das Schlüsselwort 'static' hat in C zwei grundlegend unterschiedliche Bedeutungen.

Begrenzung des Umfangs

In diesem Zusammenhang wird „static“ mit „extern“ gepaart, um den Geltungsbereich eines Variablen- oder Funktionsnamens zu steuern. Static bewirkt, dass der Variablen- oder Funktionsname nur innerhalb einer einzelnen Kompilierungseinheit und nur für Code verfügbar ist, der nach der Deklaration/Definition im Text der Kompilierungseinheit vorhanden ist.

Diese Einschränkung selbst bedeutet nur dann wirklich etwas, wenn Sie mehr als eine Kompilationseinheit in Ihrem Projekt haben. Wenn Sie nur eine Kompilierungseinheit haben, dann macht es immer noch Dinge, aber diese Effekte sind meistens sinnlos (es sei denn, Sie graben gerne in Objektdateien, um zu lesen, was der Compiler generiert hat.)

Wie bereits erwähnt, paart sich dieses Schlüsselwort in diesem Zusammenhang mit dem Schlüsselwort 'extern', das das Gegenteil bewirkt – indem es den Variablen- oder Funktionsnamen mit demselben Namen verknüpfbar macht, der in anderen Kompilierungseinheiten gefunden wird. Sie können also „statisch“ so betrachten, dass die Variable oder der Name innerhalb der aktuellen Kompilierungseinheit gefunden werden muss, während „extern“ eine Verknüpfung zwischen Kompilierungseinheiten ermöglicht.

Statische Lebensdauer

Statische Lebensdauer bedeutet, dass die Variable während der gesamten Dauer des Programms existiert (wie lange das auch sein mag). Wenn Sie „statisch“ verwenden, um eine Variable innerhalb einer Funktion zu deklarieren/definieren, bedeutet dies, dass die Variable irgendwann vor ihrer ersten Verwendung erstellt wird ( was bedeutet, jedes Mal, wenn ich es erlebt habe, dass die Variable erstellt wird, bevor main() startet) und danach nicht zerstört wird. Auch nicht, wenn die Ausführung der Funktion abgeschlossen ist und sie zu ihrem Aufrufer zurückkehrt. Und genau wie statische Lebensdauervariablen, die außerhalb von Funktionen deklariert werden, werden sie im selben Moment initialisiert – bevor main() startet – auf eine semantische Null (wenn keine Initialisierung bereitgestellt wird) oder auf einen angegebenen expliziten Wert, falls angegeben.

Dies unterscheidet sich von Funktionsvariablen des Typs 'auto', die bei jedem Aufruf der Funktion neu (oder als ob neu) erstellt und dann beim Beenden der Funktion zerstört (oder als ob sie zerstört würden) werden.

Im Gegensatz zu den Auswirkungen der Anwendung von „statisch“ auf eine Variablendefinition außerhalb einer Funktion, die sich direkt auf ihren Gültigkeitsbereich auswirkt, hat die Deklaration einer Funktionsvariablen (offensichtlich innerhalb eines Funktionskörpers) als „statisch“ keine Auswirkung auf ihren Gültigkeitsbereich . Der Geltungsbereich wird dadurch bestimmt, dass er innerhalb eines Funktionskörpers definiert wurde. Statische Lebensdauervariablen, die in Funktionen definiert sind, haben denselben Gültigkeitsbereich wie andere „Auto“-Variablen, die in Funktionskörpern definiert sind – den Funktionsbereich.

Zusammenfassung

Das Schlüsselwort „statisch“ hat also unterschiedliche Kontexte mit „sehr unterschiedlichen Bedeutungen“. Der Grund, warum es auf zwei Arten verwendet wurde, war, die Verwendung eines anderen Schlüsselworts zu vermeiden. (Es gab eine lange Diskussion darüber.) Es wurde angenommen, dass Programmierer die Verwendung tolerieren könnten und der Wert, ein weiteres Schlüsselwort in der Sprache zu vermeiden, wichtiger war (als andere Argumente).

(Alle Variablen, die außerhalb von Funktionen deklariert werden, haben eine statische Lebensdauer und benötigen das Schlüsselwort 'static' nicht, um dies wahr zu machen. Auf diese Weise wurde das Schlüsselwort freigegeben, das dort verwendet werden soll, um etwas völlig anderes zu bedeuten: 'nur in einer einzigen Kompilierung sichtbar Einheit." Es ist eine Art Hack.)

Spezifischer Hinweis

statisches flüchtiges Zeichen ohne Vorzeichen PORTB @ 0x06;

Das Wort „statisch“ sollte hier so interpretiert werden, dass der Linker nicht versucht, mehrere Vorkommen von PORTB abzugleichen, die möglicherweise in mehr als einer Kompilierungseinheit gefunden werden (vorausgesetzt, Ihr Code hat mehr als eine).

Es verwendet eine spezielle (nicht portierbare) Syntax, um den "Standort" (oder den numerischen Wert des Labels, der normalerweise eine Adresse ist) von PORTB anzugeben. Der Linker erhält also die Adresse und muss keine dafür finden. Wenn Sie zwei Kompilierungseinheiten hätten, die diese Zeile verwenden, würden sie sowieso auf dieselbe Stelle zeigen. Es ist also nicht nötig, es hier als 'extern' zu kennzeichnen.

Hätten sie 'extern' verwendet, könnte dies ein Problem darstellen. Der Linker wäre dann in der Lage, mehrere Verweise auf PORTB zu sehen (und würde versuchen, sie abzugleichen), die in mehreren Kompilierungen gefunden wurden. Wenn alle eine Adresse wie diese angeben und die Adressen aus irgendeinem Grund NICHT gleich sind [Fehler?], was soll es dann tun? Sich beschweren? Oder? (Technisch gesehen wäre bei 'extern' die Faustregel, dass nur EINE Kompilationseinheit den Wert angeben würde und die anderen nicht.)

Es ist einfach einfacher, es als "statisch" zu kennzeichnen, um zu vermeiden, dass sich der Linker über Konflikte Sorgen macht, und einfach die Schuld für Fehler bei nicht übereinstimmenden Adressen demjenigen zu geben, der die Adresse in etwas geändert hat, das sie nicht sein sollte.

In jedem Fall wird die Variable so behandelt, als hätte sie eine „statische Lebensdauer“. (Und „flüchtig“.)

Eine Deklaration ist keine Definition , aber alle Definitionen sind Deklarationen

In C erstellt eine Definition ein Objekt. Das wird auch deklariert. Aber eine Deklaration erzeugt normalerweise kein Objekt (siehe Aufzählungszeichen unten).

Es folgen Definitionen und Deklarationen:

static int a;
static int a = 7;
extern int b = 5;
extern int f() { return 10; }

Die folgenden sind keine Definitionen, sondern nur Deklarationen:

extern int b;
extern int f();

Beachten Sie, dass die Deklarationen kein tatsächliches Objekt erstellen. Sie deklarieren nur die Details darüber, die der Compiler dann verwenden kann, um korrekten Code zu generieren und gegebenenfalls Warn- und Fehlermeldungen bereitzustellen.

  • Oben sage ich absichtlich "normalerweise". In einigen Fällen kann eine Deklaration ein Objekt erstellen und wird daher vom Linker (niemals vom Compiler) zu einer Definition befördert. Selbst in diesem seltenen Fall denkt der C-Compiler also immer noch, dass die Deklaration nur eine Deklaration ist. Es ist die Linker-Phase, die alle notwendigen Beförderungen einer Deklaration vornimmt. Behalten Sie dies sorgfältig im Hinterkopf.

    Sollte sich herausstellen, dass es in den obigen Beispielen nur Deklarationen für ein "extern int b;" gibt. in allen verlinkten Übersetzungseinheiten, dann ist der Linker mit der Erstellung einer Definition beauftragt. Beachten Sie, dass dies ein Ereignis zur Verbindungszeit ist. Der Compiler ist während der Kompilierung völlig unbewusst. Ob eine solche Erklärung am meisten beworben wird, kann erst zum Zeitpunkt der Verlinkung festgestellt werden.

    Der Compiler ist sich bewusst, dass "static int a;" kann vom Linker zur Linkzeit nicht hochgestuft werden, daher ist dies tatsächlich eine Definition zur Kompilierzeit .

Tolle Antwort, +1! Nur ein kleiner Punkt: Sie hätten verwenden können extern, und es wäre der richtigere C-Weg, dies zu tun: Deklarieren Sie die Variable externin einer Header-Datei, um sie mehrmals in das Programm aufzunehmen, und definieren Sie sie in einer Nicht-Header-Datei, die kompiliert werden soll und genau einmal eingebunden. Schließlich PORTB soll genau eine Instanz der Variable sein, auf die sich verschiedene cu's beziehen können. Die Verwendung von statichere ist also eine Art Abkürzung, um zu vermeiden, dass zusätzlich zur Header-Datei eine weitere .c-Datei benötigt wird.
Ich möchte auch darauf hinweisen, dass eine innerhalb einer Funktion deklarierte statische Variable nicht über Funktionsaufrufe hinweg geändert wird, was für Funktionen nützlich sein kann, die eine Art von Zustandsinformationen speichern müssen (ich habe sie in der Vergangenheit speziell für diesen Zweck verwendet).
@ Peter Ich glaube, das habe ich gesagt. Aber vielleicht nicht so, wie Sie es sich gewünscht hätten?
@JimmyB Nein, sie hätten stattdessen nicht 'extern' für Funktionsvariablendeklarationen verwenden können, die sich wie 'statisch' verhalten. 'extern' ist bereits eine Option für Variablendeklarationen (nicht Definitionen) innerhalb von Funktionskörpern und dient einem anderen Zweck – dem Bereitstellen von Link-Time-Zugriff auf Variablen, die außerhalb einer Funktion definiert sind. Aber es ist möglich, dass ich deinen Punkt auch falsch verstehe.
@JimmyB Eine externe Verknüpfung wäre definitiv möglich, obwohl ich nicht weiß, ob es "richtiger" ist. Eine Überlegung ist, dass der Compiler möglicherweise optimierteren Code ausgeben kann, wenn die Informationen in der Übersetzungseinheit gefunden werden. Für eingebettete Szenarien kann das Einsparen von Zyklen bei jeder IO-Anweisung eine große Sache sein.
Um hinzuzufügen: Denken Sie daran, dass die Direktive #include der Präprozessor ist, und dies könnte tatsächlich ein Vielfaches von PORTB in der gesamten Kompilierung erstellen, wenn es nicht von den include guards abgefangen wird . Dies gilt als mehrfaches Auftreten.
@CortAmmon Ich denke, es ist der "richtigere" Weg, weil der staticWeg in diesem Fall nur funktioniert, weil er auf der (nicht portierbaren) Erweiterung ( @ 0x06;) beruht, die es ermöglicht, mehrere (statische) Instanzen einer Variablen einem einzelnen Speicher zuzuweisen Standort. Die Verwendung externwäre kein Problem.
@jonk Tatsächlich glaube ich, dass du mich missverstanden hast. Beachten Sie, dass wir nicht über Variablendeklarationen/-definitionen innerhalb von Funktionen sprechen, sondern über globale . Beachten Sie die unterschiedliche Bedeutung von staticfür globale Variablen im Gegensatz zu funktionslokalen Variablen.
"Technisch gesehen wäre bei 'extern' die Faustregel, dass nur EINE Kompilationseinheit den Wert angeben würde und die anderen nicht." - Dies sollte deutlich machen, dass das @ 0x06Teil der Definition der Variablen ist, nicht ihrer Deklaration. Es kann nur eine Definition einer (nicht statischen) Variablen in einem Programm geben, alles andere führt zur Verbindungszeit (oder davor) zu einem Fehler. Sie können die (nicht statische) Variable mehrfach über deklarierenextern , aber es kann nur eine Definition geben.
@JimmyB Ich lasse Ihre Kommentare für andere interpretieren. Ich habe schon einmal einen C-Compiler geschrieben. Aus dieser Erfahrung weiß ich etwas mehr als viele andere. Aber ich bin mir nicht sicher, was Sie sagen. Es kann jedoch die Zeit sein, die ich damit verbringen muss, es zu analysieren. Wenn ich einen Moment und Lust habe, kann ich versuchen, es zu verstehen. Unabhängig davon glaube ich, dass das, was ich geschrieben habe, meinen Erfahrungen entspricht, und ich wünschte, ich wäre Ihren Motivationen und Kommentaren gefolgt.
@JimmyB Und ja, ich bin mir des Unterschieds zwischen Deklarationen und Definitionen GUT bewusst - insbesondere in Bezug auf globale Variablen und extern. Auch hier verstehe ich Ihr Problem mit meinem Schreiben nicht. Aber ich würde gerne verbessern, was ich geschrieben habe, was bedeutet, dass ich Ihre Kritik gerne verstehen würde (falls sie es ist).
Ich wollte nicht wirklich kritisieren :) Und nachdem ich Ihre Antwort noch einmal gelesen habe, ist mir aufgefallen, dass Sie wirklich derjenige waren, der auf die verschiedenen Bedeutungen von hingewiesen hat static. Mein Punkt ist nur, dass das, wofür die statische Deklaration (und Definition) in der Header-Datei in diesem Fall verwendet wird, dh das Verweisen auf dieselbe Variable/den gleichen Speicherort aus mehreren .c-Dateien, nur aufgrund der (nicht portierbaren , nicht- Standard) @ 0x06;Adressbezeichner, während der "richtige" C-Weg darin bestünde, externbei der Deklaration(en) und einer separaten, einzelnen Definition der Variablen zu verwenden. Ohne den @ 0x06Adressbezeichner, every
@JimmyB Wenn Sie mich genau lesen, sollten Sie sehen, wo ich genau das sage, was Sie meiner Meinung nach sagen. Schau dir das Ende näher an. Vielleicht der dritte Absatz von unten?
statische Instanz der Variablen würde ein eigener, separater Speicherplatz zugewiesen, was durch vermieden wird extern.
Lesen Sie es jetzt noch einmal, der Punkt, dem ich nicht zustimme, ist der Absatz, der mit "Hätten sie 'extern' verwendet hätten, könnte dies ein Problem darstellen." - externwäre kein Problem gewesen, da es dafür sorgt, dass es immer nur eine Definition der Variablen gibt.
@JimmyB Ich sage nicht, dass 'extern' ein Problem ist. Tatsächlich stimmen Sie und ich genau darin überein, dass es kein Problem ist. Natürlich nur, solange Sie nur einmal definieren. Ich spreche dort darüber, warum der Autor möglicherweise einen anderen Weg gewählt hat und stattdessen "statisch" verwendet. Das ist alles.

statics sind außerhalb der aktuellen Übersetzungseinheit oder "Übersetzungseinheit" nicht sichtbar. Dies ist nicht dasselbe wie dieselbe Datei .

Beachten Sie, dass Sie die Header-Datei in jede Quelldatei einfügen , in der Sie möglicherweise die im Header deklarierten Variablen benötigen. Diese Einbeziehung macht die Header-Datei zu einem Teil der aktuellen Übersetzungseinheit und (einer Instanz von) der darin sichtbaren Variablen.

Danke für deine Antwort. "Compilation Unit" , Tut mir leid, ich verstehe nicht, können Sie diesen Begriff erklären. Lassen Sie mich Ihnen noch eine Frage stellen, selbst wenn wir Variablen und Funktionen verwenden möchten, die in einer anderen Datei geschrieben sind, müssen wir diese Datei zuerst in unsere Hauptquelldatei einschließen. Warum dann das Schlüsselwort "static volatile" in dieser Header-Datei.
Vielen Dank für alle Erklärungen, aber ich verstehe immer noch nicht wirklich, warum das statische Schlüsselwort für globale Variablen gilt. Ich habe ein Programm geschrieben, das eine Header-Datei "includer.h" in C++ enthält. In dieser Datei habe ich static int var= 56 und eine statische void say_hello() Funktion deklariert. Nachdem ich diese Datei in meine Hauptquelldatei aufgenommen habe, kann ich sowohl die Funktion als auch die Variable verwenden. Aber im Forum heißt es so "Statische globale Variablen sind außerhalb der C-Datei, in der sie definiert sind, nicht sichtbar." "Statische Funktionen sind außerhalb der C-Datei, in der sie definiert sind, nicht sichtbar."
@ElectroVoyager; Wenn Sie denselben Header mit einer statischen Deklaration in mehrere C-Quelldateien einfügen, hat jede dieser Dateien eine statische Variable mit demselben Namen, aber es handelt sich nicht um dieselbe Variable .
Aus @JimmyB-Link: Files included by using the #include preprocessor directive become part of the compilation unit.Wenn Sie Ihre Header-Datei (.h) in eine .c-Datei einfügen, stellen Sie sich vor, Sie fügen den Inhalt des Headers in die Quelldatei ein, und jetzt ist dies Ihre Kompilierungseinheit. Wenn Sie diese statische Variable oder Funktion in einer .c-Datei deklarieren, können Sie sie nur in derselben Datei verwenden, die am Ende eine weitere Kompilierungseinheit sein wird.
Der richtige Begriff ist Übersetzungseinheit , nicht "Kompilierungseinheit". Siehe 5.1.1.1/1: „Eine Quelldatei zusammen mit allen Headern und Quelldateien, die über die Vorverarbeitungsanweisung eingeschlossen wurden, #includewird als vorverarbeitende Übersetzungseinheit bezeichnet . Nach der Vorverarbeitung wird eine vorverarbeitende Übersetzungseinheit als Übersetzungseinheit bezeichnet .“

Ich werde versuchen, die Kommentare und die Antwort von @ JimmyB mit einem erklärenden Beispiel zusammenzufassen:

Angenommen, dieser Satz von Dateien:

static_test.c:

#include <stdio.h>
#if USE_STATIC == 1
    #include "static.h"
#else
    #include "no_static.h"
#endif

void var_add_one();

void main(){

    say_hello();
    printf("var is %d\n", var);
    var_add_one();
    printf("now var is %d\n", var);
}

static.h:

static int var=64;
static void say_hello(){
    printf("Hello!!!\n");
};

no_static.h:

int var=64;
void say_hello(){
    printf("Hello!!!\n");
};

static_src.c:

#include <stdio.h>

#if USE_STATIC == 1
    #include "static.h"
#else
    #include "no_static.h"
#endif

void var_add_one(){
    var = var + 1;
    printf("Added 1 to var: %d\n", var);
    say_hello();
}

Sie können den Code kompilieren und ausführen, gcc -o static_test static_src.c static_test.c -DUSE_STATIC=1; ./static_testindem Sie den statischen Header oder gcc -o static_test static_src.c static_test.c -DUSE_STATIC=0; ./static_testden nicht statischen Header verwenden.

Beachten Sie, dass hier zwei Kompilierungseinheiten vorhanden sind: static_src und static_test. Wenn Sie die statische Version des Headers ( -DUSE_STATIC=1) verwenden, wird eine Version von varund say_hellofür jede Kompilationseinheit verfügbar sein, das heißt, beide Einheiten können sie verwenden, aber überprüfen Sie, ob die Funktion ihrevar_add_one() Variable erhöht , wenn die Hauptfunktion ihre Variable ausgibt , es ist immer noch 64: var var

$ gcc -o static_test static_src.c static_test.c -DUSE_STATIC=1; ./static_test                                                                                                                       14:33:12
Hello!!!
var is 64
Added 1 to var: 65
Hello!!!
now var is 64

Wenn Sie nun versuchen, den Code mit der nicht statischen Version ( ) zu kompilieren und auszuführen -DUSE_STATIC=0, wird aufgrund der doppelten Variablendefinition ein Verknüpfungsfehler ausgegeben:

$ gcc -o static_test static_src.c static_test.c -DUSE_STATIC=0; ./static_test                                                                                                                       14:35:30
/tmp/ccLBy1s7.o:(.data+0x0): multiple definition of `var'
/tmp/ccV6izKJ.o:(.data+0x0): first defined here
/tmp/ccLBy1s7.o: In function `say_hello':
static_test.c:(.text+0x0): multiple definition of `say_hello'
/tmp/ccV6izKJ.o:static_src.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
zsh: no such file or directory: ./static_test

Ich hoffe, dies konnte Ihnen helfen, diese Angelegenheit zu klären.

#include pic.hbedeutet ungefähr "den Inhalt von pic.h in die aktuelle Datei kopieren". Als Ergebnis pic.herhält jede Datei, die enthält, ihre eigene lokale Definition von PORTB.

Vielleicht fragen Sie sich, warum es keine einheitliche globale Definition von gibt PORTB. Der Grund ist ganz einfach: Sie können eine globale Variable nur in einer C-Datei definieren, wenn Sie sie also PORTBin mehreren Dateien in Ihrem Projekt verwenden möchten, müssten Sie pic.hmit einer Deklaration von PORTBund pic.cmit ihrer Definition . Indem Sie jede C-Datei ihre eigene Kopie von definieren lassen, wird PORTBdie Codeerstellung einfacher, da Sie keine Dateien in Ihr Projekt aufnehmen müssen, die Sie nicht geschrieben haben.

Ein zusätzlicher Vorteil von statischen Variablen gegenüber globalen ist, dass Sie weniger Namenskonflikte bekommen. Eine AC-Datei, die keine MCU-Hardwarefunktionen verwendet (und daher keine enthält pic.h), kann den Namen PORTBfür ihren eigenen Zweck verwenden. Nicht, dass es eine gute Idee wäre, dies absichtlich zu tun, aber wenn Sie zB eine MCU-agnostische Mathematikbibliothek entwickeln, werden Sie überrascht sein, wie einfach es ist, versehentlich einen Namen wiederzuverwenden, der von einer der MCUs da draußen verwendet wird.

"Sie wären überrascht, wie einfach es ist, versehentlich einen Namen wiederzuverwenden, der von einer der MCUs da draußen verwendet wird" - Ich wage zu hoffen, dass alle Mathematikbibliotheken nur Kleinbuchstaben verwenden und dass alle MCU-Umgebungen nur Großbuchstaben für die Registrierung verwenden Namen.
@vsz LAPACK zum Beispiel ist voller historischer Namen in Großbuchstaben.

Es gibt bereits einige gute Antworten, aber ich denke, die Ursache der Verwirrung muss einfach und direkt angegangen werden:

Die PORTB-Deklaration ist kein Standard-C. Es ist eine Erweiterung der Programmiersprache C, die nur mit dem PIC-Compiler funktioniert. Die Erweiterung wird benötigt, da PICs nicht für die Unterstützung von C entwickelt wurden.

Die Verwendung des staticSchlüsselworts hier ist verwirrend, da Sie es staticin normalem Code niemals so verwenden würden. Für eine globale Variable würden Sie externim Header verwenden, nicht static. Aber PORTB ist keine normale Variable . Es ist ein Hack, der den Compiler anweist, spezielle Assembler-Anweisungen für Register IO zu verwenden. Das Deklarieren von PORTB statichilft dem Compiler, das Richtige zu tun.

Beschränkt bei Verwendung im Dateibereich staticden Bereich der Variablen oder Funktion auf diese Datei. "Datei" bedeutet die C-Datei und alles, was vom Präprozessor hineinkopiert wurde. Wenn Sie #include verwenden, kopieren Sie Code in Ihre C-Datei. Aus diesem Grund macht die Verwendung staticin einem Header keinen Sinn – statt einer globalen Variable würde jede Datei, die den Header enthält, eine separate Kopie der Variable erhalten.

Bedeutet entgegen der landläufigen Meinung staticimmer dasselbe: statische Allokation mit begrenztem Umfang. Folgendes passiert mit Variablen vor und nach der Deklaration static:

+------------------------+-------------------+--------------------+
| Variable type/location |    Allocation     |       Scope        |
+------------------------+-------------------+--------------------+
| Normal in file         | static            | global             |
| Normal in function     | automatic (stack) | limited (function) |
| Static in file         | static            | limited (file)     |
| Static in function     | static            | limited (function) |
+------------------------+-------------------+--------------------+

Was es verwirrend macht, ist, dass das Standardverhalten von Variablen davon abhängt, wo sie definiert sind.

Der Grund, warum die Hauptdatei die "statische" Portdefinition sehen kann, liegt an der #include-Direktive. Diese Direktive entspricht dem Einfügen der gesamten Header-Datei in Ihren Quellcode in derselben Zeile wie die Direktive selbst.

Der Mikrochip XC8-Compiler behandelt .c- und .h-Dateien genau gleich, sodass Sie Ihre Variablendefinitionen in beide einfügen können.

Normalerweise enthält eine Header-Datei "externe" Verweise auf Variablen, die woanders definiert sind (normalerweise eine .c-Datei).

Die Portvariablen mussten an bestimmten Speicheradressen angegeben werden, die der tatsächlichen Hardware entsprechen. Also musste irgendwo eine tatsächliche (nicht externe) Definition existieren.

Ich kann nur vermuten, warum die Microchip Corporation die eigentlichen Definitionen in die .h-Datei geschrieben hat. Eine wahrscheinliche Vermutung ist, dass sie nur eine Datei (.h) anstelle von 2 (.h und .c) wollten (um es dem Benutzer einfacher zu machen).

Wenn Sie jedoch die eigentlichen Variablendefinitionen in eine Header-Datei einfügen und diesen Header dann in mehrere Quelldateien einfügen, beschwert sich der Linker darüber, dass die Variablen mehrfach definiert sind.

Die Lösung besteht darin, die Variablen als statisch zu deklarieren, dann wird jede Definition als lokal für diese Objektdatei behandelt und der Linker wird sich nicht beschweren.