Получение бесконечно запущенной программы при использовании scanf

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

scanf("%s", strk_zgr_fp->bezeichnung, (int)sizeof(strk_zgr_fp->bezeichnung - 1));

Просто ничего не происходит после того, как эта строка будет достигнута, и программа будет работать бесконечно.

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

Любая помощь очень ценится, заранее спасибо.

#include <stdio.h>

typedef struct {
    int nummer;
    char bezeichnung;
    int menge;
    float preis;
} artikel;

void eingabe_artikel(artikel *strk_zgr_fp, int i_fp);
void ausgabe_artikel(artikel *strk_zgr_fp, int i_fp);

void main(void) {
    artikel artikelliste[10];
    artikel *strk_zgr;

    int anzahl;

    do {
        printf("Bitte eine #Artikel eingeben [<= 10]: ");
        scanf("%d", &anzahl);

        if (anzahl < 1 || 10 < anzahl)
            printf("\nEs wurde eine falsche #Artikel eingegeben.");
    } while(anzahl < 1 || 10 < anzahl);

    for(int i = 0; i < anzahl; i++)
        eingabe_artikel(&artikelliste[i], i);

    int i;
    for(strk_zgr = artikelliste, i = 0; strk_zgr < artikelliste + anzahl; 
        strk_zgr++, i++)
        ausgabe_artikel(strk_zgr, i);
}

void eingabe_artikel(artikel *strk_zgr_fp, int i_fp) {
    printf("\nBitte den %d. Artikel eingeben: ", ++i_fp);

    printf("\nNummer: ");
    scanf("%d", &strk_zgr_fp->nummer);

    printf("Bezeichnung: );
    scanf("%s", strk_zgr_fp, (int)sizeof(strk_zgr_fp->bezeichnung - 1));     /* <-- */

    printf("Menge: ");
    scanf("%d", &strk_zgr_fp->menge);

    float preis;
    printf("Preis: );
    scanf("%f", &preis);
    strk_zgr_fp->preis = preis;
}

void ausgabe_artikel(artikel *strk_zgr_fp, int i_fp) {
    printf("\n%d. Artikel: ", ++i_fp);

    printf("\nNummer:\t%d", strk_zgr_fp->nummer);
    printf("\nBezeichnung:\t%s", strk_zgr_fp->bezeichnung);
    printf("\nMenge:\t%d", strk_zgr_fp->menge);
    printf("\nPreis:\t%.2f EUR\n", strk_zgr_fp->preis);    
}

Версия NetBeans

Соответствующая версия

вы не можете сканировать в структуру. вы должны включить сканирование в член структуры.

pmg 02.11.2018 14:22

Также у scanf("%s", one, two); на 1 аргумент слишком много.

pmg 02.11.2018 14:23

и вам действительно следует проверить возвращаемое значение scanf (и большинство функций с прототипом в <stdio.h>): if (scanf(...) != EXPECTED_ASSIGNMENTS) /* error */;

pmg 02.11.2018 14:24

Вы не проверяете, действительно ли scanf() работал.

Andrew Henle 02.11.2018 14:42

Я понимаю, что вы все еще учитесь, поэтому я просто хочу убедиться, что вы усвоили дополнительный важный урок: проблемы, которые у вас возникают, - это именно Почему, часто рекомендуется избегать использования scanf. scanf оказывается плохо определенной функцией. Он плохо работает, его трудно использовать правильно, с ним трудно, если вообще возможно, делать более сложные вещи. Опытные программисты на C его вообще не используют; он используется только новичками. Но так как им так сложно пользоваться, у новичков с всегда возникают проблемы.

Steve Summit 02.11.2018 15:32

Если бы вводные учебники C и начинающие инструкторы C прекратили обучать программистов C использованию scanf, C стало бы намного проще для новичков.

Steve Summit 02.11.2018 15:34

Одна большая проблема с циклом, который читает целое число с использованием scanf("%d", &anzahl);, - это то, что происходит, когда вы вводите что-то, что не начинается с необязательного пробела, за которым следует необязательный знак +/-, за которым следует цифра. В этом случае scanf вернет 0, а входной поток останется на позиции нецифрового символа. Итак, когда вы снова обойдете цикл, этот нецифровой символ все еще будет там, и он точно так же выйдет из строя.

Ian Abbott 02.11.2018 15:43

(At) Стив: Спасибо за ваш комментарий. Мне известно о проблемах, связанных с устаревшей функцией scanf (). Курс Си написан еще в 2009 году, во всяком случае, даже не в первые дни Си. Однако я хотел бы следовать всем примерам. К сожалению, они используют эту плохо определенную функцию. (At) Ян: Спасибо за ваш комментарий. Я постараюсь помнить о вашей рекомендации всякий раз, когда сталкиваюсь с такой ситуацией.

xp10r3r 05.11.2018 13:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
8
86
2

Ответы 2

Много проблем в коде. Пожалуйста, исправьте хотя бы недостающие конечные кавычки в вызовах printf ().

Теперь к говядине:

1) Ваша структура неверна. «Bezeichnung» определяется как отдельный символ, а не строка.

typedef struct {
   int nummer;
   char bezeichnung[100];
   int menge;
   float preis;
} artikel;

2) Вы не можете использовать scanf () так, как вы это делали. Если вы хотите ограничить длину ввода (что всегда является хорошей идеей), вам необходимо передать максимальную длину в строку формата. Вам нужно использовать scanf () ?? Потому что с этого момента все становится грязным .... Поскольку максимальная длина ввода может быть переменной или изменяться (см. 1.), вам необходимо создать строку формата для scanf. Что-то вроде этого:

