我有一个程序,可以选择大学课程,然后根据学时分配价格。目前,我已经创建了一个函数,该函数运行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函数,那么我会为它选择太多而产生错误。不知道这是否可能。谢谢
答案 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 。如果用户可以对您的代码做一些愚蠢的事情,他们会的。从逻辑上看,应防止尽可能多的滥用。如果您还有其他问题,请告诉我。