Не уверен, почему цикл while работает иначе при использовании scanf - PullRequest
1 голос
/ 13 июня 2019

Я новичок в изучении C и, насколько мне известно, я знал, что цикл while будет работать вечно, если кто-то даст условие, такое как while (1 == 1), оно станет циклом навсегда.

Но, читая книгу, я заметил такой код, как

#include<stdio.h>

int main() {

    char a;
    int started = 0;
    puts("Scanning\n");
    while (scanf("%s[^\n]",&a) == 1)
    {
        if (a >= 65 && a <= 90)
        {
            printf("Char is capital\n");
        }
        else
        {
            printf("Not cap.\n");
        }

    }
    puts("Done\n");
    return 0;


}

Командная строка => char.exe < file.txt

Он принимает входные данные от stdin, но scanf возвращает номер аргумента, т.е. 1, поэтому условие должно стать, пока (1 == 1)

Но почему это не вечный цикл и существует после чтения файла?

Спасибо

Ответы [ 5 ]

2 голосов
/ 13 июня 2019

В

char a;
...
while (scanf("%s[^\n]",&a) == 1)

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

сделать

char a;
...
while (scanf(" %c",&a) == 1)

и обратите внимание на пробел в формате, удалите его, если хотите управлять всеми символами

В

 if (a >= 65 && a <= 90)

неправильно использовать код ASCII, он не читается и не совместим с не ASCII

вы можете сделать

if (a >= 'A' && a <= 'Z')

или лучше использовать isupper (<ctype.h>), потому что он не гарантирует, что заглавные буквы являются последовательными

Ваш printf может быть заменен на , помещает , удаляя \ n в строке, или fputs , бесполезно иметь printf для печати простой строки (без% и arg)

Но почему это не вечный цикл и существует после чтения файла?

scanf не возвращает 1 в EOF, поэтому цикл останавливается в EOF, около scanf family:

Эти функции возвращают количество элементов ввода, успешно сопоставленных и назначенных, которое может быть меньше предусмотренного или даже равно нулю в случае сбоя раннего сопоставления.

Значение EOF возвращается, если достигнут конец ввода перед первым успешным преобразованием или ошибкой сопоставления. EOF также возвращается, если возникает ошибка чтения, и в этом случае устанавливается индикатор ошибки для потока (см. Ferror (3)), и устанавливается errno, обозначающий ошибку.

1 голос
/ 13 июня 2019

Но почему это не вечный цикл и существует после чтения файла?

Он будет повторяться бесконечно, только если scanf возвращает 1 при каждом вызове. Цикл завершается в первый раз, когда функция возвращает другой результат. *

Возвращаемое значение

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


* При условии, что поведение программы четко определено. Ваша программа не предназначена по причинам, описанным в комментариях и другом ответе.

0 голосов
/ 13 июня 2019

При вводе буквы условие (1==1) становится истинным, и тело цикла while повторяется.

После итерации цикла while она переходит к следующей букве. scanf ожидает ввода следующего символа.

Снова условие (1==1) выполнено.

Этот цикл является неопределенным циклом, и вы можете продолжать вводить буквы, и вы никогда не достигнете оператора puts.

0 голосов
/ 13 июня 2019

&a указывает, что вы пытаетесь прочитать целую строку в переменной стека.

Но почему это не вечный цикл и существует после чтения файла?

Попытка чтения файла в переменной стека приведет к разрушению стека и вашей программе произойдет сбой.

Ищите различные типы хранилищ и управления памятью на C в интернете. a - это символьная переменная, размещенная в программном стеке, и предполагается, что она содержит только один символ Если вы возьмете его адрес и попытаетесь поместить больше данных, он повредит соседнюю память / переменные стека, пока ОС не обнаружит это и не убьет вашу программу. Ваш формат указывает, что он будет читать всю строку, т.е. до новой строки. scanf все равно вернет 1, но попытается сохранить эту строку по адресу a.

Вы запустили это как:

cat <your file>| ./a.out

Выделите достаточно большой буфер, например char a[4096];, и передайте a как

while(scanf("%s[^\n]", a) == 1)

Обратите внимание, что a теперь является массивом и не нуждается в & в начале. Ваш цикл все равно будет завершен, но на EOF, когда файл завершится. Попробуйте использовать файл размером менее 4 Кбайт в худшем случае, если новая строка не появится до 4 Кбайт.

Условия для чтения файла / строк большего размера кажутся выходящими за рамки вашего эксперимента.

Рабочий код

#include<stdio.h>

int main() {

char a[4096];
int started = 0;
puts("Scanning\n");
while (scanf("%s[^\n]", a) == 1)
{
    if (a >= 65 && a <= 90)
    {
        printf("Char is capital\n");
    }
    else
    {
        printf("Not cap.\n");
    }

}
puts("Done\n");
return 0;
}
0 голосов
/ 13 июня 2019

Функция scanf возвращает следующее значение:

  • >0 Количество элементов, преобразованных и успешно присвоенных.
  • 0 - элемент не был назначен.

  • <0 - обнаружена ошибка чтения или достигнут конец файла (EOF) до выполнения какого-либо назначения. </p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...