Was können Sie in C ohne “std”-Includes tun? Sind sie Teil von “C” oder nur Bibliotheken?

Lesezeit: 11 Minuten

Benutzeravatar von Chris Cooper
Chris Cooper

Ich entschuldige mich, wenn dies eine subjektive oder wiederholte Frage ist. Es ist irgendwie umständlich, danach zu suchen, daher war ich mir nicht sicher, welche Begriffe ich einschließen sollte.

Was ich gerne wissen würde, ist, was die grundlegenden Werkzeuge/Funktionen in C sind, wenn Sie keine Standardbibliotheken wie einschließen stdio und stdlib.

Was könnte ich tun, wenn es keine gibt printf(), fopen()etc?

Sind diese Bibliotheken technisch gesehen Teil der Sprache “C” oder sind sie nur sehr nützliche und effektiv wesentliche Bibliotheken?

Benutzeravatar von Steve Jessop
Steve Jessop

Die C-Norm sagt dazu (5.1.2.3/5):

Die Mindestanforderungen an eine konforme Implementierung sind:

— An Sequenzpunkten sind flüchtige Objekte in dem Sinne stabil, dass frühere Zugriffe abgeschlossen sind und nachfolgende Zugriffe noch nicht stattgefunden haben.

— Bei Programmende müssen alle in Dateien geschriebenen Daten mit dem Ergebnis identisch sein, das die Ausführung des Programms gemäß der abstrakten Semantik hervorgebracht hätte.

— Die Eingangs- und Ausgangsdynamik von interaktiven Geräten muss wie in 7.19.3 spezifiziert erfolgen.

Ohne die Standardbibliotheksfunktionen bezieht sich das einzige garantierte Verhalten eines Programms also auf die Werte flüchtiger Objekte, da Sie keinen der garantierten Dateizugriffe oder “interaktiven Geräte” verwenden können. “Pure C” bietet nur Interaktion über Standardbibliotheksfunktionen.

Reines C ist jedoch nicht die ganze Geschichte, da Ihre Hardware bestimmte Adressen haben könnte, die beim Lesen oder Schreiben bestimmte Dinge tun (sei es ein SATA- oder PCI-Bus, roher Videospeicher, ein serieller Anschluss, etwas zum Piepsen oder eine blinkende LED). So, etwas über Ihre Hardware wissen, können Sie eine ganze Menge in C schreiben, ohne Standardbibliotheksfunktionen zu verwenden. Möglicherweise könnten Sie die C-Standardbibliothek implementieren, obwohl dies möglicherweise den Zugriff auf spezielle CPU-Anweisungen sowie spezielle Speicheradressen erfordert.

Aber in reinem C, ohne Erweiterungen und ohne die Standardbibliotheksfunktionen, können Sie im Grunde nichts anderes tun, als die Befehlszeilenargumente zu lesen, etwas zu tun und einen Statuscode zurückzugeben main. Daran ist nicht zu rütteln, es ist immer noch Turing vollständig, abhängig von Ressourcenbeschränkungen, obwohl Ihre einzige Ressource automatische und statische Variablen sind, keine Heap-Zuweisung. Es ist keine sehr reichhaltige Programmierumgebung.

Die Standardbibliotheken sind Teil der C-Sprachspezifikation, aber in jeder Sprache wird tendenziell eine Grenze zwischen der Sprache “als solcher” und den Bibliotheken gezogen. Das ist ein konzeptioneller Unterschied, aber letztlich im Prinzip kein sehr wichtiger, weil der Standard sagt, dass sie zusammenkommen. Jeder, der etwas nicht Standardmäßiges tut, könnte genauso leicht Sprachfeatures wie Bibliotheken entfernen. So oder so ist das Ergebnis keine konforme Implementierung von C.

Beachten Sie, dass eine “freistehende” Implementierung von C nur eine Teilmenge der Standard-Includes implementieren muss, die keine I/O enthalten, sodass Sie sich in der oben beschriebenen Position auf hardwarespezifische Erweiterungen verlassen müssen, um etwas Interessantes zu erledigen . Wenn Sie basierend auf dem Standard zwischen der “Kernsprache” und “den Bibliotheken” unterscheiden möchten, ist dies möglicherweise ein guter Ort, um die Grenze zu ziehen.

  • Auf Hardware mit speicherabgebildeter E/A können Sie sie über verwenden volatile uint32_t * oder Wasauchimmer; das ist ein Teil von was volatile ist für. Obwohl ja, benötigen Sie häufig Barriereanweisungen oder andere Dinge, die nicht Teil von reinem C sind und über Erweiterungen in normalen C-Implementierungen verfügbar gemacht werden.

    – Peter Cordes

    16. Oktober 2019 um 22:52 Uhr

