C Программа завершается после if / else или повторяется, если я использую fputs / fgets - PullRequest
0 голосов
/ 05 ноября 2011

Я очень новичок в C и балуюсь Objective-C, AppleScript и HTML / CSS. Я уверен, что мою проблему очень легко решить. Я пытаюсь написать что-то, что позволит мне вводить исходные данные и упорядочивать их определенным образом в качестве выходных данных (в данном случае цитат). По сути, я хочу сохранить имя, заголовок, издателя и т. Д. В качестве переменных и распечатать их в определенном порядке.

Вот проблема: код здесь заканчивается слишком рано, и когда я использую fputs и fgets с stdout и stdin, он застревает и задает один и тот же вопрос навсегда. Чего мне не хватает?

int source_type;
int NumberofAuthors;
char AuthorName1[20];
char AuthorName2[20];
char AuthorName3[20];
char title[20];
char url[100];
char publishingCity[20];
char publisher[20];
char yearPublished[20];
char pageNumbers[20];
int valid;

printf("Welcome to Jackson's Chicago Manual of Style Auto-Footnoter.\n");

fputs("Choose source type:\n a.Book\n b.Journal\n c.Article\n d.Website\n ", stdout);
source_type = getchar();

if (source_type == 'a') {
    valid = 1;
} else {
    printf("Invalid source selection");
}

while ( valid == 1 && source_type == 'a' )
{
    printf("Number of authors [1 or 2]: ");
    scanf( "%d", &NumberofAuthors);
    if ( NumberofAuthors > 0 && NumberofAuthors < 3 ) {
        valid = 1;
        printf("Got it, %d author(s).\n", NumberofAuthors);
    }
    else {
        printf( "That's not enough people to write a book.\n" );
    }

    if ( NumberofAuthors == 1 ) {
        printf( "Author's name: " );
        scanf("%c", &AuthorName1);

    } 
    if (NumberofAuthors == 2) {
        printf("First author's name: " );
        scanf("%c", &AuthorName2);
        printf("Second author's name: " );
        scanf("%c", &AuthorName3);
    }
    else {
        valid = 0;
    }

    printf("Book title: " );
    fgets(title, sizeof(title), stdin);

    printf("Publication city: " );
    fgets(publishingCity, sizeof(publishingCity), stdin);


    } 


return 0;

Ответы [ 3 ]

1 голос
/ 05 ноября 2011

В начале программы:

if (source_type == 'a') {
    valid = 1;
} else {
    printf("Invalid source selection");
}

В случае, если source_type недопустимо, valid все еще содержит значение мусора, а использование неинициализированной переменной является неопределенным поведением. Двигаемся дальше.

