沒有充分理解題主的第一問題,最好以示例說明。

那個 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。


推薦閱讀:
相关文章