🏠 Startseite

🔒 Microsoft Visual Studio: Sichere Funktionen (_s)

CRT Security Enhancements - Umfassender Leitfaden
HTW Berlin • WiSe 2025/26

Inhaltsverzeichnis

1. Einführung

Grundlagen

Was sind CRT Security Enhancements?

Die CRT Security Enhancements (C Runtime Library Security Enhancements) sind eine Sammlung von sichereren Funktionsvarianten, die Microsoft in Visual Studio eingeführt hat. Diese Funktionen enden auf _s und sollen Buffer Overflow-Attacken verhindern.

Hauptmerkmale:

  • Zusätzliche Buffer-Größen-Parameter
  • Automatische Überprüfung der Puffergrenzen
  • Definiertes Verhalten bei Fehlern
  • Warnung bei Verwendung unsicherer Funktionen
Grundlagen

Warum hat Microsoft _s Funktionen eingeführt?

Traditionelle C-Funktionen wie strcpy, scanf und sprintf sind anfällig für Buffer Overflow-Fehler, die zu schwerwiegenden Sicherheitslücken führen können.

Probleme mit klassischen Funktionen:

  • Keine automatische Prüfung der Puffergröße
  • Risiko von Speicherüberschreibungen
  • Möglichkeit von Security-Exploits
  • Undefiniertes Verhalten bei zu kleinen Puffern

⚠️ Wichtig zu verstehen

Die _s Funktionen sind NICHT Teil des C-Standards! Sie sind eine Microsoft-spezifische Erweiterung und funktionieren nur in Visual Studio unter Windows.

Grundlagen

Wann müssen Sie _s Funktionen verwenden?

Nur bei Verwendung von Microsoft Visual Studio unter Windows!

Wenn Sie mit anderen Compilern arbeiten (GCC, Clang), sind die _s Funktionen nicht verfügbar und führen zu Compiler-Fehlern.

ℹ️ Compiler-Erkennung

Visual Studio definiert automatisch das Makro _MSC_VER. Dies kann verwendet werden, um plattformspezifischen Code zu schreiben.

Grundlagen

Warnungen deaktivieren (nicht empfohlen)

Visual Studio zeigt standardmäßig Warnungen an, wenn Sie unsichere Funktionen verwenden. Diese Warnungen können deaktiviert werden, aber das wird nicht empfohlen!

// Am Anfang der Datei (VOR allen #include Anweisungen):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

// Oder in den Projekteigenschaften:
// Configuration Properties → C/C++ → Preprocessor
// → Preprocessor Definitions: _CRT_SECURE_NO_WARNINGS

⚠️ Sicherheitshinweis

Das Deaktivieren dieser Warnungen macht Ihr Programm anfälliger für Sicherheitslücken. Verwenden Sie stattdessen die sicheren _s Funktionen oder schreiben Sie plattformunabhängigen Code!

2. scanf vs scanf_s

Eingabefunktionen

Unterschiede zwischen scanf und scanf_s

Der Hauptunterschied liegt in der Buffer-Größenangabe bei Strings. Bei scanf_s muss für jeden String-Parameter die maximale Größe angegeben werden.

❌ scanf (Standard C)

#include <stdio.h>

int main() {
    char name[50];
    int alter;

    // Gefährlich: Keine Größenprüfung!
    printf("Name: ");
    scanf("%s", name);

    printf("Alter: ");
    scanf("%d", &alter);

    printf("Hallo %s, %d Jahre\n",
           name, alter);

    return 0;
}

✅ scanf_s (Visual Studio)

#include <stdio.h>

int main() {
    char name[50];
    int alter;

    // Sicher: Größe wird geprüft!
    printf("Name: ");
    scanf_s("%s", name, 50);

    printf("Alter: ");
    scanf_s("%d", &alter);

    printf("Hallo %s, %d Jahre\n",
           name, alter);

    return 0;
}

