Присвоение значения структуре указателя дает ошибку сегментации - PullRequest
0 голосов
/ 03 ноября 2018

Я создал структуру и указатель этого типа. Я выделил для него память с помощью malloc, но когда я пытаюсь фактически присвоить ему некоторые значения (в частности, прочитать в целых числах и числах с плавающей точкой из файла), это вызывает ошибку сегментации, говоря: «Источник недоступен» ungetwc () при somelocation"".

Вот части кода, касающиеся указателей и структур:

typedef struct {
    int *rain;
    float *avgtemp;
    float *avgwind;
} weather;

weather *year = (weather*) malloc(n*sizeof(weather));
if (year == NULL)
{
    return 1;
}

for (i = 0; i!=12; i++)
{
    fscanf(infile, "%i %f %f", (year+i)->rain, (year+i)->avgtemp, (year+i)->avgwind);
}

Я подумал, что, возможно, проблема в отсутствующем & in fscanf, но когда я добавляю его, моя среда IDE выдает предупреждение, что ожидается int *, но предоставлено int **.

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

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

Есть несколько способов решить вашу проблему. Я создал четыре разные функции и использовал два разных типа структуры.

  • структура с указателями типа int и float;
  • структура с полями int и float, которая уже распределила память.

код

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

typedef struct {
    int rain;
    float avgtemp;
    float avgwind;
} weather;

typedef struct {
    int *rain;
    float *avgtemp;
    float *avgwind;
} weatherp;


/* Obtain amount of items in array (implemented in <sys/params.h> header). */
#define nitems(x)       (sizeof((x)) / sizeof((x)[0]))


/* Ex. 1: Using array of defined size. */
void
first_option()
{
        weather year[2];
        int ii;

        for (ii = 0; ii < nitems(year); ii++) {
                fscanf(stdin, "%d %f %f", &year[ii].rain, &year[ii].avgtemp, &year[ii].avgwind);
        }

        for (ii = 0; ii < nitems(year); ii++) {
                fprintf(stdout, "%d %f %f\n", year[ii].rain, year[ii].avgtemp, year[ii].avgwind);
        }    
}

/* Ex. 2: Using malloc(3). */
void
second_option()
{
        const int n = 2;
        weather *year =  (weather *)malloc(n * sizeof(weather));
        int ii;

        for (ii = 0; ii < n; ii++) {
                fscanf(stdin, "%d %f %f", &year[ii].rain, &year[ii].avgtemp, &year[ii].avgwind);
        }

        for (ii = 0; ii < n; ii++) {
                fprintf(stdout, "%d %f %f\n", year[ii].rain, year[ii].avgtemp, year[ii].avgwind);
        }

        free(year);
}

/* Ex. 3: Values in struct are pointers. */
void
third_option()
{
        const int n = 2;
        weatherp *year =  (weatherp *) malloc(n * sizeof(weatherp));
        int ii;

        for (ii = 0; ii < n; ii++) {
                year[ii].rain = (int *)malloc(sizeof(int));
                year[ii].avgtemp = (float *)malloc(sizeof(float));
                year[ii].avgwind = (float *)malloc(sizeof(float));

                fscanf(stdin, "%d %f %f", year[ii].rain, year[ii].avgtemp, year[ii].avgwind);
        }

        for (ii = 0; ii < n; ii++) {
                fprintf(stdout, "%d %f %f\n", *year[ii].rain, *year[ii].avgtemp, *year[ii].avgwind);
        }

        for (ii = 0; ii < n; ii++) {
                free(year[ii].rain);
                free(year[ii].avgtemp);
                free(year[ii].avgwind);
        }
        free(year);


 }

/* Ex. 4: Using array of defined size but struct fields are pointers. */
void
fourth_option()
{
        weatherp year[2];
        int ii;

        for (ii = 0; ii < nitems(year); ii++) {
                year[ii].rain = (int *)malloc(sizeof(int));
                year[ii].avgtemp = (float *)malloc(sizeof(float));
                year[ii].avgwind = (float *)malloc(sizeof(float));
                fscanf(stdin, "%d %f %f", year[ii].rain, year[ii].avgtemp, year[ii].avgwind);
        }

        for (ii = 0; ii < nitems(year); ii++) {
                fprintf(stdout, "%d %f %f\n", *year[ii].rain, *year[ii].avgtemp, *year[ii].avgwind);
        }

        for (ii = 0; ii < nitems(year); ii++) {
                free(year[ii].rain);
                free(year[ii].avgtemp);
                free(year[ii].avgwind);
        }
}


