Как использовать динамически выделяемую память в C

Я разрабатывал блок в Scilab/Xcos (вариант Matlab/Simulink с открытым исходным кодом) с использованием языка C. Для этой цели Scilab/Xcos предлагает специальный блок под названием CBLOCK4. Как только я помещаю этот блок в симуляцию, автоматически генерируется заглушка языка C. Часть этой заглушки является шаблоном для функции, реализующей поведение блока. Эта функция принимает среди своих параметров указатель на структуру scicos_block:

typedef struct {
  int nevprt;
  voidg funpt ;
  int type;
  int scsptr;
  int nz;
  double *z;
  int noz;
  int *ozsz;
  int *oztyp;
  void **ozptr;
  int nx;
  double *x;
  double *xd;
  double *res;
  int nin;
  int *insz;
  void **inptr;
  int nout;
  int *outsz;
  void **outptr;
  int nevout;
  double *evout;
  int nrpar;
  double *rpar;
  int nipar;
  int *ipar;
  int nopar;
  int *oparsz;
  int *opartyp;
  void **oparptr;
  int ng;
  double *g;
  int ztyp;
  int *jroot;
  char *label;
  void **work;
  int nmode;
  int *mode;
} scicos_block;

Пункт work, вероятно, предназначен для хранения адреса, по которому хранится другой адрес, по которому хранятся внутренние переменные состояния блока. Я попытался реализовать блок Scilab/Xcos с использованием элемента work:

#include "scicos_block4.h"

#define U  ((double *)GetRealInPortPtrs(block, 1))
#define Y  ((double *)GetRealOutPortPtrs(block, 1))

// parameters
#define Tu (GetRparPtrs(block)[0])
#define Td (GetRparPtrs(block)[1])
#define T  (GetRparPtrs(block)[2])


void Ramp(scicos_block *block, int flag)
{
 
  double *target;
  double *inputDelta;
  double *out;

  if(flag == 4) 
  {
    /* init */
    if((*(block->work) = (double *)scicos_malloc(sizeof(double)*3)) == NULL)
    {
        set_block_error(-16);
        return;
    }
        
    target      = (double*)(*(block->work));
    inputDelta  = (double*)(*(block->work + 1));
    out         = (double*)(*(block->work + 2));

    *target     = 0;
    *inputDelta = 0;
    *out        = 0; 

  }
  else if(flag == 1) 
  {
    /* output computation */ 
    if(U[0] != *target)
    {
        *target = U[0];
        
        if(*target - Y[0] < 0)
        {
            *inputDelta = Y[0] - *target;
        }
        else
        {
            *inputDelta = *target - Y[0];
        }
    }
    
    if(*target > Y[0])
    {
        *out += *inputDelta*T/Tu;
        if(*out > *target)
        {
            *out = *target;
        }
    }
    else if(*target < Y[0])
    {
        *out -= *inputDelta*T/Td;
        if(*out < *target)
        {
            *out = *target;
        }
    }
    
    Y[0] = *out;  
  } 
  else  if (flag == 5) 
  {
    /* ending */
    scicos_free(*(block->work));

  }

}

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

Пожалуйста, кто-нибудь может взглянуть на мой код (а именно на часть в теле if с флагом == 4) и сказать мне, что я делаю неправильно?


person L3sek    schedule 28.09.2020    source источник
comment
Ваше использование наверное не внушает доверия. Где документация для API языка C, для которого вы пытаетесь запрограммировать? Это должно быть вашим первым обращением, прежде чем вы даже начнете писать код, и никто здесь не может дать вам совет, не полагаясь на сами документы.   -  person John Bollinger    schedule 28.09.2020
comment
Вы разыменовываете указатели, которые не инициализированы   -  person Chemik    schedule 28.09.2020


Ответы (2)


Эта линия

target      = (double*)(*(block->work));

правильно, но эти строки

inputDelta  = (double*)(*(block->work + 1));
out         = (double*)(*(block->work + 2));

не правы.