💡 Wichtige Syntaxregeln für scanf_s

  • Für Strings (%s, %c): Zusätzlicher Parameter mit Puffergröße erforderlich
  • Für Zahlen (%d, %f, %lf): Keine Änderung, genau wie scanf
  • Bei %c: Größe muss angegeben werden (meist 1)
  • Puffergröße: Anzahl der Zeichen inklusive Nullterminator
Eingabefunktionen

Detaillierte Beispiele

Beispiel 1: Ganzzahl einlesen (keine Änderung)

scanf

int zahl;
scanf("%d", &zahl);

scanf_s

int zahl;
scanf_s("%d", &zahl);

Beispiel 2: String einlesen (WICHTIG!)

scanf

char name[100];
scanf("%s", name);

scanf_s

char name[100];
scanf_s("%s", name, 100);

Beispiel 3: Einzelnes Zeichen einlesen

scanf

char zeichen;
scanf(" %c", &zeichen);

scanf_s

char zeichen;
scanf_s(" %c", &zeichen, 1);

Beispiel 4: Mehrere Werte einlesen

scanf

char vorname[50], nachname[50];
int alter;
scanf("%s %s %d",
      vorname, nachname, &alter);

scanf_s

char vorname[50], nachname[50];
int alter;
scanf_s("%s %s %d",
        vorname, 50, nachname, 50,
        &alter);

⚠️ Häufige Fehler bei scanf_s

  • Fehler 1: Puffergröße vergessen: scanf_s("%s", name) → Compiler-Fehler!
  • Fehler 2: Falsche Größe: scanf_s("%s", name, sizeof(name*)) → Gibt Pointergröße, nicht Array-Größe!
  • Fehler 3: Größe bei Zahlen angeben: scanf_s("%d", &zahl, 4) → Unnötig und falsch!
  • Fehler 4: Whitespace vergessen bei %c: scanf_s("%c", &c, 1) → Liest Newline!

3. String-Funktionen

String-Operationen

strcpy vs strcpy_s

Die strcpy Funktion kopiert einen String, ohne die Zielgröße zu prüfen. strcpy_s verhindert Buffer Overflows durch Größenprüfung.

❌ strcpy (unsicher)

#include <string.h>

char ziel[10];
char quelle[] = "Sehr langer Text";

// GEFAHR: quelle ist länger als ziel!
strcpy(ziel, quelle);
// → Buffer Overflow!

✅ strcpy_s (sicher)

#include <string.h>

char ziel[10];
char quelle[] = "Sehr langer Text";

// Sicher: Prüft die Größe!
strcpy_s(ziel, 10, quelle);
// → Fehler, kein Buffer Overflow!

ℹ️ Syntax von strcpy_s

errno_t strcpy_s(char *ziel, size_t groesse, const char *quelle);

  • ziel: Ziel-Buffer
  • groesse: Maximale Größe des Ziel-Buffers (in Bytes)
  • quelle: Quell-String
  • Rückgabewert: 0 bei Erfolg, Fehlercode bei Fehler
String-Operationen

strcat vs strcat_s

Die strcat Funktion hängt einen String an einen anderen an. strcat_s prüft, ob genug Platz vorhanden ist.

❌ strcat (unsicher)

char text[20] = "Hallo";
char zusatz[] = " Welt!";

// Gefahr bei zu kleinem Buffer!
strcat(text, zusatz);
printf("%s\n", text);
// "Hallo Welt!"

✅ strcat_s (sicher)

char text[20] = "Hallo";
char zusatz[] = " Welt!";

// Sicher: Prüft verfügbaren Platz!
strcat_s(text, 20, zusatz);
printf("%s\n", text);
// "Hallo Welt!"

💡 Praktisches Beispiel: Name zusammensetzen

strcpy + strcat

char vollername[100];
char vorname[] = "Anna";
char nachname[] = "Schmidt";

strcpy(vollername, vorname);
strcat(vollername, " ");
strcat(vollername, nachname);
// "Anna Schmidt"

strcpy_s + strcat_s

