Структура указателя языка C - PullRequest
2 голосов
/ 22 мая 2019

Я пытаюсь загрузить значения в мою структуру, но она присоединяет первый элемент каждой новой структуры к последнему из предыдущего.

Изначально у меня было поле 'godiste' как int, и я заметил, что оно не работает, поэтому я изменил его на char и заметил, что по какой-то причине моя функция ввода 'upis' присваивала значение второго члена igraci's. «Имей» первого «крестника». Не могу понять, почему это происходит.

Я поместил printf в main, который просто печатает «godiste» члена igraci 0, чтобы удостовериться, что это не проблема с моей функцией записи «ispis», и это фактически делает значение «godiste» как « Godiste 'члена 0 +' ime 'члена 1.

«ime» - имя игрока prezime - фамилия игроков «pozicija» - позиция игроков 'godiste' - год рождения игроков

и 'igraci' - список игроков.

Программа сначала запрашивает количество игроков, которые вы хотели бы ввести, затем запрашивает их данные и распечатывает то, что вы указали.

#include <stdio.h>

struct futbaler
    {
        char ime[15];
        char prezime[20];
        char pozicija[15];
        char godiste[4];
    };

void upis(struct futbaler* pok, int n)
{
    int i;

    for(i=0; i<n;i++)
    {
        printf("Ime igraca #%d: ",i+1);
        scanf("%s",(pok+i)->ime);
        printf("Prezime igraca #%d: ",i+1);
        scanf("%s",(pok+i)->prezime);
        printf("Pozicija igraca #%d: ",i+1);
        scanf("%s",(pok+i)->pozicija);
        printf("Godiste igraca #%d: ",i+1);
        scanf("%s",(pok+i)->godiste);
    }
}

void ispis(struct futbaler* pok, int n)
{
    int i;

    for(i=0; i<n;i++)
    {
        printf("\nIme igraca #%d je: %s\n",i+1,(*(pok+i)).ime);
        printf("\nPrezime igraca #%d je: %s\n",i+1,(*(pok+i)).prezime);
        printf("\nPozicija igraca #%d je: %s\n",i+1,(*(pok+i)).pozicija);
        printf("\nGodiste igraca #%d je: %s\n",i+1,(*(pok+i)).godiste);
    }
}

main()
{
    int n;
    printf("Koliko bi igraca uneli? ");
    scanf("%d",&n);
    struct futbaler* pok;
    struct futbaler igraci[n];
    pok = igraci;
    upis(pok,n);
    ispis(pok,n);

    printf("%s",igraci[0].godiste);
}

Я просто хочу выяснить, почему и как это связывает эти ценности, которые не имеют ничего общего друг с другом.

1 Ответ

2 голосов

Отредактировано для исправлений, указанных Дэвидом К. Ранкиным.

Компилятор должен знать длину вашего массива во время компиляции, чтобы знать, сколько памяти выделить в стеке.В противном случае, если вы делаете это так, как вы делаете это сейчас, спрашивая пользователя, насколько велик размер массива, а затем выделяя его в стеке, вы используете функцию, известную как массивы переменной длины (VLA).Из соображений безопасности это не очень хорошая идея.

Сказав это, ваша проблема, похоже, связана с тем, что поле года представляет собой массив из четырех символов.Символьные массивы являются строками в C, и по соглашению они должны заканчиваться нулем, чтобы указать, где они останавливаются.Поскольку вы пишете четырехзначное значение (год) в пространстве, которое может содержать только четыре символа, в результате программа фактически не назначает первый элемент последнему из предыдущего;происходит то, что когда он читает последнее поле структуры, он не находит нулевой терминатор и читает прямо через него.Следовательно, массив является непрерывным блоком памяти, и поэтому он видит эти два поля как одно.

Наконец, я настоятельно рекомендую использовать более традиционный способ разыменования массивов.arr[i] - это просто синтаксический сахар для *(arr + i), но его намного легче читать.Не сказать, что это корень проблемы, но все, что делает вашу жизнь проще, вероятно, хорошая идея.

Это моя реализация вашей проблемы.

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>

#ifndef TRUE
enum { FALSE= 0, TRUE = !FALSE };
#endif

#define MAX_LINE_LENGTH 80