char format_str[15];
format_str[0] = '%';
//Dont use itoa(), it is not C standard.
sprintf(&format_str[1], "%d", (int)sizeof(strk_zgr_fp->bezeichnung) - 1);
strcat(format_str, "s");
scanf(format_str, strk_zgr_fp->bezeichnung);     

Надеюсь, это поможет вам.

PS: Вам нужно включить string.h для strcat ().

Альтернатива char format_str[15]; ... strcat(format_str, "s"); -> char format_str[1+20+1 +1]; sprintf(format_str, "%%" "%zu" "s", sizeof(strk_zgr_fp->bezeichnung) - 1);

chux - Reinstate Monica 02.11.2018 16:14

Ты следуешь за мной? ;) Во всяком случае, почему это: format_str [1 + 20 + 1 +1]; ?

GermanNerd 02.11.2018 16:29

Ваше хорошее решение в 3 строчки кода можно просто сделать более плотным с 1 sprintf(). Я предложил больший размер буфера (1 для%, 20 для обработки всех размеров до 2 ^ 64, 1 для s, 1 для \ 0), поскольку 15 казался неясным для его происхождения.

chux - Reinstate Monica 02.11.2018 16:33

@chux Хорошо, я вижу, что вы делаете. Когда я пытаюсь написать код для очевидных новичков, я стараюсь писать его так, чтобы они были легко понятны. Это может означать неэлегантный код ... Но разве цель ответов на вопросы - помочь людям лучше понять? То же самое и с простым алгоритмом. И да, 15 было совершенно произвольным.

GermanNerd 02.11.2018 16:38

Re: «То же самое и с основным алгоритмом» -> Не следую за вами - просто совпадение, поскольку у нас, очевидно, есть общие интересы.

chux - Reinstate Monica 02.11.2018 16:45

Относительно «герметичности» кода: конечно, как программист на C я это ценю. Однако в контексте вопроса OP ясно, что он / она не понимает строки форматирования для семейства функций scanf / printf. Зачем делать это ненужным? Я намеренно написал код, который разбивал все на мелкие кусочки ... И вообще, всегда есть вопрос о кратком коде или явном коде. Это означает удобочитаемость (для человека). После стольких десятилетий программирования на разных языках я бы сказал: удобочитаемость имеет первостепенное значение. Только мои 2 цента.

GermanNerd 03.11.2018 19:08

(At) GermanNerd: Спасибо за ответ. 1) Моя ошибка, в примере кода вместо одного символа определена строка. 2) Я попробую и дам вам знать, если это решит мою проблему.

xp10r3r 05.11.2018 13:58

Я попробовал, и у меня все сработало. Не уверен в этой функции sprintf (). Не могли бы вы объяснить, почему я должен его использовать? К настоящему времени я использовал этот код: char format_str[20]; format_str[0] = '%'; strcat(format_str, "s"); printf("Bezeichnung: "); scanf(format_str, strk_zgr_fp->bezeichnung);

xp10r3r 05.11.2018 15:33

Извините за то, что опять беспокою вас. Был еще один пример, который я сделал, и руководитель курса предоставил функцию scanf (), подобную этой, для чтения строки: scanf("%s", &(strk_zgr_fp->bezeichnung));. Я думал, что когда я читаю строку, адресный оператор не используется. Единственная разница в том, что теперь используется адресный оператор, а элемент заключен в квадратные скобки.

xp10r3r 05.11.2018 15:43

I tried it out and it worked fine for me. Not sure on this sprintf() function. Could you please explain why I'm supposed to use it? By now, I used this code: char format_str[20]; format_str[0] = '%'; strcat(format_str, "s"); printf("Bezeichnung: "); scanf(format_str, strk_zgr_fp->bezeichnung);

Хотя это работает, вы упускаете возможность ограничить длину ввода пользователя. Вот почему я предложил использовать sprintf () для создания (под) строки, содержащей максимально допустимую длину пользовательского ввода, в зависимости от того, насколько большой ваш bezeichnung определен в структуре. Предположим, что 'bezeichnung' имеет ограничение в 100 символов, вы хотите ограничить ввод до 99 (+1 для нулевого завершения), поэтому вам нужна строка формата scanf, подобная этой: «% 99s».

chux предоставил гораздо более компактную версию моих трех строк, но я думаю, что вначале вам будет проще просто собирать такие строки формата по частям, в то же время узнавая, как а) изменять отдельные символы в string, как использовать sprintf () в основном и как объединять строки с помощью strcat ().

There was another example which I did and the course leader provided a scanf() function like this to read a string: scanf("%s", &(strk_zgr_fp->bezeichnung));. I thought when I'm reading a string the address operator isn't used. The only difference is the address operator now is used and the element was put into brackets.

Я считаю, что это плохая практика. Работает, но лишнее. Рассмотрим этот небольшой фрагмент кода:

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

struct test{
    int i;
    char a_str[10];
};

int main()
{
    struct test a_test;
    printf("Normal array adress taking: %p\n", a_test.a_str);
    printf("Using '&' to take adress of array: %p\n", &(a_test.a_str));
    return 0;
}

Надеюсь, это поможет.

Другие вопросы по теме