-2

I am making a typedef to a structure for a "person" The person has a name, ssn and yearOfBirth. I am getting errors I do not understand with my for loops.

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

This is the first file:

#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]);
    }
}

This is my structure file:

#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;
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • what is the error? – eyllanesc Apr 01 '19 at 01:44
  • 3
    Why do you tag `c++` on a `c` question? – tkausl Apr 01 '19 at 01:44
  • 1
    What errors are you getting? – dieckie Apr 01 '19 at 01:44
  • There are other problems, too. For example, `NAME_SIZE` is undefined. Compile with `-Wall`. For this specific error, the message says what's wrong: You are passing a `person_t` to a function that accepts a `person_t*`. – Raymond Chen Apr 01 '19 at 01:50
  • Anybody teaching students that code and calling it C++ deserves a pie in the face. Dont 'add' code to the question in the comments - not helpful. Edit the question so that it includes any extra information that was initially missing.. – enhzflep Apr 01 '19 at 01:54
  • `getOnePerson(p[i]);` -> `getOnePerson(&p[i]);` – David C. Rankin Apr 01 '19 at 02:13
  • 1
    You fail to ***validate*** the return of `scanf`. You must check the return Every Time, or you are tempting *Undefined Behavior*. You also must change `"%99[^\n]"` to `" %99[^\n]"` because neither `"%c"` or `"%[...]"` consume leading whitespace. – David C. Rankin Apr 01 '19 at 02:20

2 Answers2

1

First of all, it seems to be pointers and references task. You may need to read this to understand them. In other words, cannot convert person_t to person_t* means you are trying to use your object person instead of reference to that specific person. * means reference, so you need to pass an address to it using &. Im not best explainer, check out the link instead and all answers, not only accepted one.

Code seems quite messy, I tried to fix it to compilable code, although I dont have C compiler (you may need to edit/fix according to your homework details):

#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]);
   }
}

So, briefly, your getPeople(person_t *person[], int num) function's first parameter is person_t *person[], therefore you need to pass a &people. Same as getOnePerson(person_t *person) parameter person_t *person means you need to pass address to a single person object &person[i]. The meaning behind them that using references, you can edit the values in these objects directly in the function. While printPeople(person_t person[], int num) and printOnePerson(person_t p) are used for reading (not editing) thereby you can pass values themselves.

Nomad Developer
  • 473
  • 4
  • 14
0

You have such a large number of small problems, it is difficult to know where to begin. First a nit, you never include spaces around "->" when referencing a structure member. Use p->name, not p -> name. Continuing...

You fail to validate the return of scanf. You must check the return Every Time, or you are tempting Undefined Behavior. You also must change "%99[^\n]" to " %79[^\n]" because neither "%c" or "%[...]" consume leading whitespace. Failing to add the " " before %12[^\n] would make it impossible to read p->ssn and lead to a matching failure reading p->yearOfBirth.

Note the change from 99 to 79. You #define NAME_SIZE 80 and declare char name[NAME_SIZE];, what do you think you are doing using a field-width modifier of 99 when at most 79 characters can be stored in name? (You have the same problem with #define SSN_SIZE 13). You use the field-width modifier with scanf to protect your array bounds. Setting the *field-width modifier greater than your array size (-1) removes the protection it should provide altogether.

Your failure to check the return of scanf and handle the three cases of return necessary will lead to Undefined Behavior if the user accidentally makes a single error in input. Failure to check the return of scanf is one of the most common pitfall new C programmer fall into. It is mandatory for every user input. Otherwise, you can have no confidence your code is actually processing valid data.

scanf can be used, if used correctly. This means you are responsible for checking the return of scanf every time. You must handle three conditions

  1. (return == EOF) the user canceled input by generating a manual EOF by pressing Ctrl+d (or on windows Ctrl+z, but see CTRL+Z does not generate EOF in Windows 10 (early versions));
  2. (return < expected No. of conversions) a matching or input failure occurred. For a matching failure you must account for every character left in your input buffer. (scan forward in the input buffer reading and discarding characters until a '\n' or EOF is found); and finally
  3. (return == expected No. of conversions) indicating a successful read -- it is then up to you to check whether the input meets any additional criteria (e.g. positive integer, positive floating-point, within a needed range, etc..).

A short function implementation to empty all remaining characters in stdin in the event of matching failure could be as simple as:

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

(implementing in your code is left as an exercise for you)

Further, using type void as the return of an input function makes no sense. You must choose your return to provide the return of required information AND provide an indication of whether the input succeeded or failed. Using void for getOnePerson() means you have no way of knowing whether you received all valid input, or just received name, but not ssn, or if the user simply generated a manual EOF canceling input at each prompt. A simple integer return is all you need (e.g. return 0; on failure or return 1; only after all 3-inputs are validated) You could do something like:

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 */
}

(note: input is complete when EOF is encountered, either manually generated by the user or encountered in the input stream)

void is also meaningless as a return for getPeople(). You can't use a for loop and just assume all inputs were successful, instead, you need to take input only while input is available, while protecting your array bounds, and then return the number of input actually received (which may be less than NUM_PEOPLE). Further, choose your type properly. For counters, size_t is the proper type (you can't have a negative number of persons), e.g.

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

When you pass an array as a parameter to a function, the array is converted to a pointer to the first element. So when you do sizeof(p) within a function -- that is not what you want and does not provide the number of elements in the array referenced by p -- what it does provide is sizeof(a_pointer), which is fixed by your compiler (e.g. 8-bytes on x86_64, 4-bytes on x86). You pass numOfPeople -- use it, e.g.

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]);
    }
}

You will also want to fix printf("%s\n", p.yearOfBirth); (yearOfBirth is not a string...)

Your header is fine, but it is missing something. Always include header guards around the content of your header files to prevent multiple inclusions of the file, e.g.

#ifndef mystructures_h
#define mystructures_h  1
...
/* your header content */
...
#endif

(note: the 1 isn't required, but if you are defining a constant, it is never a bad idea to give it an affirmative value of your choosing)

There are probably more that were corrected, but those were the major points. Putting it altogether, you could do:

structures.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

(can you figure out why #include <stdio.h> was moved from structures.c into structures.h? do you know why the function prototypes for getPeople() and printPeople() are required in the header and not the rest?)

structures.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]);
    }
}

A short test program peopletest.c

#include "structures.h"

int main (void) {

    person_t people[NUM_PEOPLE] = {{ .name = "" }};
    size_t npeople = getPeople (people, NUM_PEOPLE);

    printPeople (people, npeople);
}

Example Use/Output

$ ./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

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85