繁体   English   中英

是否可以从C中的本地开关内部向全局变量添加值?

[英]is it possible to add a value to a global variable from inside of a local switch in C?

我有一个程序,可以选择大学课程,然后根据学时分配价格。 目前,我已经创建了一个函数,该函数运行swtich来区分每个课程,然后进行打印。 在每个开关循环开始时,它将重置值。

我的问题是我试图弄清楚如何限制分配的学时。 例如,如果我选了3个班级,但学分超过7个,则需要打印一个错误,指出“选择的学分太多”。

我想我也许可以在开关内添加另一个变量,以增加学分时数,然后我就可以轻松解决其他问题,但是我还没有了解局部或全局变量,因此不确定是否创建值在局部变量中可以全局调用。

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


int studentId, var, i;
float first;
int course, course1, course2;
float second, amount;
float credit2, credit3, credit4, credit5, credit6, credit7, credit8;
float paymentA, paymentB, paymentC, paymentD, total = 0;
float creditA, creditB;
char newInvoice, Y = 1, N = 0;




void courseInfo(int myCourse){
    float credit1 = 0;
    char a[7];
    switch(myCourse)
    {
             case 4587:
                credit1 = 4;
                strcpy(a, "MAT 236");
                break;
            case 4599:
                credit1 = 3;
                strcpy(a,"COP 220");
                break;
            case 8997:
                credit1 = 1;
                strcpy(a, "GOL 124");
                break;
            case 9696:
                credit1 = 5;
                strcpy(a, "COP 100");
                break;
            case 4580:
                credit1 = 3;
                strcpy(a, "MAT 230");
                break;
            case 4581:
                credit1 = 4;
                strcpy(a, "MAT 231");
                break;
            case 4582:
                credit1 = 2;
                strcpy(a, "MAT 232");
                break;
            case 4583:
                credit1 = 2;
                strcpy(a,  "MAT 233");
                break;
            case 3587:
                credit1 = 4;
                strcpy(a, "MAT 256");
                break;
            case 4519:
                credit1 = 3;
                strcpy(a, "COP 420");
                break;
            case 6997:
                credit1 = 1;
                strcpy(a, "GOL 127");
                break;
            case 9494:
                credit1 = 3;
                strcpy(a, "COP 101");
                break;
            default:
                printf("Sorry invalid entry!\n\n");
                }
        total = total + (credit1*120.25);
        printf("%.2d\t%s\t\t%.2f\t\t$ %.2f\n\n", myCourse, a, credit1 , credit1*120.25);

}

return 0;
}

我在想,如果我可以存储“ flag”变量之类的东西来获取和存储相同的学分时数,然后在开关外部调用if函数,那么我可能会为选择太多而写一个错误。 不知道这是否可能。 谢谢

要回答您的问题:是的,那根本没有问题。 可以对变量执行的操作而言(就赋值,加法等而言),C不在乎您使用的变量是局部变量还是全局变量。 函数返回后,全局变量将保留其值。 这是一个例子:

#include <stdio.h>

char * last_value = "none";  /* this is global */

void foo(int type)
{
    switch (type) {
        case 1:
            last_value = "one";
            break;
        default:
            last_value = "other";
            break;
    }
}

int main()
{

    printf("last: %s\n", last_value);

    foo(1);
    printf("last: %s\n", last_value);

    foo(39);
    printf("last: %s\n", last_value);

    return 0;
}

输出将是:

last: none
last: one
last: other

关于您的问题,如果您可以创建一个局部变量并为其全局赋值,那么答案是否定的。 这就是为什么局部变量和全局变量之间存在差异的原因。

为何要对必须精确的数字使用浮点值是个坏主意,请查看以下Wi​​ki页面: IEE 754 Wiki

通常,您可以采用以下两种方法之一。 要么分配一个全局变量,然后使用它在函数之间共享数据,要么将相应的变量传递给每个函数。

让我们看看您的代码。 通常,您不需要那么多的全局变量。 它们中的大多数都可以进入主要功能。 您的代码中有很多printf 您也可以将它们外包给函数。 (您多次使用的所有内容都会进入一个函数)。