char vollername[100];
char vorname[] = "Anna";
char nachname[] = "Schmidt";

strcpy_s(vollername, 100, vorname);
strcat_s(vollername, 100, " ");
strcat_s(vollername, 100, nachname);
// "Anna Schmidt"
String-Operationen

gets vs gets_s (deprecated)

Die gets Funktion ist extrem unsicher und wurde aus dem C11-Standard entfernt! gets_s ist sicherer, aber fgets ist die beste Wahl.

❌ gets (NIEMALS VERWENDEN!)

char text[50];

// EXTREM GEFÄHRLICH!
// Keine Größenprüfung möglich!
gets(text);

// Diese Funktion sollte
// NIEMALS verwendet werden!

✅ gets_s (Visual Studio)

char text[50];

// Besser, aber immer noch
// nicht standardkonform
gets_s(text, 50);

// Funktioniert nur in VS!
String-Operationen

fgets - Die beste Alternative (Standard C)

fgets ist die empfohlene Methode für sichere String-Eingabe und funktioniert auf allen Plattformen!

✅ Beste Praxis: Verwenden Sie fgets

#include <stdio.h>

char name[50];
printf("Geben Sie Ihren Namen ein: ");
fgets(name, 50, stdin);

// Optional: Newline entfernen
name[strcspn(name, "\n")] = '\0';

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

Vorteile von fgets:

  • Teil des C-Standards (funktioniert überall)
  • Sichere Größenprüfung
  • Liest auch Leerzeichen
  • Keine VS-spezifischen Abhängigkeiten

4. sprintf vs sprintf_s

Formatierte Ausgabe

sprintf vs sprintf_s

Die sprintf Funktion formatiert Daten in einen String. sprintf_s verhindert Buffer Overflows durch Größenprüfung.

❌ sprintf (unsicher)

char buffer[50];
int zahl = 42;
char text[] = "Antwort";

// Gefahr: Keine Größenprüfung!
sprintf(buffer,
    "Die %s ist %d",
    text, zahl);

printf("%s\n", buffer);
// "Die Antwort ist 42"

✅ sprintf_s (sicher)

char buffer[50];
int zahl = 42;
char text[] = "Antwort";

// Sicher: Größe wird geprüft!
sprintf_s(buffer, 50,
    "Die %s ist %d",
    text, zahl);

printf("%s\n", buffer);
// "Die Antwort ist 42"

ℹ️ Syntax von sprintf_s

int sprintf_s(char *buffer, size_t groesse, const char *format, ...);

  • buffer: Ziel-Buffer für den formatierten String
  • groesse: Maximale Größe des Buffers (in Bytes)
  • format: Format-String (wie bei printf)
  • ...: Variable Argumente
  • Rückgabewert: Anzahl der geschriebenen Zeichen (ohne '\0'), oder -1 bei Fehler
Formatierte Ausgabe

Unterschiede im Rückgabewert

Ein wichtiger Unterschied zwischen sprintf und sprintf_s liegt im Fehlerverhalten:

sprintf Rückgabewert

char buffer[10];
int result = sprintf(buffer,
    "Sehr langer Text");

// result = Anzahl geschriebener
// Zeichen (kann > 10 sein!)
// → Buffer Overflow möglich!

sprintf_s Rückgabewert

char buffer[10];
int result = sprintf_s(buffer, 10,
    "Sehr langer Text");

// result = -1 (Fehler)
// buffer bleibt unverändert
// → Kein Buffer Overflow!

💡 Praktische Beispiele

Beispiel 1: Dateiname generieren

sprintf

char filename[100];
int nummer = 42;

sprintf(filename,
    "datei_%03d.txt",
    nummer);
// "datei_042.txt"

sprintf_s

char filename[100];
int nummer = 42;

sprintf_s(filename, 100,
    "datei_%03d.txt",
    nummer);
// "datei_042.txt"

Beispiel 2: Benutzerinfo formatieren

sprintf

char info[200];
char name[] = "Anna";
int alter = 25;
double gehalt = 3500.50;

