位域,類/結構體的數據成員,用於容納必定的位數.c++
位域的定義: 整數類型 變量名: 位數.如:函數
typedef unsigned char Flags; class Stream{ Flags _Error: 1;/* _Error 就是一個位域,在內存中佔有 1 bit.因此其可取值:0/1 */ Flags _Eof: 1; public: Flags _Mode: 2;/* _Mode 在內存中佔有2bit,可取值:0,1,2,3 */ };
整數類型能夠是 unsigned,或者 signed,建議爲 unsigned 類型,signed 類型並無什麼意義.spa
位域的使用: 與類或者結構體的其餘數據成員同樣..net
取址運算符不能應用於位域設計
位域不能夠是類的靜態數據成員.code
不管是那種分配方式,位域與結構體相似,即先聲明的成員位於低地址,後聲明的成員位於高地址.blog
__attribute__((__gcc_struct__)) 默認的分配方式內存
此時將不區分類型合併全部的位域,並將原類型中最大尺寸做爲基本存儲單元.作用域
基本存儲單元: 是編譯器在給位域分配內存空間時的基本單位,而且這些分配給位域的內存是以基本存儲單元大小的整數倍遞增的.get
/* 此時基本存儲單元爲: unsigned short,而且 sizeof(unsigned short)*8 > 3+7. * 因此此時只須要一個基本存儲單元的大小便可.即 sizeof(Test)==sizeof(unsigned short); * 而且此時 [0,3) 位存放着 a,[8,15) 位存放着 b. */ struct Test{ unsigned short a: 3; unsigned char b: 7; }__attribute__((__gcc_struct__)) ; /* 因爲 sizeof(unsigned short)*8 < 3+14;因此須要 2 個基本存儲單元. * 即 sizeof(Test)==2*sizeof(unsigned short). * 此時 b 在下一個可存儲下它的存儲單元中分配內存,即: [0,3) 用於存放 a,[16,30) 用於存放 b. */ struct Test{ unsigned short a: 3; unsigned char b: 14; } ;
__attribute__((__packed__)) 最少存儲空間策略,此時合併全部位域,對變量使用單字節方式對齊.
/* 此時位域所需位數 3+7=10 bit,因此將分配 2 個字節(16 bit)存放 a,b. * 而且 [0,3) 存放着 a,[3,10) 存放着 b,即 a,b 緊鄰. */ struct Test{ unsigned short a: 3; unsigned char b: 7; } __attribute__((__packed__)) ;
連接指示,用於使用其餘程序設計語言(好比: C 語言)編寫的函數.或者生成其餘語言可用的函數(函數自己由 C++ 實現).下面以 C 語言爲例來解釋連接指示的用法.
extern "C" size_t strlen(const char *); extern "C"{ int strcmp(const char *,const char *); char* strcat(char *,const char *); }
其中'{}'的做用僅是將應用連接指示的聲明聚合起來.並不起到做用域的做用.即此時'{}'內的函數在'{}'外也是可見的.
extern "C" int max(int a,int b); int max(double a,double b); char* max(const char *left,const char *right); int main(int argc,char *argv[]){ int a=3,b=7; max(a,b); /* 調用的是 max(int,int) 使用 C 語言編寫 */ double ad=3.0,bd=7.0; max(ad,bd); /* 調用的是 max(double,double),C++ 編寫 */ max("Hello","World");/* 調用的是 max(const char *,const char *),C++ 編寫 */ }
即重載集合中最多隻能有一個 C 語言編寫的函數,其餘使用 C++ 編寫.
連接指示容許嵌套,此時函數聲明將使用據其最近的連接指示.
/* funcs.h 文件 */ extern "C"{ int strcmp(const char *,const char *); int strlen(const char *); } /* funcs.cc 文件 */ extern "C"{ #include "funcs.h" } /* 則對 funcs.cc 文件預編譯後的結果爲: */ extern "C"{ extern "C"{ int strcmp(const char *,const char *); /* 使用據其最近的連接指示,即 extern "C" */ int strlen(const char *);/* 同上 */ } }
在函數定義時,若使用 extern "C",則編譯器爲該函數產生代碼時,他將生成適合於 C 語言的代碼.這樣就能夠在 C 語言中調用該函數,如:
/* max.cc */ extern "C" int max(int a,int b){ return a>b?a:b; } /* main.c */ int main(int argc,char *argv[]){ max(3,7);/* 將調用 max.cc 中使用 C++ 語言編寫的函數. */ }
一個模塊若想調用一個函數,則在連接時,必需要在當前模塊或其餘模塊中能夠找到該函數的定義.
爲何不能直接聲明使用 C 語言編寫的函數,如:
/* main.cc */ int max(int a,int b); extern "C" int min(int a,int b); int main(int argc,char *argv[]){ max(3,7); min(3,7); } /* max.c */ int max(int a,int b){ return a>b?a:b; } /* min.c */ int min(int a,int b){ return a>b?b:a; } /* 調用 g++ -S mian.cc; gcc -S *.c 生成 main.s,max.s,min.s 文件 */ /* main.s */ main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) movl $7, %esi movl $3, %edi call _Z3maxii /* 此時調用的是 _Z3maxii 函數,要求其餘模塊具備 _Z3maxii 函數的定義 */ movl $7, %esi movl $3, %edi call min /* 此時調用的是 min 函數. */ movl $0, %eax leave ret /* min.s */ min: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl %esi, -8(%rbp) movl -8(%rbp), %eax cmpl %eax, -4(%rbp) cmovle -4(%rbp), %eax popq %rbp ret /* max.s */ max: pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl %esi, -8(%rbp) movl -4(%rbp), %eax cmpl %eax, -8(%rbp) cmovge -8(%rbp), %eax popq %rbp ret /* 據上,能夠看出在其餘模塊下僅有 min 函數的定義,而沒有 _Z3maxii 函數的定義, * 因此連接階段會報錯,提示未定義的引用. */
g++ 編譯器在編譯時,會對源文件中出現的全部函數名進行名稱修飾,用於支持語言特性(好比函數重載...)
gcc 編譯器則不會對函數名進行修飾,如:
/* max.cc */ int max(int a,int b){ return a>b?a:b; } /* g++ -x 'c++' -S max.c -o max.s */ _Z3maxii: // 函數名 max() 被修飾爲 _Z3maxii; pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl %esi, -8(%rbp) movl -4(%rbp), %eax cmpl -8(%rbp), %eax jle .L2 movl -4(%rbp), %eax jmp .L3 .L2: movl -8(%rbp), %eax .L3: popq %rbp ret /* gcc -S max.c -o cmax.s */ max: // 沒有進行函數名修飾 pushq %rbp movq %rsp, %rbp movl %edi, -4(%rbp) movl %esi, -8(%rbp) movl -4(%rbp), %eax cmpl %eax, -8(%rbp) cmovge -8(%rbp), %eax //PS: gcc 使用了條件轉送指令,而 g++ 沒有使用.. popq %rbp ret
extern "C" 的做用: 禁止編譯器對同名同形參表(即參數的個數,類型均相同)的函數名進行名稱修飾如:
/* main.cc */ extern "C" int max(int a,int b);//則說明禁止對 max(int,int) 進行名稱修飾. int max(int a,int b,int c); int main(int argc,char *argv[]){ max(3,7); /* 匹配 max(int,int),由於 max(int,int) 使用了 extern "C" 修飾. * 因此此時不會進行名稱修飾.即不會 call _Z3maxii;而是 call max. */ max(3,7,37); /* 匹配 max(int,int,int),並且並無使用 extern "C" 修飾. * 因此是 call _Z3maxiii */ } /* g++ -S main.cc,能夠驗證上述 */ main: pushq %rbp movq %rsp, %rbp subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) movl $7, %esi movl $3, %edi call max movl $37, %edx movl $7, %esi movl $3, %edi call _Z3maxiii movl $0, %eax leave ret