Float是一种用于计算分数的数据类型。 如果不需要它们,请不要使用它们(例如courseInfo )。

您的开关盒不容易阅读。 看一下enum数据类型Enums

这样,您的代码就更易于理解和维护。

希望能对您有所帮助。

尽管您已经对其中一个问题有了有效的答案,但是您的代码还有很多很多其他问题。 我赞扬您的学习努力,并且菜单项目始终是一种很好的学习体验,您必须正确处理输入内容,以避免出现Undefined Behavior或调用无限循环。

为什么scanf给新的C程序员带来麻烦

scanf因其滥用而引起的细微(而不是细微)问题而使新的C程序员感到满意。 具体来说, scanf (和家族)不会消耗用户输入的输入行中的所有字符。 此外,如果用户输入的内容与使用的转换说明符不匹配,则会发生匹配失败 ,字符提取停止,导致失败的字符留在输入流中, 未读,等待再次咬住您。下一次尝试输入。 复杂的scanf在输入流中留下的内容取决于所使用的转换说明符 除非程序员正确地处理了每一个问题,否则代码中都会发生不良情况。

使用scanf作为输入的问题与使用像fgets这样的面向行的输入函数的简单性形成对比,当提供足够大小的缓冲区时,它将消耗用户提供的整个输入行。 你不能被什么东西咬了scanf输入流中离开。 此外,在将整个输入行读取到适当大小的缓冲区中之后,可以使用sscanf从填充的缓冲区中解析所需信息,而不会出现匹配失败影响下一次尝试的用户输入的风险。

在使用scanf ,您无法每次都检查返回值,例如

    scanf("%f", &amount);

您无法知道amount是否实际包含值,或者用户是否滑过5键并轻按'r' 这是未定义行为的公开邀请。 此外,如我的评论中所述,您肯定会使用以下命令调用未定义行为

    scanf("%s", &newInvoice);

您不能使用%s保存到单个字符中。 至少一个字符串 (这是"%s"说明符表示的含义)需要2个字符的存储空间(一个用于字符,另一个用于以n 结尾的字符)