sprintf(info,
    "Name: %s\nAlter: %d\n"
    "Gehalt: %.2f EUR",
    name, alter, gehalt);

sprintf_s

char info[200];
char name[] = "Anna";
int alter = 25;
double gehalt = 3500.50;

sprintf_s(info, 200,
    "Name: %s\nAlter: %d\n"
    "Gehalt: %.2f EUR",
    name, alter, gehalt);

✅ Alternative: snprintf (Standard C99)

Für plattformunabhängigen Code verwenden Sie snprintf, das seit C99 Standard ist:

char buffer[50];
snprintf(buffer, 50, "Die Antwort ist %d", 42);

// Funktioniert auf allen Plattformen (C99+)!

5. sscanf vs sscanf_s

String-Parsing

sscanf vs sscanf_s

Die sscanf Funktion liest formatierte Daten aus einem String. sscanf_s benötigt wie scanf_s zusätzliche Größenangaben für Strings.

❌ sscanf (Standard C)

char eingabe[] = "Anna 25 Berlin";
char name[50];
int alter;
char stadt[50];

// Keine Größenprüfung!
sscanf(eingabe,
    "%s %d %s",
    name, &alter, stadt);

printf("%s, %d, %s\n",
    name, alter, stadt);

✅ sscanf_s (Visual Studio)

char eingabe[] = "Anna 25 Berlin";
char name[50];
int alter;
char stadt[50];

// Mit Größenprüfung!
sscanf_s(eingabe,
    "%s %d %s",
    name, 50, &alter, stadt, 50);

printf("%s, %d, %s\n",
    name, alter, stadt);

ℹ️ Syntax von sscanf_s

int sscanf_s(const char *string, const char *format, ...);

  • Für Strings (%s, %c): Zusätzlicher Größenparameter erforderlich
  • Für Zahlen (%d, %f, etc.): Keine Änderung gegenüber sscanf
  • Rückgabewert: Anzahl erfolgreich gelesener Elemente
String-Parsing

Praktische Beispiele

Beispiel 1: CSV-Zeile parsen

sscanf

char zeile[] = "12345,Laptop,899.99";
int id;
char produkt[100];
double preis;

sscanf(zeile, "%d,%[^,],%lf",
    &id, produkt, &preis);

printf("ID: %d\n", id);
printf("Produkt: %s\n", produkt);
printf("Preis: %.2f\n", preis);

sscanf_s

char zeile[] = "12345,Laptop,899.99";
int id;
char produkt[100];
double preis;

sscanf_s(zeile, "%d,%[^,],%lf",
    &id, produkt, 100, &preis);

printf("ID: %d\n", id);
printf("Produkt: %s\n", produkt);
printf("Preis: %.2f\n", preis);

Beispiel 2: Datum parsen

sscanf

char datum[] = "2025-11-27";
int jahr, monat, tag;

sscanf(datum, "%d-%d-%d",
    &jahr, &monat, &tag);

printf("%02d.%02d.%d\n",
    tag, monat, jahr);

sscanf_s

char datum[] = "2025-11-27";
int jahr, monat, tag;

sscanf_s(datum, "%d-%d-%d",
    &jahr, &monat, &tag);

printf("%02d.%02d.%d\n",
    tag, monat, jahr);

Beispiel 3: IP-Adresse parsen

sscanf

char ip[] = "192.168.1.1";
int a, b, c, d;

sscanf(ip, "%d.%d.%d.%d",
    &a, &b, &c, &d);

printf("IP: %d.%d.%d.%d\n",
    a, b, c, d);

sscanf_s

char ip[] = "192.168.1.1";
int a, b, c, d;

sscanf_s(ip, "%d.%d.%d.%d",
    &a, &b, &c, &d);

printf("IP: %d.%d.%d.%d\n",
    a, b, c, d);

💡 Wichtiger Hinweis zu %[^]

Das Format %[^,] liest alle Zeichen bis zum Komma. Bei sscanf_s muss auch hier die Größe angegeben werden:

