Отредактировано для исправлений, указанных Дэвидом К. Ранкиным.
Компилятор должен знать длину вашего массива во время компиляции, чтобы знать, сколько памяти выделить в стеке.В противном случае, если вы делаете это так, как вы делаете это сейчас, спрашивая пользователя, насколько велик размер массива, а затем выделяя его в стеке, вы используете функцию, известную как массивы переменной длины (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 правильно, это был хорватский.Если это правда, поздравляю с выходом в финал в прошлом году.Удачи