int
main()
{

        first_option();
        second_option();
        third_option();
        fourth_option();

        return (0);
}

В функции first_option () Я определил двухэлементный массив структур (определен как weather тип). Поля массива и структуры уже распределили память, потому что я не использовал поля как указатели, а экземпляры типов int / float.

В функции second_option () Я определил указатель, который будет содержать экземпляры структуры погоды. Поля структуры уже распределили память, потому что внутри структуры я не использовал поля, являющиеся указателями. Есть экземпляры int / float типов. Но я должен назначить память для указателя. По сравнению с предыдущим примером, у меня нет массива структур, поэтому я должен его создать. Поэтому я выделяю память для n экземпляров weather struct. Это похоже на создание нескольких (n) полок для нескольких блоков данных (контейнеров / структур, содержащих некоторую важную информацию). В конце концов, память, выделенная malloc, должна быть освобождена функцией free(3).

В функции third_option () У меня нет определенной памяти для полей массива и структуры. Как видите, теперь я использовал только что созданный тип weatherp. В этом случае я должен назначить память как для структурных полей, так и для «полок» для блоков данных (массив). Поэтому я выделяю память для n экземпляров структуры weatherp, а затем, перед использованием fscanf(3), я назначаю память для каждого поля структуры отдельно. В конце концов, память, выделенная malloc(3), должна быть освобождена с помощью free(3) (каждое поле структуры и в конце - контейнер для экземпляров структуры).

Последняя функция четвертая_опция () определен двухэлементный массив структур (определен как weather тип), но поля структуры не имеют выделенной памяти. Поэтому перед использованием fscanf(3) я назначаю память для каждого поля структуры отдельно. В конце концов, память, выделенная malloc(3), должна быть освобождена с помощью free(3).

Дополнительная информация:

Каждый адрес для переменных, объявленных как int a;, struct type name; или char tab[10];, уже динамически распределяет память в стеке, и эта память автоматически «освобождается» после завершения функции. Пространство, выделенное вручную (например, используя malloc(3)), выделяется в куче до вызова функции free(3). В настоящее время ядра операционной системы после завершения программы могут освободить память, выделенную вручную, но рекомендуется устранить утечки памяти, и это показывает, что вы знаете, что делаете.

PS. Во время тестов я заменил количество месяцев с 12 на 2, и я читаю данные из stdin, а не из файла. И, конечно, мы должны проверять такие вещи, как успешное выполнение функции (например, если указатель после malloc(3) вызова не равен NULL). Я просто не хотел усложнять код.

0 голосов
/ 04 ноября 2018

На основании вашего кода это то, что требуется:

typedef struct {
    int *rain;
    float *avgtemp;
    float *avgwind;
} weather;

weather *years = malloc(n * sizeof(weather));
if (year == NULL) {
    return 1;
}

weather *year = years;
for (i = 0; i < n; ++i, ++year) {
    year->rain = malloc(sizeof(int));
    year->avgtemp = malloc(sizeof(float));
    year->avgwind = malloc(sizeof(float));
    fscanf(infile, "%i %f %f",
        year->rain, year->avgtemp, year->avgwind);
}

Но я действительно хочу, чтобы вы не использовали указатели в struct:

typedef struct {
    int rain;
    float avgtemp;
    float avgwind;
} weather;

weather *years = malloc(n * sizeof(weather));
if (year == NULL) {
    return 1;
}

weather *year = years;
for (i = 0; i < n; ++i, ++year) {
    fscanf(infile, "%i %f %f",
        &year->rain, &year->avgtemp, &year->avgwind);
}

UPDATE:

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

Может быть. Первый метод (т. Е. ваша версия) является действительным для некоторых более сложных случаев использования. Например, если struct имеет char * для строки, длина которой может быть произвольно длинной.

Вторая версия более идиоматична и с ней проще работать.

В противном случае, везде в вашем коде при доступе к элементу мы будем делать (например,) int rain = *year->rain; вместо [более простого] int rain = year->rain;

Если один из struct членов должен быть массивом значений (например), структура предназначена для годового отчета, и нам нужен (например) ежемесячно количество осадков за каждый месяц (в сравнении с совокупным количеством осадков за год), может быть нормально, что rain до [снова] будет int *rain;. Но, учитывая это, поскольку число месяцев в году фиксировано, мы можем сделать: int rain[12];, чтобы сохранить простоту.

...