// Liest bis zum Komma
sscanf_s(text, "%[^,]", buffer, 100); // Größe 100 erforderlich!

6. File I/O Funktionen

Datei-Operationen

fopen vs fopen_s

Der größte Unterschied zwischen fopen und fopen_s liegt in der Fehlerbehandlung und dem Rückgabewert.

fopen (Standard C)

#include <stdio.h>

FILE *datei;

// Gibt FILE* zurück
datei = fopen("test.txt", "r");

if (datei == NULL) {
    printf("Fehler beim Öffnen!\n");
    return 1;
}

// Mit Datei arbeiten...
fclose(datei);

fopen_s (Visual Studio)

#include <stdio.h>

FILE *datei;
errno_t err;

// Gibt Fehlercode zurück!
err = fopen_s(&datei, "test.txt", "r");

if (err != 0) {
    printf("Fehler beim Öffnen!\n");
    return 1;
}

// Mit Datei arbeiten...
fclose(datei);

ℹ️ Wichtige Unterschiede bei fopen_s

  • Rückgabewert: fopen gibt FILE* zurück, fopen_s gibt errno_t (Fehlercode) zurück
  • FILE-Pointer: Bei fopen_s wird der FILE-Pointer als Adresse übergeben (&datei)
  • Fehlerprüfung: fopen prüft auf NULL, fopen_s prüft auf != 0
  • Sicherheit: fopen_s setzt den Pointer auf NULL bei Fehler
Datei-Operationen

Syntax von fopen_s

errno_t fopen_s(FILE **dateiptr, const char *dateiname, const char *modus);

  • dateiptr: Zeiger auf FILE-Pointer (Adresse der FILE*-Variablen)
  • dateiname: Pfad zur Datei
  • modus: Öffnungsmodus ("r", "w", "a", etc.)
  • Rückgabewert: 0 bei Erfolg, Fehlercode bei Fehler

💡 Vollständiges Beispiel: Datei schreiben

fopen + fprintf

#include <stdio.h>

int main() {
    FILE *datei = fopen("output.txt", "w");

    if (datei == NULL) {
        printf("Fehler!\n");
        return 1;
    }

    fprintf(datei, "Hallo Welt!\n");
    fprintf(datei, "Zahl: %d\n", 42);

    fclose(datei);
    return 0;
}

fopen_s + fprintf_s

#include <stdio.h>

int main() {
    FILE *datei;
    errno_t err = fopen_s(&datei, "output.txt", "w");

    if (err != 0) {
        printf("Fehler!\n");
        return 1;
    }

    fprintf_s(datei, "Hallo Welt!\n");
    fprintf_s(datei, "Zahl: %d\n", 42);

    fclose(datei);
    return 0;
}
Datei-Operationen

Datei lesen: fgets ist bereits sicher!

Wichtig: Für das Lesen von Dateien ist fgets bereits sicher und benötigt keine _s Version!

#include <stdio.h>

FILE *datei;
char zeile[256];

#ifdef _MSC_VER
    fopen_s(&datei, "input.txt", "r");
#else
    datei = fopen("input.txt", "r");
#endif

if (datei != NULL) {
    // fgets ist auf allen Plattformen sicher!
    while (fgets(zeile, 256, datei) != NULL) {
        printf("%s", zeile);
    }
    fclose(datei);
}

7. Andere Common _s Funktionen

Weitere Funktionen

strtok vs strtok_s

Die strtok Funktion zerlegt einen String in Tokens. strtok_s ist threadsicher und verhindert Race Conditions.

strtok (nicht threadsicher)

#include <string.h>

char text[] = "Hallo,Welt,Test";
char *token;

// Erster Aufruf
token = strtok(text, ",");
while (token != NULL) {
    printf("%s\n", token);
    token = strtok(NULL, ",");
}

// Ausgabe:
// Hallo
// Welt
// Test

strtok_s (threadsicher)

#include <string.h>