/* munch: remove newline from input buffer */
void munch(char* s) {
    size_t len = strlen(s);

    for (size_t i = len - 1; i >= 0; --i) {
        if (s[i] == '\n') {
            s[i] = '\0';
            break;
        }
    }
}

void strcpy_(char* s, const char* t) {
    while ((*s++ = *t++) != '\0')
        ;
}

char* strdup_(const char* s) {
    size_t len = strlen(s);
    char* str = malloc((len + 1) * sizeof (char));

    if (str == NULL) {
        fprintf(stderr, "[Error] Memory allocation failure\n");

        exit(EXIT_FAILURE);
    }

    return memcpy(str, s, len + 1);
}

enum position_t {
    POSITION_UNDEFINED,
    POSITION_STRIKER,
    POSITION_MIDFIELDER,
    POSITION_DEFENDER,
    POSITION_GOALKEEPER
};

const char* position_striker_strings[] = {
    "Striker",
    "striker",
    "False 9",
    "false 9"
};

const char* position_midfielder_strings[] = {
    "Midfielder",
    "midfielder",
    "Centerback",
    "centerback"
};

const char* position_defender_strings[] = {
    "Defender",
    "defender",
    "Fullback",
    "fullback"
};

const char* position_goalkeeper_strings[] = {
    "Goalkeeper",
    "goalkeeper",
    "Goalie",
    "goalie"
};

int position_read(const char* s) {
    for (size_t i = 0; i < (sizeof (position_striker_strings) / sizeof(const char*)); ++i) {
        if (strcmp(s, position_striker_strings[i]) == 0)
            return POSITION_STRIKER;
    }

    for (size_t i = 0; i < (sizeof (position_midfielder_strings) / sizeof(const char*)); ++i) {
        if (strcmp(s, position_midfielder_strings[i]) == 0)
            return POSITION_MIDFIELDER;
    }

    for (size_t i = 0; i < (sizeof (position_defender_strings) / sizeof(const char*)); ++i) {
        if (strcmp(s, position_defender_strings[i]) == 0)
            return POSITION_DEFENDER;
    }

    for (size_t i = 0; i < (sizeof (position_goalkeeper_strings) / sizeof(const char*)); ++i) {
        if (strcmp(s, position_goalkeeper_strings[i]) == 0)
            return POSITION_GOALKEEPER;
    }

    return POSITION_UNDEFINED;
}

char* position_str(int position) {
    switch (position) {
        case POSITION_STRIKER: {
            return "Striker";
        } break;

        case POSITION_MIDFIELDER: {
            return "Midfielder";
        } break;

        case POSITION_DEFENDER: {
            return "Defender";
        } break;

        case POSITION_GOALKEEPER: {
            return "Goalkeeper";
        } break;

        default: {
            return "Unknown Position Code";
        }
    }
}

struct player_t {
    char* first_name;
    char* last_name;
    int position;
    int year;
};

struct player_t* player_allocate() {
    struct player_t* player = calloc(1, sizeof(struct player_t));

    if (player == NULL) {
        fprintf(stderr, "[Error] Memory allocation failure\n");

        exit(EXIT_FAILURE);
    }

    return player;
}

struct player_t* player_new(const char* first_name, const char* last_name, int position, int year) {
    struct player_t* p = player_allocate();

    p->first_name = strdup_(first_name);
    p->last_name = strdup_(last_name);
    p->position = position;
    p->year = year;

    return p;
}

void player_print(struct player_t* player) {
    if (player == NULL)
        return;

    printf("\t%s, %s\n", player->last_name, player->first_name);
    printf("\t\tPosition: %s\n", position_str(player->position));
    printf("\t\tYear: %d\n", player->year);
}

void player_list_print(struct player_t** player_list, size_t n) {
    if (player_list == NULL)
        return;

    printf("\n\nPlayer List: \n\n");

    for (size_t i = 0; i < n; ++i) {
        if (player_list[i] == NULL)
            continue;

        player_print(player_list[i]);
    }

    printf("\n\n");
}

void clear_buffer(char* buffer, size_t n) {
    memset(buffer, 0, n * sizeof(char));
}

