Работа над домашним заданием c и получение ошибок я не понимаю - PullRequest
0 голосов
/ 01 апреля 2019

Я создаю typedef структуру для "человека". У человека есть name, ssn и yearOfBirth. Я получаю ошибки, которые я не понимаю, с моими циклами 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;

Ответы [ 2 ]

1 голос
/ 01 апреля 2019

Прежде всего, это, кажется, задача указателей и ссылок. Возможно, вам придется прочитать это , чтобы понять их. Другими словами, не может преобразовать person_t в person_t* означает, что вы пытаетесь использовать объект объекта вместо ссылки на этого конкретного человека. * означает ссылку, поэтому вам нужно передать ей адрес, используя &. Я не лучший объяснитель, проверьте ссылку и все ответы, а не только один принятый.

Код кажется довольно грязным, я пытался исправить его в компилируемом коде, хотя у меня нет компилятора 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[], поэтому вам нужно передать &people. То же, что getOnePerson(person_t *person) параметр person_t *person означает, что вам нужно передать адрес одному объекту-человеку &person[i]. Смысл за ними в том, что используя ссылки, вы можете редактировать значения в этих объектах непосредственно в функции. Хотя printPeople(person_t person[], int num) и printOnePerson(person_t p) используются для чтения (не редактирования), тем самым вы можете сами передавать значения.

0 голосов
/ 01 апреля 2019

У вас такое большое количество мелких проблем, трудно понять, с чего начать. Прежде всего, вы никогда не включаете пробелы вокруг "->" при ссылке на элемент структуры. Используйте p->name, а не p -> name. Продолжение ...

Вы не можете проверить возврат scanf. Вы должны проверить возврат каждый раз, или вы соблазняете Неопределенное поведение . Вы также должны изменить "%99[^\n]" на " %79[^\n]", поскольку ни "%c", ни "%[...]" не занимают начальные пробелы. Неспособность добавить " " перед %12[^\n] сделает невозможным чтение p->ssn и приведет к ошибочному совпадению чтению p->yearOfBirth.

Примечание изменение с 99 на 79. Вы #define NAME_SIZE 80 и объявляете char name[NAME_SIZE];, что, по вашему мнению, вы делаете, используя модификатор field-width , равный 99, когда в name можно сохранить не более 79 символов? (У вас такая же проблема с #define SSN_SIZE 13). Вы используете модификатор field-width с scanf до для защиты границ массива . Установка модификатора * field-width больше размера вашего массива (-1) снимает защиту, которую он должен обеспечивать в целом.

Ваша неспособность проверить возврат scanf и обработать три необходимых случая возврата приведет к Неопределенное поведение , если пользователь случайно допустит одну ошибку при вводе. Неспособность проверить возвращение scanf является одной из наиболее распространенных ошибок, с которыми сталкиваются новые программисты на Си. Это обязательно для каждого пользовательского ввода. В противном случае вы не можете быть уверены, что ваш код действительно обрабатывает правильные данные.

scanf можно использовать, если используется правильно. Это означает, что вы несете ответственность за проверку возврата из scanf каждый раз . Вы должны справиться с тремя условиями

  1. (return == EOF) пользователь отменил ввод, сгенерировав руководство EOF, нажав Ctrl + d (или в Windows Ctrl + z , но см. CTRL + Z не генерирует EOF в Windows 10 (ранние версии) );
  2. (return < expected No. of conversions) a соответствие или вход произошла ошибка. Для сбоя match вы должны учитывать каждый символ, оставшийся в буфере ввода. (сканирование вперед во входном буфере с чтением и отбрасыванием символов до тех пор, пока не будет найдено '\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; только после проверки всех трех входов). Вы можете сделать что-то вроде:

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), которое фиксируется вашим компилятор (например, 8 байтов в x86_64, 4 байта в x86). Вы передаете 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 не требуется, но если вы определяете константу, никогда не стоит придавать ей утвердительное значение по вашему выбору)

Возможно, есть еще что-то, что было исправлено, но это были главные моменты. В целом, вы можете сделать:

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

(можете ли вы выяснить, почему #include <stdio.h> был перемещен из structures.c в structures.h? Знаете ли вы, почему прототипы функций для getPeople() и printPeople() требуются в заголовке, а не в остальных?)

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

Краткая программа испытаний 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

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...