【问题标题】:Working on a c homework assignment and getting errors I do not understand做 c 家庭作业并遇到我不明白的错误
【发布时间】:2019-04-01 01:41:27
【问题描述】:

我正在为“人”的结构制作typedef 这个人有name, ssnyearOfBirth。我的 for 循环出现了我不理解的错误。

[Error] cannot convert 'person_t' to 'person_t*' for argument '1' to 
'void getOnePerson(person_t*)'

这是第一个文件:

#include <stdio.h>
#include <string.h>
#include "structures.h"
void getOnePerson(person_t *p)
{
    printf("Enter full name: ");
    scanf("%99[^\n]", p -> name);

    printf("Enter ssn: ");
    scanf("%99[^\n]", p -> ssn);

    printf("Enter year of birth: ");
    scanf("%d", &p -> yearOfBirth);
}
void printOnePerson(person_t p)
{
    printf("%s:", p.name);
    printf("%s:", p.ssn);
    printf("%s\n", p.yearOfBirth);

}
void getPeople(person_t p[], int numOfPeople)
{
    for(int i = 0; i < sizeof(p); i++)
    {
        getOnePerson(p[i]);
    }
}
void printPeople(person_t p[], int numOfPeople)
{
    for(int i = 0; i < sizeof(p); i++)
    {
        printOnePerson(p[i]);
    }
}

这是我的结构文件:

#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;

【问题讨论】:

  • 错误是什么?
  • 为什么在c 问题上标记c++
  • 你遇到了什么错误?
  • 还有其他问题。例如,NAME_SIZE 未定义。使用-Wall 编译。对于此特定错误,消息说明出了什么问题:您正在将 person_t 传递给接受 person_t* 的函数。
  • 您未能验证 scanf 的返回。你必须每次都检查返回,否则你是在诱惑Undefined Behavior。您还必须将"%99[^\n]" 更改为" %99[^\n]",因为"%c""%[...]" 都不使用前导空格。

标签: c


【解决方案1】:

首先,它似乎是指针和引用任务。您可能需要阅读 this 才能理解它们。换句话说,无法将person_t 转换为person_t* 意味着您正在尝试使用您的对象人而不是引用该特定人。 * 表示引用,因此您需要使用&amp; 将地址传递给它。我不是最好的解释者,而是查看链接和所有答案,不仅接受了一个。

代码看起来很乱,我尝试将其修复为可编译的代码,虽然我没有 C 编译器(您可能需要根据您的作业细节进行编辑/修复):

#include <stdio.h>
#include <string.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;

int main()
{
   person_t people[NUM_PEOPLE];
   printf("Get people\n");
   getPeople(&people, 3);

   printf("\nPrint people\n");
   printPeople(people, 3);
   return 0;
}

void getOnePerson(person_t *person)
{
  printf("Enter full name: ");
  scanf("%s", person -> name);
  printf("\nEnter ssn: ");
  scanf("%s", person -> ssn);
  printf("\nEnter year of birth: ");
  scanf("%s", person -> yearOfBirth);
}

void printOnePerson(person_t p)
{
   printf("%s:%s:%d\n", p.name, p.ssn, p.yearOfBirth);
}

void getPeople(person_t *person[], int num)
{
   int i;
   for(i=0; i<num; i++)
   { 
      getOnePerson(&person[i]);
   }
}

void printPeople(person_t person[], int num)
{
   int i;
   for(i=0; i<num; i++)
   {
      printOnePerson(person[i]);
   }
}

简单来说,您的getPeople(person_t *person[], int num) 函数的第一个参数是person_t *person[],因此您需要传递一个&amp;people。与getOnePerson(person_t *person) 参数person_t *person 相同意味着您需要将地址传递给单个人对象&amp;person[i]。它们背后的含义是使用引用,您可以直接在函数中编辑这些对象中的值。 printPeople(person_t person[], int num)printOnePerson(person_t p) 用于读取(而非编辑),因此您可以自己传递值。

【讨论】:

    【解决方案2】:

    你有这么多的小问题,很难知道从哪里开始。首先,在引用结构成员时,您永远不会在 "-&gt;" 周围包含空格。使用p-&gt;name,而不是p -&gt; name。继续...

    您未能验证scanf 的返回。你必须每次都检查返回,否则你是在诱惑Undefined Behavior。您还必须将"%99[^\n]" 更改为" %79[^\n]",因为"%c""%[...]" 都不使用前导空格。未能在%12[^\n] 之前添加" " 将导致无法读取p-&gt;ssn 并导致匹配失败 读取p-&gt;yearOfBirth

    注意99 更改为79。你#define NAME_SIZE 80 并声明char name[NAME_SIZE];,当最多79 字符可以存储在name 中时,你认为使用99field-width 修饰符在做什么? (#define SSN_SIZE 13 也有同样的问题)。您可以使用带有scanffield-width 修饰符来保护您的数组边界。将 *field-width 修饰符设置为大于数组大小 (-1) 会完全移除它应该提供的保护。

    如果用户不小心在输入中犯了一个错误,您未能检查scanf 的返回并处理三种必要的返回情况将导致未定义的行为。未能检查scanf 的返回是新C 程序员最容易陷入的陷阱之一。每个用户输入都是强制性的。否则,您无法确信您的代码实际上正在处理有效数据。

    scanf 可以使用,如果使用正确的话。这意味着有责任检查scanf返回每次。你必须处理三个条件

    1. (return == EOF) 用户通过按 Ctrl+d(或在 Windows 上 Ctrl+z,但参见 CTRL+Z does not generate EOF in Windows 10 (early versions))生成手动 EOF 来取消输入;李>
    2. (return &lt; expected No. of conversions) 匹配输入失败。对于 匹配 失败,您必须考虑输入缓冲区中剩余的每个字符。 (在输入缓冲区中向前扫描读取并丢弃字符,直到找到'\n'EOF);最后
    3. (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 &lt;stdio.h&gt;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
    

    检查一下,如果您还有其他问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 2011-11-12
      • 2012-09-17
      • 1970-01-01
      • 1970-01-01
      • 2021-06-20
      • 2019-04-14
      相关资源
      最近更新 更多