C Datei-E/A
Im vorherigen Kapitel haben wir erklärt, wie die Sprache C Standardeingabe- und Standardausgabegeräte behandelt. In diesem Kapitel wird vorgestellt, wie C-Programmierer Textdateien oder Binärdateien erstellen, öffnen und schließen.
Eine Datei, ob Textdatei oder Binärdatei, repräsentiert eine Folge von Bytes. Die Sprache C bietet nicht nur Top-Level-Funktionen, sondern auch Low-Level-(OS-)Aufrufe zur Behandlung von Dateien auf Speichergeräten. Dieses Kapitel erläutert die wichtigen Aufrufe für die Dateiverwaltung.
Eine Datei öffnen
Sie könnenfopen( )die Funktion verwenden, um eine neue Datei zu erstellen oder eine vorhandene Datei zu öffnen. Dieser Aufruf initialisiert ein Objekt vom TypFILE, ein Objekt vom TypFILEdas alle notwendigen Informationen zur Steuerung des Streams enthält. Nachfolgend finden Sie den Prototyp dieses Funktionsaufrufs:
FILE *fopen( const char *filename, const char *mode );
Hier istfilenameeine Zeichenkette zur Benennung der Datei; der Zugriffsmodusmodekann einen der folgenden Werte annehmen:
| Mode | Beschreibung |
|---|---|
| r | Öffnet eine vorhandene Textdatei zum Lesen. |
| w | Öffnet eine Textdatei zum Schreiben. Wenn die Datei nicht existiert, wird eine neue Datei erstellt. Hier schreibt Ihr Programm den Inhalt ab dem Anfang der Datei. Wenn die Datei existiert, wird ihr Inhalt gelöscht (d. h. die Dateilänge wird auf 0 gekürzt). |
| a | Öffnet eine Textdatei zum Schreiben im Anhängemodus. Wenn die Datei nicht existiert, wird eine neue Datei erstellt. Hier hängt Ihr Programm Inhalt an den vorhandenen Dateiinhalt an. |
| r+ | Öffnet eine Textdatei sowohl zum Lesen als auch zum Schreiben. |
| w+ | Öffnet eine Textdatei sowohl zum Lesen als auch zum Schreiben. Wenn die Datei bereits existiert, wird sie auf die Länge null gekürzt; wenn die Datei nicht existiert, wird eine neue Datei erstellt. |
| a+ | Öffnet eine Textdatei sowohl zum Lesen als auch zum Schreiben. Wenn die Datei nicht existiert, wird eine neue Datei erstellt. Das Lesen beginnt am Anfang der Datei, während das Schreiben nur im Anhängemodus erfolgen kann. |
Wenn Sie mit Binärdateien arbeiten, verwenden Sie die folgenden Zugriffsmodi anstelle der oben genannten Zugriffsmodi:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
Eine Datei schließen
Um eine Datei zu schließen, verwenden Sie die Funktion fclose( ). Der Prototyp der Funktion lautet wie folgt:
int fclose( FILE *fp );
Wenn die Datei erfolgreich geschlossen wurde,fclose( )Die Funktion gibt null zurück; wenn beim Schließen der Datei ein Fehler auftritt, gibt die FunktionEOFEOF zurück. Diese Funktion leert tatsächlich die Daten im Puffer, schließt die Datei und gibt den gesamten für die Datei verwendeten Speicher frei. EOF ist in der Header-Datei definiertstdio.hdie Konstante darin.
Die C-Standardbibliothek bietet verschiedene Funktionen zum Lesen und Schreiben von Dateien Zeichen für Zeichen oder in Form von Zeichenketten fester Länge.
In eine Datei schreiben
Das Folgende ist die einfachste Funktion zum Schreiben eines Zeichens in einen Stream:
int fputc( int c, FILE *fp );
Funktionfputc()schreibt den Zeichenwert des Parameters c in den Ausgabestream, auf den fp zeigt. Bei Erfolg gibt sie das geschriebene Zeichen zurück; tritt ein Fehler auf, gibt sieEOFSie können die folgende Funktion verwenden, um eine nullterminierte Zeichenkette in einen Stream zu schreiben:
int fputs( const char *s, FILE *fp );
Funktionfputs()die Zeichenkettesschreibt in den Ausgabestream, auf den fp zeigt. Bei Erfolg gibt sie einen nicht negativen Wert zurück; tritt ein Fehler auf, gibt sieEOFSie können auchint fprintf(FILE *fp,const char *format, ...)Die Funktion schreibt eine Zeichenkette in die Datei. Probieren Sie das folgende Beispiel aus:
Hinweis:Bitte stellen Sie sicher, dass Sie ein nutzbarestmpVerzeichnis haben. Wenn das Verzeichnis nicht existiert, müssen Sie es zuerst auf Ihrem Computer erstellen.
/tmpDies ist im Allgemeinen das temporäre Verzeichnis auf Linux-Systemen. Wenn Sie unter Windows arbeiten, müssen Sie es in ein vorhandenes Verzeichnis in Ihrer lokalen Umgebung ändern, zum Beispiel: C:\tmp、D:\tmpusw.
Beispiel
Wenn der obige Code kompiliert und ausgeführt wird, erstellt er eine neue Datei im Verzeichnis /tmptest.txtund verwendet zwei verschiedene Funktionen, um zwei Zeilen zu schreiben. Als Nächstes lesen wir diese Datei.
Aus einer Datei lesen
Das Folgende ist die einfachste Funktion, um ein einzelnes Zeichen aus einer Datei zu lesen:
int fgetc( FILE * fp );
fgetc()Die Funktion liest ein Zeichen aus der Eingabedatei, auf die fp zeigt. Der Rückgabewert ist das gelesene Zeichen; wenn ein Fehler auftritt, gibt sieEOFDie folgende Funktion ermöglicht es Ihnen, eine Zeichenkette aus einem Stream zu lesen:
char *fgets( char *buf, int n, FILE *fp );
Funktionfgets()Sie liest n - 1 Zeichen aus dem Eingabestream, auf den fp zeigt. Sie kopiert die gelesene Zeichenkette in den Pufferbuf, und hängt einnullZeichen, um die Zeichenkette zu beenden.
Wenn diese Funktion auf ein Zeilenumbruchzeichen '\n' oder das Dateiende EOF stößt, bevor das letzte Zeichen gelesen wird, gibt sie nur die gelesenen Zeichen zurück, einschließlich des Zeilenumbruchs. Sie können auchint fscanf(FILE *fp, const char *format, ...)Funktion verwenden, um Zeichenketten aus einer Datei zu lesen, aber sie stoppt das Lesen, wenn sie auf das erste Leerzeichen oder einen Zeilenumbruch trifft.
Beispiel
Wenn der obige Code kompiliert und ausgeführt wird, liest er die im vorherigen Abschnitt erstellte Datei und erzeugt die folgenden Ergebnisse:
1: This 2: is testing for fprintf... 3: This is testing for fputs...
Zuerst,fscanf()Die Methode las nurDies, weil sie danach auf ein Leerzeichen stieß. Als Nächstes rufen Siefgets()um den verbleibenden Teil bis zum Ende der Zeile zu lesen. Zum Schluss rufen Siefgets()auf, wodurch die zweite Zeile vollständig gelesen wird.
Binäre E/A-Funktionen
Die folgenden zwei Funktionen werden für die binäre Eingabe und Ausgabe verwendet:
size_t fread(void *ptr, size_t size, size_t count, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
freadundfwritesind die Kernfunktionen in der C-Standardbibliothek zum Lesen und Schreiben binärer Dateiblöcke. Sie arbeiten direkt auf Speicherdatenblöcken, wodurch der Aufwand der Textformatkonvertierung vermieden wird, und eignen sich besonders für das stapelweise Lesen und Schreiben großer Dateien und strukturierter Daten.
Parameterbeschreibung
Die vier Parameter haben in beiden Funktionen die gleiche Bedeutung, aber die Datenflussrichtung ist entgegengesetzt:
| Parameter | Type | Beschreibung | fread-Verhalten | fwrite-Verhalten |
|---|---|---|---|---|
| ptr | void* / const void* | Zeiger auf Speicherpuffer | Zieladresse für Lesevorgang (in Speicher schreiben) | Datenquelladresse (aus Speicher lesen) |
| size | size_t | Anzahl der Bytes pro Datenblock | Blockgröße pro Lesevorgang | Blockgröße pro Schreibvorgang |
| count | size_t | Anzahl der zu lesenden oder zu schreibenden Datenblöcke | Erwartete Anzahl der zu lesenden Blöcke | Erwartete Anzahl der zu schreibenden Blöcke |
| stream | FILE* | Geöffneter Dateizeiger | Muss mit "rb" geöffnet werden | Muss mit "wb" geöffnet werden |
Die tatsächliche Anzahl der bearbeiteten Bytes istsize × countDer Rückgabewert ist die Anzahl der erfolgreich gelesenen oder geschriebenen Blöcke, nicht die Anzahl der Bytes. Wenn der Rückgabewert kleiner als count ist, kann dies durch Dateiende (EOF) oder einen Lese-/Schreibfehler verursacht werden, die separat geprüft werden müssen.
Binärmodus vs. Textmodus
Bei Verwendung von fread/fwrite muss die Datei im Binärmodus geöffnet werden; andernfalls kann es zu Datenbeschädigung kommen.
Warum der Binärmodus verwendet werden muss
Textmodus unter Windows konvertiert Zeilenumbruchzeichen automatisch\nUmgewandelt in\r\n, was die Integrität binärer Daten zerstört.
Unter Linux/macOS verhalten sich Textmodus und Binärmodus gleich, aber plattformübergreifender Code sollte explizit"b"Flag verwenden, um Portabilität zu gewährleisten.
Beispiel
FILE *fp_read = fopen("data.bin", "rb"); // Binär lesen
FILE *fp_write = fopen("data.bin", "wb"); // Binär schreiben
// Falscher Ansatz: Textmodus (Daten können unter Windows beschädigt werden)
FILE *fp_bad = fopen("data.bin", "r"); // Fehlendes "b"-Flag
Vergleich mit Textfunktionen
Welche Funktion gewählt wird, hängt vom Datentyp und vom Anforderungsszenario ab:
| Funktionen | fread / fwrite | fprintf / fscanf |
|---|---|---|
| Datenverarbeitungsmethode | Binärdaten unverändert im Speicher lesen und schreiben | Formatbasierte Konvertierung (Zahl ↔ Zeichenkette) |
| Anwendbare Szenarien | Strukturen, Arrays, Bilder, Audio usw. | Für Menschen lesbarer Text wie Konfigurationsdateien, Logs, Berichte usw. |
| Leistung | Schnell, kein Konvertierungsaufwand | Langsam, die Formatzeichenfolge muss analysiert werden |
| Precision | Kein Präzisionsverlust | Gleitkommazahlen können Präzision verlieren |
| Menschenlesbar | Nein (Binärformat) | Ja (Klartextformat) |
Flussdiagramm zum Lesen/Schreiben von Daten
Die folgende Abbildung zeigt den Datenfluss zwischen fread und fwrite sowie zwischen Datei und Speicher:
Typische Anwendungsszenarien
Lesen und Schreiben von Struktur-Daten
Dies ist das häufigste Anwendungsszenario von fread/fwrite; Sie können eine gesamte Struktur auf einmal als einen einzelnen Datenblock lesen oder schreiben.
Beispiel
#include <stdlib.h>
// Studenten-Struktur definieren
// Hinweis: Es muss ein Array fester Länge verwendet werden, ein char *name-Zeigermitglied ist nicht erlaubt
typedef struct {
int id; // Studenten-ID
char name[50]; // Name (festes Array, Zeiger nicht erlaubt)
float score; // Punktzahl
} Student;
int main() {
// ===== Struktur in Datei schreiben =====
Student s = {101, "Alice", 95.5f}; // Daten initialisieren
FILE *fp = fopen("student.bin", "wb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
// Die gesamte Struktur als einen Datenblock schreiben
size_t written = fwrite(&s, sizeof(Student), 1, fp);
if (written != 1) {
printf("Schreiben fehlgeschlagen\n");
}
fclose(fp);
// ===== Struktur aus Datei lesen =====
fp = fopen("student.bin", "rb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
Student loaded; // Nicht initialisiert, wird von fread gefüllt
size_t read_count = fread(&loaded, sizeof(Student), 1, fp);
if (read_count != 1) {
printf("Lesen fehlgeschlagen oder Dateiende\n");
fclose(fp);
return 1;
}
printf("ID: %d, Name: %s, Punktzahl: %.1f\n", geladen.id, geladen.name, geladen.score);
fclose(fp);
return 0;
}
学号: 101, 姓名: Alice, 成绩: 95.5
Strukturen können keine Zeiger-Member enthalten (wie z. B.char *name), weil fwrite nur den Adresswert des Zeigers speichert, nicht den Inhalt der Zeichenkette, auf die er zeigt. Beim Zurücklesen ist diese Adresse nicht mehr gültig, was zu einem hängenden Zeiger (dangling pointer) führt. Die korrekte Vorgehensweise ist, ein Array fester Länge zu verwenden, wie z. B.char name[50]。
Arraydaten lesen und schreiben
Arrays werden zusammenhängend im Speicher abgelegt, sodass das gesamte Array auf einmal gelesen oder geschrieben werden kann.
Beispiel
int main() {
// Rohdaten-Array
int data[] = {1, 2, 3, 4, 5};
int count = 5; // Anzahl der Array-Elemente
// ===== Array in Datei schreiben =====
FILE *fp = fopen("array.bin", "wb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
// data entspricht &data[0] und zeigt auf die erste Adresse des Arrays
fwrite(data, sizeof(int), count, fp);
fclose(fp);
printf("%d Ganzzahlen in array.bin geschrieben\n", count);
// ===== Array aus Datei lesen =====
int buffer[5] = {0}; // Auf 0 initialisieren, um Leseergebnisse leicht überprüfen zu können
fp = fopen("array.bin", "rb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
size_t read_count = fread(buffer, sizeof(int), count, fp);
if (read_count < count) {
printf("Unvollständiger Lesevorgang: %d Elemente erwartet, %zu Elemente erhalten"\n", count, read_count);
}
fclose(fp);
// Leseergebnis ausgeben
printf("Gelesene Daten: ");
for (int i = 0; i < count; i++) {
printf("%d ", Puffer[i]);
}
printf("\n");
return 0;
}
已写入 5 个整数到 array.bin 读取到的数据: 1 2 3 4 5
Stapelweises Lesen und Schreiben großer Dateien
Bei großen Dateien sollten Sie eine Schleife zum blockweisen Lesen/Schreiben verwenden, um zu vermeiden, dass der gesamte Inhalt auf einmal in den Speicher geladen wird.
Beispiel
#include <stdlib.h>
#define BUFFER_SIZE 1024 // Jedes Mal 1KB lesen/schreiben, nach Bedarf anpassen
// Effiziente Dateikopierfunktion
// Rückgabewert: 0 bedeutet Erfolg, -1 bedeutet Fehler
int copy_file(const char *src, const char *dst) {
char buffer[BUFFER_SIZE]; // Zwischenpuffer
FILE *in = fopen(src, "rb");
FILE *out = fopen(dst, "wb");
// Prüfen, ob beide Dateien erfolgreich geöffnet wurden
if (!in) {
perror("Quelldatei konnte nicht geöffnet werden");
return -1;
}
if (!out) {
perror("Zieldatei konnte nicht geöffnet werden");
fclose(in); // Die geöffnete Quelldatei schließen
return -1;
}
// In einer Schleife lesen, jeweils bis zu BUFFER_SIZE Bytes
size_t n;
while ((n = fread(buffer, 1, BUFFER_SIZE, in)) > 0) {
// fwrite mit size=1, count=n stellt sicher, dass die tatsächliche Anzahl der gelesenen Bytes geschrieben wird
if (fwrite(buffer, 1, n, out) != n) {
perror("Schreiben fehlgeschlagen");
fclose(in);
fclose(out);
return -1;
}
}
// Prüfen, ob das Lesen aufgrund eines Fehlers beendet wurde (statt eines normalen EOF)
if (ferror(in)) {
perror("Lesefehler");
fclose(in);
fclose(out);
return -1;
}
fclose(in);
fclose(out);
printf("Datei erfolgreich kopiert: %s -> %s\n"\n", src, dst);
return 0;
}
int main() {
// Verwendungsbeispiel: die Beispiel-Testdatei kopieren
copy_file("example_input.bin", "example_output.bin");
return 0;
}
文件复制成功: example_input.bin -> example_output.bin
Der Schlüssel zu diesem Modus ist, dassfreaddie Anzahl der tatsächlich gelesenen Bytes zurückgibt; verwenden Sie dannfwrite, um nur die Anzahl der tatsächlich gelesenen Bytes zu schreiben und das Schreiben zusätzlicher Daten am Ende zu vermeiden.
Wichtige Überlegungen
Prüfung des Rückgabewerts
Nach dem Aufruf von fread/fwrite müssen Sie den Rückgabewert prüfen, um zwischen normalem Abschluss und abnormalen Bedingungen zu unterscheiden.
Beispiel
int main() {
int buffer[100];
FILE *fp = fopen("data.bin", "rb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
// Versuche, 100 ganze Zahlen zu lesen
size_t count = fread(buffer, sizeof(int), 100, fp);
if (count < 100) {
// EOF von Fehlern unterscheiden
if (feof(fp)) {
printf("Ende der Datei erreicht, tatsächlich %zu Elemente gelesen\n"\n", count);
// An diesem Punkt können wir mit der Verarbeitung der gelesenen Daten fortfahren
} else if (ferror(fp)) {
perror("Beim Lesen ist ein Fehler aufgetreten");
fclose(fp);
return 1;
}
} else {
printf("Alle %zu Elemente erfolgreich gelesen\n", count);
}
fclose(fp);
return 0;
}
已到达文件末尾,实际读取 47 个元素
Fallstricke beim Lesen/Schreiben von Structs
Das Speicherlayout von Strukturen wird durch die Ausrichtungsstrategie des Compilers beeinflusst, was zu plattformübergreifenden Kompatibilitätsproblemen führen kann.
Inkonsistente Speicherausrichtung
Bei unterschiedlichen Compilern oder unterschiedlichen Kompilierungsoptionen kann dieselbe Struktur unterschiedliche Speicherlayouts aufweisen.
Beispiel
// Beispielproblem: Bei Standardausrichtung gibt es 3 Byte Padding nach char
typedef struct {
char c; // Belegt 1 Byte, aber danach folgt Padding
int i; // 4-Byte-Ausrichtung, beginnt tatsächlich bei Offset 4
} Mixed;
// Lösung 1: pragma pack verwenden, um kompakte Ausrichtung zu erzwingen
#pragma pack(push, 1) // Aktuelle Ausrichtungseinstellungen speichern, auf 1-Byte-Ausrichtung setzen
typedef struct {
char c; // Offset 0, belegt 1 Byte
int i; // Offset 1, belegt 4 Byte (kein Padding)
} PackedMixed;
#pragma pack(pop) // Vorherige Ausrichtungseinstellungen wiederherstellen
// Lösung 2: Member manuell nacheinander serialisieren (der sicherste Weg)
// Diese Methode hängt nicht von der Compiler-Ausrichtung ab und bietet die beste plattformübergreifende Kompatibilität
int main() {
// Größenunterschied zwischen den beiden Strukturen ausgeben
printf("Größe von standardmäßig ausgerichtetem Mixed: %zu bytes\n", sizeof(Mixed)); // Normalerweise 8
printf("Größe von kompakt ausgerichtetem PackedMixed: %zu bytes\n", sizeof(PackedMixed)); // Sie ist 5
return 0;
}
默认对齐 Mixed 大小: 8 字节 紧凑对齐 PackedMixed 大小: 5 字节
Empfohlene VorgehensweiseFür plattformübergreifende oder langfristige Speicherung ist die manuelle Serialisierung (Lesen/Schreiben Member für Member) dem direkten Schreiben der Struktur in eine Datei vorzuziehen. Dadurch lassen sich Ausrichtungs- und Byte-Reihenfolge-Probleme vollständig vermeiden.
Zeiger-Member sind verboten
Dies ist einer der häufigsten Fehler von Anfängern.
Beispiel
#include <string.h>
#include <stdlib.h>
// Falsches Beispiel: struct enthält ein Zeiger-Member
typedef struct {
int id;
char *name; // Gefahr! fwrite speichert nur den Zeigerwert (Adresse), nicht den String-Inhalt
} BadStudent;
// Korrektes Beispiel: ein festes Array verwenden
typedef struct {
int id;
char name[100]; // Die tatsächlichen Daten werden in der Struktur gespeichert, sodass sie sicher gelesen und geschrieben werden können
} GoodStudent;
int main() {
// Die Folgen des falschen Ansatzes demonstrieren
BadStudent bs;
bs.id = 1;
bs.name = malloc(50);
strcpy(bs.name, "EXAMPLE");
// BadStudent schreiben: An diesem Punkt wird die Adresse des name-Zeigers geschrieben, aber der String-Inhalt wird nicht geschrieben
FILE *fp = fopen("bad.bin", "wb");
fwrite(&bs, sizeof(BadStudent), 1, fp);
fclose(fp);
// BadStudent zurücklesen: Der Wert des name-Zeigers ist die Adresse aus einem vorherigen malloc in einem anderen Prozess, vollständig ungültig
BadStudent geladen;
fp = fopen("bad.bin", "rb");
fread(&loaded, sizeof(BadStudent), 1, fp);
fclose(fp);
// loaded.name ist ein hängender Zeiger; der Zugriff darauf führt zu undefiniertem Verhalten!
free(bs.name);
printf("Demo abgeschlossen, aber der geladene name-Zeiger ist ungültig\n");
return 0;
}
演示完成,但 load 出来的 name 指针是无效的
Puffer leeren
Die E/A der C-Standardbibliothek ist gepuffert; kritische Daten müssen explizit geleert werden, um die Persistenz zu gewährleisten.
Beispiel
int main() {
FILE *fp = fopen("critical.bin", "wb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
int data[] = {1, 2, 3, 4, 5};
fwrite(data, sizeof(int), 5, fp);
// Methode 1: Puffer explizit leeren, Daten werden sofort auf die Festplatte geschrieben
fflush(fp);
printf("Verwende fflush, um das Leeren auf die Festplatte zu erzwingen\n");
// Methode 2: Automatisch beim Schließen der Datei leeren (empfohlen)
// fclose ruft intern automatisch fflush auf
fclose(fp);
printf("fclose leert den Puffer ebenfalls automatisch\n");
return 0;
}
使用 fflush 强制刷新到磁盘 fclose 也会自动刷新缓冲区
Positionierung des Dateizeigers
Die Kombination von fseek und ftell ermöglicht den wahlfreien Zugriff auf eine Datei und das Lesen von Daten an angegebenen Positionen nach Bedarf.
Beispiel
typedef struct {
int id;
char name[50];
float score;
} Student;
int main() {
FILE *fp = fopen("students.bin", "rb");
if (fp == NULL) {
perror("Datei konnte nicht geöffnet werden");
return 1;
}
// Zum Ende der Datei springen, um die Dateigröße zu ermitteln
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
printf("Dateigröße: %ld Bytes\n", file_size);
// Berechne, wie viele Struct-Datensätze in der Datei sind
long record_count = file_size / sizeof(Student);
printf("Enthält %ld Studentendatensätze\n", record_count);
// Zum 6. Struct springen (Index beginnt bei 0, Offset = 5 * sizeof(Student))
fseek(fp, sizeof(Student) * 5, SEEK_SET);
Student s;
fread(&s, sizeof(Student), 1, fp);
printf("Datensatz #6: id=%d, name=%s\n", s.id, s.name);
fclose(fp);
return 0;
}
文件大小: 6000 字节 包含 100 条学生记录 第 6 条记录: id=106, name=EXAMPLE
Zusammenfassung häufiger Fehler
Die folgende Tabelle fasst die häufigsten Probleme und korrekten Vorgehensweisen bei der Verwendung von fread/fwrite zusammen:
| Fehlertyp | Fehlersymptom | Korrekte Vorgehensweise |
|---|---|---|
| Ungeprüfter Dateizeiger | fread direkt nach fopen aufzurufen, wenn fopen fehlschlägt, verursacht einen Absturz. | Immer prüfenfp != NULL |
| Binärmodus ignorieren | Binärdaten werden unter Windows durch Zeilenumbruchkonvertierung beschädigt. | Verwendung"rb" / "wb" |
| Verwechslung der Blockanzahl mit der Byteanzahl | Behandlung des Rückgabewerts als Anzahl von Bytes, was zu Logikfehlern führt. | Beachten Sie, dass der Rückgabewert die Anzahl der Blöcke ist; Anzahl der Bytes =Rückgabewert × Größe |
| Pufferüberlauf | Nicht ausreichend zugewiesener Speicher, Schreibzugriff außerhalb der Grenzen | Pufferplatz ≥size × count sicherstellen |
| Struct enthält Zeiger | Zeigeradresse ist nach dem Zurücklesen ungültig, was zu einem Dangling Pointer führt. | Verwenden Sie ein festes Array oder serialisieren Sie jedes Member manuell. |
| Ignorieren von Fehlerprüfungen | EOF kann nicht von Lesefehlern unterschieden werden, was zu unvollständigen Daten führt. | Rückgabewert prüfen,feof() / ferror() verwenden,Ursache bestimmen |
| Daten nicht synchronisiert | Programmabsturz führt zu Datenverlust im Puffer | Nach dem Schreiben kritischer Datenfflush(fp) aufrufen |