Benutzeravatar von Calmarius
Calmarius

Was kann ich tun, wenn es kein printf(), fopen() usw. gibt?

Solange Sie wissen, wie Sie das von Ihnen verwendete System anbinden, können Sie ohne die Standard-C-Bibliothek leben. In eingebetteten Systemen, in denen Sie nur über mehrere Kilobyte Speicher verfügen, möchten Sie die Standardbibliothek wahrscheinlich überhaupt nicht verwenden.

Hier ist ein Hallo Welt! Beispiel unter Linux und Windows ohne Verwendung von Standard-C-Funktionen:

Unter Linux können Sie beispielsweise die Linux-Systemaufrufe direkt in der Inline-Assemblierung aufrufen:

/* 64 bit linux. */

#define SYSCALL_EXIT 60
#define SYSCALL_WRITE 1

void sys_exit(int error_code)
{
    asm volatile
    (
        "syscall"
        : 
        : "a"(SYSCALL_EXIT), "D"(error_code)
        : "rcx", "r11", "memory"
    );
}

int sys_write(unsigned fd, const char *buf, unsigned count)
{
    unsigned ret;

    asm volatile
    (
        "syscall"
        : "=a"(ret)
        : "a"(SYSCALL_WRITE), "D"(fd), "S"(buf), "d"(count)
        : "rcx", "r11", "memory"
    );
    
    return ret;
}

void _start(void)
{
    const char hwText[] = "Hello world!\n";

    sys_write(1, hwText, sizeof(hwText));
    sys_exit(12);
}

Sie können die Handbuchseite für “syscall” nachschlagen, auf der Sie finden, wie Sie Systemaufrufe durchführen können. Auf Intel x86_64 geben Sie die Systemaufruf-ID in RAX ein, und der Rückgabewert wird dann in RAX gespeichert. Die Argumente müssen in dieser Reihenfolge in RDI, RSI, RDX, R10, R9 und R8 eingegeben werden (wenn das Argument verwendet wird).

Sobald Sie dies haben, sollten Sie nachschlagen, wie Sie Inline-Assembly in gcc schreiben. Der Syscall-Befehl ändert die RCX-, R11-Register und den Speicher, sodass wir dies der Clobber-Liste hinzufügen, um GCC darauf aufmerksam zu machen.

Der standardmäßige Einstiegspunkt für den GNU-Linker ist _start. Normalerweise stellt die Standardbibliothek es bereit, aber ohne es müssen Sie es bereitstellen. Es ist nicht wirklich eine Funktion, da es keine aufrufende Funktion gibt, zu der zurückgekehrt werden kann. Also müssen wir einen weiteren Systemaufruf machen, um unseren Prozess zu beenden.

Kompilieren Sie dies mit:

gcc -nostdlib nostd.c 

Und es gibt aus Hello world!und beendet.

Unter Windows werden die Systemaufrufe nicht veröffentlicht, sondern hinter einer weiteren Abstraktionsschicht, der kernel32.dll, versteckt. Welches immer geladen wird, wenn Ihr Programm startet, ob Sie es wollen oder nicht. Sie können also einfach windows.h aus dem Windows SDK einbinden und wie gewohnt die Win32-API verwenden:

#include <windows.h>

void _start(void)
{
    const char str[] = "Hello world!\n";
    HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD written;

    WriteFile(stdout, str, sizeof(str), &written, NULL);
    ExitProcess(12);
}

Das windows.h hat nichts mit der Standard-C-Bibliothek zu tun, da Sie Windows-Programme auch in jeder anderen Sprache schreiben können sollten.

Sie können es mit den MinGW-Tools wie folgt kompilieren:

gcc -nostdlib C:\Windows\System32\kernel32.dll nostdlib.c

Dann ist der Compiler schlau genug, um die Importabhängigkeiten aufzulösen und Ihr Programm zu kompilieren.

Wenn Sie das Programm disassemblieren, können Sie sehen, dass nur Ihr Code vorhanden ist, es gibt keine aufgeblähten Standardbibliotheken.

