Ошибка сегментации с функцией getline ()? - PullRequest
0 голосов
/ 19 января 2019

Я изучаю C на терминале в Linux, и я сделал небольшую игру в тексте, поэтому все работает (особенно мой большой цикл, если игрок хочет перезапустить игру), и я просто добавляю дамп »Введите ваше псевдо "в начале.

Он не работает, когда я его ввожу, он говорит мне

Segmentation Error , core dumped

Вот весь код, самые важные - это первые строки, потому что все остальное работает отлично:

#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);
}

Ответы [ 4 ]

0 голосов
/ 19 января 2019

getline() - это функция POSIX.1 , которая считывает строки в динамически размещенные буферы, позволяя использовать строки любой длины (ограниченные только объемом доступной памяти).Он возвращает количество прочитанных символов или -1, если больше нет ввода или произошла ошибка.

Вот один пример использования шаблона:

    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 */

    }

В любой момент выможет освободить динамически выделенный буфер, используя

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

Причина, по которой вы хотите очистить указатель на NULL и размер до нуля, заключается в том, что вы случайно не пытаетесь получить доступ к уже освобожденной памяти, но вы можетевызовите getline(&line, &size, handle), чтобы прочитать больше строк, так как вызов просто распознает, что у него нет буфера, и выделит новый.


Вы можете манипулировать динамическими данными любым способом, если хотите, еслиВы осторожны.Например:

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);
}

будет работать, но это будет довольно медленно, поскольку библиотека C будет выполнять по крайней мере один вызов malloc() для каждой прочитанной строки и, возможно, дополнительные вызовы realloc(), в зависимости отдлина строки.

Причина, по которой getline() написана как есть, заключается в том, что она позволяет повторно использовать один и тот же буфер для любого количества строк.Если вы читаете файлы последовательно, вы можете использовать один и тот же буфер.Давайте рассмотрим более сложный пример:

#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;
}

Если мы сохраним вышеизложенное как, например, example.c , скомпилируем его, например, gcc -Wall -O2 example.c -o example, и запустим программу, предоставивимена текстовых файлов в качестве параметров, например, ./example example.c, он будет выводить что-то вроде

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)

Что делает программа, это просто читать каждый указанный файл построчно, удалять любые начальные и конечные пробелы накаждую строку и объединить все последовательные пробелы в один пробел.Меньшее количество символов - это количество оставленных символов (и показанных), большее число - исходное количество символов, считанных из файла.

Дополнительные примечания к примеру программы, если она вас заинтересует

  • Вызов setlocale(LC_ALL, "") говорит вашей библиотеке C использовать локаль пользователя (обычно определяется в переменных окружения LANG или LC_ALL).Эта программа использует только определения типов символов для набора символов, используемого текущей локалью (чтобы определить, какие коды являются «пробелами»), поэтому это также может быть ограничено с помощью setlocale(LC_CTYPE, "").Вызов вернет NULL, если текущая локаль не поддерживается библиотекой C.Обычно это происходит из-за ошибки в пользовательской конфигурации, поэтому полезно, чтобы программа предупреждала затем.

  • isspace() (и все другие функции is*(), определенные в <ctype.h>) взять код без знака (или EOF).Поскольку тип char может быть подписанным или без знака, мы явно приводим символ к (unsigned char) перед передачей в функцию.Рассмотрим этот глупый исторический багаж, с которым нам просто нужно иметь дело.

  • Поскольку line указывает на начало динамически распределяемого буфера памяти, мы не должны его изменять (кроме как черезrealloc() или free() и затем установите на NULL).Если мы изменим его, любой последующий вызов getline() или free() с использованием этого указателя, скорее всего, выйдет из строя и приведет к аварийному завершению программы, поскольку им действительно нужен указатель, указывающий на начало буфера, а не только где-то внутри него.

  • Мне нравится использовать указатели (char *in, *out, *end) вместо индексов.Здесь in начинается с line и поднимается до line+len, но не включает *1072*, где getline() ставит nul \0 конца строки, чтобы указать конец строки.Вот почему я также часто использую указатель с именем end, чтобы указать на это.out начинается также с line, но увеличивается только тогда, когда символы находятся в строке.

    Если вы думаете о ряду плиток с буквами, как в скрэббле, out указывает на следующую позициюВы положите сохраненную плитку, и in указывает на следующую плитку, которую вы получите.

  • Когда getline() или getdelim() возвращает ноль или отрицательное значение (или fgets() возвращает NULL), это означает, что либо не было больше данных для чтения, либо операция завершилась неудачно по какой-либо другой причине .

  • После цикла (!feof(src) || ferror(src)) проверяет, был ли входной поток прочитан полностью без ошибок. Ну, скорее, обратное: выражение истинно, только если произошла ошибка или весь файл не был прочитан.

    Если я записал данные в какой-нибудь файл, скажем, FILE *dst, я обычно предшествую этому тесту с тестом if (fflush(dst)). Это правда, если при записи в файл последней из данных, буферизованных библиотекой C, произошла ошибка.

  • fclose(src) закрывает файл. Лично я предпочитаю проверять его возвращаемое значение, потому что, хотя в настоящее время он может потерпеть неудачу только в очень специфических обстоятельствах, я, как пользователь, определенно предпочел бы знать, были ли у ОС проблемы с записью моих данных! Тест в основном ничего не стоит, но может иметь решающее значение для пользователя. Я не хочу, чтобы какие-либо программы «забыли», говоря мне, что при работе с моими данными возникла проблема; мои данные важны для меня.

    • free(NULL) безопасен и ничего не делает. (Кроме того, realloc(NULL, size) эквивалентно malloc(size), поэтому, если вы инициализируете указатель на NULL, вам не нужен начальный malloc, вы можете просто realloc() всегда получить нужный вам размер.)

Я предлагаю вам поиграть с приведенным выше кодом. Вы даже можете запустить его в ltrace (ltrace ./example example.c), чтобы увидеть, какие стандартные вызовы библиотеки C фактически выполняются, и их результаты; или под strace (strace ./example example.c), чтобы увидеть системные вызовы (от процесса до самого ядра ОС).

В качестве примера можно добавить, скажем,

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

сразу после строки linenum++, чтобы увидеть, что происходит с седьмыми строками текстовых файлов. (Они пропускаются, и даже если буфер освобождается, ничего плохого не происходит (потому что continue запускает следующую итерацию тела цикла while), так как следующий getline() просто динамически выделит новую строку.

Если вы решили сохранить копию части строки, просто рассчитайте необходимую длину, добавив ее для nul в конце строки (\0), выделите столько символов для дубликата (* 1130) * в C всегда; поэтому malloc () и др. берут количество символов, выделяемых для действительно), memcpy() данных и добавляют завершающий nul. Например,

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;
}

Если вам нужна полная строка (до конца строки nul), вы можете использовать вместо нее POSIX.1-2008 strdup().

0 голосов
/ 19 января 2019

Переменная 'buffer' указывает на какой-то адрес мусорной памяти.Вы должны сначала выделить необходимую память, используя функцию 'malloc' или сделать 'буфер' статическим массивом вместо указателя.

0 голосов
/ 19 января 2019

getline() может использоваться двумя различными способами:

  1. Считывание в буфер, предоставленный вызывающей стороной.
  2. Выделите память при чтении.

Для использования 1. инициализируйте переданный указатель, чтобы указать на допустимую память, а также укажите размер последнего.

#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. */
}

Для использования 2.инициализируйте указатель, переданный NULL, и передайте 0 как размер.

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. */
}

Примечание: getline() возвращает ssize_t не size_t.

0 голосов
/ 19 января 2019

Вы забыли инициализировать buffer.И вам лучше завершить вашу управляющую строку формата printf (3) с помощью \n или же вызвать fflush (3) перед любым вводом (так как stdio (3) буферизовано).

Неинициализированный указатель содержит мусор.Его использование неопределенное поведение .Будьте напуганы !

Поэтому я предлагаю кодировать

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

и более поздние

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

В следующий раз включите все предупреждения и отладочную информацию, когдакомпилирование, поэтому скомпилируйте ваш код с помощью gcc -Wall -Wextra -g при использовании GCC .Вы бы получили несколько предупреждений.

Конечно, прочитайте Как отлаживать небольшие программы и отладка с помощью GDB

В Linux, в вашем конкретном случае, вас может заинтересовать использование readline (3) вместо getline (3) .

Donне забудьте прочитать документацию по каждой функции, которую вы используете (например, здесь , если это стандартная функция C).

...