char text[] = "Hallo,Welt,Test";
char *token;
char *context = NULL;

// Context-Pointer erforderlich!
token = strtok_s(text, ",", &context);
while (token != NULL) {
    printf("%s\n", token);
    token = strtok_s(NULL, ",", &context);
}

// Ausgabe:
// Hallo
// Welt
// Test

ℹ️ Syntax von strtok_s

char* strtok_s(char *string, const char *delimiters, char **context);

  • string: Zu zerlegender String (beim ersten Aufruf), NULL für weitere Tokens
  • delimiters: Trennzeichen
  • context: Zeiger auf Context-Pointer (für Threadsicherheit)
  • Standard-Alternative: strtok_r (POSIX-Standard, auch threadsicher)
Weitere Funktionen

getenv vs getenv_s

Die getenv Funktion liest Umgebungsvariablen. getenv_s bietet sichereren Zugriff mit Größenprüfung.

getenv (Standard C)

#include <stdlib.h>
#include <stdio.h>

char *wert;

wert = getenv("PATH");
if (wert != NULL) {
    printf("PATH: %s\n", wert);
} else {
    printf("PATH nicht gefunden\n");
}

getenv_s (Visual Studio)

#include <stdlib.h>
#include <stdio.h>

char wert[1024];
size_t laenge;

errno_t err = getenv_s(&laenge,
    wert, 1024, "PATH");

if (err == 0 && laenge > 0) {
    printf("PATH: %s\n", wert);
} else {
    printf("PATH nicht gefunden\n");
}
Weitere Funktionen

Weitere wichtige _s Funktionen

Hier ist eine Übersicht weiterer häufig verwendeter sicherer Funktionen:

Standard-Funktion Sichere Alternative (_s) Beschreibung
strncpy strncpy_s String mit maximaler Länge kopieren
strncat strncat_s String mit maximaler Länge anhängen
memcpy memcpy_s Speicherbereich kopieren
memmove memmove_s Überlappende Speicherbereiche kopieren
asctime asctime_s Zeit in String konvertieren
ctime ctime_s Zeitstempel in String konvertieren

💡 Beispiel: memcpy_s

memcpy

int quelle[5] = {1, 2, 3, 4, 5};
int ziel[5];

// Keine Größenprüfung!
memcpy(ziel, quelle,
    5 * sizeof(int));

memcpy_s

int quelle[5] = {1, 2, 3, 4, 5};
int ziel[5];

// Mit Größenprüfung!
memcpy_s(ziel, sizeof(ziel),
    quelle, 5 * sizeof(int));

8. Praktische Tipps

Best Practices

Plattformunabhängigen Code schreiben

Der beste Ansatz ist, Code zu schreiben, der sowohl auf Visual Studio als auch auf GCC/Clang funktioniert. Dies erreichen Sie mit Präprozessor-Direktiven.

💡 Methode 1: #ifdef _MSC_VER

Visual Studio definiert automatisch das Makro _MSC_VER. Nutzen Sie dies für bedingte Kompilierung:

#include <stdio.h>

int main() {
    char name[50];
    int alter;

    printf("Name: ");
    #ifdef _MSC_VER
        // Visual Studio (Windows)
        scanf_s("%s", name, 50);
    #else
        // GCC/Clang (Linux/macOS)
        scanf("%49s", name); // Breite begrenzen!
    #endif

    printf("Alter: ");
    #ifdef _MSC_VER
        scanf_s("%d", &alter);
    #else
        scanf("%d", &alter);
    #endif

    printf("Hallo %s, %d Jahre\n", name, alter);
    return 0;
}

✅ Methode 2: Makros definieren

Erstellen Sie eigene Makros für plattformunabhängigen Code:

#include <stdio.h>

// Makro für sichere scanf-Funktion
#ifdef _MSC_VER
    #define SCANF_STRING(buffer, size) scanf_s("%s", buffer, size)
    #define SCANF_INT(var) scanf_s("%d", var)