Sie können also C ohne die Standardbibliothek verwenden.

  • Danke, du hast nicht nur gesagt, dass es möglich ist, du hast auch ein Beispiel gegeben. Dies sollte die akzeptierte Antwort sein

    – Christian Vincenzo Traina

    3. September 2020 um 21:29 Uhr

  • s!/* 64 bit linux. */!#assert (defined __linux__) && (defined __x86_64__ || defined _M_X64)! ?

    – JamesTheAwesomeDude

    18. August um 17:31 Uhr

Benutzeravatar von plinth
Sockel

Was könntest du tun? Alles!

Es gibt keine Magie in C, außer vielleicht dem Präprozessor.

Das Schwierigste ist vielleicht das Schreiben von putchar – da dies plattformabhängige E / A ist.

Es ist eine gute Übung für Studenten, Ihre eigene Version von varargs zu erstellen, und sobald Sie das haben, erstellen Sie Ihre eigene Version von vaprintf, dann printf und sprintf.

All das habe ich 1986 auf einem Macintosh gemacht, als ich mit den stdio-Routinen, die mit Lightspeed C bereitgestellt wurden, nicht zufrieden war – ich habe meinen eigenen Window-Handler mit win_putchar, win_printf, in_getchar und win_scanf geschrieben.

Dieser ganze Prozess wird Bootstrapping genannt und kann eine der befriedigendsten Erfahrungen beim Codieren sein – die Arbeit mit einem grundlegenden Design, das ziemlich viel praktischen Sinn macht.

  • Ja, und Sie können Putchar nicht in C schreiben.

    – Wirbelwind

    3. April 2010 um 23:19 Uhr

  • Verdammt, das kannst du nicht. putchar nimmt ein Zeichen und schreibt es aus – wo? Nun, das ist ein Implementierungsdetail. Auf einem eingebetteten System könnte es sich um einen seriellen Anschluss handeln, sodass putchar ungefähr so ​​aussieht: void putchar(char c) { *addressOfOutPort = c; } Bei dem Macintosh-Code, den ich geschrieben habe, wurde das gewünschte Zeichen per Bit-Blitting an der entsprechenden Stelle in das Fenster eingefügt und gescrollt.

    – Sockel

    3. April 2010 um 23:27 Uhr

  • putchar() erfordert zum Schreiben Assembly, nicht C. Es muss das Betriebssystem aufrufen, und jedes Mal, wenn Sie das Betriebssystem aufrufen, durchlaufen Sie den Assembly-Code. Ja, auf bestimmten Systemen, denen ein schützendes Betriebssystem fehlt, können Sie möglicherweise alles in C erledigen, wie ich in meinem Beitrag erwähnt habe.

    – Wirbelwind

    3. April 2010 um 23:29 Uhr


  • @WhirlWind – Durch Casting char * Daten zu einem Funktionszeiger, können Sie beliebigen Assembler-Code von C auf praktisch jedem System einbetten und ausführen.

    – Chris Lutz

    3. April 2010 um 23:39 Uhr

  • Oh, hören Sie auf, in Hypothesen zu sprechen – es ist leicht zu überprüfen! 🙂 Geben Sie bei der Google-Codesuche “putchar” ein – das erste Ergebnis ist putchar() von GNU libc, das in C geschrieben ist. Es ruft auf _IO_putc_unlocked Das ist ein Makro, das eine Zeigerarithmetik für eine interne Struktur durchführt.

    – Ken

    5. Juni 2010 um 23:33 Uhr

Sie sind sicherlich nicht verpflichtet, die Standardbibliotheken zu verwenden, wenn Sie sie nicht benötigen. Nicht wenige eingebettete Systeme haben entweder keine Unterstützung für Standardbibliotheken oder können sie aus dem einen oder anderen Grund nicht verwenden. Der Standard spricht sogar ausdrücklich von Implementierungen ohne Bibliotheksunterstützung, C99-Standard 5.1.2.1 “Freistehende Umgebung”:

In einer freistehenden Umgebung (in der die Ausführung von C-Programmen ohne den Nutzen eines Betriebssystems stattfinden kann) sind Name und Typ der Funktion, die beim Programmstart aufgerufen wird, implementierungsdefiniert. Alle Bibliotheksfunktionen, die für ein freistehendes Programm verfügbar sind, mit Ausnahme des in Abschnitt 4 erforderlichen Mindestsatzes, sind implementierungsdefiniert.

Die Header, die von C99 benötigt werden, um in einer freistehenden Implementierung verfügbar zu sein, sind <float.h>, <iso646.h>, <limits.h>, <stdarg.h>, <stdbool.h>, <stddef.h>und <stdint.h>. Diese Header definieren nur Typen und Makros, sodass keine Funktionsbibliothek zu ihrer Unterstützung erforderlich ist.