int main(void)
{
    char input_buffer[MAX_LINE_LENGTH];
    clear_buffer(input_buffer, MAX_LINE_LENGTH);

    printf("How many players would you like to enter? ");
    fgets(input_buffer, MAX_LINE_LENGTH, stdin);

    errno = 0;
    char* endptr = NULL;
    long n = strtol(input_buffer, &endptr, 10);

    if ((errno == ERANGE && (n == LONG_MAX || n == LONG_MIN)) || (errno != 0 && n == 0)) {
        perror("strtol");

        return EXIT_FAILURE;
    }

    if (endptr == input_buffer) {
        fprintf(stderr, "No digits were found\n");

        return EXIT_FAILURE;
    }

    printf("Enter %ld player(s).\n", n);

    struct player_t** player_list = calloc(n, sizeof (struct player_t *));

    for (size_t i = 0; i < (size_t) n; ++i) {
        player_list[i] = player_allocate();

        printf("\nFirst Name: ");
        clear_buffer(input_buffer, MAX_LINE_LENGTH);
        fgets(input_buffer, MAX_LINE_LENGTH, stdin);
        munch(input_buffer);
        player_list[i]->first_name = strdup_(input_buffer);

        printf("Last Name: ");
        clear_buffer(input_buffer, MAX_LINE_LENGTH);
        fgets(input_buffer, MAX_LINE_LENGTH, stdin);
        munch(input_buffer);
        player_list[i]->last_name = strdup_(input_buffer);

        printf("Position: ");
        clear_buffer(input_buffer, MAX_LINE_LENGTH);
        fgets(input_buffer, MAX_LINE_LENGTH, stdin);
        munch(input_buffer);
        player_list[i]->position = position_read(strdup_(input_buffer));

        printf("Year: ");
        clear_buffer(input_buffer, MAX_LINE_LENGTH);
        fgets(input_buffer, MAX_LINE_LENGTH, stdin);
        munch(input_buffer);
        player_list[i]->year = atoi(input_buffer);
    }

    player_list_print(player_list, n);

    return EXIT_SUCCESS;
}

Выполнение программы:

How many players would you like to enter? 2
Enter 2 player(s).

First Name: Christiano 
Last Name: Ronaldo
Position: Striker
Year: 1985

First Name: Lionel
Last Name: Messi
Position: striker
Year: 1986


Player List: 

    Ronaldo, Christiano
        Position: Striker
        Year: 1985
    Messi, Lionel
        Position: Striker
        Year: 1986

Вы заметите, что я написал свои собственные функции strdup_ и strcpy_.Я подумал, что было бы интересно посмотреть, как они реализованы, и я также добавил некоторые функциональные возможности для большого количества символов новой строки и табуляции.Это потому, что я предпочитаю не использовать scanf, и поэтому я также включил в этот пример как atoi, так и strtol.

Вы также заметите, что strtol имеет много ошибок-проверка включена, а atoi нет;именно поэтому использование atoi не рекомендуется .

Что касается реализации, поскольку число игроков в списке во время компиляции не определено, я использовал двойной указатель, называемый player_list, для динамическоговыделить каждого игрока.Чтобы сделать это, вы должны сначала выделить сам указатель player_list, а затем выполнить итерацию по каждому указателю, поочередно выделяя каждую структуру игрока.

Я также использовал целочисленное значение для позиции и перечисление длядействительные значения позиции.При чтении пользовательского ввода я затем проверял допустимые строки позиции на совпадение, добавляя только конкретную позицию, если действительно было совпадение.Это для проверки данных, так что пользователи не могут просто добавлять новые позиции;это будет работа администратора базы данных.Вместо этого позиция игрока просто устанавливается на POSITION_UNDEFINED, если совпадение не найдено.

Количество допустимых строк позиции рассчитывается на основе количества позиций в массиве, поэтому вы можете добавлять большедопустимые строки в каждом массиве позиции, не беспокоясь об изменении кода соответствия строки позиции.

Если вы просто хотели базовую реализацию, вы могли бы просто немного изменить свой код, чтобы справиться с проблемой год / терминатор, и, возможно, изменить поля имени на указатели для обработки имен любого размера.В существующем состоянии вы уязвимы для переполнения стека.Я не слишком внимательно посмотрел на ваш механизм ввода, но я почти уверен, что в вашем вводе должны быть лишние символы новой строки из-за символа новой строки в буфере ввода после вызова scanf.Может быть, стоит на это обратить внимание.

Надеюсь, это поможет, чувак.Я перевел имена в вашем коде, и если Google Translate правильно, это был хорватский.Если это правда, поздравляю с выходом в финал в прошлом году.Удачи

...