函数会弄乱数字

  • 本文关键字:数字 会弄 函数 c
  • 更新时间 :
  • 英文 :


当我在for循环中通过main添加worker时,一切都很好。但是,当我用我的函数insert_one_with_pointersinsert_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),并使用该值来调整nizVLA(可变长度数组)的大小。一旦设置,就无法更改。因此,当您稍后尝试使用菜单添加其他辅助程序时,您会尝试在数组末尾之外进行写入(导致未定义的行为),从而导致您看到的问题输出。

您的替代方案是为员工动态分配存储,并跟踪您分配了多少存储和填充了多少存储,以及在filled == allocated时重新分配更多存储。另一种选择是简单地声明一个合理的最大工作者数量,然后声明一个由该数量组成的数组,跟踪添加的人数以及添加的工作者数量何时等于数组大小——只需指示数组已满即可。(尽管你不能扩大这里的员工数量)

关于使用VLA的附加说明。从C11标准开始,对VLA的编译器支持是可选的——尽管我不知道有什么编译器不继续支持它们。

代码中的弱点

最明显的弱点是当需要数字转换时,您没有检查scanf()的返回。这会带来问题。为什么?scanf()可能以两种方式失败(1)匹配失败,其中提供的输入与指定的转换不匹配。(例如,用户输入年龄"twenty-one"而不是21)。当匹配失败时,从输入缓冲区中提取字符的操作会在失败时停止——输入缓冲区中将"twenty-one"保留为未读状态——等待下一次尝试输入。

事实上,如果匹配失败发生在您进入菜单while(1)循环之前,那么您的代码将锁定在一个无限循环中。

(2)scanf()可能会因输入失败而失败,其中EOF在第一次有效转换发生之前到达。(用户可以通过在窗口上使用Ctrl+d或`

Ctrl+z您的方法的另一个弱点是使用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。

将上面的所有建议放在一起,并为workers数组使用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

相关内容

最新更新