位域,連接指示

位域

  • 位域,類/結構體的數據成員,用於容納必定的位數.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 語言爲例來解釋連接指示的用法.

使用 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 *);/* 同上 */
    }
}

生成 C 語言使用的函數

  • 在函數定義時,若使用 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
相關文章
相關標籤/搜索