Проблема с scanf для поля в структуре - PullRequest
0 голосов
/ 26 апреля 2019

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

Я пробовал разные подходы - scanf("%s",arr[i]->code); или scanf("%s",(*(arr+i))->code); - и все равно не работает.

Вот начало моего кода:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>

#define N 5

typedef struct DEPARTMENT
{
    char code[11];
    int sales;
}
department;

int main()
{
    department *arr[N];
    int i;
    printf("Enter values for %d departments:", N);
    for (i = 0; i < N; i++)
    {
        printf("\nThe %d department-", (i + 1));
        printf("\nCode:");
        scanf("%s",(arr[i])->code);
        printf("\nNumber of sales:");
        scanf("%d", &((arr[i])->sales));
    }
}

Ответы [ 2 ]

2 голосов
/ 26 апреля 2019

Несмотря на то, что вы объявили свой массив department, вы не выделяли память для каждого department.

Вы можете сделать это внутри цикла, где вы заполняете свой массив:

for (i = 0; i < N; i++)
{
    arr[i] = malloc(sizeof(department));
    /* .. */
}

Более чистое решение, как упомянуто в комментариях, должно быть достаточно одного выделения:

department *arr = malloc(sizeof(department) * N);

Не забудьте освободить выделенную память и проверить наличие malloc s возвращаемого значения.

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

Ваша непосредственная проблема - попытка присвоить значения недопустимым ячейкам памяти. Ваша декларация:

    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 наготове.

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

...