C- Ошибка сегментации в strlen () для проблемы со списком слов - PullRequest
0 голосов
/ 01 ноября 2019

Я пытаюсь создать расширяемый список из 20 букв. Я использую 'char word [20]' в качестве хоста для каждого нового слова перед добавлением в список. Для ввода слов длиной 20- программа работает нормально. Если я добавлю слово длиной 20+, моя программа должна проигнорировать и продолжить.

Но в следующий раз я получил ошибку сегментации где-то в strlen (слово) в этом операторе if-else.

Странная вещь, иногда моя программа может преодолеть этот сбой, но в большинстве случаев он вылетает.

Некоторые попытки: - Если я использую 'char word [1000]', он всегда работает нормально (пока). Я догадался, что указатель аргумента strlen () ошибочно указывает на что-то неправильное после 'continue', поэтому я отслеживал это с помощью & word [0] и писал strlen (& word [0]) и использовал 'char word [20]'. Это не решает проблему, и & word [0] остается прежним, когда происходит ошибка сегментации.

char *wordList[] = {""};
char word[10];
char * p, c;
int i = -1;
int size = 10;
int strlength = -1;

while(strlength != 0){
    //get word by using loop of getchar() and detect \0
    printf("Enter word :");
    scanf("%s",word);
    printf("Taken word: %s at %p\n",word,&word[0]);

    //detect word length to avoid 4-letter words and words with length 20+
    if (strlen(word) == 0){
        printf("Break due to length 0\n");
        break;
    }else if(strlen(word) > 20){
        printf("Ignore due to length 20+\n");
        continue;
    }
    printf("End checking length");

    //add word to wordList by extending memory for one new word and assign each char to memory
    i++;
    wordList[i] = malloc(sizeof(char)*strlen(word)-1);
    strcpy(wordList[i],word);
    printf("[");
    for(int j = 0; j <= i; j++){
        printf(" %s,",wordList[j]);
    }
    printf("]\n");

1 Ответ

0 голосов
/ 01 ноября 2019

Имейте в виду, что в C строки - это просто непрерывные наборы символов в памяти, начиная с известного адреса (char* или char[], если хотите) и заканчивая нулевым байтом \0 C строки делаютне по своей сути знать их размер; вы программист несет ответственность за то, чтобы вы не пытались получить доступ к памяти за пределами того, что было выделено вашей строке. Когда вы это сделаете, вы получите Segfault.

scanf("%s",&word[0]);

Вы говорите scanf прочитать строку из стандартного ввода и сохранить ее в word (&word[0] это просто word). Но вы не указываете scanf, сколько символов нужно прочитать максимально, поэтому вы не препятствуете scanf получить доступ к world[20], что на один символ слишком далеко в 10-символьной строке (C - это смещение, основанное на 0, конечно,поэтому первый действительный символ в world - это world[0], а последний - world[19].). Введите более 19 символов (оставляя еще один для завершающего \0), и вы переполните свое выделение памяти, и все может произойти (если ваша программа имеет действительные данные по этому адресу памяти, вы перезапишете ее. Если данные есть)это указатель, вы перезапишете адрес и перестанете использовать segfault при следующей попытке разыменования. Если память вашей программы на этом заканчивается, вы напрямую перестанете работать с segfault.

Итак, вам нужно сказать scanf максимальное количество байтов для чтения. Самый простой способ - с помощью необязательного «спецификатора ширины поля» для scanf:

scanf("%9s",word)

, который говорит scanf прочитать строку и сохранить ее в слове, но толькохранить до 9 символов (10-й - это завершающий \0 символ).

Если я добавлю слово длиной 20+, моя программа должна игнорировать и продолжить

В вашей логике есть изъян - вы не знаете, будет ли слово длиной 10 символов, пока вы не прочитаете все это из ввода и , затем символrs от word до первого появления '\ 0'. Прежде чем вы сможете проверить с помощью strlen и, необязательно, continue, вы уже допустили ошибку, записав 11-й символ в word[10] в scanf. Помните, что только из word он не может знать, сколько байтов выделено по этому адресу.

Код списка слов также должен быть исправлен.

char *wordList[] = {""};`
...
//add word to wordList by extending memory for one new word and assign each char to memory

Ну, wordList[] это ровно одна char* длина, и эта строка "", пустая строка, которую вы определили как единственное содержимое статически определенного списка неопределенной длины []размер которого во время компиляции определяется как 1. Вместо этого вы должны просто определить свой статический список, как вы делаете для word.

char* wordList[20];

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

Теперь у вас на самом деле есть wordList[i] для распределения до i >= 20.

unsigned int wordListLen = 0;
...
if(wordListLen >= 20){
   // list full!
   break;
} else {
   unsigned int wlen = strnlen(word,10)+1;
   wordList[wordListLen] = malloc( wlen * sizeof(char) );
   strncpy(wordList[wordListLen], word, 10);
   wordListLen++;
   ...print...
}

Не забудьте выделить место для '\0' при выделении копии word!

...