Ohne die Standardbibliothek sind Sie vollständig auf Ihren eigenen Code, alle nicht standardmäßigen Bibliotheken, die Ihnen möglicherweise zur Verfügung stehen, und alle Systemaufrufe des Betriebssystems angewiesen, zu denen Sie möglicherweise eine Schnittstelle herstellen können (die möglicherweise als nicht standardmäßige Bibliothek angesehen werden Anrufe). Möglicherweise müssen Sie Ihr C-Programm Assembler-Routinen aufrufen lassen, um eine Schnittstelle zu Geräten und / oder einem beliebigen Betriebssystem auf der Plattform herzustellen.

Sie können nicht viel tun, da die meisten Standardbibliotheksfunktionen auf Systemaufrufen beruhen; Sie sind auf das beschränkt, was Sie mit den eingebauten C-Schlüsselwörtern und -Operatoren tun können. Es hängt auch vom System ab; In einigen Systemen können Sie möglicherweise Bits so manipulieren, dass sie zu einer externen Funktionalität führen, aber dies ist wahrscheinlich eher die Ausnahme als die Regel.

Die Eleganz von C liegt jedoch in seiner Einfachheit. Im Gegensatz zu Fortran, das viele Funktionen als Teil der Sprache enthält, ist C ziemlich abhängig von seiner Bibliothek. Dies verleiht ihm ein hohes Maß an Flexibilität, allerdings auf Kosten einer etwas weniger konsistenten Ausführung von Plattform zu Plattform.

Dies funktioniert zum Beispiel gut im Betriebssystem, wo vollständig getrennte “Bibliotheken” implementiert sind, um ähnliche Funktionalität mit einer Implementierung innerhalb des Kernels selbst bereitzustellen.

Einige Teile der Bibliotheken sind als Teil von ANSI C spezifiziert; sie sind Teil der Sprache, nehme ich an, aber nicht in ihrem Kern.

Benutzeravatar von Uri
Uri

Keines davon ist Teil der Sprachschlüsselwörter. Alle C-Distributionen müssen jedoch eine Implementierung dieser Bibliotheken enthalten. Dies gewährleistet die Portabilität von viele Programme.

Zunächst einmal könntest du all diese Funktionen theoretisch selbst implementieren, indem du eine Kombination aus C und Assembler verwendest, also könntest du theoretisch alles machen.

In der Praxis sollen Bibliotheksfunktionen in erster Linie Ihnen die Arbeit ersparen, das Rad neu zu erfinden. Einige Dinge (wie String- und Bibliotheksfunktionen) sind einfacher zu implementieren. Andere Dinge (wie E/A) hängen stark vom Betriebssystem ab. Das Schreiben Ihrer eigenen Version wäre für ein Betriebssystem möglich, aber es wird das Programm weniger portabel machen.

Aber Sie könnten Programme schreiben, die viele nützliche Dinge tun (z. B. PI oder den Sinn des Lebens berechnen oder einen Automaten simulieren). Wenn Sie das Betriebssystem jedoch nicht direkt für die E/A verwenden, wäre es sehr schwierig, die Ausgabe zu beobachten.

In der täglichen Programmierung erfordert der Erfolg einer Programmiersprache typischerweise die Verfügbarkeit einer nützlichen hochwertigen Standardbibliothek und von Bibliotheken für viele nützliche Aufgaben. Diese können von Erstanbietern oder Drittanbietern sein, aber sie müssen vorhanden sein.

Die std-Bibliotheken sind “Standard”-Bibliotheken, da diese Bibliotheken “inklusive” sein müssen, damit ein C-Compiler mit einem Standard (z. B. C99) kompatibel ist. Für ein interessantes Beispiel, das helfen könnte, zu verstehen, was das bedeutet, sehen Sie sich hier die Herausforderung von Jessica McKellar an:

http://blog.ksplice.com/2010/03/libc-free-world/

Bearbeiten: Der obige Link ist gestorben (danke Oracle …) Ich denke, dieser Link spiegelt den Artikel wider: https://sudonull.com/post/178679-Hello-from-the-libc-free-world-Part-1

  • Ich glaube nicht, dass dies die Frage beantwortet hat. Es wurden nur angrenzende Themen zur eigentlichen Abfrage erläutert.

    – Markieren

    19. September um 12:41 Uhr

1410360cookie-checkWas können Sie in C ohne “std”-Includes tun? Sind sie Teil von “C” oder nur Bibliotheken?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy