在 C 中隐藏结构定义是一种好习惯吗?
Is it a good practice to hide structure definition in C?
在我看来,在 C 中隐藏结构的定义通常会使代码更安全,因为您在编译器的帮助下强制执行结构的任何成员都不能直接访问。
但是,它有一个缺点,即结构的用户无法声明其类型的变量以放入堆栈,因为这样结构的大小变得不可用(因此,用户必须通过 malloc()
在堆上进行分配,即使这是不受欢迎的。
这可以通过 alloca(3)
function that is present in all major libc implementations, even though it does not conform to POSIX.
解决(部分)
考虑到这些优点和缺点,这样的设计总体上可以认为是好的吗?
在lib.h
中:
struct foo;
extern size_t foo_size;
int foo_get_bar(struct foo *);
在lib.c
中:
struct foo {
int bar;
};
size_t foo_size = sizeof foo;
int foo_get_bar(struct foo *foo)
{
return foo->bar;
}
在example.c
中:
#include "lib.h"
int bar(void)
{
struct foo *foo = alloca(foo_size);
foo_init(foo);
return foo_get_bar(foo);
}
函数 bar
调用未定义的行为:foo
指向的结构未初始化。
如果您要隐藏结构细节,请提供一个 foo_create()
来分配一个并对其进行初始化,并提供一个 foo_finalize
来释放任何资源并释放它。
您提出的建议可以实现,但容易出错,不是通用解决方案。
是的,隐藏数据是个好习惯。
作为alloca(foo_size);
模式的替代方法,可以声明一个对齐的字符数组并执行指针转换。但是,指针转换不是完全可移植的。如果大小由变量而不是编译时常量定义,则字符数组需要是 VLA:
extern size_t size;
struct sfoo;
#include <stddef.h>
int main(void) {
unsigned char _Alignas (max_align_t) cptr[size];
// or unsigned char _Alignas (_Complex long double) cptr[size]; // some widest type
struct sfoo *sfooptr = (struct sfoo *) cptr;
...
如果 VLA 不需要或不可用,请将大小声明为常量 (#define foo_N 100
),保证 至少 所需的大小。
在我看来,在 C 中隐藏结构的定义通常会使代码更安全,因为您在编译器的帮助下强制执行结构的任何成员都不能直接访问。
但是,它有一个缺点,即结构的用户无法声明其类型的变量以放入堆栈,因为这样结构的大小变得不可用(因此,用户必须通过 malloc()
在堆上进行分配,即使这是不受欢迎的。
这可以通过 alloca(3)
function that is present in all major libc implementations, even though it does not conform to POSIX.
考虑到这些优点和缺点,这样的设计总体上可以认为是好的吗?
在lib.h
中:
struct foo;
extern size_t foo_size;
int foo_get_bar(struct foo *);
在lib.c
中:
struct foo {
int bar;
};
size_t foo_size = sizeof foo;
int foo_get_bar(struct foo *foo)
{
return foo->bar;
}
在example.c
中:
#include "lib.h"
int bar(void)
{
struct foo *foo = alloca(foo_size);
foo_init(foo);
return foo_get_bar(foo);
}
函数 bar
调用未定义的行为:foo
指向的结构未初始化。
如果您要隐藏结构细节,请提供一个 foo_create()
来分配一个并对其进行初始化,并提供一个 foo_finalize
来释放任何资源并释放它。
您提出的建议可以实现,但容易出错,不是通用解决方案。
是的,隐藏数据是个好习惯。
作为alloca(foo_size);
模式的替代方法,可以声明一个对齐的字符数组并执行指针转换。但是,指针转换不是完全可移植的。如果大小由变量而不是编译时常量定义,则字符数组需要是 VLA:
extern size_t size;
struct sfoo;
#include <stddef.h>
int main(void) {
unsigned char _Alignas (max_align_t) cptr[size];
// or unsigned char _Alignas (_Complex long double) cptr[size]; // some widest type
struct sfoo *sfooptr = (struct sfoo *) cptr;
...
如果 VLA 不需要或不可用,请将大小声明为常量 (#define foo_N 100
),保证 至少 所需的大小。