Используя mallo c in C, чтобы убедиться, что пользователь вводит определенные цифры - PullRequest
1 голос
/ 26 января 2020

Итак, я хочу написать программу, чтобы проверить, если пользователь вводит. Скажем, требование состоит из 4 цифр, если пользователь вводит 5, то программа продолжает просить пользователя снять ровно 4 цифры git.

Я получил рабочий код примерно так: в основном используйте scanf для чтения строкового значения, затем используйте strlen для подсчета количества цифр. Если пользователь введет правильный di git, тогда я использую atoi, чтобы преобразовать эту строку в int, который я буду использовать позже. Скажем, требование: 4 di git number:

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

int main() {
    int digit, mynumber;        
    int digit = 5;
    char str[5]; 

    /* Checking if enter the correct digit */
    do {
        printf("Enter a %d digit number\n", digit);
        scanf("%s", &str);
        if (strlen(str) != digit) {
            printf("You entered %d digits. Try again \n", strlen(str));
        } else { 
            printf("You entered %d digits. \n", strlen(str)); 
            printf("Converting string to num.....\n"); 
            mynumber = atoi(str); 
            printf("The number is %d\n", mynumber); 
        } 
    } while (strlen(str) != digit);
    return 0;
}

Я хочу немного это изменить. Вместо char str[5] для 5 di git строки. Я хочу попробовать массив Dynami c.

Таким образом, вместо char str[5] я делаю это:

char *str;
str = malloc(sizeof(char) * digit);

Запуск этого кода приводит к ошибке seg. Кто-нибудь может мне с этим помочь?

Это полный код вопроса

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

int main() {
    int mynumber;
    int digit = 5;
    char *str;
    str = malloc(sizeof(char) * digit);

    /* Checking if enter the correct digit */
    do {
        printf("Enter a %d digit number\n", digit);
        scanf("%s", &str);
        if (strlen(str) != digit) {
            printf("You entered %d digits. Try again \n", strlen(str));
        } else {
            printf("You entered %d digits. \n", strlen(str));
            printf("Converting string to num.....\n");
            mynumber = atoi(str);
            printf("The number is %d\n", mynumber);
        }
    } while (strlen(str) != digit);
    return 0;
}

Ответы [ 2 ]

1 голос
/ 27 января 2020

Хотя вы можете использовать отформатированную функцию ввода scanf, чтобы принять ваш ввод в виде строки, scanf полон ряда ловушек, которые могут оставить случайные символы в вашем потоке ввода (stdin) в зависимости от того, является ли происходит сбой сопоставления . Он также имеет ограничение на использование "%s" спецификатора преобразования только для чтения до первого пробела . Если ваш пользователь проскальзывает и вводит "123 45", вы читаете "123", ваши тесты не пройдены, а "45" остаются в stdin непрочитанными, ожидая, пока вы не укусите вас при следующей попытке чтения, если только вы вручную не очистите stdin.

Кроме того, если вы используете "%s" без модификатора width-width - вы также можете использовать gets(), так как scanf с радостью прочитает неограниченное количество символов в ваш массив из 5 или 6 символов, запись за пределы вашего массива вызывает Undefined Behavior .

Более разумный подход - обеспечить буфер символов, достаточно большой для обработки всего, что пользователь может ввести. ( не экономьте на размере буфера ). Чтение всей строки за раз с помощью fgets(), что с буфером достаточного размера гарантирует, что вся строка будет занята, исключая возможность для непрочитанных символов в stdin. Единственное предостережение с fgets (и с каждой линейно-ориентированной функцией ввода, такой как POSIX getline) - это '\n', также считывается и включается в заполненный буфер. Вы просто обрезаете '\n' с конца, используя strcspn() в качестве удобного метода, получая количество символов, введенных одновременно.

( примечание: вы можете до go обрезка '\n', если вы отрегулируете свои тесты, чтобы включить '\n' в длину, с которой вы проверяете, поскольку преобразование в int будет игнорировать конечный '\n')

Ваш лог c отсутствует еще одна необходимая проверка. Что если пользователь введет "123a5"? Введены все 5 символов, но не все цифры. atoi() не имеет возможности сообщать об ошибках и с радостью преобразует строку в 123 в автоматическом режиме, без указания каких-либо дополнительных символов. У вас есть два варианта: либо использовать strtol для преобразования и проверить, что не осталось символов, либо просто l oop над символами в строке, проверяя каждый с помощью isdigit(), чтобы убедиться, что введены все цифры.

В целом, вы можете сделать что-то вроде следующего:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define NDIGITS     5   /* if you need a constant, #define one (or more) */
#define MAXC     1024

int main (void) {

    int mynumber;
    size_t digit = NDIGITS;        
    char buf[MAXC];                         /* buffer to hold MAXC chars */

    /* infinite loop until valid string entered, or manual EOF generated */
    for (;;) {
        size_t len;
        printf("\nEnter a %zu digit number: ", digit);  /* prompt */
        if (!fgets (buf, sizeof buf, stdin)) {          /* read entire line */
            fputs ("(user canceled input)\n", stdout);
            break;
        }
        buf[(len = strcspn(buf, "\n"))] = 0;            /* trim \n, get len */
        if (len != digit) {                             /* validate length */
            fprintf(stderr, "  error: %zu characters.\n", len);
            continue;
        }
        for (size_t i = 0; i < len; i++) {              /* validate all digits */
            if (!isdigit(buf[i])) {
                fprintf (stderr, "  error: buf[%zu] is non-digit '%c'.\n",
                        i, buf[i]);
                goto getnext;
            }
        }
        if (sscanf (buf, "%d", &mynumber) == 1) {   /* validate converstion */
            printf ("you entered %zu digits, mynumber = %d\n", len, mynumber);
            break;      /* all criteria met, break loop */
        }
        getnext:; 
    }
    return 0;
}

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

Всякий раз, когда вы пишете подпрограмму ввода, go пробуйте и разбить его. Подтвердите, что он делает то, что вам нужно, и обнаруживает случаи, от которых вы хотите защитить (и еще будет больше проверок, которые вы можете добавить). Здесь он охватывает большинство ожидаемых злоупотреблений:

$ ./bin/only5digits

Enter a 5 digit number: no
  error: 2 characters.

Enter a 5 digit number: 123a5
  error: buf[3] is non-digit 'a'.

Enter a 5 digit number: 123 45
  error: 6 characters.

Enter a 5 digit number: ;alsdhif aij;ioj34 ;alfj a!%#$%$ ("cat steps on keyboard...")
  error: 61 characters.

Enter a 5 digit number: 1234
  error: 4 characters.

Enter a 5 digit number: 123456
  error: 6 characters.

Enter a 5 digit number: 12345
you entered 5 digits, mynumber = 12345

Пользователь отменяет ввод с помощью ctrl + d на Linux (или ctrl + z на windows) создание руководства EOF:

$ ./bin/only5digits

Enter a 5 digit number: (user canceled input)

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

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

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

1 голос
/ 26 января 2020

В вашем коде у вас есть массив из 5 байтов. Если пользователь вводит более 4 цифр, scanf с радостью переполнит массив и повредит память. Это повредит память в обоих случаях, как массив и как malloc. Однако не каждое повреждение памяти приводит к взлому sh.

Так что вам нужно ограничить количество байтов, которые может прочитать scanf. Способ сделать это - использовать %4s в строке формата.

Однако в этом случае вы не сможете определить, когда пользователь вводит более 4 цифр. Вам понадобится как минимум еще 1 байт: str[6] и %5s;

Я предлагаю вместо scanf использовать getchar. Это позволит вам читать символ за символом и считать их по пути.

...