Доступ к переменной снаружи во время цикла C - PullRequest
0 голосов
/ 01 марта 2019

Я очень плохо знаком с C, поэтому, пожалуйста, прости этот глупый вопрос.У меня есть следующий код, который читает 3-ю строку из текстового файла с номером 38. Я присваиваю его int в цикле while, однако при доступе к этой переменной вне цикла while я получаю другой результат.Это мой код:

int main() {

    FILE* file = fopen("blocks.txt", "r");
    char line[256];
    int number;
    int i = 0;
    while (fgets(line, sizeof(line), file)) {
        i++;
        if (i == 3)
        {
            number = line;

            //prints 38
            printf("%s", line);
        }
    }
    //prints something random!
    printf("%d", number);
    fclose(file);
    getchar();
    return 0;
}

Извините, если это расплывчато, и, вероятно, оно будет удалено, но, пожалуйста, помогите мне, ха-ха!

Ответы [ 3 ]

0 голосов
/ 01 марта 2019

Ваш цикл while продолжает работать даже после первой печати.И массив char line продолжает обновляться в строке gets(line, sizeof(line), file).

#include <stdio.h>
#include <stdlib.h>

int main()
{

    FILE *file = fopen("blocks.txt", "r");
    char line[256];
    long int number;
    int i = 0;
    char *stopped;

    /*keeps updating line till fget returns a failed response*/
    while (fgets(line, sizeof(line), file))
    {
        i++;
        if (i == 3)
        {
            number = (int)strtol(line, &stopped, 10);
            if (!*stopped)
            { /* handle error */
                printf("Error in strtol\n");
                return -1;
            }
            //prints 38
            printf("%s", line);
        }
    }
    /*Once the while loop ends it will have the last 
    line of the file block.txt*/
    printf("%ld", number);
    fclose(file);
    getchar();
    return 0;
}

предостережение для atoi

Мы могли бы использовать atoi .Но atoi небезопасен и может привести ко многим неосторожным ошибкам.См ссылка .Вы должны использовать strtol, который преобразует строку в long int.

0 голосов
/ 01 марта 2019

Ваша путаница возникает из-за печати line внутри цикла, а затем из-за попытки печати number после цикла.line - это тип char[256], который при обращении преобразуется в простой char*.number - это тип int.Вы не можете присвоить char* int.

. Чтобы преобразовать цифры ASCII в line в целое число number, вы можете использовать sscanf (подходит в этом контексте) или использоватьstrtol (или вы можете вручную преобразовать его по модулю и делению).

Здесь вы правильно читаете строку ввода с помощью строчно-ориентированной функции ввода fgets (это хорошо),Вам просто нужно преобразовать цифры ASCII в line в целое число.Вы можете сделать это с помощью sscanf, например:

    while (fgets(line, sizeof(line), file)) {
        i++;
        if (i == 3) {
            if (sscanf (line, "%d", &number) != 1) {
                fputs ("error: no integer at start of line 3\n", stderr);
                return 1;
            }
            printf ("number inside loop : %d\n", number);
            break;  /* no reason to read further */
        }
    }

( примечание: проверка конверсии с помощью sscanf путем проверки возврата по числу запрошенных конверсий -- проверять каждое чтение или преобразование)

Обратите внимание, не жесткий код имена файлов или номера.main() принимает аргументы, argc содержит существующее количество аргументов (всегда будет не менее 1 аргумент, имя исполняемого файла), а argv - ваш вектор аргумента (массив указателей на каждый аргумент сследующий указатель после последнего аргумента установлен на NULL) Передайте ваше имя файла вашей программе в качестве аргумента.Вы также можете условно настроить вашу программу на чтение из имени файла, указанного в качестве первого аргумента, или из stdin по умолчанию, если аргумент не указан.

Если поместить его в целом, вы можете сделать:

#include <stdio.h>

int main (int argc, char **argv) {
    /* read from file given as 1st argument (stdin by default) */
    FILE* file = argc > 1 ? fopen (argv[1], "r") : stdin;
    char line[256];
    int number = 0,  /* initialize number - what if you have a 2-line file */
        i = 0;

    while (fgets(line, sizeof(line), file)) {
        i++;
        if (i == 3) {
            if (sscanf (line, "%d", &number) != 1) {
                fputs ("error: no integer at start of line 3\n", stderr);
                return 1;
            }
            printf ("number inside loop : %d\n", number);
            break;  /* no reason to read further */
        }
    }
    fclose(file);   /* close file - read operations done */

    if (i == 3)     /* validate 3 lines read from file */
        printf ("number outside loop: %d\n", number);
    else
        fputs ("error: less than 3-lines in file.\n", stderr);

    getchar();
    return 0;
}

Пример входного файла

$ cat dat/3line.txt
10
20
38
40

Пример использования / Вывод

$ ./bin/readline3 dat/3line.txt
number inside loop : 38
number outside loop: 38

Просмотрите все и дайте мне знать, если выесть дополнительные вопросы.

0 голосов
/ 01 марта 2019

В C тип переменной важен.Тип line равен char[256], то есть это массив из 256 символов, что в этом случае более чем достаточно для хранения длины строки.

Однако number является int.C старается преобразовать типы разумным способом, но его определение «разумный» не ваше.Он хранит адрес , где line находится в памяти в number.Затем, в printf внутри цикла while, вы указываете ему печатать, используя "%s", что означает «обрабатывать это так, как если бы это был адрес группы символов в памяти», что, как оказалось, и работает.

Вне цикла while вы используете "%d", который говорит: «напечатайте это, как будто это число».Технически это число, но это не число 38;это число, которое не имеет никакого значения для вас, и не имеет большого значения даже для людей, которые являются экспертами в C.

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

. Вы должны включить предупреждения на своем компиляторе, чтобы он сообщал вам, если вы будете делать подобные вещи в будущем.В зависимости от того, как вы компилируете, вы можете изменить некоторые параметры в вашей IDE (например, Visual Studio или Eclipse) или добавить что-то в командную строку компилятора, например, используя gcc -Wall -Wextra -pedantic вместо просто gcc.

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