Ich lerne C auf dem Terminal unter Linux und habe ein kleines Spiel im Text gemacht, damit alles funktioniert (insbesondere meine große Schleife, wenn der Spieler das Spiel neu starten möchte), und ich füge einfach einen Speicherauszug hinzu: "Geben Sie Ihr Pseudo ein " am Anfang.

Es funktioniert nicht, wenn ich es betrete, sagt es mir

Segmentation Error , core dumped

Hier ist der gesamte Code, der wichtigste sind die ersten Zeilen, da der Rest einwandfrei funktioniert:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
int main (void) {
  // déclaration des variables
  bool essais8  = false ;
  bool essais10 = false ;
  bool essais12 = false;
  bool essais14 = false;
  bool condition = true;
  bool condition_diff = false;
  bool condition_recommencer = false;
  bool condition_bonus1 = true;
  bool condition_bonus2 = true;
  bool condition_bonus3 = true;
  char* buffer;
  char* recommencer;
  char* pseudo ;
  size_t bufsize = 32;
  size_t characters;
  int difficulte;
  int sheitan = 0;
  int sheitan2 = 0;
  int a;
  int bonus1;
  int bonus2;
  int bonus3;
  int ecart;
  int essais;
  int nb_bonus2;
  srand(time(NULL));




  // on récupère le pseudo du joueur
   printf("Salut ! Quel est ton nom ? ");
  characters = getline(&buffer,&bufsize,stdin);
  pseudo = buffer;
// after this I have the segmentation errror




  while (condition == true) {   // Boucle de recommencement du Jeu
// here is the big game that was functioning perfectly before I add the pseudo option on top.
  }
  printf("Fin du programme \n");
  return(0);
}
-1
Chetrit 19 Jän. 2019 im 14:07

4 Antworten

Beste Antwort

Sie haben vergessen, buffer zu initialisieren. Und Sie sollten Ihr printf (3) -Format besser beenden Steuerzeichenfolge mit \n oder rufen Sie fflush (3) vor jeder Eingabe (seit stdio (3) ist gepuffert).

Ein nicht initialisierter Zeiger enthält Müll. Die Verwendung ist undefiniertes Verhalten. Seien Sie ängstlich!

Mein Vorschlag ist also, zu codieren

size_t bufsiz = 64;
char *buffer = malloc(bufsiz);
if (!buffer) { perror("malloc buffer"); exit(EXIT_FAILURE); };

Und später

printf("Salut ! Quel est ton nom ?\n");
characters = getline(&buffer,&bufsize,stdin);

Aktivieren Sie beim nächsten Kompilieren alle Warnungen und Debug-Informationen beim Kompilieren. Kompilieren Sie daher Ihren Code mit gcc -Wall -Wextra -g bei Verwendung von GCC. Sie hätten einige Warnungen erhalten.

Lesen Sie natürlich So debuggen Sie kleine Programme und Debugging mit GDB

Unter Linux könnten Sie in Ihrem speziellen Fall daran interessiert sein, readline ( 3) anstelle von getline (3).

Vergessen Sie nicht, die Dokumentation aller von Ihnen verwendeten Funktionen zu lesen (z. B. hier, wenn es ist ein Standard C eins).

2
Basile Starynkevitch 19 Jän. 2019 im 11:32

getline() kann in zwei verschiedene Möglichkeiten:

  1. In einen vom Anrufer bereitgestellten Puffer einlesen.
  2. Ordnen Sie beim Lesen Speicher zu.

So verwenden Sie 1. Initialisieren Sie den übergebenen Zeiger, um auf einen gültigen Speicher zu zeigen, und übergeben Sie auch dessen Größe.

#define INITIAL_SIZE (42)

int main(void)
{  
  char * buffer = malloc(INITIAL_SIZE * sizeof *buffer);
  size_t bufsize = INITIAL_SIZE;
  ssize_t characters = getline(&buffer, &bufsize, stdin);
  if (-1 == characters)
  {
     /* handle error. */
  }
  else
  {
     /* use buffer */
  }

  free(buffer); /* Free buffer. */
}

Um 2. zu verwenden, initialisieren Sie den an NULL übergebenen Zeiger und übergeben Sie 0 als Größe.

int main(void)
{
  char * buffer = NULL;
  size_t bufsize = 0;
  ssize_t characters = getline(&buffer, &bufsize, stdin);
  if (-1 == characters)
  {
     /* handle error. */
  }
  else
  {
     /* use buffer */
  }

  free(buffer); /* Free buffer. */
}

Hinweis: getline() gibt ssize_t nicht size_t zurück.

2
alk 19 Jän. 2019 im 13:35

Die Variable 'buffer' zeigt auf eine Speicheradresse. Sie müssen zuerst den erforderlichen Speicher mit der Funktion 'malloc' zuweisen oder 'buffer' anstelle des Zeigers zu einem statischen Array machen.

1
Starl1ght 19 Jän. 2019 im 11:11

getline() ist ein POSIX.1-Funktion, die Zeilen in dynamisch zugewiesene Puffer liest und Zeilen beliebiger Länge zulässt ( nur durch die Menge des verfügbaren Speichers begrenzt). Es gibt die Anzahl der gelesenen Zeichen zurück oder -1, wenn keine Eingabe mehr erfolgt oder ein Fehler aufgetreten ist.

Hier ist ein Beispiel für ein Verwendungsmuster:

    char   *line = NULL;
    size_t  size = 0;
    ssize_t len;

    while (1) {

        len = getline(&line, &size, stdin);
        if (len < 1)
            break;

        /* Do something with the input line */

    }

Sie können den dynamisch zugewiesenen Puffer jederzeit mit freigeben

    free(line);
    line = NULL;
    size = 0;

Der Grund, warum Sie den Zeiger auf NULL und die Größe auf Null löschen möchten, ist, dass Sie auf diese Weise nicht versehentlich versuchen, auf den bereits freigegebenen Speicher zuzugreifen, sondern getline(&line, &size, handle) aufrufen können, um weitere Zeilen zu lesen, da der Aufruf dies einfach erkennt Es hat keinen Puffer und weist einen neuen zu.


Sie können die dynamischen Daten beliebig bearbeiten, wenn Sie vorsichtig sind. Beispielsweise:

while (1) {
    char   *line = NULL;
    size_t  size = 0;
    ssize_t len;

    len = getline(&line, &size, stdin);
    if (len < 1) {
        free(line);
        break;
    }

    /* Do something with the contents of the line */

    free(line);
}

Wird funktionieren, aber es wird ziemlich langsam sein, da die C-Bibliothek mindestens einen malloc() Aufruf für jede gelesene Leitung und möglicherweise zusätzliche realloc() Aufrufe abhängig von der Leitungslänge ausführt.

Der Grund, warum getline() so geschrieben wird, ist, dass der gleiche Puffer für eine beliebige Anzahl von Zeilen wiederverwendet werden kann. Wenn Sie Dateien nacheinander lesen, können Sie denselben Puffer wiederverwenden. Schauen wir uns ein komplexeres Beispiel an:

#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    unsigned long  linenum;
    char          *line = NULL, *in, *out, *end;
    size_t         size = 0, n;
    ssize_t        len;
    FILE          *src;
    int            arg;

    if (!setlocale(LC_ALL, ""))
        fprintf(stderr, "Warning: Your C library does not support your current locale.\n");

    if (argc < 2) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s FILENAME [ FILENAME ... ]\n", argv[0]);
        fprintf(stderr, "\n");
        exit(EXIT_FAILURE);
    }

    for (arg = 1; arg < argc; arg++) {

        src = fopen(argv[arg], "r");
        if (!src) {
            fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
            free(line);
            exit(EXIT_FAILURE);
        }

        linenum = 0;

        while (1) {

            len = getline(&line, &size, src);
            if (len < 1)
                break;

            linenum++;

            /* First character in the line read: */
            in = line;
            out = line;

            /* Pointer to the end-of-string character on the line: */
            end = line + len;

            /* Skip all leading whitespace characters. */
            while (in < end && isspace((unsigned char)(*in)))
                in++;

            /* Character copy loop. */
            while (in < end)
                if (isspace((unsigned char)(*in))) {
                    /* Replace consecutive whitespace characters with spaces. */
                    *(out++) = ' ';
                    do {
                        in++;
                    } while (in < end && isspace((unsigned char)(*in)));
                } else {
                    /* Copy all other characters as-is. */
                    *(out++) = *(in++);
                }

            /* There may be a single space before out. Backtrack it, if so. */
            if (out > line && out[-1] == ' ')
                out--;

            /* Mark the end of the string at out. */
            *out = '\0';

            /* Calculate the new length, just for output purposes. */
            n = (size_t)(out - line);

            /* Print the line. */
            printf("%s: Line %lu: '%s' (%zu of %zd characters)\n",
                   argv[arg], linenum, line, n, len);

        }
        if (!feof(src) || ferror(src)) {
            fprintf(stderr, "%s: Read error.\n", argv[arg]);
            fclose(src);
            free(line);
            exit(EXIT_FAILURE);
        }
        if (fclose(src)) {
            fprintf(stderr, "%s: Error closing file: %s.\n",
                            argv[arg], strerror(errno));
            free(line);
            exit(EXIT_FAILURE);
        }
    }

    free(line);
    line = NULL;
    size = 0;

    return EXIT_SUCCESS;
}

