没有充分理解题主的第一问题,最好以示例说明。

那个 sizeof(A)=32 的需求,确实应借以填料成员完成(你所谓的 reserved 成员)。例如:

#include &
#include &

typedef struct _A
{
char padding_top[16];
int a; // 我希望 a 偏移为 16
int b; // b 的偏移为 20
char padding_bottom[8];
} A; // 我希望 sizeof(A)=32

int main()
{
// for Linux x64 glibc, replace specifier %Iu with %lu.
printf("sizeof(A)=%Iu
"
"offsetof(A, a)=%Iu
"
"offsetof(A, b)=%Iu
",
sizeof(A), offsetof(A, a), offsetof(A, b));
return 0;
}

MinGW x64 GCC 8.1 和 Linux x64 GCC 7.3 测试结果均为:

sizeof(A)=32
offsetof(A, a)=16
offsetof(A, b)=20

不知题主是鉴于什么原因不想用填料成员?我一般鉴于移植性考虑,尽量避免 compiler-specific modifiers 的使用 (e.g. GCC __attribute__, MSVC __declspec)。

关于成员信息隐藏

C/C++ 的 struct/class 成员隐藏有两类性质:

  1. 访问性隔离 (accessibility):private, public, protected, friend 关键字,仅 C++ 可用。
  2. 可见性隔离 (visibility):C/C++ 均可用。常用方法有(以下 struct/class 可相互代替):

方法1. pImp 惯用法

在暴露给用户的 X.h 中,前置声明(非定义式)struct XImpl,然后在 struct X 的定义式中包含 struct XImpl* 指针(或智能指针)成员(即一层间接)。实现文件 XImpl.c 中有 struct XImpl 的定义式,及其相关操作(自由和成员函数)的实现。应用分离编译。利用原理:编译器会核算当前编译单元中 struct 的 size,但指针的 size 是确定的。

具体的隔离/隐藏技巧,C++ 的见《C++ 编程规范》Item 43、《Effective C++》3Ed Item 31;OOP with C 的见《系统程序员成长计划》(大概是前几章容器的适配,以及附录部分介面定义技巧)。

方法2. 含有填料成员的(兼容 size)struct 之间的强制类型转换

参考 sockets 中的 struct sockaddr(用于内部处理)和 struct sockaddr_in(供用户使用)。见这篇简介。这种方法,为了兼容性和向后扩展考虑,需要预留 fixed size 的头部(里面还可有 magic 标识其版本,the sizeof whole structure 也可以充当 magic)。Windows API 中有相当一部分这种结构。


关于封装问题,Linux内核里的搞法是放置一个结构体指针,指向一个「没必要暴露」的结构体,并在头文件里只给出struct internal;一类的声明。

至于偏移量和结构体的大小,不同平台完全可以采取完全不同的对齐和分配策略,不可能有通用的方法。


你的需求和你的问题没什么关联性。

你的目的相当于用C实现C++私有成员变数的功能。

就题目的问题来说,你可以用char[]来做内存占位,补足另一个结构体的内存容量。在使用的时候通过强制类型转换来访问。

pack是对结构体内存对齐的一个设置。在计算结构体的大小的时候,需要用到。

内存对齐的概念,举个例子

struct{

char a;

int b;

}

这个结构体的内存占用通常不是5,而是8。int占4位元组,char 不够4位元组,因为4位元组对齐,占用一个位元组,浪费3个位元组。pack就是告诉编译器,内存要按照多少来对齐。


补一个例子

#pragma pack(1)
struct PRIVATE_ZONE{
int a;
int b;
char c;
};

struct PUBLIC_ZONE{
int h;
int i;
short int j;
};

struct A{
struct PUBLIC_ZONE public_var;
char private_var[9];
};

void func(struct A * param)
{
struct PRIVATE_ZONE* p = (struct PRIVATE_ZONE*)(param-&>private_var);
p-&>a;
p-&>b;
p-&>c;
......
}

