隐藏 C 结构体中的成员

发布于 2024-08-29 15:38:18 字数 638 浏览 9 评论 0原文

我一直在阅读有关 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;
}

从这里您可以将一个结构转换为另一个结构。 这被认为是不好的做法吗?或者经常这样做吗?

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.

// =========================================
// 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

感性不性感 2024-09-05 15:38:18

sizeof(SomeStruct) != sizeof(SomeStructSource)。这导致有人找到你并在某一天谋杀你。

sizeof(SomeStruct) != sizeof(SomeStructSource). This will cause someone to find you and murder you someday.

蘸点软妹酱 2024-09-05 15:38:18

就我个人而言,我更喜欢这样:

typedef struct {
  int _public_member;
  /*I know you wont listen, but don't ever touch this member.*/
  int _private_member;
} SomeStructSource;

毕竟是 C,如果人们想搞砸,他们应该被允许 - 不需要隐藏东西,除了:

如果你需要的是保持 ABI/API 兼容,有 2从我所见的情况来看,这是更常见的方法。

  • 不要让你的客户访问结构体,给他们一个不透明的句柄(一个带有漂亮名字的 void* ),为所有东西提供 init/destroy 和访问器函数。这确保您可以更改
    如果您正在编写库,则无需重新编译客户端即可更改结构。

  • 提供一个不透明的句柄作为结构的一部分,您可以根据需要进行分配。这种方法甚至在 C++ 中用于提供 ABI 兼容性。

例如

 struct SomeStruct {
  int member;
  void* internals; //allocate this to your private struct
 };

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:

If what you need is to keep the ABI/API compatible, there's 2 approaches that's more common from what I've seen.

  • 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.

e.g

 struct SomeStruct {
  int member;
  void* internals; //allocate this to your private struct
 };
顾北清歌寒 2024-09-05 15:38:18

您即将拥有它,但还远远不够。

在标头中:

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);

在 .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 ...

重点是,现在消费者对 SomeStruct 的内部结构不了解,即使没有消费者,您也可以随意更改它,随意添加和删除成员需要重新编译。他们也不能“意外地”直接 munge 成员,或在堆栈上分配 SomeStruct。这当然也可以被视为一个缺点。

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:

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 no knowledge 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.

雨夜星沙 2024-09-05 15:38:18

我不建议使用公共结构模式。对于 C 中的 OOP 来说,正确的设计模式是提供访问每个数据的函数,而绝不允许公共访问数据。类数据应该在源头声明,以使其私有,并以前向方式引用,其中 CreateDestroy 进行数据的分配和释放。这样,公私困境将不再存在。

/*********** 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 */

另一方面,如果您不想使用 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 */
}

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 Create and Destroy does allocation and free of the data. In a such way the public/private dilemma won't exist any more.

/*********** 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.

/*********** 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 */
}
长亭外,古道边 2024-09-05 15:38:18

永远不要那样做。如果你的 API 支持任何以 SomeStruct 作为参数的东西(我希望它会这样做),那么他们可以在堆栈上分配一个并将其传递进去。尝试访问私有成员时,你会遇到重大错误,因为编译器为客户端类分配的空间不包含它的空间。

隐藏结构中成员的经典方法是将其设为 void*。它基本上是一个只有您的实现文件知道的句柄/cookie。几乎每个 C 库都对私有数据执行此操作。

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.

枯叶蝶 2024-09-05 15:38:18

有时确实会使用与您提出的方法类似的方法(例如,请参阅 BSD 套接字 API 中 struct sockaddr* 的不同变体),但在不违反 C99 严格别名规则的情况下几乎不可能使用。

不过,您可以安全地执行此操作:

somestruct.h:

struct SomeStructPrivate; /* Opaque type */

typedef struct {
  int _public_member;
  struct SomeStructPrivate *private;
} SomeStruct;

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;
}

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.

You can, however, do it safely:

somestruct.h:

struct SomeStructPrivate; /* Opaque type */

typedef struct {
  int _public_member;
  struct SomeStructPrivate *private;
} SomeStruct;

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;
}
雪化雨蝶 2024-09-05 15:38:18

