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

时间:2019-02-07 23:11:59

标签: 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函数,那么我会为它选择太多而产生错误。不知道这是否可能。谢谢

3 个答案:

答案 0 :(得分:1)

回答您的问题:是的,那完全没有问题。根据变量的用途(在赋值,加法等方面),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

答案 1 :(得分:0)

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

为什么要对必须精确的数字使用浮点值是个坏主意,请查看以下维基页面:IEE 754 wiki

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

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

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

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

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

希望我能为您提供帮助。

答案 2 :(得分:0)

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

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

scanf因使新的C程序员因使用不当而引起的细微(而不是细微)问题而变得臭名昭著。具体来说,scanf(和家族)不会在用户输入的输入行中占用所有字符。此外,如果用户输入的内容和使用的转换说明符之间不匹配,则会发生匹配失败,字符提取停止,并且导致失败的字符保留在输入流< em> unread 等待下一次尝试输入时再次咬你。复杂的事情是,scanf在输入流中留下的 取决于所使用的转换说明符。除非程序员正确地处理了每一个问题,否则代码中都会发生不良情况。

对比一下使用scanf进行输入的问题,以及使用像fgets这样的面向 line _ 的输入函数的简单性,该函数在提供足够大小的缓冲区时会消耗用户提供的整个输入行。您不会被输入流中剩余的scanf所咬住。此外,在将整行输入读入适当大小的缓冲区后,sscanf可用于从已填充的缓冲区解析需要的信息,而不会出现匹配失败影响下一个尝试的用户的风险输入。

在使用scanf时,您每次都无法检查退货,例如

    scanf("%f", &amount);

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

    scanf("%s", &newInvoice);

您不能使用%s保存到单个字符中。至少 string (这是"%s"指示符所表示的,需要2个字符的存储空间(一个用于字符,一个用于终止符) )

改为使用面向行的输入功能(例如POSIX fgets的{​​{1}})

我们来看看如何使用getline并处理可能导致的任何输入错误。首先,如果您需要一个常量来声明缓冲区,请使用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]的调用被存储了包含学生ID(fgets)的输入行,例如

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 */ } fgets上返回NULL,否则返回指向EOF的指针。您检查退货以处理用户生成手册buf(在Linux上为 Ctrl + d 或在Windows上为 Ctrl + z )的情况。如果用户通过生成手册EOF取消输入,则通常会希望正常退出代码(或至少退出该输入块)。

输入行之后,可以调用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 */ } 并再次提示输入学生ID。这样可以确保您在继续学习程序之前输入了有效的学生ID,否则用户将取消输入并退出。

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

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

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

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

)简单地循环填充数组。
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函数中,对总数求和并从{{1 }},最后输出总计的页脚信息,并提示用户是否要打印其他发票,例如

courseInfo

全部放入

将其完全放在一个简短的示例中,您可以执行以下操作。考虑每个循环的逻辑流程。如果您要询问用户是否要打印另一张发票,那么处理该发票的所有代码都必须包含在外部循环中。还要注意变量在什么 scope 中声明。发票循环外唯一声明的变量是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条件下进行比较,要求在外部可见循环。

声明的唯一全局变量是查找表-通常这是需要使用全局变量的有限情况之一。否则,除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

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

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