对于使用库的人来说,只有一个不知道什么结构的privte_var可以访问,但是不知道具体到结构。

也可以反过来,先定义A,再用 PUBLIC_ZONE去截断内存,类似于 int 强转short int,但是最好使用malloc出来的堆内存。

总之,控制好内存想隐藏的方法有很多,可以通过函数定义到方式来隐藏,比如返回void*,只开放一部分API允许使用者获取这个 void*内的部分数据,同样可以达到限制使用者获取其它数据的目的。


结构体里包结构体应该可以实现吧

struct tag_public{
int id;
int len;
void *packet;
};

struct tag_private{
int id;
struct tag_public data;
}

介面把public丢也去,另外的在private里用

如果你要把介面隐藏,对齐,好像解决不了问题吧,这不应该是介面切分的问题吗


正常的做法是分成public和private两个结构,然后在public中用指针引用private。


#include &
#include &
#include &
#include &

struct Node;

typedef struct Node * PtrToNode;
typedef PtrToNode Position;

struct Node {
Position prev;
Position next;
};

#define item_position(p) ((void *)((char *)(p) + sizeof(struct Node)))

typedef struct {
Position sentinel;
int item_size;
int n;
void (* free_fn)(void *);
} LinkedDeque;

void init_deque(LinkedDeque * deque, int item_size, void (* free_fn)(void *)) {
deque-&>sentinel = malloc(sizeof(struct Node));
assert(deque-&>sentinel != NULL);
deque-&>sentinel-&>prev = deque-&>sentinel-&>next = deque-&>sentinel;
deque-&>item_size = item_size;
deque-&>n = 0;
deque-&>free_fn = free_fn;
}

void dispose_deque(LinkedDeque * deque) {
Position p, tmp;
p = deque-&>sentinel-&>next;
if (deque-&>free_fn != NULL)
while (deque-&>n-- &> 0) {
tmp = p-&>next;
deque-&>free_fn(item_position(p));
free(p);
p = tmp;
}
free(deque-&>sentinel);
}

void push(LinkedDeque * deque, void * item_addr) {
Position p = malloc(sizeof(struct Node) + deque-&>item_size);
assert(p != NULL);
memcpy(item_position(p), item_addr, deque-&>item_size);
p-&>next = deque-&>sentinel-&>next;
p-&>prev = deque-&>sentinel;
deque-&>sentinel-&>next = p-&>next-&>prev = p;
deque-&>n++;
}

void pop(LinkedDeque * deque, void * item_addr) {
assert(deque-&>n &> 0);
Position p = deque-&>sentinel-&>next;
memcpy(item_addr, item_position(p), deque-&>item_size);
deque-&>sentinel-&>next = p-&>next;
p-&>next-&>prev = deque-&>sentinel;
free(p);
deque-&>n--;
}

void inject(LinkedDeque * deque, void * item_addr) {
Position p = malloc(sizeof(struct Node) + deque-&>item_size);
assert(p != NULL);
memcpy(item_position(p), item_addr, deque-&>item_size);
p-&>prev = deque-&>sentinel-&>prev;
p-&>next = deque-&>sentinel;
deque-&>sentinel-&>prev = p-&>prev-&>next = p;
deque-&>n++;
}

void eject(LinkedDeque * deque, void * item_addr) {
assert(deque-&>n &> 0);
Position p = deque-&>sentinel-&>prev;
memcpy(item_addr,item_position(p), deque-&>item_size);
deque-&>sentinel-&>prev = p-&>prev;
p-&>prev-&>next = deque-&>sentinel;
free(p);
deque-&>n--;
}

static void str_free(void * p) {
free(*(char **)p);
}

typedef struct {
int number;
int age;
int gender;
} People;

static void people_ptr_free(void * p) {
free(*(People **)p);
}