我会编写一个隐藏结构,并使用公共结构中的指针引用它。例如,您的 .h 可能有:

typedef struct {
    int a, b;
    void *private;
} public_t;

而您的 .c:

typedef struct {
    int c, d;
} private_t;

它显然不能防止指针算术,并且会增加一点分配/解除分配的开销,但我想这超出了问题的范围。

I'd write a hidden structure, and reference it using a pointer in the public structure. For example, your .h could have:

typedef struct {
    int a, b;
    void *private;
} public_t;

And your .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.

温柔女人霸气范 2024-09-05 15:38:18

有更好的方法可以做到这一点,例如使用指向公共结构中的私有结构的 void * 指针。你这样做的方式是在欺骗编译器。

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.

只想待在家 2024-09-05 15:38:18

这种方法是有效的、有用的、标准的 C。

由 BSD Unix 定义的套接字 API 使用的稍微不同的方法是 struct sockaddr 所使用的样式。

This approach is valid, useful, standard C.

A slightly different approach, used by sockets API, which was defined by BSD Unix, is the style used for struct sockaddr.

帅的被狗咬 2024-09-05 15:38:18

使用以下解决方法:

#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;
}

结果是:

t.a = 1
t.x = 2
tref->a = 3
tref->x = 4

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
少女七分熟 2024-09-05 15:38:18

我发现如果您确实想隐藏某些内容,位字段可能是一个很好的解决方案。

struct person {
    unsigned long :64;
    char          *name;
    int           age;
};

struct wallet {
    char *currency;
    double balance;
};

person 结构体的第一个成员是一个未命名的位域。在本例中用于 64 位指针。它是完全隐藏的,并且无法通过结构变量名称访问

由于该结构体中的前 64 位未使用,因此我们可以将其用作私有指针。我们可以通过内存地址而不是变量名来访问该成员。

void init_person(struct person* p, struct wallet* w) {
    *(unsigned long *)p = (unsigned long)w;
    // now the first 64-bit of person is a pointer of wallet
}

struct wallet* get_wallet(struct person* p) {
    return (struct wallet*)*(unsigned long *)p;
}

一个小的工作示例,在我的 intel mac 上测试:

//
// Created by Rieon Ke on 2020/7/6.
//

#include <stdlib.h>
#include <string.h>
#include <assert.h>


#if __x86_64__ || __LP64__
#define PRIVATE_SET(obj, val) *(unsigned long *) obj = (unsigned long) val;
#define PRIVATE_GET(obj, type) (type)*(unsigned long *) obj;
#define PRIVATE_POINTER unsigned long:64
#else
#define PRIVATE_SET(obj, val) *(unsigned int *) obj = (unsigned int) val;
#define PRIVATE_GET(obj, type) (type)*(unsigned int *) obj;
#define PRIVATE_POINTER unsigned int:32
#endif

struct person {
    PRIVATE_POINTER;
    char *name;
    int age;
};

struct wallet {
    char *currency;
    double balance;
};

int main() {

    struct wallet w;
    w.currency = strdup("$");
    w.balance = 99.9;

    struct person p;
    PRIVATE_SET(&p, &w) //set private member

    p.name = strdup("JOHN");
    p.age = 18;

    struct wallet *pw = PRIVATE_GET(&p, struct wallet*) //get private member

    assert(strcmp(pw->currency, "$") == 0);
    assert(pw->balance == 99.9);

    free(w.currency);
    free(p.name);

    return 0;
}

I found that bit-field might be a good solution if you really want to hide something.

struct person {
    unsigned long :64;
    char          *name;
    int           age;
};

struct wallet {
    char *currency;
    double balance;
};

The first member of struct person is an unnamed bit-field. used for a 64-bit pointer in this case. It's completely hidden and cannot be accessed by struct variable name.

Because of the first 64-bit in this struct is unused, so we can use it as a private pointer. We can access this member by its memory address instead of variable name.

void init_person(struct person* p, struct wallet* w) {
    *(unsigned long *)p = (unsigned long)w;
    // now the first 64-bit of person is a pointer of wallet
}