Wenn wir das Obige als example.c speichern und es mit z. gcc -Wall -O2 example.c -o example, und führen Sie das Programm aus, das die Namen von Textdateien als Parameter bereitstellt, z. B. ./example example.c. Es gibt so etwas wie aus

example.c: Line 1: '#define _POSIX_C_SOURCE 200809L' (31 of 33 characters)
example.c: Line 2: '#include <stdlib.h>' (19 of 20 characters)
example.c: Line 3: '#include <locale.h>' (19 of 20 characters)
example.c: Line 4: '#include <string.h>' (19 of 20 characters)
example.c: Line 5: '#include <stdio.h>' (18 of 19 characters)
example.c: Line 6: '#include <ctype.h>' (18 of 19 characters)
example.c: Line 7: '#include <errno.h>' (18 of 19 characters)
example.c: Line 8: '' (0 of 1 characters)
example.c: Line 9: 'int main(int argc, char *argv[])' (32 of 33 characters)

Das Programm liest einfach jede angegebene Datei zeilenweise, entfernt alle führenden und nachfolgenden Leerzeichen in jeder Zeile und kombiniert alle aufeinander folgenden Leerzeichen in einem einzigen Leerzeichen. Die kleinere Zeichenanzahl ist die Anzahl der verbleibenden (und angezeigten) Zeichen, die größere Anzahl ist die ursprüngliche Anzahl der aus der Datei gelesenen Zeichen.

