Ошибка сегментации при доступе к указателю структуры в структуре - PullRequest
0 голосов
/ 10 января 2019

У меня проблема с указателями на структуру, в которых есть члены, которые тоже являются указателями на структуру.

Просматривая предложенные похожие вопросы я узнал это:

Доступ к элементам внутри указателя структуры внутри другого указателя на структуру

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

Я думаю, это правильно сделано в моем коде.

typedef struct {
  int id_vec;
  float *vec_value;
} Vector;

typedef struct cluster{
  int id_cluster;
  float *centroid;
  Vector *patternInCluster; 
} Cluster;

int main(void){
  Cluster *cluster_ptr= malloc(3 * sizeof(Cluster));
  if (cluster_ptr==NULL){
    printf("NULL");
  }
  cluster_ptr->patternInCluster=malloc(2 * sizeof(Vector *));
  if (cluster_ptr->patternInCluster==NULL){
    printf("NULL");
    cluster_ptr->patternInCluster=NULL;
  }

  float p1[3]={0.0f,1.0f,2.0f};
  Vector *somePattern=malloc(2 * sizeof(Vector));
  somePattern[0].id_vec=1;
  somePattern[0].vec_value=p1;
  somePattern[1].id_vec=2;
  somePattern[1].vec_value=p1;
}

Тогда я ожидаю, что это утверждение работает:

cluster_ptr[1].patternInCluster[1]=somePattern[1];

Но он компилирует и выдает ошибку сегментации.

Неожиданно следующее утверждение не получит ошибок:

cluster_ptr[0].patternInCluster[1]=somePattern[1];

и тест покажет мне правильный результат (somePattern [1] id и значение)

Я попытался отладить с помощью GDB, но я вижу только это:

Program received signal SIGSEGV, Segmentation fault. 0x00005555555547fe in main () at test_struct.c:36 36 cluster_ptr[1].patternInCluster[1]=somePattern[1];

Я пропустил некоторые ошибки при распределении?

Ответы [ 3 ]

0 голосов
/ 10 января 2019

Вы не выделяете достаточно памяти:

cluster_ptr->patternInCluster=malloc(2 * sizeof(Vector *));

Если patternInCluster имеет тип Vector *, вы должны выделить память для хранения элементов типа Vector, а не Vector*.

cluster_ptr->patternInCluster=malloc(2 * sizeof(Vector));
0 голосов
/ 10 января 2019

Ваша проблема не в доступе к указателю внутри структуры. Ваша проблема в том, как вы используете malloc().

Когда у вас есть один указатель, вы используете malloc только один раз:

int *pointer = (int* )malloc(sizeof(int));
*pointer = 1;
printf("*pointer:%d\n", *pointer);

Если у вас есть указатель на указатель, вы используете malloc () один раз для **pointer_to_pointer, но вы также должны выполнить malloc () один раз для *pointer_to_pointer:

int** pointer_to_pointer = (int** )malloc(sizeof(int*));
*pointer_to_pointer = (int* )malloc(sizeof(int));
**pointer_to_pointer = 2;
printf("**pointer:%d\n", **pointer_to_pointer);

И если у вас есть более одного указателя в месте, указанном **pointer_to_pointer, вам необходим цикл for, чтобы назначить память каждому из этих *pointer_to_pointer s.

for (unsigned int i = 0; i < 3; i++)
{
    *(pointer_to_pointer + i*sizeof(int)) = (int* )malloc(sizeof(int));
}
**(pointer_to_pointer + sizeof(int)) = 3;
**(pointer_to_pointer + 2UL*sizeof(int)) = 4;
printf("**(pointer_to_pointer + sizeof(int):%d\n", **(pointer_to_pointer + sizeof(int)));
printf("**(pointer_to_pointer + 2UL*sizeof(int):%d\n", **(pointer_to_pointer + 2UL*sizeof(int)));

Вы ошибаетесь, полагая, что Cluster *cluster_ptr= malloc(3 * sizeof(Cluster)); автоматически / магически назначит память для Cluster[0] и Cluster[1] и Cluster[2].

Ваше утверждение фактически назначает память только для Cluster[0], но достаточно большой для 3 Cluster с.

Таким образом, модифицированный код будет выглядеть так:

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

typedef struct {
    int id_vec;
    float *vec_value;
} Vector;

typedef struct cluster{
    int id_cluster;
    float *centroid;
    Vector **patternInCluster;
} Cluster;

int main(void){

    Cluster **cluster_ptr = (Cluster **)malloc(sizeof(Cluster*));
    for (long unsigned int i = 0; i < 3; i++) {
        cluster_ptr[i] = (Cluster *)malloc(sizeof(Cluster));
        if (cluster_ptr[i]==NULL){
            printf("NULL");
        }

        cluster_ptr[i]->patternInCluster = (Vector **) malloc(sizeof(Vector*));
        for (long unsigned int j = 0; j < 3; j++) {
            (*cluster_ptr)->patternInCluster[j] = (Vector *) malloc(sizeof(Vector));
            if ((*cluster_ptr)->patternInCluster[j]==NULL){
                printf("NULL");
                (*cluster_ptr)->patternInCluster[j]=NULL;
            }
        }
    }



    float p1[3]={0.0f,1.0f,2.0f};
    Vector *somePattern= (Vector *) malloc(sizeof(Vector));
    somePattern[0].id_vec=1;
    somePattern[0].vec_value=p1;
    somePattern[1].id_vec=2;
    somePattern[1].vec_value=p1;

    cluster_ptr[1]->patternInCluster[1] = &somePattern[0];
    cluster_ptr[0]->patternInCluster[1] = &somePattern[1];
    cluster_ptr[1]->patternInCluster[0] = &somePattern[1];
    cluster_ptr[2]->patternInCluster[1] = &somePattern[0];

    printf("%d\n", cluster_ptr[1]->patternInCluster[1]->id_vec);
    printf("%d\n", cluster_ptr[0]->patternInCluster[1]->id_vec);
    printf("%d\n", cluster_ptr[1]->patternInCluster[0]->id_vec);
    printf("%d\n", cluster_ptr[2]->patternInCluster[1]->id_vec);

    return 0;
}
  • В моей системе я только что скомпилировал, и она безошибочно собирает и запускает.
0 голосов
/ 10 января 2019

Это потому, что вы не заполняете вещи полностью.

Эта строка

cluster_ptr->patternInCluster=malloc(2 * sizeof(Vector *));

- это то же самое, что сказать

cluster_ptr[0].patternInCluster=malloc(2 * sizeof(Vector *));

и действительно, учитывая, что cluster_ptr было выделено как 3 Cluster, в вашем коде было бы более понятным сделать последнее.

Поскольку cluster_ptr[1].patternInCluster не было присвоено значение, попытка разыменования приведет к неопределенному поведению, но, скорее всего, к ошибке сегментации.

...