Buffl

Kl

BB
by Bernd B.

Was macht die Funktion strcpy() und wie ist ihre Syntax?

strcpy() (string copy) kopiert eine Zeichenkette (inklusive des abschließenden Nullzeichens \0) von einer Quelladresse zu einer Zieladresse. [2004]


#include <string.h> // Wichtig, um strcpy nutzen zu können! [1995]

char *strcpy(char *ziel, const char *quelle);


  1. ziel: Ein Zeiger auf den Speicherbereich, wohin die Zeichenkette kopiert werden soll. Wichtig: Der Zielspeicher muss groß genug sein, um die Quellzeichenkette (inkl. \0) aufzunehmen! Du bist dafür verantwortlich, genügend Speicher bereitzustellen (z.B. durch ein ausreichend großes Array oder dynamische Speicherallokation).

  2. quelle: Ein Zeiger auf die zu kopierende Zeichenkette. Dieser String wird nicht verändert (const).

  3. Rückgabewert: strcpy gibt einen Zeiger auf ziel zurück. [2004]


#include <stdio.h>

#include <string.h> // Für strcpy

#include <stdlib.h> // Für malloc und free

int main() {

char quelle[] = "Hallo Olchis!";

char ziel1[50]; // Ausreichend großer Puffer auf dem Stack

// Kopieren in ein Array auf dem Stack

strcpy(ziel1, quelle); [2005]

printf("Ziel1: %s\n", ziel1);

// Dynamisches Kopieren

char *ziel2;

int laenge = strlen(quelle) + 1; // Länge des Quellstrings + 1 für '\0'

ziel2 = (char*) malloc(laenge * sizeof(char));

if (ziel2 == NULL) {

printf("Speicherfehler!\n");

return 1;

}

strcpy(ziel2, quelle);

printf("Ziel2 (dynamisch): %s\n", ziel2);

free(ziel2);

ziel2 = NULL;

return 0;

}


Achtung: strcpy prüft nicht, ob der Zielpuffer groß genug ist. Wenn nicht, kommt es zu einem Pufferüberlauf, was ein schwerwiegendes Sicherheitsproblem sein kann! Sicherere Alternativen sind strncpy (kopiert maximal n Zeichen) oder strcpy_s (in neueren C-Standards). [2006]*


Wie öffnet und schließt man eine Datei in C?

  1. Öffnen: Mit der Funktion fopen() aus <stdio.h>. [1672]

    • Sie benötigt den Dateinamen und einen Modus (z.B. "r" für Lesen, "w" für Schreiben, "a" für Anhängen). [1675]

    • fopen() gibt einen Zeiger vom Typ FILE zurück. Dieser Zeiger (oft Dateizeiger oder File-Handle genannt) wird für alle weiteren Operationen mit dieser Datei benötigt.

    • Wenn das Öffnen fehlschlägt (z.B. Datei nicht gefunden im Lesemodus), gibt fopen() NULL zurück. Immer prüfen! [1674]

  2. Schließen: Mit der Funktion fclose() aus <stdio.h>. [1685]

    • Sie benötigt den FILE-Zeiger der zu schließenden Datei.

    • Schließt die Datei und gibt alle damit verbundenen Puffer frei.

    • Es ist sehr wichtig, jede geöffnete Datei auch wieder zu schließen, um Datenverlust zu vermeiden und Ressourcen freizugeben. [1693]

    #include <stdio.h> // Für fopen, fclose, printf, etc. [1668]

    int main() {

    FILE *dateizeiger;

    char dateiname[] = "meine_datei.txt";

    // Datei im Schreibmodus öffnen (erstellt die Datei, falls sie nicht existiert,

    // oder überschreibt sie, falls sie existiert)

    dateizeiger = fopen(dateiname, "w"); [1691]

    if (dateizeiger == NULL) {

    printf("Fehler: Datei '%s' konnte nicht zum Schreiben geoeffnet werden.\n", dateiname);

    return 1;

    }

    // Hier könnten Operationen wie Schreiben in die Datei erfolgen

    printf("Datei '%s' erfolgreich zum Schreiben geoeffnet.\n", dateiname);

    // Datei schließen

    if (fclose(dateizeiger) == 0) { // fclose gibt 0 bei Erfolg zurück, EOF bei Fehler [1685]

    printf("Datei '%s' erfolgreich geschlossen.\n", dateiname);

    } else {

    printf("Fehler: Datei '%s' konnte nicht geschlossen werden.\n", dateiname);

    }

    return 0;

    }


Wie schreibt man formatiert in eine Datei und liest formatiert aus einer Datei?

  1. Formatiert schreiben: Mit fprintf() aus <stdio.h>. [1682]

    • Funktioniert sehr ähnlich wie printf(), aber das erste Argument ist der FILE-Zeiger der Datei, in die geschrieben werden soll.

    • int fprintf(FILE *stream, const char *format, ...); [1684]

  2. Formatiert lesen: Mit fscanf() aus <stdio.h>. [1694]

    • Funktioniert sehr ähnlich wie scanf(), aber das erste Argument ist der FILE-Zeiger der Datei, aus der gelesen werden soll.

    • int fscanf(FILE *stream, const char *format, ...); [1696]



      #include <stdio.h>

    int main() {

    FILE *datei;

    char text[] = "Olchis";

    int alter = 42;

    float groesse = 0.75;

    char gelesener_text[50];

    int gelesenes_alter;

    float gelesene_groesse;

    // Datei zum Schreiben öffnen

    datei = fopen("olchi_daten.txt", "w");

    if (datei == NULL) {

    perror("Fehler beim Oeffnen zum Schreiben");

    return 1;

    }

    // Formatiert in die Datei schreiben

    fprintf(datei, "Name: %s\n", text); [1692]

    fprintf(datei, "Alter: %d Jahre\n", alter);

    fprintf(datei, "Groesse: %.2f Meter\n", groesse);

    fclose(datei);

    printf("Daten in 'olchi_daten.txt' geschrieben.\n\n");

    // Dieselbe Datei zum Lesen öffnen

    datei = fopen("olchi_daten.txt", "r");

    if (datei == NULL) {

    perror("Fehler beim Oeffnen zum Lesen");

    return 1;

    }

    // Formatiert aus der Datei lesen

    // Wichtig: fscanf erwartet eine bestimmte Struktur der Datei.

    // Wenn die Datei anders formatiert ist, schlägt das Lesen fehl oder liefert falsche Werte.

    if (fscanf(datei, "Name: %s\nAlter: %d Jahre\nGroesse: %f Meter\n",

    gelesener_text, &gelesenes_alter, &gelesene_groesse) == 3) { [1700, 1704]

    // fscanf gibt die Anzahl der erfolgreich eingelesenen Elemente zurück.

    printf("Gelesene Daten:\n");

    printf("Name: %s\n", gelesener_text);

    printf("Alter: %d\n", gelesenes_alter);

    printf("Groesse: %.2f\n", gelesene_groesse);

    } else {

    printf("Fehler beim Lesen der Daten oder unerwartetes Dateiformat.\n");

    }

    fclose(datei);

    return 0;

    }


Was ist der Präcompiler und was machen #include und #define?

Der Präcompiler (oder Präprozessor) ist ein Programm, das deinen C-Quellcode bevor dem eigentlichen Kompilieren bearbeitet. [1732] Er führt Anweisungen aus, die mit einem # beginnen (sogenannte Präcompiler-Direktiven). [1737]


#include: [1752]

  • Fügt den Inhalt einer anderen Datei an dieser Stelle in den Quellcode ein.

  • Typische Verwendung: Einbinden von Header-Dateien (.h-Dateien), die Deklarationen von Funktionen und Definitionen von Typen oder Makros enthalten (z.B. #include <stdio.h> für Standard-Ein-/Ausgabe [98] oder #include "meine_header.h" für eigene Header [2570]).

#define: [1760]

  • Definiert ein Makro. Ein Makro ist im einfachsten Fall ein symbolischer Name für einen konstanten Wert oder einen Textabschnitt. Der Präcompiler ersetzt jedes Vorkommen des Makronamens im Code durch den definierten Ersatztext.

  • Kann auch für komplexere Makros mit Parametern verwendet werden (ähnlich wie sehr einfache Funktionen, aber es ist eine reine Textersetzung!). [1768]

// Praecompiler_Beispiel.c

#include <stdio.h> // Fuegt den Inhalt der Standard-Input/Output-Headerdatei ein [1741]

#define PI 3.14159 // Definiert PI als Konstante [1762]

#define GRUSS "Hallo Welt!" // Definiert GRUSS als String-Konstante

#define QUADRAT(x) ((x)*(x)) // Definiert ein Makro mit Parameter (Klammern sind wichtig!) [1770, 1776]

int main() {

double radius = 5.0;

double flaeche = PI * QUADRAT(radius); // PI und QUADRAT(radius) werden vom Praecompiler ersetzt

printf("%s\n", GRUSS);

printf("Die Flaeche eines Kreises mit Radius %.2f ist %.2f\n", radius, flaeche);

printf("Quadrat von 5+2: %d\n", QUADRAT(5+2)); // Wird zu ((5+2)*(5+2))

return 0;

}

nach durchlauf:


// ... Inhalt von stdio.h ...

int main() {

double radius = 5.0;

double flaeche = 3.14159 * ((radius)*(radius));

printf("%s\n", "Hallo Welt!");

printf("Die Flaeche eines Kreises mit Radius %.2f ist %.2f\n", radius, flaeche);

printf("Quadrat von 5+2: %d\n", ((5+2)*(5+2)));

return 0;

}


Wie vermeidet man Mehrfachdeklarationen mit Präcompiler-Steueranweisungen?

Wenn eine Header-Datei versehentlich mehrfach in eine Quelldatei eingebunden wird (z.B. weil andere Header-Dateien sie ebenfalls einbinden), kann dies zu Fehlern führen, da Deklarationen oder Definitionen dann doppelt vorhanden wären. [1802]

Um dies zu verhindern, verwendet man sogenannte Include Guards (oder Header Guards) mit Hilfe von Präcompiler-Steueranweisungen: #ifndef, #define und #endif. [1807]

Prinzip in einer Header-Datei (z.B. meine_header.h):


#ifndef MEINE_HEADER_H // (1) Prueft, ob das Makro MEINE_HEADER_H NOCH NICHT definiert ist [1793, 1807]

#define MEINE_HEADER_H // (2) Wenn nicht, wird MEINE_HEADER_H jetzt definiert [1807]

// (3) Hier kommen jetzt alle Deklarationen und Definitionen der Header-Datei rein:

// z.B.:

#define MAX_ELEMENTE 100

struct MeinTyp {

int id;

char name[50];

};

void meineFunktion(int wert);

#endif // (4) Ende des #ifndef-Blocks [1807]


  • Funktionsweise:

    1. Beim ersten Einbinden von meine_header.h ist MEINE_HEADER_H noch nicht definiert.

    2. Die #ifndef-Bedingung ist wahr, MEINE_HEADER_H wird definiert, und der Inhalt der Header-Datei wird verarbeitet.

    3. Wird meine_header.h später erneut eingebunden (in derselben Kompiliereinheit), ist MEINE_HEADER_H nun definiert.

    4. Die #ifndef-Bedingung ist falsch, und der gesamte Inhalt zwischen #ifndef und #endif wird vom Präcompiler übersprungen. So werden Mehrfachdeklarationen vermieden.

Der Name des Makros (hier MEINE_HEADER_H) sollte einzigartig für diese Header-Datei sein. Eine gängige Konvention ist der Dateiname in Großbuchstaben mit Unterstrichen und einem zusätzlichen Präfix/Suffix (z.B. __ am Anfang und _H__am Ende), um Namenskonflikte zu minimieren. [1808]

Was sind Mehrdateienprogramme und wie funktionieren sie?

Mehrdateienprogramme sind eine praktische Umsetzung der modularen Programmierung in C. Anstatt den gesamten Quellcode in eine einzige .c-Datei zu schreiben, wird er auf mehrere Dateien aufgeteilt:

  • .c-Dateien (Quelldateien/Implementierungsdateien): Enthalten die Implementierung (Definition) von Funktionen und globalen Variablen eines Moduls. [1881]

  • .h-Dateien (Header-Dateien/Schnittstellendateien): Enthalten die Deklarationen von Funktionen, Definitionen von Typen (wie structs, enums), Makros und Deklarationen von externen globalen Variablen, die von anderen Modulen verwendet werden sollen. Sie definieren die Schnittstelle eines Moduls. [1882]

Zusammenspiel:

  1. Eine .c-Datei, die Funktionen oder Variablen aus einem anderen Modul verwenden möchte, bindet dessen Header-Datei mit #include "modulname.h" ein. [1883]

  2. Dadurch kennt der Compiler die Signaturen der Funktionen und die Typen aus dem anderen Modul.

  3. Beim Kompilieren wird jede .c-Datei einzeln in eine Objektdatei (.o oder .obj) übersetzt. [2621]

  4. Der Linker verbindet dann alle Objektdateien (und ggf. Bibliotheken) zu einem einzigen ausführbaren Programm. Er löst dabei die Referenzen zwischen den Modulen auf (z.B. wo sich die implementierte Funktion befindet, die in einem anderen Modul aufgerufen wurde). [2621]

Beispiel-Struktur:

  • main.c (enthält die main-Funktion und ruft Funktionen aus berechnungen.c auf)

  • berechnungen.h (deklariert die Funktionen aus berechnungen.c)

  • berechnungen.c (implementiert die Funktionen, die in berechnungen.h deklariert wurden)



// berechnungen.h

#ifndef BERECHNUNGEN_H

#define BERECHNUNGEN_H

int addiere(int a, int b); // Funktionsdeklaration (Prototyp) [1896]

// Weitere Deklarationen...

#endif


// berechnungen.c

#include "berechnungen.h" // Eigene Header-Datei einbinden

int addiere(int a, int b) { // Funktionsdefinition

return a + b;

}

// Weitere Implementierungen...


// main.c

#include <stdio.h>

#include "berechnungen.h" // Header des Berechnungsmoduls einbinden

int main() {

int summe = addiere(5, 3); // Funktion aus berechnungen.c aufrufen [1895]

printf("Die Summe ist: %d\n", summe);

return 0;

}


Um das zu kompilieren und zu linken (z.B. mit GCC): gcc -c main.c -o main.o gcc -c berechnungen.c -o berechnungen.o gcc main.o berechnungen.o -o mein_programm


Wie übergibt man Parameter an ein Programm beim Start (Kommandozeilenargumente)? (11.3)

Man kann einem C-Programm beim Aufruf über die Kommandozeile (Terminal) Parameter übergeben. Diese werden der main-Funktion über zwei spezielle Argumente zugänglich gemacht: argc und argv. [1908, 1909]

Syntax der main-Funktion:


int main(int argc, char *argv[]) {

// ... Code ...

return 0;

}


Oder auch: char **argv (Zeiger auf einen Zeiger auf char).

  • int argc (argument count): [1910]

    • Enthält die Anzahl der übergebenen Kommandozeilenargumente.

    • Der Name des Programms selbst zählt immer als erstes Argument, daher ist argc mindestens 1.

  • char *argv[] (argument vector): [1911]

    • Ist ein Array von Zeigern auf Zeichenketten (Strings).

    • Jedes Element argv[i] ist ein String, der ein Kommandozeilenargument darstellt.

    • argv[0] ist immer der Name des aufgerufenen Programms. [1912]

    • argv[1] ist das erste tatsächliche Argument, argv[2] das zweite, usw.

    • argv[argc] ist ein NULL-Zeiger (markiert das Ende des Arrays).

    #include <stdio.h>

    #include <stdlib.h> // Für atoi() [1918]

    int main(int argc, char *argv[]) {

    printf("Programmname: %s\n", argv[0]); [1924]

    printf("Anzahl der Argumente (argc): %d\n", argc);

    if (argc > 1) {

    printf("Uebergebene Argumente:\n");

    for (int i = 1; i < argc; i++) {

    printf(" argv[%d]: %s\n", i, argv[i]);

    }

    } else {

    printf("Keine zusaetzlichen Argumente uebergeben.\n");

    }

    // Beispiel: Erstes Argument als Zahl interpretieren

    if (argc > 1) {

    int zahl = atoi(argv[1]); // atoi wandelt String in Integer um [1920, 2187]

    // Beachte: Keine Fehlerbehandlung für ungültige Eingabe hier!

    printf("Das erste Argument als Zahl (falls vorhanden): %d\n", zahl);

    }

    return 0;

    }

Aufruf im Terminal: ./param_test Olchis sind gruen 123

Ausgabe wäre dann (ungefähr):

Programmname: ./param_test Anzahl der Argumente (argc): 5 Uebergebene Argumente: argv[1]: Olchis argv[2]: sind argv[3]: gruen argv[4]: 123 Das erste Argument als Zahl (falls vorhanden): 0



Author

Bernd B.

Information

Last changed