Ошибка сегментации, Неверная запись размера 4 - PullRequest
3 голосов
/ 09 января 2020

У меня есть следующая структура:

typedef struct ann_t {
  uint32_t xs;
  uint32_t hs;
  uint32_t ys;
  float *x;
  float *h;
  float *y;
  float **wxh;
  float **why;
} ann_t;

Инициализируется следующим образом:

ann_t* ann_init(uint32_t xs, uint32_t hs, uint32_t ys) {
  ann_t *ann = malloc(sizeof(ann_t));
  ann->xs = xs;
  ann->hs = hs;
  ann->ys = ys;
  ann->x = calloc(xs, sizeof(float));
  ann->h = calloc(hs, sizeof(float));
  ann->y = calloc(ys, sizeof(float));


  ann->wxh = calloc(xs, sizeof(float*));
  ann->why = calloc(hs+1, sizeof(float*));


  int i, j;
  for(i = 0; i < xs; i++) {
    ann->wxh[i] = malloc(hs * sizeof(float));
  }

  for(i = 0; i < hs+1; i++) {
    ann->why[i] = malloc(ys * sizeof(float));
  }
  // printf("%p\n", ann->x);
  return ann;
}

Включение этого кода в другую программу:

  ...

  ann_t *ann = ann_init(25, 10, 4);
  // printf("%p\n", ann->x);
  ann->x[0] = 1.0;

  ...

результат:

Ошибка сегментации (ядро сброшено)

Использование valgrind:

== 26436 == Использование неинициализированного значения размера 8

...

== 26436 ==

== 26436 == Неверная запись размера 4

...

== 26436 == Адрес 0x4c3a78000000000 не в стеке, mallo c 'd или (недавно) free'd

Я пытался воспроизвести это в меньшей программе, но не смог .

Изменение структуры на uint64_t вместо uint32_t решает проблему.

Печать указателя ann->x внутри ann_init Я получаю 0x55601051f080 и снаружи 0x1051f08000000000 .

Почему это происходит?

РЕДАКТИРОВАТЬ: Найден преступник в одном из включенных файлов:

#pragma pack(1)

До сих пор не уверен, почему этот ка SES проблема.

Если бы я делал арифметику указателей c для доступа к полям структуры, это имело бы смысл, но я обращаюсь к полю структуры по имени, так почему же оно вычисляет неправильное значение?

Почему это нормально внутри функции инициализации, но за ее пределами доступ не удается?

1 Ответ

3 голосов
/ 09 января 2020

Ответ, полученный из комментариев к вопросу ...

Предположим, у вас есть более простая структура, определенная в заголовке:

// header.h
typedef struct {
  char foo;
  int *ptr;
} fish_t;

и два исходных файла:

// src1.c
#include "header.h"

int dummy_int = 5;

fish_t my_fish;
my_fish.foo = 'a';
my_fish.ptr = &dummy_int;

use_fish_fn( &my_fish );
// src2.c
#pragma pack(1)
#include "header.h"

void use_fish_fn( fish_t *f )
{
  int bar = *f->ptr;
}

Первый файл (без упаковки), вероятно, видит fish_t как имеющий структуру памяти, такую ​​как:

0: |  foo  |  pad  |  pad  |  pad  |     // one byte char, three bytes padding
4: |              ptr              |     // four byte pointer

, но второй файл (с упаковкой) видит это как:

0: |  foo  |        ptr ...        |     // one byte char, 3/4 of pointer
4: |...ptr |                             // last part of pointer

Поэтому, когда второй файл пытается прочитать (и впоследствии разыменовать) указатель, он фактически читает то, что произошло в заполнении, и это определенно будет go неверным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...