你有这么多的小问题,很难知道从哪里开始。首先,在引用结构成员时,您永远不会在 "->" 周围包含空格。使用p->name,而不是p -> name。继续...
您未能验证scanf 的返回。你必须每次都检查返回,否则你是在诱惑Undefined Behavior。您还必须将"%99[^\n]" 更改为" %79[^\n]",因为"%c" 或"%[...]" 都不使用前导空格。未能在%12[^\n] 之前添加" " 将导致无法读取p->ssn 并导致匹配失败 读取p->yearOfBirth。
注意从99 更改为79。你#define NAME_SIZE 80 并声明char name[NAME_SIZE];,当最多79 字符可以存储在name 中时,你认为使用99 的field-width 修饰符在做什么? (#define SSN_SIZE 13 也有同样的问题)。您可以使用带有scanf 的field-width 修饰符来保护您的数组边界。将 *field-width 修饰符设置为大于数组大小 (-1) 会完全移除它应该提供的保护。
如果用户不小心在输入中犯了一个错误,您未能检查scanf 的返回并处理三种必要的返回情况将导致未定义的行为。未能检查scanf 的返回是新C 程序员最容易陷入的陷阱之一。每个用户输入都是强制性的。否则,您无法确信您的代码实际上正在处理有效数据。
scanf 可以使用,如果使用正确的话。这意味着您有责任检查scanf的返回每次。你必须处理三个条件
-
(return == EOF) 用户通过按 Ctrl+d(或在 Windows 上 Ctrl+z,但参见 CTRL+Z does not generate EOF in Windows 10 (early versions))生成手动 EOF 来取消输入;李>
-
(return < expected No. of conversions) 匹配或输入失败。对于 匹配 失败,您必须考虑输入缓冲区中剩余的每个字符。 (在输入缓冲区中向前扫描读取并丢弃字符,直到找到'\n' 或EOF);最后
-
(return == expected No. of conversions) 表示读取成功 - 然后由您来检查输入是否满足任何其他条件(例如正整数、正浮点、在所需范围内等)。
在匹配失败的情况下清空stdin中所有剩余字符的简短函数实现可以很简单:
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
(在您的代码中实现留给您作为练习)
此外,使用类型void 作为输入函数的返回是没有意义的。您必须选择您的退货以提供所需信息的退货并提供输入是成功还是失败的指示。将void 用于getOnePerson() 意味着您无法知道您是否收到了所有有效输入,或者只收到了name,但没有收到ssn,或者用户是否只是生成了手动EOF 以取消每个输入迅速的。您只需要一个简单的整数返回(例如 return 0; 失败或 return 1; 仅在所有 3 输入都经过验证后)您可以执行以下操作:
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
/* validate each input for all 3 cases */
fputs ("\nEnter full name: ", stdout); /* no need for printf, no conversion */
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
/* validate each input for all 3 cases */
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1; /* indicates all 3 input successfully received */
}
(注意:输入完成时遇到EOF,无论是用户手动生成的还是在输入流中遇到)
void 作为getPeople() 的返回也毫无意义。您不能使用for 循环并假设所有输入都成功,相反,您只需要在输入可用时接受输入,同时保护您的数组边界,然后返回实际接收的输入数(可能是小于NUM_PEOPLE)。此外,请正确选择您的类型。对于计数器,size_t 是正确的类型(您不能有负数),例如
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
当您将数组作为参数传递给函数时,数组被转换为指针,指向第一个元素。因此,当您在函数中执行sizeof(p) 时——这不是您想要的,并且不提供p 引用的数组中的元素数量——它提供的是sizeof(a_pointer),它由您的编译器(例如 x86_64 上为 8 字节,x86 上为 4 字节)。你通过numOfPeople - 使用它,例如
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
您还需要修复printf("%s\n", p.yearOfBirth);(yearOfBirth 不是字符串...)
您的标题很好,但缺少一些东西。始终在头文件的内容周围包含标头保护,以防止文件被多次包含,例如
#ifndef mystructures_h
#define mystructures_h 1
...
/* your header content */
...
#endif
(注意:1 不是必需的,但如果您要定义一个常量,最好给它一个您选择的肯定值)
可能还有更多被纠正的地方,但这些是重点。总而言之,您可以这样做:
结构.h
#ifndef mystructures_h
#define mystructures_h 1
#include <stdio.h>
#define NAME_SIZE 80
#define SSN_SIZE 13
#define NUM_PEOPLE 10
typedef struct {
char name[NAME_SIZE];
char ssn[SSN_SIZE];
int yearOfBirth;
} person_t;
size_t getPeople (person_t *p, size_t numOfPeople);
void printPeople (person_t *p, size_t numOfPeople);
#endif
(你能弄清楚为什么#include <stdio.h>从structures.c移动到structures.h吗?你知道为什么getPeople()和printPeople()的函数原型需要在标题中而不是其余部分吗?)
结构.c
#include "structures.h"
int getOnePerson (person_t *p)
{
int rtn; /* scanf return */
fputs ("\nEnter full name: ", stdout);
if ((rtn = scanf (" %79[^\n]", p->name)) != 1) {
if (rtn == EOF)
puts ("(input complete)");
else
fputs ("error: invalid format 'p->name'.\n", stderr);
return 0;
}
fputs ("Enter ssn: ", stdout); /* ditto */
if ((rtn = scanf (" %12[^\n]", p->ssn)) != 1) { /* " */
if (rtn != EOF)
fputs ("error: invalid format 'p->ssn'.\n", stderr);
return 0;
}
fputs ("Enter year of birth: ", stdout);
if ((rtn = scanf ("%d", &p->yearOfBirth)) != 1) {
if (rtn != EOF)
fputs ("error: invalid format 'p->yearOfBirth'.\n", stderr);
return 0;
}
return 1;
}
size_t getPeople (person_t *p, size_t numOfPeople)
{
// for(int i = 0; i < sizeof(p); i++)
// {
// getOnePerson(p[i]);
// }
size_t n = 0;
while (n < numOfPeople && getOnePerson (&p[n]))
n++;
return n;
}
void printOnePerson (person_t p)
{
printf("%s:", p.name);
printf("%s:", p.ssn);
// printf("%s\n", p.yearOfBirth);
printf("%d\n", p.yearOfBirth);
}
void printPeople (person_t *p, size_t numOfPeople)
{
puts ("\nStored People\n");
// for(int i = 0; i < sizeof(p); i++)
for (size_t i = 0; i < numOfPeople; i++)
{
printOnePerson(p[i]);
}
}
一个简短的测试程序peopletest.c
#include "structures.h"
int main (void) {
person_t people[NUM_PEOPLE] = {{ .name = "" }};
size_t npeople = getPeople (people, NUM_PEOPLE);
printPeople (people, npeople);
}
使用/输出示例
$ ./bin/peopletest
Enter full name: Person A. One
Enter ssn: 123456789
Enter year of birth: 2001
Enter full name: Person B. Two
Enter ssn: 234567890
Enter year of birth: 2002
Enter full name: Person C. Three
Enter ssn: 345678901
Enter year of birth: 2003
Enter full name: (input complete)
Stored People
Person A. One:123456789:2001
Person B. Two:234567890:2002
Person C. Three:345678901:2003
检查一下,如果您还有其他问题,请告诉我。