C 仅公开API中的空指针

C 仅公开API中的空指针,c,api,void,C,Api,Void,我见过很多C库,它们没有将它们内部处理的对象表示为不同的类型,而是在允许您访问它们之前用空指针将它们包装起来。实际上,“私有”源文件类似于: typedef struct { int value; } object; void * object_new(void) { object *o = malloc(sizeof(object)); o->value = 1; return o; } int object_get(void *o) { re

我见过很多C库,它们没有将它们内部处理的对象表示为不同的类型,而是在允许您访问它们之前用空指针将它们包装起来。实际上,“私有”源文件类似于:

typedef struct {
    int value;
} object;

void * object_new(void)
{
    object *o = malloc(sizeof(object));
    o->value = 1;
    return o;
}

int object_get(void *o)
{
    return (object *)o->value;
}

void * object_free(void *o)
{
    free(o);
}
typedef void* object;
在主标题中,您只有:

void * object_new(void);
int object_get(void *o);
void * object_free(void *o);

现在我想知道:他们这样做有什么特别的原因吗?如果这样做是为了确保API用户无法访问对象的内部,那么仅仅在主库头中公开类型名称,并在实现文件中隐藏底层结构(或任何实际对象)的详细信息难道还不够吗?

在对象的世界中(Obj-C和C++),我认为原因主要与继承有关。如果从基类创建子类,则在创建该类的新实例时,返回值的类型没有问题。仅仅使用C,似乎没有明确的原因,因为没有透露内部细节或创建依赖项。

你是对的。。在大多数情况下,其想法是将API用户限制在对象的内部。关于类型名的决定实际上只是风格的问题。如果按照您的建议(某些API会这样做)在标题中公开类型名称,它可能看起来像:

typedef struct {
    int value;
} object;

void * object_new(void)
{
    object *o = malloc(sizeof(object));
    o->value = 1;
    return o;
}

int object_get(void *o)
{
    return (object *)o->value;
}

void * object_free(void *o)
{
    free(o);
}
typedef void* object;
从编译器的角度来看,这样做没有真正的优点或缺点。尽管它确实让API用户更好地了解了正在发生的事情:

object object_new(void);
int object_get(object o);
void object_free(object o);

隐藏
void
指针后面的类型的原因可能是试图隐藏(在某种意义上)内部细节(误导)。这是危险的,因为它会将编译器可能执行的任何类型检查抛出窗外

更好的做法是这样:

用于user.h

struct internalstuff;
void somefunc(struct internalstuff *p);
#include "for-user.h"

struct internalstuff { ...};
供内部使用。h

struct internalstuff;
void somefunc(struct internalstuff *p);
#include "for-user.h"

struct internalstuff { ...};
implementation.c

#include "for-internal-use.h";


void somefunc(struct internalstuff *p)
{
   ...
}
这样,没有人会将
internalstuff
与随机字符串或
malloc(3)
的原始结果混淆,而至少不会得到警告。只要您在C中只提到指向
struct internalstuff
的指针,就可以不使用
struct
的定义


< C++ >同一行中的某些东西可以用<代码>类< /代码>来完成,如果目标C不允许,我会感到惊讶。但是面向对象编程语言有自己的、更灵活的工具来实现这一点。在那里,您可以定义要导出的基本类,同时使用内部扩展。查看一本好的C++书籍,详细说明(这里有很多的列表)。你知道有没有开源的图书馆?我对这种模式很好奇,如果有真实世界的示例可以查看,那就太好了。例如,这个数据库库:。它只公开常量,它内部处理的三个主要对象(数据库、光标和“环境”)只能通过空指针访问,尽管它们在实现中被适当地定义为typedef。@MBlanc GLib是一个大对象事实上,你一开始正是指出了让我困惑的问题,那就是:为什么要忽略类型信息?但我认为在这个模式背后有一些我不知道的“聪明”的东西。这给了读者一些提示,但编译器不会在意。仍然很危险,可能更危险,因为读者没有意识到潜在的问题。对,这只提供可读性。不过,我喜欢你关于更好地使用structs的建议;我将来肯定会使用这种模式。但是,在C语言中,读者总是能够做一些危险的事情。。人们可以通过铸造来完成的事情真是太神奇了。