C语言中的可重入库设计

C语言中的可重入库设计,c,encapsulation,reentrancy,C,Encapsulation,Reentrancy,比如说,我正在建立一个库来存储C语言中的squxes Quuxes需要两个状态变量才能成功触发: static int quux_state; static char* quux_address; /* function to spork quuxes found in a file, reads a line from the file each time it's called. */ void spork_quux(FILE*); 如果我将这些数据存储为全局变量,那么在同一时间

比如说,我正在建立一个库来存储C语言中的squxes

Quuxes需要两个状态变量才能成功触发:

static int quux_state;
static char* quux_address;

/* function to spork quuxes found in a file, 
   reads a line from the file each time it's called. */
void spork_quux(FILE*);
如果我将这些数据存储为全局变量,那么在同一时间只有一个客户机能够发送QUUXE,否则状态变量将被第二个调用方弄乱,灾难可能接踵而至

问题是用C设计可重入库的最佳方法是什么

我受理了下列案件,但没有得出令人满意的结论

在以下情况下,问题是如何将客户机与每个状态关联

/* library handles all state data allocation */
static int* quux_state; 
static char** quux_address;
在下面的情况下,客户机能够搞乱状态,这是非常不可取的

/* let each client store state */
typedef struct { int state; char* address; } QuuxState; 
QuuxState spork_quux(FILE*);

那么,如何正确地做到这一点呢?

使用结构,但不要告诉客户端它是一个结构。传递一个不透明指针-void*,或者更好的是一个指向空虚拟结构的指针-并在需要时将其强制转换回。

使用结构,但不要将定义公开给客户端

即在.h头文件中放入:

typedef struct QuuxState QuuxState;

QuuxState *spork_quux(FILE*);
在.c实现文件中:

struct QuuxState
{
    int state;
    char* address;
};

QuuxState *spork_quuxFILE *f)
{
    QuuxState *quuxState = calloc(1, sizeof(*quuxState));
    if (!quuxState)
        return NULL;

    quuxState->state = ....;

    ........

    return quuxState;
}
这种方法的优点是:

  • 您可以更改结构的内容,而无需重新编译所有客户端代码
  • 客户端代码无法访问成员,即quuxState->state将给出编译器错误
  • QuuxState结构对调试器仍然是完全可见的,因此您可以轻松地查看值并设置观察点等
  • 无需浇铸
  • 您返回的类型是一个特定类型,因此您将得到一些编译器检查,以确保传递了正确的内容(与void*指针相比)
  • 唯一的缺点是您必须分配一个内存块——但是,假设您的库正在做任何不平凡的事情(如果它正在做文件I/O,那肯定是不平凡的),那么单个malloc的开销可以忽略不计

    您可能希望将上述函数重命名为类似“QuuxSpork_create”的函数,并添加更多函数来处理逐行执行的工作,以及在完成后销毁状态

    void QuuxSpork_readLine(QuuxState *state)
    {
        ....
    }
    
    void QuuxSpork_destroy(QuuxState *state)
    {
        free(state);
    }
    

    POSIX线程库pthreads就是一个随机的例子。大多数库函数处理这个问题的方式是以用户需要的任何数据类型将状态信息返回给用户。在您的例子中,是一个结构。(以斯特托克对斯特托克为例)。我相信这是一个先例,你应该把它传给用户。一个空*起作用。你甚至可以打字定义它,使它看起来漂亮

    此外,strtok_r通过编辑命令行参数来实现这一点,而不是返回指向状态的指针。我希望我使用的任何重入函数都遵循类似的格式。当然,我的大脑被一些疯狂的C代码扭曲了

    void spork_quux(FILE*, QuuxState **);
    

    或指向不完整类型的指针,即put
    struct qux,然后返回
    struct qux*
    。定义可以保留在仅对库代码使用/可见的私有头文件中。Re:“缺点”:代码可以静态分配struct QuuxState结构数组,并每次返回一个新结构,直到用完为止。当程序完成对其qux的排序时,它会调用“unsporack_qux()”或其他东西来返回值。这就给一次可以被分解的quux的数量设定了一个有限的界限。这仍然是一个问题,那么您可以在静态分配用完后动态分配,在测试返回的指针是否位于静态数组的边界内时,运行(名义上的而不是实际的)未定义行为的风险。谢谢!这肯定是一种可行的方法——当然,关于避免过早优化和保持代码简单的通常规则也适用,但是如果您在紧密循环中使用此模式,或者在其他非常关键的性能(或在嵌入式系统上)中使用此模式像您概述的预分配方法肯定是谨慎的,而且可能是必要的。+1这几乎是C中用于封装对象的标准模式。你会看到到处都在使用它。另外,如果您发现正在分配和释放大量Quux,可以将指向Quux的指针添加到结构定义中,而不是释放它们,将它们添加到未使用Quux的列表中。当你需要一个新的时,只需从列表顶部取下一个,除非它是空的,在这种情况下,只需要一个新的。