Zusätzliche Hinweise zum Beispielprogramm, falls es Sie interessiert

  • Der Aufruf setlocale(LC_ALL, "") weist Ihre C-Bibliothek an, das Gebietsschema des Benutzers zu verwenden (normalerweise definiert in den Umgebungsvariablen LANG oder LC_ALL). Dieses Programm verwendet nur die Zeichentypdefinitionen für den Zeichensatz, der vom aktuellen Gebietsschema verwendet wird (um zu bestimmen, welche Codes "Leerzeichen" sind), daher kann dies auch über setlocale(LC_CTYPE, "") darauf beschränkt werden. Der Aufruf gibt NULL zurück, wenn das aktuelle Gebietsschema von der C-Bibliothek nicht unterstützt wird. Normalerweise liegt dies an einem Fehler in der Benutzerkonfiguration. Daher ist es hilfreich, das Programm dann warnen zu lassen.

  • Die isspace() (und alle anderen in <ctype.h> definierten is*() Funktionen) verwenden einen vorzeichenlosen Zeichencode (oder EOF). Da der Typ char signiert oder nicht signiert sein kann, setzen wir das Zeichen explizit in (unsigned char) um, bevor wir die Funktion bereitstellen. Betrachten Sie dieses alberne historische Gepäck, mit dem wir nur so umgehen müssen.

  • Da line auf den Anfang des dynamisch zugewiesenen Speicherpuffers zeigt, dürfen wir ihn nicht ändern (außer über realloc() oder free() und dann auf NULL setzen). Wenn wir es ändern, wird jeder nachfolgende Aufruf von getline() oder free(), der diesen Zeiger verwendet, wahrscheinlich ausflippen und das Programm zum Absturz bringen, da der Zeiger wirklich auf den Anfang des Puffers zeigen muss, nicht nur irgendwo drin.

  • Ich verwende gerne Zeiger (char *in, *out, *end) anstelle von Indizes. Hier beginnt in bei line und geht bis zu line+len, wobei getline() das Ende des Strings nul \0 anfügt Geben Sie das Ende der Zeile an. Deshalb benutze ich auch oft einen Zeiger namens end, um darauf hinzuweisen. Das out beginnt ebenfalls bei line, erhöht sich jedoch nur, wenn die Zeichen in einer Zeile gehalten werden.

    Wenn Sie an eine Reihe von mit Buchstaben versehenen Kacheln denken, wie in Scrabble, zeigt out auf die nächste Position, und in zeigt auf die nächste Kachel, die Sie erhalten.

  • Wenn getline() oder getdelim() eine Null oder einen negativen Wert zurückgibt (oder fgets() NULL zurückgibt), bedeutet dies, dass entweder keine Daten mehr zu lesen waren oder der Vorgang aus einem anderen Grund fehlgeschlagen ist .

  • Nach der Schleife prüft (!feof(src) || ferror(src)), ob der Eingabestream vollständig fehlerfrei gelesen wurde. Nun, eher umgekehrt: Der Ausdruck ist nur dann wahr, wenn ein Fehler aufgetreten ist oder die gesamte Datei nicht gelesen wurde.

    Wenn ich Daten in eine Datei geschrieben habe, z. B. FILE *dst, gehe ich diesem Test normalerweise mit if (fflush(dst)) Test voran. Es ist wahr, wenn beim Schreiben der letzten von der C-Bibliothek gepufferten Daten in die Datei ein Fehler auftritt.

  • Das fclose(src) schließt die Datei. Ich persönlich bevorzuge es, den Rückgabewert zu überprüfen, denn obwohl derzeit nur unter ganz bestimmten Umständen fehlschlagen kann, würde ich als Benutzer definitiv lieber wissen, ob das Betriebssystem Probleme beim Schreiben meiner Daten hat! Der Test kostet grundsätzlich nichts, kann aber für den Benutzer entscheidend sein. Ich möchte nicht, dass Programme "vergessen", dass bei der Arbeit an meinen Daten ein Problem aufgetreten ist. Meine Daten sind mir wichtig.

    • free(NULL) ist sicher und tut nichts. (Außerdem entspricht realloc(NULL, size) malloc(size). Wenn Sie also einen Zeiger auf NULL initialisieren, benötigen Sie kein anfängliches Malloc. Sie können es einfach realloc() immer auf die gewünschte Größe bringen .)

Ich schlage vor, Sie spielen mit dem obigen Code. Sie können es sogar unter ltrace (ltrace ./example example.c) ausführen, um zu sehen, welche Standardaufrufe der C-Bibliothek tatsächlich ausgeführt werden und welche Ergebnisse sie erzielen. oder unter Strace (strace ./example example.c), um die Systemaufrufe anzuzeigen (vom Prozess bis zum eigentlichen Betriebssystemkern).

Als Beispiel könnten Sie sagen hinzufügen

        if (linenum == 7) {
            /* We skip line 7, and even destroy it! Muahhahah! */
            free(line);
            line = NULL;
            size = 0;
            continue;
        }

Direkt nach der Zeile linenum++, um zu sehen, was mit den siebten Zeilen der Textdateien passiert. (Sie werden übersprungen, und selbst wenn der Puffer freigegeben wird, passiert nichts Schlimmes (weil continue die nächste Iteration des while-Schleifenkörpers startet), da das nächste getline() nur dynamisch eine neue Zeile zuweist .

Wenn Sie eine Kopie eines Teils der Zeile behalten möchten, berechnen Sie einfach die Länge, die Sie für das Ende der Zeichenfolge nul (\0) benötigen, und weisen Sie dem Duplikat ({{X1) so viele Zeichen zu }} in C immer; also nehmen malloc () et al. die Anzahl der Zeichen, die wirklich zugewiesen werden sollen), memcpy() die Daten und fügen die abschließende Zahl hinzu. Beispielsweise,

char *sdup(const char *const source, const size_t  length)
{
    char *s;

    s = malloc(length + 1);
    if (!s) {
        /* Either return NULL, or: */
        fprintf(stderr, "sdup(): Not enough memory for %zu chars.\n", length + 1);
        exit(EXIT_FAILURE);
    }

    if (length > 0)
        memcpy(s, source, length);

    s[length] = '\0';
    return s;
}

Wenn Sie die vollständige Zeichenfolge (bis zum Ende der Zeichenfolge nul) möchten, können Sie POSIX.1-2008 strdup() stattdessen.

0
Nominal Animal 19 Jän. 2019 im 14:14