改用面向行的输入函数(例如POSIX getline fgets

相反,让我们看一下如何使用fgets并处理可能导致的任何输入错误。 首先,如果您需要一个常量来声明一个缓冲区,请#define大小或使用一个enum来完成同样的事情。 避免使用幻数 例如

#define NCRS    3   /* if you need a constant, #define one (or more) */
#define MAXA   64   /* (do NOT skimp on buffer sizes) */
#define MAXC 1024
...
int main (void) {

    char buf[MAXC] = "";    /* only variable needed outside loop */

    do {    /* loop continually until user chooses No more invoices */
        int sid, ncrs, n = 0, crsno[NCRS] = {0};
        double total = 0.0;

        /* get student ID (sid) */
        printf("\nPlease enter Student ID: ");
        if (fgets (buf, MAXC, stdin)) {             /* read entire line */
            if (sscanf (buf, "%d", &sid) != 1) {    /* parse int value */
                fputs ("error: invalid integer input.\n", stderr);
                continue;   /* on invalid int - get another */
            }
        }
        else {  /* on manual EOF (ctrl+d on Linux, or ctrl+z on windows) */
            fputs ("(user canceled input)\n", stderr);
            return 1;   /* gracefully exit */
        }
        ...

看上面发生了什么。 首先,将缓冲区的最大字符数( MAXC )定义为1024 ,这应该足以处理预期的输入(并处理踩在键盘上的猫声)。 buf[MAXC]声明为缓冲区,以保存用户输入,并调用fgets来存储包含学生ID( sid )的输入行,例如

        printf("\nPlease enter Student ID: ");
        if (fgets (buf, MAXC, stdin)) {             /* read entire line */
        ...
        }
        else {  /* on manual EOF (ctrl+d on Linux, or ctrl+z on windows) */
            fputs ("(user canceled input)\n", stderr);
            return 1;   /* gracefully exit */
        }

fgetsEOF上返回NULL ,否则返回指向buf的指针。 您检查退货以处理用户生成手动EOF (在Linux上为Ctrl + d,在Windows上为Ctrl + z )。 如果用户通过生成手动EOF取消输入,则通常将需要正常退出代码(或至少退出该输入块)。

输入行之后,可以调用sscanf来解析buf的值(就像您尝试在代码中调用scanf )。 但是,请注意,通过使用fgets ,您已经验证了是否有一行输入,现在您可以单独并独立地验证将该行中的信息解析为所需的值,例如

        ...
            if (sscanf (buf, "%d", &sid) != 1) {    /* parse int value */
                fputs ("error: invalid integer input.\n", stderr);
                continue;   /* on invalid int - get another */
            }

由于这是发票循环中的第一个输入,因此,如果读取了无效的整数,则只需continue并再次提示输入学生ID。 这样可以确保在继续执行程序之前输入有效的学生ID,否则用户将取消输入并退出。

相同的方法将应用于程序中的所有其余输入。

courseInfo提供有意义的返回类型将解决您的total问题

您提出的下一个问题是如何避免重复调用courseInfo来保持运行总计。 通过更改courseInfo返回类型,可以很容易地处理此courseInfo以便它(1)可以报告函数成功还是失败,以及(2)以提供所需信息的方式返回该信息(例如所用课程号的费用)作为函数的输入(或返回0表示提供了无效的课程号)。如注释中所述,您不应将浮点数用于货币,因为会出现舍入错误。(但是,在此处进行练习是可以的) ,但对于实际货币处理,请使用精确类型)

那么如何处理总数呢? 由于您的代码要求用户最多输入3个课程号,因此处理输入的一种简单方法就是将课程号读入数组。 然后,在确认它们是有效的课程号之后,您所需要做的就是遍历数组,将返回的成本添加到运行总计中。 例如,请注意循环顶部声明的课程编号数组( crsno[NCRS] )。 在用户输入课程号之后,您可以使用我们上面介绍的相同输入技术和验证来简单地循环填充数组,例如

        /* loop until array filled with ncrs valid course numbers */
        printf ("Enter the %d course number(s)\n", ncrs);
        do {
            printf ("  enter course[%d]: ", n + 1); /* add 1 for display */
            if (fgets (buf, MAXC, stdin)) {         /* read line/validate */
                if (sscanf (buf, "%d", &crsno[n]) == 1 &&
                    lookup (crsno[n])) {    /* lookup valid no. ? */
                    n++;    /* only increment array index if valid course */
                }
                else
                    fputs ("error: invalid course no.\n", stderr);
            }
            else {
                fputs ("(user canceled input)\n", stderr);
                return 1;
            }
        } while (n < ncrs);

现在, crsno数组保存用户请求的有效课程号的数量。 lookup (crsno[n])函数就是这样做的,它查询您的课程编号的查找表以确保用户输入的编号是有效的-如下例所示)

阵列填满后,您就可以准备输出发票了。 注意:您只需要一个printf调用即可输出连续的文本块(无论多少行)。 因此,要输出发票,您只需输出发票的标头信息,然后循环遍历crsno数组,将每个课程号传递给您的courseInfo函数,对课程总数求和,然后从courseInfo输出详细信息,最后输出包含总计和提示用户是否要打印其他发票,例如

        /* you only need 1 printf to output all continous text */
        printf ("\nVALENCE COMMUNITY COLLEGE\nORLANDO FL 10101\n"
                "---------------------\n\n"
                "Fee Invoice Prepared for Student V%d\n\n"
                "1 Credit Hour = $120.25\n"
                "CRN\tCR_PREFIX\tCR_HOURS\n", sid);

        /* loop over array outputting course specifics, summing total */
        for (int i = 0; i < ncrs; i++)
            total += courseInfo(crsno[i]);

        /* output total and prompt to print another */
        printf ("\tHealth & id fees\t$ 35.00\n\n"
                "--------------------------------------\n"
                "\tTotal Payments\t    $  %.2f\n\n"
                "Would you like to print another invoice (Y/N): "
                , total +35.00);

放在一起

简而言之,您可以执行以下类似操作。 考虑每个循环的逻辑流程。 如果您要询问用户是否要打印另一张发票,那么处理该发票的所有代码都必须包含在外部循环中。 还要注意变量在什么范围内声明。 发票循环外唯一声明的变量是buf ,这仅是因为该变量可用于所有输入,然后在do { } while ();while ()条件下进行比较do { } while (); 循环,要求它在循环外可见。

声明的唯一全局变量是查找表-通常是需要使用全局变量的有限情况之一。 否则,除buf外的所有变量都在发票循环的范围(主体)内声明,例如

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

#define NCRS    3   /* if you need a constant, #define one (or more) */
#define MAXA   64   /* (do NOT skimp on buffer sizes) */
#define MAXC 1024

int valid[] = { 4587, 4599, 8997, 9696, /* valid course lookup table */
                4580, 4581, 4582, 4583,
                3587, 4519, 6997 };

#define NVALID sizeof valid / sizeof *valid

/* simple lookup function, returns 1 if course valid, 0 otherwise */
int lookup (int course)
{
    for (size_t i = 0; i < NVALID; i++)
        if (course == valid[i])
            return 1;

    return 0;
}

/* function prototype for courseInfo - defintion moved to end */
double courseInfo (int myCourse);

int main (void) {

    char buf[MAXC] = "";    /* only variable needed outside loop */

    do {    /* loop continually until user chooses No more invoices */
        int sid, ncrs, n = 0, crsno[NCRS] = {0};
        double total = 0.0;

        /* get student ID (sid) */
        printf("\nPlease enter Student ID: ");
        if (fgets (buf, MAXC, stdin)) {             /* read entire line */
            if (sscanf (buf, "%d", &sid) != 1) {    /* parse int value */
                fputs ("error: invalid integer input.\n", stderr);
                continue;   /* on invalid int - get another */
            }
        }
        else {  /* on manual EOF (ctrl+d on Linux, or ctrl+z on windows) */
            fputs ("(user canceled input)\n", stderr);
            return 1;   /* gracefully exit */
        }

        for (;;) {  /* loop until valid number of courses (ncrs) entered */
            printf ("Enter how may courses-up to 3: ");
            if (fgets (buf, MAXC, stdin)) {           /* read entire line */
                if (sscanf (buf, "%d", &ncrs) != 1) { /* same validations */
                    fputs ("error: not an integer value\n", stderr);
                    continue;
                }
            }
            else {
                fputs ("(user canceled input)\n", stderr);
                return 1;
            }
            if (0 <= ncrs && ncrs <= 3) /* additional range check */
                break;
            else
                fputs ("error: valid no. of courses is 0-3.\n", stderr);
        }

        /* loop until array filled with ncrs valid course numbers */
        printf ("Enter the %d course number(s)\n", ncrs);
        do {
            printf ("  enter course[%d]: ", n + 1); /* add 1 for display */
            if (fgets (buf, MAXC, stdin)) {         /* read line/validate */
                if (sscanf (buf, "%d", &crsno[n]) == 1 &&
                    lookup (crsno[n])) {    /* lookup valid no. ? */
                    n++;    /* only increment array index if valid course */
                }
                else
                    fputs ("error: invalid course no.\n", stderr);
            }
            else {
                fputs ("(user canceled input)\n", stderr);
                return 1;
            }
        } while (n < ncrs);

        /* you only need 1 printf to output all continous text */
        printf ("\nVALENCE COMMUNITY COLLEGE\nORLANDO FL 10101\n"
                "---------------------\n\n"
                "Fee Invoice Prepared for Student V%d\n\n"
                "1 Credit Hour = $120.25\n"
                "CRN\tCR_PREFIX\tCR_HOURS\n", sid);

        /* loop over array outputting course specifics, summing total */
        for (int i = 0; i < ncrs; i++)
            total += courseInfo(crsno[i]);

        /* output total and prompt to print another */
        printf ("\tHealth & id fees\t$ 35.00\n\n"
                "--------------------------------------\n"
                "\tTotal Payments\t    $  %.2f\n\n"
                "Would you like to print another invoice (Y/N): "
                , total +35.00);

        if (!fgets (buf, MAXC, stdin))
            break;
    } while (tolower (*buf) == 'y');

    return 0;
}

/* choose meaningful return type that can indicate success/failure
 * and can also return needed information. returns cost of credits,
 * or zero indicating failure.
 */
double courseInfo (int myCourse)
{
    int credit1 = 0;
    double cost = 0.0;
    char a[MAXA];       /* don't use magic-number, use a constant */

    switch(myCourse)
    {
        case 4587:
            credit1 = 4;
            strcpy(a, "MAT 236");
            break;
        case 4599:
            credit1 = 3;
            strcpy(a,"COP 220");
            break;
        case 8997:
            credit1 = 1;
            strcpy(a, "GOL 124");
            break;
        case 9696:
            credit1 = 5;
            strcpy(a, "COP 100");
            break;
        case 4580:
            credit1 = 3;
            strcpy(a, "MAT 230");
            break;
        case 4581:
            credit1 = 4;
            strcpy(a, "MAT 231");
            break;
        case 4582:
            credit1 = 2;
            strcpy(a, "MAT 232");
            break;
        case 4583:
            credit1 = 2;
            strcpy(a,  "MAT 233");
            break;
        case 3587:
            credit1 = 4;
            strcpy(a, "MAT 256");
            break;
        case 4519:
            credit1 = 3;
            strcpy(a, "COP 420");
            break;
        case 6997:
            credit1 = 1;
            strcpy(a, "GOL 127");
            break;
        case 9494:
            credit1 = 3;
            strcpy(a, "COP 101");
            break;
        default:
            printf("Sorry invalid entry!\n\n");
            return 0;
    }

    cost = credit1 * 120.25;
    printf ("%.2d\t%s\t\t%d\t\t$ %.2f\n\n", 
            myCourse, a, credit1 , credit1 * 120.25);

    return cost;
}

注意:包含ctype.h可以将输入的Y/N字符转换为小写,这样一个比较就可以处理Yy以确定用户是否选择了另一个字符)