(*(block->work) даст вам указатель на первый из трех двойников malloc.

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

(double*)(*(block->work + 1));   // Wrong !!
(double*)(*(block->work)) + 1;   // Correct !!
^^^^^^^^^^^^^^^^^^^^^^^^^   ^
Get pointer to malloced     Then add 1 to get to the second malloc'ed double
memory (i.e. the first
malloc'ed double) and use 
it as a double-pointer

Или просто сделайте это так:

target      = (double*)(*(block->work));
inputDelta  = target + 1;
out         = target + 2;

Кстати: Вам не нужен весь кастинг. Просто удалите его.

Редактировать: в комментарии OP говорится, что он по-прежнему падает

Опубликованный код не говорит нам, был ли инициализирован block->work перед вызовом Ramp. Если он не был инициализирован ранее, выполнение *(block->work) будет недопустимым и может привести к сбою.

Так что, возможно, в коде отсутствует что-то вроде:

/* init */
block->work = malloc(sizeof *block->work);  // Add this line
if((*(block->work) = (double *)scicos_malloc(sizeof(double)*3)) == NULL)
person 4386427    schedule 28.09.2020
comment
большое спасибо за объяснение. Я только что исправил свою ошибку (я решил использовать предложенный вами более простой подход) и снова запустил симуляцию. К сожалению, результат точно такой же — симуляция вылетает. - person L3sek; 28.09.2020
comment
@L3sek L3sek Тогда вам нужен отладчик (например, gdb), чтобы указать, где происходит сбой. В качестве альтернативы, введите несколько операторов печати, чтобы понять это. - person 4386427; 28.09.2020
comment
@ L3sek Инициализируется ли block->work при вызове функции Ramp? - person 4386427; 28.09.2020
comment
Я только что перепроверил (с использованием текстового файла и fprintf), что блочная работа инициализирована значением 417531104. - person L3sek; 28.09.2020
comment
@ L3sek Ну, распечатать его значение недостаточно. Вам нужно найти код, который сделал это, чтобы быть уверенным - person 4386427; 28.09.2020
comment
Я только что проверил исходный код scilab (v5.5.2, с которым я работал) и я осмотрели его. Исходя из этого, мне кажется, что строка 787 в модуле scicos.c выполняет инициализацию рабочего указателя. - person L3sek; 28.09.2020
comment
пример из официальной документации не вдохновляет большая уверенность, когда у него есть глобальная переменная work, инициализированная вне функции как void**work = block->work;. Такой код даже не скомпилируется вне функции! - person Ian Abbott; 28.09.2020
comment
Насколько я могу судить, block->work предварительно выделено и не может быть выделено клиентским кодом. Есть несколько примеров использования выделения *block->work в существующих модулях, которые используют scicos_block, например: scilab/modules/scicos_blocks/src/c/assignment.c. - person Ian Abbott; 28.09.2020
comment
Итак, я бы избавился от существующего редактирования о выделении памяти для block->work и упомянул проблему инициализации переменных target, inputDelta и out только в том случае, если flag равно 4. - person Ian Abbott; 28.09.2020

Как указано в 4386427 >ответ, (double*)(*(block->work + 1)) должно быть (double*)(*(block->work)) + 1. Это особенно важно, если sizeof(double) и sizeof(void*) имеют разные значения. (В типичной 64-битной системе они будут иметь одинаковое значение, но в типичной 32-битной системе они будут иметь разные значения.)

Другая серьезная проблема заключается в том, что когда flag == 1, переменные target, inputDelta и out содержат неинициализированные значения, но разыменовываются. Переменные должны быть назначены в блоке else if (flag == 1), прежде чем их можно будет разыменовать так же, как в блоке if (flag == 4).

Я думаю, что код был бы чище, если бы для хранения рабочих данных был определен тип struct:

struct Ramp_work
{
    double target;
    double inputDelta;
    double out;
};

void Ramp(scicos_block *block, int flag)
{
    struct Ramp_work *work;

    if (flag == 4)
    {
        /* init */
        work = scicos_malloc(sizeof(*work));
        *block->work = work;
        if (work == NULL)
        {
            set_block_error(-16);
            return;
        }
        work->target = 0;
        work->inputDelta = 0;
        work->out = 0;
    }
    else if (flag == 1)
    {
        /* output computation */
        work = *block->work;
        if (U[0] != work->target)
        {
             /* etc. */
        }
        /* etc. */
    }
    else if (flag == 5)
    {
        /* ending */
        scicos_free(*block->work);
    }
}
person Ian Abbott    schedule 28.09.2020
comment
Спасибо за ваш ответ. Я только что сделал изменения, которые вы предложили. Компиляция прошла успешно, но когда я запустил симуляцию, она снова вылетела. - person L3sek; 29.09.2020
comment
@ L3Sek Я только что заметил ошибку в своем коде. В блоке else if (flag == 1) work = block->work; должно быть work = *block->work;. сейчас поправлю. - person Ian Abbott; 29.09.2020