这里需要一些帮助。有点不为人知的故事,自从我开始编码已经1个月了。从C语言开始,现在我才知道Structure和Arrays。下周将做指针。所以,这已经成为问题,而已。在这里,我将链接我的源代码,所需的输出以及程序给出的输出。
这是一个员工记录,您可以在其中添加受限员工,查看添加的员工,将其删除和更新。使用C语言且没有指针的基本初学者雇员记录。我做的一切正确,除了最后搞砸了。您可以从我得到的输出中看到与我想要的输出有些不同。
在删除员工后,它也会被删除,但是当我在删除旧员工后添加新员工时,当我想查看那里有多少员工时,它不会显示。从我提供的2个输出中可以看到,当我打印出已保存的员工时,除最后一个输出外,其他都相同。我在这里没有得到我的源代码有什么问题。
是否可以为我提供解释和缺少的代码行,这将使我的代码正常工作并输出所需的输出?
我们将Visual Studio 2017用于C语言(我的学院推荐)。 并且需要通过pUTTY(矩阵服务器)提交输出。
我的源代码-可以在此处https://pastebin.com/spLhtrt5的链接中找到,源代码也在此处提供
#include <stdio.h>
#define SIZE 4
struct Emp {
int id;
int age;
double sal;
};
int main (void) {
int i, option = 0, counter = 0, new = 0, srch, don = 0;
struct Emp emp[SIZE] = { {0} };
printf ("---=== EMPLOYEE DATA ===---\n\n");
do {
printf ("1. Display Employee Information\n");
printf ("2. Add Employee\n");
printf ("3. Update Employee Salary\n");
printf ("4. Remove Employee\n");
printf ("0. Exit\n\n");
printf ("Please select from the above options: ");
scanf ("%d", &option);
printf ("\n");
switch (option) {
case 0: // Exit the program
break;
case 1: // Display Employee Data
// @IN-LAB
printf ("EMP ID EMP AGE EMP SALARY\n");
printf ("====== ======= ==========\n");
// Use "%6d%9d%11.2lf" formatting in a
// printf statement to display
// employee id, age and salary of
// all employees using a loop construct
// The loop construct will be run for SIZE times
// and will only display Employee data
// where the EmployeeID is > 0
for (i = 0; i < SIZE; i++) {
if (emp[i].id > 0 && emp[i].age > 0 && emp[i].sal > 0) {
printf ("%6d%9d%11.2lf\n", emp[i].id, emp[i].age,
emp[i].sal);
}
}
printf ("\n");
break;
case 2: // Adding Employee
// @IN-LAB
printf ("Adding Employee\n");
printf ("===============\n");
if (counter < SIZE) {
printf ("Enter Employee ID: ");
scanf ("%d", &emp[new].id);
printf ("Enter Employee Age: ");
scanf ("%d", &emp[new].age);
printf ("Enter Employee Salary: ");
scanf ("%lf", &emp[new].sal);
new++;
counter++;
printf ("\n");
break;
} else {
printf ("ERROR!!! Maximum Number of Employees Reached\n\n");
}
break;
case 3:
don = 0;
printf ("Update Employee Salary\n");
printf ("======================\n");
do {
printf ("Enter Employee ID: ");
scanf ("%d", &srch);
for (i = 0; i < SIZE; i++) {
if (emp[i].id == srch) {
printf ("The current salary is %.2lf\n", emp[i].sal);
printf ("Enter Employee New Salary: ");
scanf ("%lf", &emp[i].sal);
don = 1;
printf ("\n");
}
}
if (don == 0) {
printf ("*** ERROR: Employee ID not found! ***\n");
}
} while (don != 1);
break;
case 4:
don = 0;
printf ("Remove Employee\n");
printf ("===============\n");
do {
printf ("Enter Employee ID: ");
scanf ("%d", &srch);
for (i = 0; i < SIZE; i++) {
if (emp[i].id == srch) {
printf ("Employee %d will be removed", emp[i].id);
emp[i].id = 0;
emp[i].age = 0;
emp[i].sal = 0;
counter--;
don = 1;
printf ("\n\n");
}
}
if (don == 0) {
printf ("*** ERROR: Employee ID not found! ***\n");
}
} while (don != 1);
break;
default:
printf ("ERROR: Incorrect Option: Try Again\n\n");
}
} while (option != 0);
printf ("Exiting Employee Data Program. Good Bye!!!\n");
return 0;
}
我想要的输出-https://pastebin.com/CcrXibB4。
我得到的输出-https://pastebin.com/mmvjBVL9。
答案 0 :(得分:1)
您要在索引new
的位置插入新员工。当您在插入4名员工之后删除时,new仍指向4,位于阵列之外,因此您的新员工将被写入数组之外,并且从不显示,这也会破坏堆栈。
可能的解决方案:添加新员工时,请扫描阵列以查找空闲位置(例如,如果我正确理解您的逻辑,请找到ID == 0的员工),然后将其位置用作插入索引。
对于与您的代码有关的问题,我也有一些一般性建议:
通常,这可以加快调试速度,如果不是这种情况,至少您可以在此处发布一个更小的示例。
答案 1 :(得分:1)
您的代码中存在大量错误。首先,不要循环for (i = 0; i < SIZE; i++)
,而只能循环填充的那些数组元素,例如for (i = 0; i < counter; i++)
。
您不需要变量new
(它是C ++中的关键字)。只需将其删除。您应该在那儿使用counter
。
您无法检查scanf
的返回值,因此在每次用户输入时都无法敲击 Undefined Behavior 。始终,总是至少检查返回来 验证 所有用户输入。
scanf
。这意味着您负责每次检查scanf
的退货。您必须处理三个条件
(return == EOF)
用户通过按 Ctrl + d (或在Windows Ctrl + z 上)生成手册EOF
来取消输入。 CTRL+Z does not generate EOF in Windows 10 (early versions)); (return < expected No. of conversions)
发生匹配或 input 失败。对于 matching 失败,您必须考虑输入缓冲区中剩余的每个字符。 (在输入缓冲区中向前扫描以读取和丢弃字符,直到找到'\n'
或EOF
为止);最后(return == expected No. of conversions)
表示读取成功-然后由您检查输入是否满足任何其他条件(例如,正整数,正浮点,在所需范围内,等等。)。 / li>
注意:在匹配失败或成功读取之后,您应该清空输入缓冲区,以确保为下一次用户输入做好准备。使用您的代码尝试输入w
(作为3
的未击键),然后看看会发生什么。此外,如果用户在手指跳过"3'"
键以进入 Enter 时输入'
,该怎么办?
那么您如何验证每个输入?在您的情况下,只需添加另一个变量int rtn;
来捕获每次scanf
的返回值,然后通过生成手册EOF
来验证用户是否已取消输入,或者返回值是否为{ {1}},然后继续操作,例如
0
什么是 rtn = scanf ("%d", &option); /* always validate scanf return */
if (rtn == EOF)
option = 0; /* handle EOF by setting exit condition */
else if (rtn == 0) { /* otherwise throw error & empty stdin */
fputs ("error: invalid input.\n", stderr);
empty_stdin(); /* always empty stdin after error */
continue;
}
?它只是您编写的一个辅助函数,它在输入缓冲区(empty_stdin()
)中向前扫描,丢弃字符,直到找到stdin
(当用户按下 Enter 时创建) )或'\n'
,例如
EOF
接下来,一旦您每次停止循环遍历void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
并像应该那样简单地使用case 4:
,您的"Remove Employee"
,SIZE
就将永远无法工作。通常,您不希望简单地将每个删除的成员设置为counter
,从而使您不知道下一个添加项应该去哪里。取而代之的是,通过将下一个元素复制到当前索引中(例如
id
)的末尾。 >
counter
(注意:您使用 /* only loop over filled struct */
for (i = 0; i < counter; i++) {
if (emp[i].id == srch) {
printf ("Employee %d will be removed", emp[i].id);
for (int j = i + 1; j < counter; j++) {
emp[j - 1].id = emp[j].id;
emp[j - 1].age = emp[j].age;
emp[j - 1].sal = emp[j].sal;
}
counter--;
don = 1;
printf ("\n\n");
}
}
标志来确定是否找到索引是非常好的)
还有几十个小虱子需要清理。喜欢:
don
输出文本时,如果涉及到转换需要 putchar ('\n'); /* putchar outputs a single-character (not printf) */
转换说明符之一,则只需使用printf
,否则只需使用{{1 }}(或printf
,如果您不需要puts
行尾)。此外,您只需要一次调用fputs
(或'\n'
或printf
即可输出任意数量的行)。您不需要每行调用一个函数。例如,以下可以正常工作:
puts
(注意:开头的fputs
消除了下面的所有 fputs ( "\n1. Display Employee Information\n"
"2. Add Employee\n"
"3. Update Employee Salary\n"
"4. Remove Employee\n"
"0. Exit\n\n"
" choice: ", stdout);
)
对于'\n'
或printf ("\n");
,如果在将任何内容添加到列表之前从菜单中选择了这两种情况中的任何一种会发生什么? (例如,case 3:
时)。你有点卡住了,不是吗。对于需要数据存在的任何操作,请在继续操作之前始终验证是否有数据。仅需对case 4:
进行简单检查,例如
counter == 0
(注意:同样应适用于counter
)
case 3:
don = 0;
puts ( "Update Employee Salary\n"
"======================");
if (counter == 0) {
puts ("(list is empty)");
break;
}
...
是case 1:
定义整数常量的很好且正确的用法,但是为什么要限制自己成为#define SIZE 4
员工呢?使其有趣。使用#define
或4
等。至少这样您将拥有一家中等规模的公司。另外,由于您不再在每个128
循环中都遍历2048
,所以无论限制有多大,您都不会受到额外的惩罚。
从代码分解的角度来看,您应该通过为常见任务创建函数来使代码更具功能性和减少重复性。就像接受验证整数输入一样。处理添加,更新和删除员工等问题。我了解到在学习的同时查看所有顺序编写的代码很有帮助,因此当控件从一个函数传递到另一个函数时,您不会按照逻辑向上或向下跳转,而是寻找重复的代码继续前进(例如示例中的SIZE
),并努力将这些通用的代码移入函数中。
我可能忽略了很多其他的细节,但是如果您进行了上述概述,请验证所有输入,在下一次输入之前清空for
中的所有多余字符,等等。您的代码将正常工作,并且界面将更加健壮。进行更改的简短示例(不是那么简短)是:
empty_stdin()
仔细检查一下,如果还有其他问题,请告诉我。
答案 2 :(得分:0)
罢免员工不会减少您的new
柜台。
将case 4:
的代码更改为此:
don = 0;
printf("Remove Employee\n");
printf("===============\n");
do {
printf("Enter Employee ID: ");
scanf("%d", &srch);
for (i = 0; i < SIZE; i++) {
if (emp[i].id == srch) {
printf("Employee %d will be removed", emp[i].id);
emp[i].id = 0;
emp[i].age = 0;
emp[i].sal = 0;
counter--;
new--; // This is the important bit
don = 1;
printf("\n\n");
}
}if (don == 0) {
printf("*** ERROR: Employee ID not found! ***\n");
}
} while (don != 1);
break;
它应该可以工作。
您的程序应该做的是更改new
变量以表示员工数组中未使用的最低点,方法是在添加元素时增加变量,在删除元素时减少变量。
但是,您忘了递减索引,使索引无限期递增。