struct wallet* get_wallet(struct person* p) {
    return (struct wallet*)*(unsigned long *)p;
}

A small working example, tested on my intel mac:

//
// Created by Rieon Ke on 2020/7/6.
//

#include <stdlib.h>
#include <string.h>
#include <assert.h>


#if __x86_64__ || __LP64__
#define PRIVATE_SET(obj, val) *(unsigned long *) obj = (unsigned long) val;
#define PRIVATE_GET(obj, type) (type)*(unsigned long *) obj;
#define PRIVATE_POINTER unsigned long:64
#else
#define PRIVATE_SET(obj, val) *(unsigned int *) obj = (unsigned int) val;
#define PRIVATE_GET(obj, type) (type)*(unsigned int *) obj;
#define PRIVATE_POINTER unsigned int:32
#endif

struct person {
    PRIVATE_POINTER;
    char *name;
    int age;
};

struct wallet {
    char *currency;
    double balance;
};

int main() {

    struct wallet w;
    w.currency = strdup("$");
    w.balance = 99.9;

    struct person p;
    PRIVATE_SET(&p, &w) //set private member

    p.name = strdup("JOHN");
    p.age = 18;

    struct wallet *pw = PRIVATE_GET(&p, struct wallet*) //get private member

    assert(strcmp(pw->currency, "$") == 0);
    assert(pw->balance == 99.9);

    free(w.currency);
    free(p.name);

    return 0;
}
预谋 2024-09-05 15:38:18

这是一种使用宏来完成此操作的非常有组织的方法。这就是我在一些大型项目中看到的它的使用方式。我将假设以下内容:

  • 具有结构的头文件
  • 可以访问私有字段的源文件
  • 无法访问私有字段的源文件(字段存在但已重命名)。

头文件:

// You can put this part in a header file
// and share it between multiple header files in your project
#ifndef ALLOW_PRIVATE_ACCESS
#define PRIVATE(T) private_##T
#else
#define PRIVATE(T) T
#endif
#define PUBLIC(T) T

typedef struct {
  int PRIVATE(m1); // private member
  int PUBLIC(m2); // public member
} mystruct;

mystruct *mystruct_create(void);
int mystruct_get_m1(mystruct *t);

可访问私有字段的源文件:

#include <stdlib.h>
#define ALLOW_PRIVATE_ACCESS
#include "mystruct.h"

mystruct *mystruct_create(void) {
  mystruct *p = (mystruct *)malloc(sizeof(mystruct));
  p->m1 = 42; // works (private)
  p->m2 = 34; // works (public)
  return (mystruct *)p;
}

int mystruct_get_m1(mystruct *t) {
    return t->m1; // works (private)
}

不可访问私有字段的源文件:

#include <stdio.h>
#include <stdlib.h>
#include "mystruct.h"

int main() {
    mystruct *t = mystruct_create();
    printf("t->m1 = %d\n", t->m1); // error (private)
    printf("t->m1 = %d\n", mystruct_get_m1(t)); // works (using function)
    printf("t->m2 = %d\n", t->m2); // works (public)
    free(t);
    return 0;
}

Here's a very organized way to do it using macros. This is how I've seen it used in some of the big projects. I will assume the following:

  • Header file with the struct
  • Source file with access to private fields
  • Source file with no access to private fields (the fields exist but are renamed).

Header file:

// You can put this part in a header file
// and share it between multiple header files in your project
#ifndef ALLOW_PRIVATE_ACCESS
#define PRIVATE(T) private_##T
#else
#define PRIVATE(T) T
#endif
#define PUBLIC(T) T

typedef struct {
  int PRIVATE(m1); // private member
  int PUBLIC(m2); // public member
} mystruct;

mystruct *mystruct_create(void);
int mystruct_get_m1(mystruct *t);

Source file with access to private fields:

#include <stdlib.h>
#define ALLOW_PRIVATE_ACCESS
#include "mystruct.h"

mystruct *mystruct_create(void) {
  mystruct *p = (mystruct *)malloc(sizeof(mystruct));
  p->m1 = 42; // works (private)
  p->m2 = 34; // works (public)
  return (mystruct *)p;
}

int mystruct_get_m1(mystruct *t) {
    return t->m1; // works (private)
}

Source file with no access to private fields:

#include <stdio.h>
#include <stdlib.h>
#include "mystruct.h"

int main() {
    mystruct *t = mystruct_create();
    printf("t->m1 = %d\n", t->m1); // error (private)
    printf("t->m1 = %d\n", mystruct_get_m1(t)); // works (using function)
    printf("t->m2 = %d\n", t->m2); // works (public)
    free(t);
    return 0;
}
笑忘罢 2024-09-05 15:38:18

相关,但并不完全隐藏。

就是有条件地弃用成员。

请注意,这适用于 GCC/Clang,但 MSVC 和其他编译器也可以弃用,
因此有可能推出更便携的版本。

如果您使用相当严格的警告或警告作为错误进行构建,这至少可以避免意外使用。

// =========================================
// in somestruct.h

#ifdef _IS_SOMESTRUCT_C
#  if defined(__GNUC__)
#    define HIDE_MEMBER __attribute__((deprecated))
#  else
#    define HIDE_MEMBER  /* no hiding! */
#  endif
#else
#  define HIDE_MEMBER
#endif

typedef struct {
  int _public_member;
  int _private_member  HIDE_MEMBER;
} SomeStruct;

#undef HIDE_MEMBER


// =========================================
// in somestruct.c
#define _IS_SOMESTRUCT_C
#include "somestruct.h"

SomeStruct *SomeStruct_Create()
{
  SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource));
  p->_private_member = 42;
  return (SomeStruct *)p;
}

Related, though not exactly hiding.

Is to conditionally deprecate members.

Note that this works for GCC/Clang, but MSVC and other compilers can deprecate too,
so its possible to come up with a more portable version.

If you build with fairly strict warnings, or warnings as errors, this at least avoids accidental use.

// =========================================
// in somestruct.h

#ifdef _IS_SOMESTRUCT_C
#  if defined(__GNUC__)
#    define HIDE_MEMBER __attribute__((deprecated))
#  else
#    define HIDE_MEMBER  /* no hiding! */
#  endif
#else
#  define HIDE_MEMBER
#endif

typedef struct {
  int _public_member;
  int _private_member  HIDE_MEMBER;
} SomeStruct;

#undef HIDE_MEMBER


// =========================================
// in somestruct.c
#define _IS_SOMESTRUCT_C
#include "somestruct.h"

SomeStruct *SomeStruct_Create()
{
  SomeStructSource *p = (SomeStructSource *)malloc(sizeof(SomeStructSource));
  p->_private_member = 42;
  return (SomeStruct *)p;
}
jJeQQOZ5 2024-09-05 15:38:18

我的解决方案是仅提供内部结构的原型,然后在 .c 文件中声明定义。对于显示 C 接口并在后面使用 C++ 非常有用。

.h :

struct internal;

struct foo {
   int public_field;
   struct internal *_internal;
};

.c :

struct internal {
    int private_field; // could be a C++ class
};

注意:在这种情况下,变量必须是指针,因为编译器无法知道内部结构的大小。

My solution would be to provide only the prototype of the internal struct and then declare the definition in the .c file. Very useful to show C interface and use C++ behind.

.h :

struct internal;

struct foo {
   int public_field;
   struct internal *_internal;
};

.c :

struct internal {
    int private_field; // could be a C++ class
};

Note: In that case, the variable have to be a pointer because the compiler is unable to know the size of the internal struct.

情未る 2024-09-05 15:38:18

不是很私密,因为调用代码可以转换回 (SomeStructSource *)。另外,当您想添加另一个公共成员时会发生什么?您必须破坏二进制兼容性。

编辑:我错过了它在 .c 文件中,但确实没有什么可以阻止客户端将其复制出来,甚至可能直接#include ing .c 文件。

Not very private, given that the calling code can cast back to a (SomeStructSource *). Also, what happens when you want to add another public member? You'll have to break binary compatibility.

EDIT: I missed that it was in a .c file, but there really is nothing stopping a client from copying it out, or possibly even #includeing the .c file directly.

揽月 2024-09-05 15:38:18

