Вопрос о strstr () и fgets () в демоверсии из книги «Head First C» - PullRequest
2 голосов
/ 08 января 2020
char tracks[][80] = {
    "I left my heart in Harvard Med School",
    "Newark, Newark - a wonderful town", 
    "Dancing with a Dork",
    "From here to maternity",
    "The girl from Iwo Jima",
};

void find_track(char search_for[]) {
    int i;

    for (i = 0; i < 5; i++) {
        char *pointer = strstr(tracks[i], search_for);
        printf("P:%s\n", pointer);
    } 
}

int main() {

    char search_for[80]; 
    printf("Search for: "); 
    fgets(search_for, 80, stdin); 
    // scanf("%79s", search_for);
    find_track(search_for);


    return 0;
}

Это пример использования strstr () и fgets (). Но когда я запускаю его, он не может вернуть совпадающий указатель, он всегда равен нулю.

Search for: wonderful
P:(null)
P:(null)
P:(null)
P:(null)
P:(null)

И я попробовал несколько разных выражений: 1. Я уменьшил емкость массива search_for с 80 до 10:

char search_for[10];

это работает!

Search for: wonderful
P:(null)
P:wonderful town
P:(null)
P:(null)
P:(null)

2.Я заменила функцию fgets () на scanf ().

scanf("%79s", search_for);

она также работает!

Search for: wonderful
P:(null)
P:wonderful town
P:(null)
P:(null)
P:(null)

Поэтому меня это смущает, и кто-нибудь может рассказать мне о глубокой причине этого явления?

1 Ответ

2 голосов
/ 08 января 2020

Это потому, что fgets читает максимум 80-1 = 79 символов (оставляет 1 символ, который становится нулевым завершением), но прекращает чтение после перевода строки \n, который добавляется к stdin после вы набираете "замечательно" и нажимаете ввод. Он копирует этот символ \n в ваш буфер, поэтому входная строка на самом деле "wonderful\n\0", что не соответствует имеющимся у вас данным из-за перевода строки.

Теперь, когда вы объявляете вместо размера буфера 10, fgets не имеет места для хранения \n, но все равно null завершает строку - поэтому он работает случайно.

scanf, с другой стороны, оставляет \n в stdin без чтения, поэтому scanf также работает. Тем не менее, fgets - лучшая и безопасная функция, поэтому вы должны продолжать ее использовать.

Быстрое решение состоит в том, чтобы сделать что-то вроде:

fgets(search_for, 80, stdin); 
search_for[strlen(search_for)-1] = '\0';
find_track(search_for);

Это перезаписывает \n с помощью нулевой терминатор. После fgets у вас есть "wonderful\n\0" в памяти, а затем search_for[strlen(search_for)-1] = '\0'; меняет это на "wonderful\0\0".

Обратите внимание, что для правильной программы, вы также должны добавить обработку ошибок:

if(fgets(search_for, 80, stdin) != NULL)
{
  search_for[strlen(search_for)-1] = '\0';
  find_track(search_for);
}
...