while ( valid == 1 && source_type == 'a' )
{
    printf("Number of authors [1 or 2]: ");
    scanf( "%d", &NumberofAuthors);
    if ( NumberofAuthors > 0 && NumberofAuthors < 3 ) {
        valid = 1;
        printf("Got it, %d author(s).\n", NumberofAuthors);
    }
    //...

Вы никогда не сбросите valid на 0. Вам следует рассмотреть возможность использования switch() для этой части цикла while. Это облегчает чтение.

Также

if ( NumberofAuthors == 1 ) {
    printf( "Author's name: " );
    scanf("%c", &AuthorName1);

} 
if (NumberofAuthors == 2) {
    printf("First author's name: " );
    scanf("%c", &AuthorName2);
    printf("Second author's name: " );
    scanf("%c", &AuthorName3);
}
else {
    valid = 0;
}

Надеюсь, вы понимаете, что в случае NumberofAuthors == 1 часть else будет выполнена и установлена ​​valid = 0. Это потому, что else прилипает только к ближайшему if и только к этому.

Полагаю, вы используете fgets и т. Д., Чтобы избежать переполнения. Хорошо. Смотрите этот трюк на scanf с. Подробнее здесь: http://www.cplusplus.com/reference/clibrary/cstdio/scanf/

Попробуйте это:

int main(int argc, char* argv[])
{
    char source_type;
    int NumberofAuthors;
    char AuthorName1[20];
    char AuthorName2[20];
    char AuthorName3[20];
    char title[20];
    char url[100];
    char publishingCity[20];
    char publisher[20];
    char yearPublished[20];
    char pageNumbers[20];
    int valid;

    printf("Welcome to Jackson's Chicago Manual of Style Auto-Footnoter.\n");

    printf("Choose source type:\n a.Book");
    scanf("%c" , &source_type);

    if (source_type == 'a') {
        valid = 1;
    } else {
        printf("Invalid source selection");
        valid = 0;
    }

    while ( valid == 1 && source_type == 'a' )
    {
        //Reset
        valid = 0;

        printf("Number of authors [1 or 2]: ");
        scanf( "%d", &NumberofAuthors);
        if ( NumberofAuthors > 0 && NumberofAuthors < 3 ) {
            valid = 1;
            printf("Got it, %d author(s).\n", NumberofAuthors);
        }
        else {
            printf( "That's not enough people to write a book.\n" );
            continue;
        }

        switch( NumberofAuthors )
        {
        case 1:
            printf( "Author's name: " );
            scanf("%19s", AuthorName1);
            break;

        case 2:
            printf("First author's name: " );
            scanf("%19s", AuthorName2);
            printf("Second author's name: " );
            scanf("%19s", AuthorName3);
            break;

        default:
            valid = 0;
            break;
        }

        if(valid)
        {
            printf("Book title: " );
            scanf("%19s" , title);

            printf("Publication city: " );
            scanf("%19s" , publishingCity );
        }

    } 
    return 0;
}
0 голосов
/ 05 ноября 2011

Вы используете %c для чтения имен; это не хорошо. Вы передаете адрес массива, а не указатель на первый элемент массива; это тоже не хорошо. Вы растоптаны повсюду и оставляете себя с неопределенными проблемами поведения.

«Очевидное» исправление - использовать %s и отказаться от & перед именами массивов, но вы не должны поддаваться очевидному, так как это неправильно. У большинства авторов есть пробел между именем и фамилией (или инициалами и фамилией), и %s останавливается на первом пробеле. Вам нужно использовать fgets() для чтения имен, но не забудьте удалить завершающий перевод строки.

Мне не совсем понятно, почему у вас есть одно имя автора в «AuthorName1», но двойные авторы идут в «AuthorName2» и «AuthorName3»; Я бы предпочел использовать «имя 1» для первого автора независимо от. В самом деле, может быть, лучше иметь массив имен авторов, который будет легче обобщать.

Когда я компилирую ваш код (оборачивая его в int main(void) { и } и включая <stdio.h>, я получаю предупреждения компиляции:

xx.c: In function ‘main’:
xx.c:43: warning: format ‘%c’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
xx.c:43: warning: format ‘%c’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
xx.c:48: warning: format ‘%c’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
xx.c:48: warning: format ‘%c’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
xx.c:50: warning: format ‘%c’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’
xx.c:50: warning: format ‘%c’ expects type ‘char *’, but argument 2 has type ‘char (*)[20]’

Я переписал код, чтобы использовать функцию для обработки запроса и чтения строк, таким образом:

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

static int get_string(const char *prompt, char *buffer, size_t bufsiz)
{
    char *nl;
    printf("%s: ", prompt);
    fflush(0);
    if (fgets(buffer, bufsiz, stdin) == 0)
        return EOF; /* Read error - EOF */
    if ((nl = strchr(buffer, '\n')) == 0)
    {
        fprintf(stderr, "Overlong string entered!\n");
        return EOF;
    }
    *nl = '\0';
    return 0;
}

int main(void)
{
    int source_type;
    int NumberofAuthors;
    char AuthorName1[20];
    char AuthorName2[20];
    char title[20];
    char publishingCity[20];
    int valid = 0;
    int c;

    printf("Welcome to Jackson's Chicago Manual of Style Auto-Footnoter.\n");

    fputs("Choose source type:\n a.Book\n b.Journal\n c.Article\n d.Website\n ", stdout);
    source_type = getchar();

    if (source_type == 'a')
        valid = 1;
    else
    {
        printf("Invalid source selection");
        return(1);
    }

    /* Lucky that scanf() skips over the newline in search of digits! */
    while (valid == 1 && source_type == 'a')
    {
        printf("Number of authors [1 or 2]");
        scanf("%d", &NumberofAuthors);
        if (NumberofAuthors > 0 && NumberofAuthors < 3)
        {
            valid = 1;
            printf("Got it, %d author(s).\n", NumberofAuthors);
        }
        else
        {
            printf("That's not enough (or too many) people to write a book.\n");
            break;
        }
        /* Gobble to newline */
        while ((c = getchar()) != EOF && c != '\n')
            ;

        if (NumberofAuthors == 1)
        {
            if (get_string("Author's name", AuthorName1, sizeof(AuthorName1)) == EOF)
            {
                valid = 0;
                break;
            }
        } 
        else
        {
            assert(NumberofAuthors == 2);
            if (get_string("First author's name",  AuthorName1, sizeof(AuthorName1)) == EOF ||
                get_string("Second author's name", AuthorName2, sizeof(AuthorName2)) == EOF)
            {
                valid = 0;
                break;
            }
        }

        if (get_string("Book title", title, sizeof(title)) == EOF ||
            get_string("Publication city", publishingCity, sizeof(publishingCity)) == EOF)
        {
            valid = 0;
            break;
        }

        printf("Author 1: %s\n", AuthorName1);
        if (NumberofAuthors == 2)
            printf("Author 2: %s\n", AuthorName2);
        printf("Book title: %s\n", title);
        printf("Publication city: %s\n", publishingCity);
    } 

    return 0;
}

Я сохранил флаг 'valid', но он действительно не окупается. Код проще и короче без него.

0 голосов
/ 05 ноября 2011

Вы изменяете source_type только вне цикла while, поэтому, как только вы вошли в цикл, единственный выход - назначить 0 на valid.Это делается каждый раз NumberofAuthors != 2, поскольку первый if не входит в цепочку if-else.Возможно, вы хотите вместо этого:

if ( NumberofAuthors == 1 ) {
    printf( "Author's name: " );
    scanf("%c", &AuthorName1);
} else if (NumberofAuthors == 2) {
    printf("First author's name: " );
    scanf("%c", &AuthorName2);
    printf("Second author's name: " );
    scanf("%c", &AuthorName3);
} else {
    valid = 0;
}
...