使用/输出示例

故意输入无效数字以强制执行代码中的错误处理。

$ ./bin/courseinv

Please enter Student ID: 1234
Enter how may courses-up to 3: 8997
error: valid no. of courses is 0-3.
Enter how may courses-up to 3: 3
Enter the 3 course number(s)
  enter course[1]: 8997
  enter course[2]: 4583
  enter course[3]: 4519

VALENCE COMMUNITY COLLEGE
ORLANDO FL 10101
---------------------

Fee Invoice Prepared for Student V1234

1 Credit Hour = $120.25
CRN     CR_PREFIX       CR_HOURS
8997    GOL 124         1               $ 120.25

4583    MAT 233         2               $ 240.50

4519    COP 420         3               $ 360.75

        Health & id fees        $ 35.00

--------------------------------------
        Total Payments      $  756.50

Would you like to print another invoice (Y/N): y

Please enter Student ID: 1235
Enter how may courses-up to 3: 2
Enter the 2 course number(s)
  enter course[1]: 10
error: invalid course no.
  enter course[1]: 3587
  enter course[2]: 6997

VALENCE COMMUNITY COLLEGE
ORLANDO FL 10101
---------------------

Fee Invoice Prepared for Student V1235

1 Credit Hour = $120.25
CRN     CR_PREFIX       CR_HOURS
3587    MAT 256         4               $ 481.00

6997    GOL 127         1               $ 120.25

        Health & id fees        $ 35.00

--------------------------------------
        Total Payments      $  636.25

Would you like to print another invoice (Y/N): n

最后,使用您编写的任何输入例程-尝试将其破坏。 如果损坏,请修复并重试。 这是您发现需要做更多工作的极端情况的唯一方法(可能还有很多,上面的代码尚未经过全面测试,但应该可以处理大多数可预见的滥用情况)

看一下,关键是验证,验证,验证 如果用户可以对您的代码做一些愚蠢的事情,他们会的。 从逻辑上看,应防止尽可能多的滥用。 如果您还有其他问题,请告诉我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM