C语言 在 C 结构中隐藏成员
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2672015/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hiding members in a C struct
提问by Marlon
I've been reading about OOP in C but I never liked how you can't have private data members like you can in C++. But then it came to my mind that you could create 2 structures. One is defined in the header file and the other is defined in the source file.
我一直在阅读 C 中的 OOP,但我从不喜欢你不能像在 C++ 中那样拥有私有数据成员。但后来我想到你可以创建 2 个结构。一个在头文件中定义,另一个在源文件中定义。
// =========================================
// in somestruct.h
typedef struct {
int _public_member;
} SomeStruct;
// =========================================
// in somestruct.c
#include "somestruct.h"
typedef struct {
int _public_member;
int _private_member;
} SomeStructSource;
SomeStruct *SomeStruct_Create()
{
SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource));
p->_private_member = 42;
return (SomeStruct *)p;
}
From here you can just cast one structure to the other. Is this considered bad practice? Or is it done often?
从这里您可以将一种结构投射到另一种结构上。这被认为是不好的做法吗?还是经常做?
采纳答案by nos
Personally, I'd more like this:
就个人而言,我更喜欢这样:
typedef struct {
int _public_member;
/*I know you wont listen, but don't ever touch this member.*/
int _private_member;
} SomeStructSource;
It's C after all, if people want to screw up, they should be allowed to - no need to hide stuff, except:
毕竟是 C,如果人们想搞砸,他们应该被允许 - 不需要隐藏东西,除了:
If what you need is to keep the ABI/API compatible, there's 2 approaches that's more common from what I've seen.
如果您需要的是保持 ABI/API 兼容,那么从我所见,有两种更常见的方法。
Don't give your clients access to the struct, give them an opaque handle (a void* with a pretty name), provide init/destroy and accessor functions for everything. This makes sure you can change the structure without even recompiling the clients if you're writing a library.
provide an opaque handle as part of your struct, which you can allocate however you like. This approach is even used in C++ to provide ABI compatibility.
不要让你的客户访问结构体,给他们一个不透明的句柄(一个带有漂亮名字的 void*),为所有东西提供 init/destroy 和 accessor 函数。如果您正在编写库,这可确保您无需重新编译客户端即可更改结构。
提供一个不透明句柄作为结构的一部分,您可以随意分配。这种方法甚至用于 C++ 以提供 ABI 兼容性。
e.g
例如
struct SomeStruct {
int member;
void* internals; //allocate this to your private struct
};
回答by hobbs
sizeof(SomeStruct) != sizeof(SomeStructSource). This willcause someone to find you and murder you someday.
sizeof(SomeStruct) != sizeof(SomeStructSource). 这将导致有人找到你并在某一天谋杀你。
回答by Logan Capaldo
You almost have it, but haven't gone far enough.
你几乎拥有它,但还远远不够。
In the header:
在标题中:
struct SomeStruct;
typedef struct SomeStruct *SomeThing;
SomeThing create_some_thing();
destroy_some_thing(SomeThing thing);
int get_public_member_some_thing(SomeThing thing);
void set_public_member_some_thing(SomeThing thing, int value);
In the .c:
在.c中:
struct SomeStruct {
int public_member;
int private_member;
};
SomeThing create_some_thing()
{
SomeThing thing = malloc(sizeof(*thing));
thing->public_member = 0;
thing->private_member = 0;
return thing;
}
... etc ...
The point is, here now consumers have noknowledge of the internals of SomeStruct, and you can change it with impunity, adding and removing members at will, even without consumers needing to recompile. They also can't "accidentally" munge members directly, or allocate SomeStruct on the stack. This of course can also be viewed as a disadvantage.
关键是,现在消费者不知道 SomeStruct 的内部结构,您可以随意更改它,随意添加和删除成员,甚至无需消费者重新编译。他们也不能“意外地”直接 munge 成员,或在堆栈上分配 SomeStruct。这当然也可以看作是一个缺点。
回答by Felipe Lavratti
I do not recommend using the public struct pattern. The correct design pattern, for OOP in C, is to provide functions to access every data, never allowing public access to data. The class data should be declared at the source, in order to be private, and be referenced in a forward manner, where Createand Destroydoes allocation and free of the data. In a such way the public/private dilemma won't exist any more.
我不建议使用公共结构模式。对于 C 语言中的 OOP,正确的设计模式是提供访问每个数据的函数,绝不允许公开访问数据。类数据应该在源头声明,以便是私有的,并以向前的方式被引用,其中Create和Destroy分配和释放数据。这样,公共/私人困境将不再存在。
/*********** header.h ***********/
typedef struct sModuleData module_t'
module_t *Module_Create();
void Module_Destroy(module_t *);
/* Only getters and Setters to access data */
void Module_SetSomething(module_t *);
void Module_GetSomething(module_t *);
/*********** source.c ***********/
struct sModuleData {
/* private data */
};
module_t *Module_Create()
{
module_t *inst = (module_t *)malloc(sizeof(struct sModuleData));
/* ... */
return inst;
}
void Module_Destroy(module_t *inst)
{
/* ... */
free(inst);
}
/* Other functions implementation */
In the other side, if you do not want to use Malloc/Free (which can be unnecessary overhead for some situations) I suggest you hide the struct in a private file. Private members will be accessible, but that on user's stake.
另一方面,如果您不想使用 Malloc/Free(在某些情况下这可能是不必要的开销),我建议您将结构隐藏在私有文件中。私人成员将可以访问,但这取决于用户的利益。
/*********** privateTypes.h ***********/
/* All private, non forward, datatypes goes here */
struct sModuleData {
/* private data */
};
/*********** header.h ***********/
#include "privateTypes.h"
typedef struct sModuleData module_t;
void Module_Init(module_t *);
void Module_Deinit(module_t *);
/* Only getters and Setters to access data */
void Module_SetSomething(module_t *);
void Module_GetSomething(module_t *);
/*********** source.c ***********/
void Module_Init(module_t *inst)
{
/* perform initialization on the instance */
}
void Module_Deinit(module_t *inst)
{
/* perform deinitialization on the instance */
}
/*********** main.c ***********/
int main()
{
module_t mod_instance;
module_Init(&mod_instance);
/* and so on */
}
回答by NG.
Never do that. If your API supports anything that takes SomeStruct as a parameter (which I'm expecting it does) then they could allocate one on a stack and pass it in. You'd get major errors trying to access the private member since the one the compiler allocates for the client class doesn't contain space for it.
The classic way to hide members in a struct is to make it a void*. It's basically a handle/cookie that only your implementation files know about. Pretty much every C library does this for private data.
永远不要那样做。如果您的 API 支持将 SomeStruct 作为参数的任何内容(我期待它这样做),那么他们可以在堆栈上分配一个并将其传入。尝试访问私有成员时会遇到重大错误,因为编译器为客户端类分配不包含空间。
在结构中隐藏成员的经典方法是使其成为 void*。它基本上是一个只有您的实现文件知道的句柄/cookie。几乎每个 C 库都为私有数据执行此操作。
回答by caf
Something similar to the method you've proposed is indeed used sometimes (eg. see the different varities of struct sockaddr*in the BSD sockets API), but it's almost impossible to use without violating C99's strict aliasing rules.
有时确实会使用类似于您提出的方法的方法(例如,请参阅struct sockaddr*BSD 套接字 API 中的不同变体),但在不违反 C99 严格的别名规则的情况下几乎不可能使用。
You can, however, do it safely:
但是,您可以安全地做到这一点:
somestruct.h:
somestruct.h:
struct SomeStructPrivate; /* Opaque type */
typedef struct {
int _public_member;
struct SomeStructPrivate *private;
} SomeStruct;
somestruct.c:
somestruct.c:
#include "somestruct.h"
struct SomeStructPrivate {
int _member;
};
SomeStruct *SomeStruct_Create()
{
SomeStruct *p = malloc(sizeof *p);
p->private = malloc(sizeof *p->private);
p->private->_member = 0xWHATEVER;
return p;
}
回答by jweyrich
I'd write a hidden structure, and reference it using a pointer in the public structure. For example, your .h could have:
我会写一个隐藏结构,并使用公共结构中的指针引用它。例如,您的 .h 可能有:
typedef struct {
int a, b;
void *private;
} public_t;
And your .c:
还有你的 .c:
typedef struct {
int c, d;
} private_t;
It obviously doesn't protect against pointer arithmetic, and adds a bit of overhead for allocation/deallocation, but I guess it's beyond the scope of the question.
它显然不能防止指针算术,并为分配/解除分配增加了一些开销,但我想这超出了问题的范围。
回答by Ilya Matveychikov
Use the following workaround:
使用以下解决方法:
#include <stdio.h>
#define C_PRIVATE(T) struct T##private {
#define C_PRIVATE_END } private;
#define C_PRIV(x) ((x).private)
#define C_PRIV_REF(x) (&(x)->private)
struct T {
int a;
C_PRIVATE(T)
int x;
C_PRIVATE_END
};
int main()
{
struct T t;
struct T *tref = &t;
t.a = 1;
C_PRIV(t).x = 2;
printf("t.a = %d\nt.x = %d\n", t.a, C_PRIV(t).x);
tref->a = 3;
C_PRIV_REF(tref)->x = 4;
printf("tref->a = %d\ntref->x = %d\n", tref->a, C_PRIV_REF(tref)->x);
return 0;
}
Result is:
结果是:
t.a = 1
t.x = 2
tref->a = 3
tref->x = 4
回答by Ilya Matveychikov
There are better ways to do this, like using a void *pointer to a private structure in the public struct. The way you are doing it you're fooling the compiler.
有更好的方法来做到这一点,比如void *在公共结构中使用指向私有结构的指针。你这样做的方式是在愚弄编译器。
回答by Heath Hunnicutt
This approach is valid, useful, standard C.
这种方法是有效的、有用的、标准的 C。
A slightly different approach, used by sockets API, which was defined by BSD Unix, is the style used for struct sockaddr.
由 BSD Unix 定义的套接字 API 使用的一种稍微不同的方法是用于struct sockaddr.

