在c中,具有专用于分配内存的功能是不是很糟糕?

The*_*rev 2 c memory-management

我已经组织了具有分配内存的功能的ac项目

例如,我得到了这个结构:

  typedef struct t_example{
    int x;
    int y;
  }example;
Run Code Online (Sandbox Code Playgroud)

我创建了这个函数来初始化一个实例:

  /**
   return value must be freed
   */
  example * example_init(){
    example *p_example = malloc(sizeof(example));
    p_example->x = 42;
    p_example->y = 42;
    return p_example;
  }
Run Code Online (Sandbox Code Playgroud)

我可以像这样调用这个函数

example *p_example = example_init();
Run Code Online (Sandbox Code Playgroud)

但随着我的项目的增长,我发现有时我不需要分配内存,如果我只需要堆栈上的局部变量,但需要初始化它,所以我将init函数更改为:

  void example_init(example *p_example){
    p_example->x = 42;
    p_example->y = 42;
  }
Run Code Online (Sandbox Code Playgroud)

所以我可以像这样调用这个函数

example o_example;
example_init(&o_example);
Run Code Online (Sandbox Code Playgroud)

当然,如果我有一个指针,这个功能也可以工作

example *p_example = malloc(sizeof(example));
example_init(p_example);
Run Code Online (Sandbox Code Playgroud)

我的问题是:这是最好的做法:1)提供,将分配内存(并正确记录本),因为它可以很方便进来的情况下的函数,或2),这应该是留给函数的调用者.

我还读到std函数没有动态分配内存,这就是strdup函数不是标准的原因.所以我会说第二种选择是最好的?

Joh*_*ger 5

我的问题是:哪个是最佳实践:1)提供一个将分配内存的功能(并正确地记录这个),因为它可能在方便的情况下,或2)这应该留给函数的调用者.

我认为这不是最佳做法的问题.创建并返回(指向)新的动态分配对象的函数没有任何内在错误.为了比直接分配空间更有用,这样的函数应该确保为对象提供一致的初始值,尽管它可能通过调用不同的函数来实现.总的来说,这是C++ new运算符与构造函数相结合的C类比.

这并不排除用户自己分配对象,无论是动态还是其他方式.如果有问题的类型是公共的,那么可能有充分的理由提供不进行分配的初始化函数.正如您所看到的,这特别适用于依赖于自动或静态分配对象的代码.

我还读到std函数没有动态分配内存,这就是strdup函数不是标准的原因.所以我会说第二种选择是最好的?

标准委员会的标准库函数策略无法合理地扩展到您自己的功能.最终结果是任何地方都不能动态分配内存,如果这是委员会的意图,那么他们至少会弃用标准库的显式内存分配函数.


Joh*_*ode 5

在处理非标量类型时,将分配、释放和初始化抽象到它们自己的函数中总是一个好主意。当您必须以特定顺序分配和取消分配多个资源时,它特别有用。

使用单独的函数进行分配和初始化:

example *example_create( void ) 
{
  example *p = malloc( sizeof *p );
  if ( !p )
    log_error(); // or not - up to you
  return p;
}

void example_init( example *p )
{
  p->x = p->y = 42;
}

example *new_example = example_create( );
if ( new_example )
  example_init( new_example );
Run Code Online (Sandbox Code Playgroud)

为了增加一些灵活性,您可以将初始化程序作为回调传递给分配器:

example *example_create( void (*example_initializer)(example *) )
{
  example *p = malloc( sizeof *p );
  if ( p )
    if ( example_initializer )
      example_initializer( p );
  return p;
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以将分配和初始化合并为一个操作,但仍然保持分配和初始化相互解耦

void init42( example *p ) { p->x = p->y = 42; }
void init0( example *p ) { p->x = p->y = 0; }
void initRand( example *p ) { p->x = rand(); p->y = rand(); }

example *p42 = example_create( init42 );
example *p0  = example_create( init0 );
example *pRand = example_create( initRand );
Run Code Online (Sandbox Code Playgroud)

而且,您仍然可以将初始化程序与auto变量一起使用:

example instance42;
init42( &instance42 );

example instance0;
init0 ( &instance0 );

example instanceRand;
initRand( &instanceRand );
Run Code Online (Sandbox Code Playgroud)