当我在for
循环中通过main添加worker时,一切都很好。但是,当我用我的函数insert_one_with_pointers
或insert_multiple_workers
之一添加它们,并用我的第三个函数display_all_workers
检查所有工作者时,我发现有问题。
它们的id
值不正确,有时程序会崩溃,或者没有显示所有的值,只是在main的开头插入的值。
我刚开始研究结构和函数,所以我不确定到底是什么原因导致了这种情况,但我认为这与我可能不正确地使用i
有关。我试着解决这个问题已经有一段时间了,但没有成功。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Worker {
int id;
char name[40];
int age;
};
void insert_one_with_pointers(struct Worker *niz, int i) {
niz[i].id = i + 1;
printf("nWhat is name of %d. worker: ", i + 1);
scanf("%s", niz[i].name);
printf("What is age of %d. worker: ", i + 1);
scanf("%d", &niz[i].age);
}
void insert_multiple_workers(struct Worker niz[], int i, int number) {
int j;
for (j = i; j < number; j++) {
niz[j].id = j + 1;
printf("nWhat is name of %d. worker: ", j + 1);
scanf("%s", niz[j].name);
printf("What is age of %d. worker: ", j + 1);
scanf("%d", &niz[j].age);
}
}
void display_all_workers(struct Worker niz[], int i) {
printf("nAll workers:n");
for (int j = 0; j < i; j++) {
printf("nID: %d", niz[j].id);
printf("nName: %s", niz[j].name);
printf("nAge: %d", niz[j].age);
}
}
int main() {
int choice, i, m, number;
printf("Add a number of workers and then their info: ");
scanf("%d", &i);
struct Worker niz[i];
for (int j = 0; j < i; j++) {
niz[j].id = j + 1;
printf("nWhat is name of %d. worker: ", j + 1);
scanf("%s", niz[j].name);
printf("What is age of %d. worker: ", j + 1);
scanf("%d", &niz[j].age);
}
while(1) {
printf("nChoose a function:n1 - Add a worker using pointersn2 - Add n workersn3 - Display all workersn4 - Leaven");
scanf("%d", &choice);
switch(choice) {
case 1:
insert_one_with_pointers(niz, i); i++;
break;
case 2:
printf("nHow many workers do you want to insert? ");
scanf("%d", &m);
number = i + m;
insert_multiple_workers(niz, i, number);
break;
case 3:
display_all_workers(niz, i);
break;
case 4:
return 0;
}
}
return 0;
}
编辑:我遵循了回复我帖子的所有内容,这修复了大部分内容。最后的变化是我添加了I=I+m;在main中函数insert_more_workers的调用下面。现在一切正常。
继续我的评论,您面临的主要问题是在程序开始时设置工作者数量(i
),并使用该值来调整niz
VLA(可变长度数组)的大小。一旦设置,就无法更改。因此,当您稍后尝试使用菜单添加其他辅助程序时,您会尝试在数组末尾之外进行写入(导致未定义的行为),从而导致您看到的问题输出。
您的替代方案是为员工动态分配存储,并跟踪您分配了多少存储和填充了多少存储,以及在filled == allocated
时重新分配更多存储。另一种选择是简单地声明一个合理的最大工作者数量,然后声明一个由该数量组成的数组,跟踪添加的人数以及添加的工作者数量何时等于数组大小——只需指示数组已满即可。(尽管你不能扩大这里的员工数量)
关于使用VLA的附加说明。从C11标准开始,对VLA的编译器支持是可选的——尽管我不知道有什么编译器不继续支持它们。
代码中的弱点
最明显的弱点是当需要数字转换时,您没有检查scanf()
的返回。这会带来问题。为什么?scanf()
可能以两种方式失败(1)匹配失败,其中提供的输入与指定的转换不匹配。(例如,用户输入年龄"twenty-one"
而不是21
)。当匹配失败时,从输入缓冲区中提取字符的操作会在失败时停止——输入缓冲区中将"twenty-one"
保留为未读状态——等待下一次尝试输入。
事实上,如果匹配失败发生在您进入菜单while(1)
循环之前,那么您的代码将锁定在一个无限循环中。
(2) Ctrl+z您的方法的另一个弱点是使用 替代方案 虽然两个不同的输入函数可能是家庭作业的要求,但不需要不同的输入功能来读取一个或多个工作者。只需编写一个可以同时处理这两种情况的函数。由于输入函数可以成功也可以失败,因此需要向调用方提供有意义的返回,以便在调用时验证输入函数的成功或失败。 在 需要覆盖的全部内容: 为了提高代码的可读性,请理解在编译过程中相邻的字符串文字是连接在一起的。这允许您提供可读性更强的代码。例如,您的菜单可以写成: 将上面的所有建议放在一起,并为workers数组使用 (注意:如何将工作人员数 使用 示例代码中显示的错误处理并非详尽无遗,还可以添加更多内容。但是,请注意,通过使用 如果你仍然坚持使用你的代码,或者如果你对上面的更改有进一步的问题,或者为什么一种方法比另一种方法有优势,只需在下面留言,我很乐意提供进一步的帮助。 示例使用/输出 以下是测试代码的一组输入/输出:scanf()
可能会因输入失败而失败,其中EOF
在第一次有效转换发生之前到达。(用户可以通过在窗口上使用Ctrl+d或`scanf()
进行用户输入。为什么?因为在匹配失败的情况下,或者在任何使用之后,stdin
中的字符都是未读的。在正常情况下,'n'
保持未读状态,如果您的下一个输入没有与scanf()
一起进行,并且您没有提取未读字符,则您的下个输入将失败。这是与使用scanf()
相关的(许多)陷阱之一。相反,作为一个一般命题,所有用户输入都应该使用fgets()
,然后任何转换都应该由sscanf()
从由fgets()
填充的缓冲区中处理。这样,有了足够大小的缓冲区,无论后续转换是否失败,都可以保证使用每一行输入。没有未读的字符会影响下一次输入尝试。void
返回类型仅适用于与代码的继续操作无关的函数(如打印函数等)switch()
语句中,应该处理超出范围的菜单项。如果用户滑动并输入5
、-32
或"foo"
,该怎么办?我们解决了上面的匹配失败,但要处理超出范围的输入,只需提供default:
情况就足够了。例如:default: fputs (" error: selection out of range.n", stderr);
break;
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
5
error: selection out of range.
printf ("nChoose a function:n"
" 1 - Add a worker using pointersn"
" 2 - Add n workersn"
" 3 - Display all workersn"
" 4 - Leaven");
display_all_workers()
函数也可以从这种方法中受益,将对printf()
的调用次数从3减少到1。200
元素的标准数组,您可以将代码重写为:#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXWRKRS 200 /* if you need a constant, #define one (or more) */
#define MAXC 1024
struct Worker
{
int id;
char name[40];
int age;
};
int insert_workers (struct Worker *workers, int *nworkers, int toadd)
{
char buf[MAXC] = "";
int i = 0, start_index = *nworkers;
size_t len = 0;
while (i < toadd) { /* loop nworkers times */
int current = i + start_index;
if (current == MAXWRKRS) { /* check array bounds */
puts ("(workers array full)");
return i;
}
printf ("nWhat is name of %d. worker: ", current + 1);
if (!fgets (buf, MAXC, stdin)) { /* read all input into buf */
return i;
}
buf[(len = strcspn (buf, "n"))] = 0; /* trim 'n', save length */
memcpy (workers[current].name, buf, len + 1);
for (;;) { /* loop continually until valid integer entered */
printf ("What is age of %d. worker: ", current + 1);
if (!fgets (buf, MAXC, stdin)) { /* read all input into buf */
return i;
}
/* parse needed information from buf with sscanf() */
if (sscanf (buf, "%d", &workers[current].age) != 1) { /* check return */
fputs (" error: invalid integer input.n", stderr);
continue;
}
else {
workers[current].id = current + 1; /* set worker ID, done */
*nworkers += 1; /* all conditions met - update worker count */
break;
}
}
i++;
}
return i; /* return number of workers added (can validate in caller) */
}
void display_all_workers (struct Worker *workers, int nworkers)
{
puts("nAll workers:");
for (int j = 0; j < nworkers; j++) {
printf ("n ID : %dn"
" Name : %sn"
" Age : %dn",
workers[j].id, workers[j].name, workers[j].age);
}
}
int main()
{
char buf[MAXC] = ""; /* buffer for all user input */
int i, nwrkrs = 0; /* tmp counter & number of workers */
struct Worker workers[MAXWRKRS]; /* array of workers */
printf("Add a number of workers and then their info: ");
if (!fgets (buf, MAXC, stdin)) {
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &i) != 1) {
fputs ("errur: invalid integer input.n", stderr);
return 1;
}
printf ("%d workers addednn", insert_workers (workers, &nwrkrs, i));
while(1) {
int choice;
printf ("nChoose a function:n"
" 1 - Add a worker using pointersn"
" 2 - Add n workersn"
" 3 - Display all workersn"
" 4 - Leaven");
if (!fgets (buf, MAXC, stdin)) {
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &choice) != 1 ) {
fputs ("error: invalid integer input.n", stderr);
continue;
}
switch (choice) {
case 1 : insert_workers (workers, &nwrkrs, 1);
break;
case 2 : printf("nHow many workers do you want to insert? ");
if (!fgets (buf, MAXC, stdin)) {
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &i) != 1) {
fputs ("error: invalid integer input.n", stderr);
break;
}
insert_workers (workers, &nwrkrs, i);
break;
case 3 : display_all_workers (workers, nwrkrs);
break;
case 4 : return 0;
default: fputs (" error: selection out of range.n", stderr);
break;
}
}
return 0;
}
nworkers
作为指向insert_workers()
函数的指针传递,以便在输入有效姓名和年龄时可以在函数内更新工作人员数)fgets()
进行输入的一个直接好处是,现在可以输入包含空格的名称。另一个是数字输入中的错误很容易处理,例如...
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
1
What is name of 6. worker: Bugs Bunny
What is age of 6. worker: dunno - really
error: invalid integer input.
What is age of 6. worker: 101
...
fgets()
并检查返回是否为NULL
(例如if (!fgets(...))
),您正在处理用户生成手动EOF
以取消输入的情况。./bin/workers_fn_new
Add a number of workers and then their info: 2
What is name of 1. worker: Mickey Mouse
What is age of 1. worker: 99
What is name of 2. worker: Minnie Mouse
What is age of 2. worker: 97
2 workers added
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
1
What is name of 3. worker: Pluto (the dog)
What is age of 3. worker: 92
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
ID : 3
Name : Pluto (the dog)
Age : 92
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
2
How many workers do you want to insert? 2
What is name of 4. worker: Daffy Duck
What is age of 4. worker: 93
What is name of 5. worker: Daisy Duck
What is age of 5. worker: 91
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
ID : 3
Name : Pluto (the dog)
Age : 92
ID : 4
Name : Daffy Duck
Age : 93
ID : 5
Name : Daisy Duck
Age : 91
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
1
What is name of 6. worker: Bugs Bunny
What is age of 6. worker: dunno - really
error: invalid integer input.
What is age of 6. worker: 101
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
3
All workers:
ID : 1
Name : Mickey Mouse
Age : 99
ID : 2
Name : Minnie Mouse
Age : 97
ID : 3
Name : Pluto (the dog)
Age : 92
ID : 4
Name : Daffy Duck
Age : 93
ID : 5
Name : Daisy Duck
Age : 91
ID : 6
Name : Bugs Bunny
Age : 101
Choose a function:
1 - Add a worker using pointers
2 - Add n workers
3 - Display all workers
4 - Leave
4