Warum ist es schwierig, portable C-Programme zu schreiben?
Lesezeit: 8 Minuten
drigoSkalWalker
Ich möchte wissen, warum es zu schwierig ist, ein Programm in vielen Betriebssystemen, wie Windows und Linux, ohne Glue-Code zum Laufen zu bringen. Sie haben alle dieselbe Architektur (x86), daher sollte es meiner Meinung nach einfach sein. Darüber hinaus ist C standardisiert, warum unterscheiden sich die Implementierungen in diesen Betriebssystemen? Warum ist es schwierig, dem Standard zu folgen und eine universelle Bibliothek für alle Betriebssysteme zu implementieren?
Ich programmiere seit fast zwei Jahren in C und verwende derzeit glib für die Portabilität.
Ich suche keine Lösung für dieses Problem, ich verwende bereits glib für diesen Zweck. Aber ich möchte wissen, warum es notwendig ist, warum es schwierig ist, eine C-Bibliothek ohne Unterschiede zu erstellen.
[ADD]
Wenn ich zum Beispiel ein Programm in C/Glib schreibe, verwende ich gchar/gint und so weiter Typen in der Tat die primitiven C-Typen, in Wikipedia über Glib heißt es:
“Merkmale
Auf einer elementaren Ebene stellt GLib Typdefinitionen bereit, die die primitiven C-Typen char, int, float usw. ersetzen, um die Portabilität zu verbessern.
Warum ist C primitive Typen nicht portierbar und die ganze Sprache?
Danke für alle Antworten.
Das Überschreiben von primitiven C-Typen dient eigentlich dazu, unterschiedliche CPUs zu bedienen. Linux läuft auf vielen verschiedenen CPUs und sogar Windows unterstützt verschiedene Betriebsmodi des x86 (32 und 64 Bit). C hat jetzt int32_t, uint16_t usw., um die Dinge ein wenig zu verbessern.
– Messstab
11. August 2009 um 6:15 Uhr
Danke für alle Antworten, ich werde eigentlich keine Antwort auswählen, um dieses Thema zu schließen, weil ich denke, dass sich jede Antwort gegenseitig ergänzt. aber ich muss die beste antwort wählen, also wähle ich die mehr abgestimmten, vielen dank für deine zeit ;D
– drigoSkalWalker
19. August 2009 um 16:35 Uhr
Ich habe Ihre Titeländerung rückgängig gemacht. Selbst wenn Sie mit dem Thema fertig sind, finden andere vielleicht immer noch eine Verwendung dafür, und “geschlossen” hat hier eine bestimmte Bedeutung.
– dmckee — Ex-Moderator-Kätzchen
20. Februar 2010 um 20:12 Uhr
Es ist einfach, Ihr grundlegendes C-Programm, das ein wenig rechnet und Ergebnisse ausgibt, zum Laufen zu bringen.
Das Problem ist, dass jedes C-Programm, das etwas Wesentliches tut, auf viele Dienste des Betriebssystems angewiesen ist, und diese unterscheiden sich in vielerlei Hinsicht. Datei-I/O sind in der Regel ähnlich, aber wenn es um Grafiken geht, unterscheiden sie sich stark. Und das ist der Kern des Problems.
Sogar Datei-I/O, für die die C-Standardbibliothek Funktionen bereitstellt, ist ein Durcheinander. Der Versuch, Datei-E/A und Berechtigungen auszuführen sicher ist bestenfalls ein Schmerz und in vielen Situationen unmöglich. Beachten Sie, dass alle C-Datei-E/A-Funktionen einen Dateinamen zur Identifizierung benötigen. Dies kann zu (unverfänglich aussehenden) Race-Conditions führen, bei denen Sie nach dem Vorhandensein einer Datei suchen und dann eine gleichnamige E/A öffnen. Sehen securecoding.cert.org/confluence/display/seccode/… für mehr 🙂
– Andreas Keeton
11. August 2009 um 1:27 Uhr
Jawohl; Die zugrunde liegende Prozessorarchitektur wird von der Sprache C gut abstrahiert (abgesehen von einigen von der Akkumulatorbreite abhängigen Typen). Es ist das Betriebssystem, das Sie wirklich brauchen und das von den Standardbibliotheken schlecht unterstützt wird. Tatsächlich: Sie können sogar Glib oder Qt als Glue-Code betrachten, nur müssen Sie es nicht selbst schreiben.
– Adrian
11. August 2009 um 7:44 Uhr
Warum ist C primitive Typen nicht portierbar und die ganze Sprache?
Als C geschaffen wurde, wurde es als am wichtigsten erachtet, dass int Verwenden Sie die natürliche Wortgröße (oder Halbwortgröße) des Zielcomputers (die damals auf einem PDP-10 18 oder 36 Bit hätte sein können!), und weniger wichtig als die Anzahl der Bits in einem int leicht vorhersehbar sein, geschweige denn auf allen Hardware gleich. Effizienz war von größter Bedeutung, und es war besonders wichtig, dass ein Programm nicht zu viele Anweisungen enthält, damit es in die kleinen Erinnerungen des Tages gepackt werden kann. Niemand möchte beispielsweise 32-Bit-Arithmetik auf einem 16-Bit-Rechner simulieren – das könnte die Anzahl der für die Arithmetik erforderlichen Anweisungen verdreifachen, wodurch es unmöglich wird, größere Programme in den Speicher zu packen.
Warum ist es schwierig, eine universelle Bibliothek zu implementieren?
Es ist überhaupt nicht schwer. Das Problem ist nicht, dass es keine universelle Bibliothek gibt; Das Problem ist, dass man sich nicht darauf einigen kann, was eine Universalbibliothek ausmacht. (Siehe: GTK, KDE, QT, AT&T U/WIN+AST usw. bis zum Erbrechen.) Aber die Implementierung von a gut Bibliothek ist Ja wirklich schwer. Warum es schwierig ist: Bibliotheksdesign ist nur ein schwieriges Problem, und vernünftige Menschen können und werden sich darüber uneins sein, was ein gutes Design ausmacht. So vermehren sich mehrere Designs, und die Portabilität vergeht.
Die Situation wird durch die sehr dünne Standardbibliothek verschärft, die mit C geliefert wird. Aber vergessen wir nicht, dass die Bibliothek in den Tagen entworfen wurde, als ein Programmierer das Glück hatte, 64 KB für Code und weitere 64 KB für Daten zu erhalten. Als C++ auf den Markt kam, konnten sie es sich leisten, auf einem riesigen aufgeblähten Schwein wie der Standard Template Library zu standardisieren. Aber als die C-Bibliothek entworfen wurde, Dinge hatte klein sein. Jetzt, da die Standard-C-Bibliothek für die anspruchsvolleren Anwendungen von heute nicht mehr ausreicht, ist es für alle C-Programmierer zu spät, sich auf einen einzigen Standard zu einigen.
Stoßen Sie einen Professor an, holen Sie sich einen Vortrag…
Ich denke, das ist vielleicht ein bisschen revisionistisch. Als C erstellt wurde, sollte es auf einer bestimmten Plattform (PDP-11) verwendet werden und sich einfach von selbst auf andere ausbreiten. Jeder einzelne Implementierer traf seine eigenen Entscheidungen darüber, welche Größen die verschiedenen herzustellen waren ints. Zu dieser Zeit gab es keinen Standard, und als ein Standard entstand, war alles, was er wirklich tat, die bestehende Vorgehensweise in der Compiler-Community zu kodifizieren. Vieles, was wir heute über ein C wissen, wurde nicht wirklich entworfen, sondern ist irgendwie organisch gewachsen.
– TED
24. Februar 2011 um 14:25 Uhr
dmckee — Ex-Moderator-Kätzchen
Es ist nicht schwer, wenn Sie sich an den Kernstandard halten (die Beispiele in K&R werden im Allgemeinen mit minimaler Korrektur ausgeführt). Aber der Kernstandard unterstützt nicht viele Dinge, die die Leute gerne tun würden. Kleinigkeiten wie:
im Dateisystem herumwühlen
anspruchsvollere IO als reiner gestreamter Text
Bitten Sie das Betriebssystem um Zugriff auf Uhren, Drucker und Netzwerke
Gespräche mit anderen Prozessen
Zur Portabilität der primitiven Typen: Der Standard legt Mindestgrößenbeschränkungen für diese Typen fest, legt jedoch keine fest tatsächlich Größen. Die meisten Plattformen unterstützen Größen, die größer als die Mindestwerte sind, aber nicht alle Plattformen haben dies gleich größere Größen. Glib und andere plattformübergreifende Projekte definieren also ihren eigenen Typ, so dass sie nur brauchen eines Satz von #ifdef Schalter, um die richtige Größe zu erhalten.
Alles, womit Ihr C-Programm interagiert, wird eine Abhängigkeit sein. Wenn diese Abhängigkeiten die sind genau bei jeder C-Implementierung auf allen Systemen gleich, dann haben Sie Glück, Ihr Programm ist portabel.
Die grundlegenden Datei-I/O-Operationen sind auf diese Weise.
Aber wenn es auch nur einen Unterschied in der Art und Weise gibt, wie Ihr Code auf einem System implementiert wird, ist er nicht mehr auf dieses System portierbar.
Wenn Ihr Code also nichts berührt, ist er tragbar.
Ich denke, die Hauptidee hier ist zu maximieren Portabilität, sodass Sie die Änderungen für die Portierung auf ein neues System minimieren können.
Der Code ist portabel, aber das ist nicht das Problem.
Das Problem ist, dass die meisten größeren Anwendungen Bibliotheken verwenden, die Teil des Betriebssystems sind. Wenn Sie also beispielsweise eine Netzwerkanwendung schreiben, verwendet diese möglicherweise die integrierte UPNP-Bibliothek für Windows und die Bonjour-Bibliothek für OS X sowie einige andere Zero-Conf-Dienste für andere Unix-Varianten.
Ein weiteres Beispiel wäre eine einfache Notepad-Anwendung. Es verwendet möglicherweise die nativen Dialoge zum Speichern von Dateien für das Betriebssystem, auf dem es ausgeführt wird. Für jedes Betriebssystem müsste eine andere Version kompiliert werden, um sich an die Dialogbibliothek zum Speichern von Dateien für dieses Betriebssystem zu binden.
Ich hoffe, diese Beispiele sind klar genug 🙂
Johannes Bode
Warum ist C primitive Typen nicht portierbar und die ganze Sprache?
Die primitiven Typen sind dahingehend portierbar, dass sie garantiert einen Mindestbereich von Werten unterstützen (z. B. muss der int-Typ wenigstens unterstützt Werte im Bereich -32767 bis 32767). Es ist nicht garantiert, dass sie die gleichen Größen- oder Ausrichtungsbeschränkungen haben, da unterschiedliche Architekturen unterschiedliche Anforderungen haben.
Der C-Standard versucht, ein vernünftiges Gleichgewicht zwischen Portabilität, Leistung und einfacher Implementierung zu finden, was bedeutet, dass es in allen drei Bereichen Kompromisse gibt. Es ist möglich, C-Programme zu schreiben, die nützlich und trivial portierbar sind, aber das bedeutet, dass Sie sich auf eine befehlszeilengesteuerte Schnittstelle, einfache Datei-I/O beschränken und keine Annahmen über Wortgröße oder Ausrichtung treffen müssen.
Denken Sie daran, dass C ein Produkt der frühen 70er Jahre ist, als die vorherrschende Computerumgebung aus großen Timesharing-Systemen bestand, auf die über dumme zeichenbasierte Terminals zugegriffen wurde. Prozessorzeit und Arbeitsspeicher waren teuer, und es kam selten vor, dass ein Rechenzentrum mehr als ein oder zwei verschiedene Plattformen gleichzeitig unterstützte, sodass die Leistung wichtiger war als die Byte-für-Byte-Portabilität. Vierzig Jahre später ist das nicht mehr der Fall, aber es gibt auch vierzig Jahre Legacy-Code zu unterstützen, sodass sich die Sprachdefinition nicht zu radikal ändern kann.
Gerhard
Für java/ruby/… hat jemand die VM portiert und Ihr Code läuft einfach. Dies macht es einfach, solange Sie mit den von der VM bereitgestellten Diensten einverstanden sind. In C müssen Sie die ganze Arbeit selbst erledigen, da Sie direkt mit der Hardware und dem Betriebssystem interagieren können. Dies fügt Abhängigkeiten hinzu, gibt Ihnen aber große Leistung und Flexibilität. Das macht C großartig, aber es kann ein Problem sein, wenn Sie das Betriebssystem oder die Hardware wechseln.
11457400cookie-checkWarum ist es schwierig, portable C-Programme zu schreiben?yes
Das Überschreiben von primitiven C-Typen dient eigentlich dazu, unterschiedliche CPUs zu bedienen. Linux läuft auf vielen verschiedenen CPUs und sogar Windows unterstützt verschiedene Betriebsmodi des x86 (32 und 64 Bit). C hat jetzt int32_t, uint16_t usw., um die Dinge ein wenig zu verbessern.
– Messstab
11. August 2009 um 6:15 Uhr
Danke für alle Antworten, ich werde eigentlich keine Antwort auswählen, um dieses Thema zu schließen, weil ich denke, dass sich jede Antwort gegenseitig ergänzt. aber ich muss die beste antwort wählen, also wähle ich die mehr abgestimmten, vielen dank für deine zeit ;D
– drigoSkalWalker
19. August 2009 um 16:35 Uhr
Ich habe Ihre Titeländerung rückgängig gemacht. Selbst wenn Sie mit dem Thema fertig sind, finden andere vielleicht immer noch eine Verwendung dafür, und “geschlossen” hat hier eine bestimmte Bedeutung.
– dmckee — Ex-Moderator-Kätzchen
20. Februar 2010 um 20:12 Uhr