#else
    #define SCANF_STRING(buffer, size) scanf("%" #size "s", buffer)
    #define SCANF_INT(var) scanf("%d", var)
#endif

int main() {
    char name[50];
    int alter;

    printf("Name: ");
    SCANF_STRING(name, 50);

    printf("Alter: ");
    SCANF_INT(&alter);

    printf("Hallo %s, %d Jahre\n", name, alter);
    return 0;
}
Best Practices

Vollständiges Beispiel: Datei-Operationen

Ein komplettes Beispiel für plattformunabhängige Datei-Operationen:

#include <stdio.h>
#include <string.h>

int main() {
    FILE *datei;
    char zeile[256];

    // Datei öffnen - plattformunabhängig
    #ifdef _MSC_VER
        errno_t err = fopen_s(&datei, "daten.txt", "w");
        if (err != 0) {
            printf("Fehler beim Öffnen!\n");
            return 1;
        }
    #else
        datei = fopen("daten.txt", "w");
        if (datei == NULL) {
            printf("Fehler beim Öffnen!\n");
            return 1;
        }
    #endif

    // Daten schreiben
    fprintf(datei, "Zeile 1\n");
    fprintf(datei, "Zeile 2\n");
    fclose(datei);

    // Datei lesen
    #ifdef _MSC_VER
        fopen_s(&datei, "daten.txt", "r");
    #else
        datei = fopen("daten.txt", "r");
    #endif

    if (datei != NULL) {
        // fgets ist auf allen Plattformen sicher!
        while (fgets(zeile, 256, datei) != NULL) {
            printf("%s", zeile);
        }
        fclose(datei);
    }

    return 0;
}
Best Practices

Empfehlungen für Studenten mit Visual Studio

Wenn Sie Visual Studio verwenden, beachten Sie folgende Tipps:

📋 Checkliste für Visual Studio-Nutzer

  • Lernphase: Verwenden Sie die _s Funktionen, um Buffer Overflow-Schutz zu verstehen
  • Praxis: Schreiben Sie plattformunabhängigen Code mit #ifdef _MSC_VER
  • Alternative: Nutzen Sie sichere Standard-Funktionen wie fgets und snprintf (C99)
  • Nicht empfohlen: Warnungen mit _CRT_SECURE_NO_WARNINGS deaktivieren
  • Beim Einreichen: Prüfen Sie, ob Ihr Code auch auf anderen Systemen kompiliert

⚠️ Häufige Probleme vermeiden

  • Problem: Code funktioniert nur in Visual Studio
    Lösung: Verwenden Sie #ifdef _MSC_VER oder Standard-Funktionen
  • Problem: Compiler-Fehler bei _s Funktionen auf Linux
    Lösung: Diese Funktionen existieren nur in VS!
  • Problem: Zu viele Compiler-Warnungen
    Lösung: Lernen Sie die _s Funktionen korrekt zu verwenden
  • Problem: Buffer Overflow trotz _s Funktionen
    Lösung: Richtige Puffergröße verwenden! (sizeof vs. strlen)

✅ Best Practice: Verwenden Sie Standard C99+

Die beste Lösung ist oft, moderne Standard-C-Funktionen zu verwenden, die auf allen Plattformen funktionieren:

  • Statt scanf_s: Verwenden Sie fgets + sscanf
  • Statt sprintf_s: Verwenden Sie snprintf (C99)
  • Statt strcpy_s: Verwenden Sie strncpy mit korrekter Nullterminierung
  • Statt gets/gets_s: Verwenden Sie immer fgets

9. Zusammenfassung

Referenz

Quick Reference: Standard vs _s Funktionen

Kategorie Standard C Visual Studio (_s) Plattformunabhängige Alternative
Eingabe scanf("%s", str) scanf_s("%s", str, size) fgets(str, size, stdin)
String kopieren strcpy(dst, src) strcpy_s(dst, size, src) strncpy(dst, src, size-1)
+ Nullterminierung
String anhängen strcat(dst, src) strcat_s(dst, size, src) strncat(dst, src, size-strlen(dst)-1)
Formatierte Ausgabe sprintf(buf, fmt, ...) sprintf_s(buf, size, fmt, ...) snprintf(buf, size, fmt, ...)
String parsen sscanf(str, fmt, ...) sscanf_s(str, fmt, ...) sscanf(str, fmt, ...)
mit Feldbreite: %49s
Datei öffnen f = fopen(name, mode) fopen_s(&f, name, mode) fopen mit NULL-Prüfung
String tokenisieren strtok(str, delim) strtok_s(str, delim, &ctx) strtok_r(str, delim, &ctx)
(POSIX)
Zeilenweise lesen fgets(buf, size, file) fgets(buf, size, file) fgets(buf, size, file)
(bereits sicher!)
Best Practices

Best Practices - Zusammenfassung

🎯 Die wichtigsten Regeln

  1. Verstehen Sie den Unterschied: _s Funktionen sind Microsoft-spezifisch, nicht Standard-C
  2. Puffergröße ist Pflicht: Bei Strings IMMER die Größe angeben
  3. Zahlen bleiben gleich: Bei int, double, etc. ändert sich nichts
  4. Fehlerbehandlung beachten: fopen_s gibt Fehlercode zurück, nicht FILE*
  5. Plattformunabhängig denken: Verwenden Sie #ifdef _MSC_VER
  6. Standard bevorzugen: Wenn möglich, nutzen Sie C99/C11-Standards (snprintf, fgets)
  7. Sicherheit ernst nehmen: Deaktivieren Sie Warnungen nicht einfach!
Referenz

Wann welche Funktion verwenden?

Situation Empfehlung
Nur Visual Studio verwenden Verwenden Sie _s Funktionen für maximale Sicherheit
Code soll auf mehreren Plattformen laufen Verwenden Sie #ifdef _MSC_VER oder Standard-Funktionen
String-Eingabe von Benutzer Beste Wahl: fgets (funktioniert überall)
Formatierte String-Ausgabe Beste Wahl: snprintf (C99, funktioniert überall)
Strings kopieren/anhängen Verwenden Sie _s Funktionen in VS oder strncpy/strncat
Datei-Operationen fopen_s in VS, sonst fopen mit NULL-Prüfung
Zeilenweise aus Datei lesen fgets - funktioniert überall und ist bereits sicher!

ℹ️ Wichtige Ressourcen

  • Microsoft Dokumentation: Security Features in the CRT
  • C11 Standard: Annex K - Bounds-checking interfaces (optional)
  • Alternative: Verwenden Sie moderne C++-Bibliotheken wie <string>
  • Static Analysis: Tools wie Visual Studio Code Analysis helfen, unsichere Funktionen zu finden

✅ Checkliste für Ihren Code

  • ☐ Keine Compiler-Warnungen (ohne _CRT_SECURE_NO_WARNINGS)
  • ☐ Code kompiliert auf Visual Studio
  • ☐ Code kompiliert auf GCC/Clang (wenn erforderlich)
  • ☐ Alle Buffer-Größen korrekt angegeben
  • ☐ Fehlerbehandlung implementiert
  • ☐ Keine veralteten Funktionen wie gets
  • ☐ Speicherlecks vermieden
  • ☐ Dokumentation/Kommentare für plattformspezifischen Code

⚠️ Abschließende Warnung

Die _s Funktionen sind eine Microsoft-spezifische Erweiterung und nicht Teil des C-Standards!
Code, der diese Funktionen verwendet, funktioniert nur in Visual Studio.

Für plattformunabhängigen Code verwenden Sie:
#ifdef _MSC_VER oder moderne Standard-C-Funktionen wie fgets und snprintf.

🔒 Sicheres Programmieren ist wichtig!

HTW Berlin • Grundlagen der Programmierung • WiSe 2025/26

Verwenden Sie sichere Funktionen, um Buffer Overflow-Attacken zu verhindern.