int main() {
/*
* test string
*/
const char * string_friends[] = {
"Amazon", "Google",
"Facebook", "LinkedIn",
"Jetbrains", "Microsoft"
};

LinkedDeque string_deque;
init_deque(string_deque, sizeof(char *), str_free);

char * string_tmp;
for (int i = 0; i &< 6; i++) { string_tmp = strdup(string_friends[i]); push(string_deque, string_tmp); } for (int i = 0; i &< 3; i++) { eject(string_deque, string_tmp); if (strcmp(string_tmp, string_friends[i]) != 0) fprintf(stderr, "test string: eject error "); } for (int i = 0; i &< 3; i++) { pop(string_deque, string_tmp); if (strcmp(string_tmp, string_friends[5 - i]) != 0) fprintf(stderr, "test string: pop error "); } dispose_deque(string_deque); /* * test int */ int int_friends[] = { 100, 20, 33, 43, 41, 82 }; LinkedDeque int_deque; init_deque(int_deque, sizeof(int), NULL); int int_temp; for (int i = 0; i &< 6; i++) { push(int_deque, int_friends + i); } for (int i = 0; i &<3; i++) { eject(int_deque, int_temp); if (int_temp != int_friends[i]) fprintf(stderr, "test int: eject error "); } for (int i = 0; i &< 3; i++) { pop(int_deque, int_temp); if (int_temp != int_friends[5 - i]) fprintf(stderr, "test int: pop error "); } dispose_deque(int_deque); /* * test struct */ People people[] = { {1, 25, 0}, {2, 26, 0}, {3, 19, 1}, {4, 31, 0}, {5, 35, 1}, {6, 15, 0} }; LinkedDeque people_deque; init_deque(people_deque, sizeof(People), NULL); People people_tmp; for (int i = 0; i &< 6; i++) { push(people_deque, people + i); } for (int i = 0; i &< 4; i++) { eject(people_deque, people_tmp); if (memcmp(people_tmp, people + i, sizeof(People)) != 0) fprintf(stderr, "test struct: eject error"); } for (int i = 0; i &< 2; i++) { pop(people_deque, people_tmp); if (memcmp(people_tmp, people + 5 - i, sizeof(People)) != 0) fprintf(stderr, "test struct: pop error"); } dispose_deque(people_deque); /* * test struct * */ People * people_ptrs[] = { malloc(sizeof(People)), malloc(sizeof(People)), malloc(sizeof(People)), malloc(sizeof(People)), malloc(sizeof(People)), malloc(sizeof(People)) }; people_ptrs[0]-&>number = 1;
people_ptrs[0]-&>age = 25;
people_ptrs[0]-&>gender = 0;

people_ptrs[1]-&>number = 2;
people_ptrs[1]-&>age = 26;
people_ptrs[1]-&>gender = 0;

people_ptrs[2]-&>number = 3;
people_ptrs[2]-&>age = 19;
people_ptrs[2]-&>gender = 1;

people_ptrs[3]-&>number = 4;
people_ptrs[3]-&>age = 31;
people_ptrs[3]-&>gender = 0;

people_ptrs[4]-&>number = 5;
people_ptrs[4]-&>age = 35;
people_ptrs[4]-&>gender = 1;

people_ptrs[5]-&>number = 6;
people_ptrs[5]-&>age = 15;
people_ptrs[5]-&>gender = 0;

LinkedDeque people_ptr_deque;
init_deque(people_ptr_deque, sizeof(People *), people_ptr_free);

for (int i = 0; i &< 6; i++) { push(people_ptr_deque, people_ptrs + i); } People * tmp; for (int i = 0; i &< 4; i++) { eject(people_ptr_deque, tmp); if (memcmp(tmp, people_ptrs[i], sizeof(People)) != 0) fprintf(stderr, "test struct *: eject error"); } for (int i = 0; i &< 2; i++) { pop(people_ptr_deque, tmp); if (memcmp(tmp, people_ptrs[5 - i], sizeof(People)) != 0) fprintf(stderr, "test struct *: pop error"); } dispose_deque(people_ptr_deque); }

这个符合吗?不会提示节点中保存的item。


推荐阅读:
相关文章