此处可以使用匿名结构。

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

typedef struct {
  int i;
  struct {
    int j;
  } MYSTRUCT_PRIVATE;

  // NOTE: Avoid putting public members after private
  int k;
} MyStruct;

void test_mystruct();

#endif

在任何应有权访问私有成员的文件中,在包含此标头之前将 MYSTRUCT_PRIVATE 定义为空标记。在这些文件中,私有成员位于匿名结构中,可以使用 mj 访问,但在所有其他位置,只能使用 m.MYSTRUCT_PRIVATE.j 访问它们。

#define MYSTRUCT_PRIVATE
#include "mystruct.h"

void test_mystruct() {
  // Can access .j without MYSTRUCT_PRIVATE in both
  // initializer and dot operator.
  MyStruct m = { .i = 10, .j = 20, .k = 30 };
  m.j = 20;
}
#include <stdio.h>
#include "mystruct.h"

int main() {
  // You can declare structs and, if you jump through
  // a small hoop, access private members
  MyStruct m = { .i = 10, .k = 30 };
  m.MYSTRUCT_PRIVATE.j = 20;

  // This will not work
  //MyStruct m2 = { .i = 10, .j = 20, .k = 30 };

  // But this WILL work, be careful
  MyStruct m3 = { 10, 20, 30 };

  test_mystruct();

  return 0;
}

我不建议将公共成员放在私人成员之后。初始化没有成员指示符的结构体,例如使用 { 10, 20, 30 } 仍然可以初始化私有成员。如果私有成员的数量发生变化,这也会默默地破坏所有没有成员指示符的初始值设定项。最好始终使用成员指示符来避免这种情况。

您必须将结构(尤其是私有成员)设计为零初始化,因为没有像 C++ 那样的自动构造函数。只要成员初始化为 0,即使没有初始化函数,它们也不会处于无效状态。除非进行成员指示符初始化,否则初始化为简单的 { 0 } 应该被设计为安全的。

我发现的唯一缺点是,这确实会扰乱调试器和代码完成等功能,当一种类型在一个文件中具有一组成员,而在另一个文件中具有不同的一组成员时,它们通常不喜欢它。

An anonymous struct can be of use here.

#ifndef MYSTRUCT_H
#define MYSTRUCT_H

typedef struct {
  int i;
  struct {
    int j;
  } MYSTRUCT_PRIVATE;

  // NOTE: Avoid putting public members after private
  int k;
} MyStruct;

void test_mystruct();

#endif

In any file that should have access to the private members, define MYSTRUCT_PRIVATE as an empty token before including this header. In those files, the private members are in an anonymous struct and can be accessed using m.j, but in all other places they can only be accessed using m.MYSTRUCT_PRIVATE.j.

#define MYSTRUCT_PRIVATE
#include "mystruct.h"

void test_mystruct() {
  // Can access .j without MYSTRUCT_PRIVATE in both
  // initializer and dot operator.
  MyStruct m = { .i = 10, .j = 20, .k = 30 };
  m.j = 20;
}
#include <stdio.h>
#include "mystruct.h"

int main() {
  // You can declare structs and, if you jump through
  // a small hoop, access private members
  MyStruct m = { .i = 10, .k = 30 };
  m.MYSTRUCT_PRIVATE.j = 20;

  // This will not work
  //MyStruct m2 = { .i = 10, .j = 20, .k = 30 };

  // But this WILL work, be careful
  MyStruct m3 = { 10, 20, 30 };

  test_mystruct();

  return 0;
}

I do not recommend putting public members after private members. Initializing a struct without member designators, such as with { 10, 20, 30 } can still initialize private members. If the number of private members changes, this will also silently break all initializers without member designators. It's probably best to always use member designators to avoid this.

You must design your structs, and especially the private members, to be zero initialized since there are no automatic constructors as in C++. As long as the members are initialized to 0 then they won't be left in an invalid state even without an initialization function. Barring a member designator initialization, initializing to simply { 0 } should be designed to be safe.

The only downside I've found is that this does mess with things like debuggers and code completion, they typically don't like it when one type has one set of members in one file, and a different set in another file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文