使用外部头文件,其定义与内部使用的定义不同

时间:2017-06-18 10:35:35

标签: c libraries

假设您正在编写一个在内部使用某些数据结构的库,并希望仅向用户导出它们的一部分(或使用void *之类的内容隐藏确切的类型)。库中使用的所有结构和函数的定义都在标题library.h中,将在构建库时使用。

在构建过程中不会使用library.h的另一个副本,但仅限于链接到库的用户,是否认为这是一种好的做法?

例如,假设库内部使用以下library.h

#ifndef LIBRARY_H
#define LIBRARY_H

struct myStruct {
    int some_x;
    void (*some_callback)(void);
};

typedef struct myStruct *myStruct_t;

#endif

虽然我们希望向用户隐藏myStruct的定义,但我们导出的标题为library.h

#ifndef LIBRARY_H
#define LIBRARY_H

typedef void *myStruct_t;

#endif

2 个答案:

答案 0 :(得分:2)

  

在构建过程中不会使用的另一个library.h副本是否被认为是一种良好的做法,但仅限于链接到库的用户?

没有。虽然您想要做的最佳实践的细节可能是一个品味问题,但在构建过程中提供未使用的标题客观上并不是一个好习惯:您可能会引入在构建项目时从未捕获的输入错误。< / p>

所以,如果不详细说明你应该如何组织,那么你应该做的就是让每个&#34; 私人&#34;标题#include各自的&#34; 公开&#34;标头,不重复私有标头中的公共声明。对于您的示例,这将看起来例如像:

<强> library.h:

#ifndef LIBRARY_H
#define LIBRARY_H

typedef struct myStruct *myStruct_t;
// there's absolutely no need to use void * here. An incomplete struct
// type is perfectly fine as long as only pointers to it are used.

#endif

<强> library_internal.h:

#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"

struct myStruct {
    int some_x;
    void (*some_callback)(void);
};

#endif

其他&#34;最佳做法&#34;注意到:

  • 不要隐藏typedef后面的指针。大多数C程序员都清楚地知道指针是声明符的一部分,并且希望在有指针时显式地指针。取消引用不像指针一样外观的东西只会引起其他人阅读代码的混淆。您也可能会混淆图书馆的消费者,希望myStruct_t能够展示按值调用语义。

  • 不要使用_t后缀定义自己的类型。至少在POSIX中,这是为(编译器/运行时)的实现保留的。定义与struct标记相同名称的类型并没有错。

这些额外建议的示例:

<强> library.h:

#ifndef LIBRARY_H
#define LIBRARY_H

typedef struct myStruct myStruct;

#endif

<强> library_internal.h:

#ifndef LIBRARY_INTERNAL_H
#define LIBRARY_INTERNAL_H
#include "library.h"

struct myStruct {
    int some_x;
    void (*some_callback)(void);
};

#endif

答案 1 :(得分:2)

请注意,C标准不保证指向void的指针具有与指向结构的指针兼容的表示!因此:

typedef struct myStruct *myStruct_t;
typedef void *myStruct_t;

这两者不兼容,不能用于严格遵守的程序。

另一件事是你通常不应该隐藏指针,除非需要。例如,考虑标准库中的FILE。它的内容没有在任何地方定义,但所有函数都专门为它返回一个指针并接受一个指针

您甚至可以使用简单的struct声明,而不是定义:

struct myStruct;

然后外部用户可以定义变量作为指向它的指针

struct myStruct *handle;

或者,如果您希望隐藏它确实是结构的事实,请使用typedef

typedef struct myStruct myStruct;

然后外部接口的用户可以简单地将其变量定义为

myStruct *handle;