Ваша непосредственная проблема - попытка присвоить значения недопустимым ячейкам памяти. Ваша декларация:
department *arr[N];
объявляет массив указателей на структуру DEPARTMENT [N] (например, 5 указателей на структуру). Однако каждый из этих указателей неинициализирован и указывает на неопределенную область памяти. Помните, что указатель - это просто нормальная переменная, которая содержит адрес чего-то другого в качестве значения, и, как и обычная переменная, она содержит неопределенное значение, пока оно не будет присвоено.
Как и любая другая объявленная локальная переменная, любая попытка получить доступ к значению, пока оно неопределенное, приводит к неопределенному поведению . Чтобы использовать массив указателей , каждому из указателей должен быть назначен начальный адрес действительному блоку памяти в качестве его значения. Здесь, поскольку вы намереваетесь предоставить хранилище для N
struct, нет необходимости объявлять N
указатели, а затем выделять хранилище для N
struct независимо. Вы можете просто объявить указатель на структуру , а затем выделить хранилище для структуры N
в одном блоке памяти, например,
#define N 5
...
typedef struct {
char code[MAXC];
int sales;
} department;
...
department *arr; /* declares a pointer to struct */
...
/* allocate/validate storage for N struct */
if ((arr = malloc (N * sizeof *arr)) == NULL) {
perror ("malloc-arr");
return 1;
}
( примечание: всегда подтверждение каждое выделение)
Выделение хранилища для N
struct в одном блоке имеет то преимущество, что предоставляет один free()
для освобождения выделенного блока памяти.
Так же, как вы должны проверять каждое выделение, вы должны проверять каждый ввод пользователя. Это означает, что, как минимум, вы должны подтвердить возврат каждого из всех до scanf
. Тем не менее, использование scanf
имеет свои недостатки. Ввод с scanf
ужасно хрупок, так как любое изменение во вводе приведет к ошибке , соответствующему , извлечение символов из stdin
прекратится в тот момент, когда произойдет ошибка , соответствующая , оставив нарушителя символ stdin
непрочитанный просто ждет, чтобы снова вас укусить при следующем вызове scanf
. Кроме того, если есть какие-то непреднамеренные символы, которые следуют за правильным вводом, они также остаются в stdin
непрочитанными.
Ваши варианты: очищать stdin
после каждого ввода, чтобы гарантировать, что не осталось никаких символов-нарушителей, или, что лучше, каждый раз читать полную строку ввода с помощью функции строчно-ориентированного ввода , например fgets()
или POSIX getline()
, а затем проанализируйте необходимое значение из заполненного буфера. Это имеет много преимуществ. Любые посторонние символы читаются и отбрасываются с каждым вводом. Вы также извлекаете выгоду из возможности самостоятельно проверить (1) чтение; и (2) анализ необходимой информации из буфера. Вы можете использовать sscanf
для анализа информации из заполненного буфера так же, как вы используете scanf
для чтения из stdin
.
В целом, вы можете переписать свой код примерно так:
#include <stdio.h>
#include <stdlib.h>
#define N 5
#define CODESZ 12 /* if you need more than 1 constant, define them */
#define MAXC 1024 /* (don't skimp on buffer size) */
typedef struct {
char code[MAXC];
int sales;
} department;
int main()
{
department *arr; /* declares a pointer to struct */
char buf[MAXC]; /* buffer to hold each line */
int i, ndx = 0;
/* allocate/validate storage for N struct */
if ((arr = malloc (N * sizeof *arr)) == NULL) {
perror ("malloc-arr");
return 1;
}
printf("Enter values for %d departments:\n", N);
while (ndx < N) { /* loop until info for N departments received */
printf ("\nThe %d department-\n Code : ", ndx + 1);
if (fgets (buf, MAXC, stdin) == NULL ||
sscanf (buf, "%11s", arr[ndx].code) != 1)
break;
fputs (" Sales : ", stdout);
if (fgets (buf, MAXC, stdin) == NULL ||
sscanf (buf, "%d", &arr[ndx].sales) != 1)
break;
ndx++;
}
puts ("\nDepartment Sales Results:\n");
for (i = 0; i < ndx; i++) /* output results, free memory */
printf ("Dept Code: %-12s Sales: %d\n", arr[i].code, arr[i].sales);
free (arr); /* don't forget to free what you allocate */
}
( примечание: используется отдельный индексный счетчик ndx
, который обеспечивает подсчет фактического количества заполненных структур, даже если пользователь отменяет ввод после ввода, например, 3 отдела вместо 5)
Пример использования / Вывод
$ ./bin/allocstructloop
Enter values for 5 departments:
The 1 department-
Code : 001
Sales : 123
The 2 department-
Code : 002
Sales : 234
The 3 department-
Code : 003 -- this department met sales goals.
Sales : 345
The 4 department-
Code : 004
Sales : 456 -- this department exceeded sales goals.
The 5 department-
Code : 005 -- this department had most sales for period.
Sales : 567
Department Sales Results:
Dept Code: 001 Sales: 123
Dept Code: 002 Sales: 234
Dept Code: 003 Sales: 345
Dept Code: 004 Sales: 456
Dept Code: 005 Sales: 567
Попробуйте ввести дополнительный текст (или даже получить дополнительное нажатие клавиши) после ввода и посмотреть, как реагирует ваш код. Держите Ctrl + C наготове.
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.