Ваш type
для scores
(int scores[row];
) не соответствует вашей попытке прочитать scores
с scanf
(scanf("%s", &scores);
). Спецификатор преобразования "%s"
предназначен для преобразования строк, разделенных пробелами, а не целых чисел. "%d"
спецификатор преобразования предоставляется для целочисленных преобразований.
Прежде чем смотреть на конкретику. Каждый раз, когда у вас есть задача кодирования, состоящая в координации различных типов данных как единой единицы (например, student
каждый с name
(char*
) и score
(int
), вы должны думать о использование struct
, содержащего name
и score
в качестве членов. Таким образом, нужен только один массив struct , вместо того, чтобы пытаться координировать несколько массивов разных типов, содержащих одну и ту же информацию.
Кроме того, не экономит на размере буфера для символьных массивов. Вы бы предпочли 10 000 символов слишком длинным, чем 1 символ слишком коротким. Если вы считаете, что ваше максимальное имя составляет 10-16 символов, используйте буфер из 64 символов (или больше), чтобы убедиться, что вы можете прочитать всю строку данных - исключая вероятность того, что несколько введенных случайных символов могут привести к тому, что символы останутся непрочитанными в stdin
.
Простой stuct
- это все, что нужно. Вы можете добавить typedef
для удобства (чтобы не вводить struct name_of_struct
для каждого объявления или параметра), например,
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
Теперь у вас есть структура, которая содержит вашего ученика name
и score
как единое целое, а не два массива: один char
и один int
, с которым вам приходится иметь дело. [ 1]
Осталось только объявить массив student_t
для использования в вашем коде, например,
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
С объявленным массивом struct вы можете перейти к обработке ввода. Надежный способ обработки ввода - это непрерывный цикл, проверка получения ожидаемых вами данных на каждой итерации, обработка любых возникающих ошибок (изящно, чтобы ваш код продолжался) и отслеживание количества вводимых данных, чтобы вы могли защищает границы вашего массива и избегает вызова неопределенного поведения путем записи за пределы конца вашего массива.
Вы можете начать цикл ввода, запрашивая и читая строку ввода с fgets
, как упомянуто в моем комментарии. Это имеет несколько преимуществ по сравнению с попытками ввода каждого с scanf
. В частности, потому что то, что остается непрочитанным во входном буфере (stdin
здесь), не зависит от используемого спецификатора преобразования . Вся строка (вплоть до завершающего '\n'
) извлекается из входного буфера и помещается в буфер, который вы даете fgets
для заполнения. Вы также можете проверить, нажимает ли пользователь просто Enter , который можно использовать для удобного указания конца ввода, например,
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
(обратите внимание, что вы можете (и должны) дополнительно проверить длину строки заполненного буфера, чтобы (1) проверить, что последний прочитанный символ равен '\n'
, гарантируя, что была прочитана полная строка, и (2) если последний символ не 't '\n'
проверка того, равна ли длина максимальной длине (-1
), что указывает на то, что символы могут быть оставлены непрочитанными. (которые оставлены вам)
Теперь, когда вы знаете, что у вас есть строка ввода, и она не пустая, вы можете позвонить sscanf
, чтобы проанализировать строку в name
и score
для каждого учащегося, при этом корректно обрабатывая любой сбой в преобразовании, например
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
Если вы обращаете внимание, вы можете увидеть одно из преимуществ использования подхода fgets
и sscanf
с точки зрения надежности. Вы получаете независимые проверки (1) чтения пользовательского ввода; и (2) разделение (или разбор) этого ввода в необходимые значения. Ошибка в любом случае может быть обработана соответствующим образом.
Собрав все части вместе в короткую программу, вы можете сделать следующее:
#include <stdio.h>
#define ROW 5 /* if you need a constant, #define one (or more) */
#define COL 64
typedef struct { /* delcare a simple struct with name/score */
char name[COL]; /* (add a typedef for convenience) */
int score;
} student_t;
int main (void) {
int n = 0; /* declare counter */
student_t student[ROW] = {{ .name = "" }}; /* array of struct */
puts ("\n[note; press Enter alone to end input]\n");
for (;;) { /* loop until all input given or empty line entered */
char buf[COL]; /* declare buffer to hold line */
fputs ("Enter student name: ", stdout); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) /* read/validate line */
break;
if (*buf == '\n') /* check for empty line */
break;
/* parse line into name and score - validate! */
if (sscanf (buf, "%63s %d", student[n].name, &student[n].score) != 2)
{ /* handle error */
fputs (" error: invalid input, conversion failed.\n", stderr);
continue;
}
n++; /* increment row count - after validating */
if (n == ROW) { /* check if array full (protect array bounds) */
fputs ("\narray full - input complete.\n", stdout);
break;
}
}
for (int i = 0; i < n; i++) /* output stored names and values */
printf ("%-16s %3d\n", student[i].name, student[i].score);
}
Пример использования / Вывод
Всякий раз, когда вы пишете подпрограмму ввода - Go Try and Break It! . Преднамеренно вводите неверные данные. Если ваша входная подпрограмма не работает - Go Fix It! . В указанном коде единственной проверкой, которую осталось выполнить и обработать, является ввод числа, превышающего COL
количество символов (например, шаги кошки на клавиатуре). Упражняйте ваш ввод:
$ ./bin/studentnamescore
[note; press Enter alone to end input]
Enter student name: zach 85
Enter student name: the dummy that didn't pass
error: invalid input, conversion failed.
Enter student name: kevin 96
Enter student name: nick 56
Enter student name: martha88
error: invalid input, conversion failed.
Enter student name: martha 88
Enter student name: tim 77
array full - input complete.
zach 85
kevin 96
nick 56
martha 88
tim 77
Хотя вы можете использовать два отдельных массива, гораздо проще использовать один массив структур. Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.
примечания:
- Помните, что POSIX указывает, что имена, заканчивающиеся суффиксом
_t
, зарезервированы для его использования. (size_t, uint64_t, etc...
) Также помните, что вы увидите этот суффикс, используемый в обычной практике. Поэтому проверьте, прежде чем придумать свой собственный (но у нас нет, POSIX student_t
тип не существует).