GCC C語言 怎麼設置結構體成員相對結構體起始的偏移值和結構體的大小?
沒有充分理解題主的第一問題,最好以示例說明。
那個 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 成員隱藏有兩類性質:
- 訪問性隔離 (accessibility):private, public, protected, friend 關鍵字,僅 C++ 可用。
- 可見性隔離 (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。
推薦閱讀: