2014-12-06 43 views
1

嗨,我有以下的設計,我不知道,如果在一般的C編譯器(GCC或鐺)將嘗試解決在編譯時的函數指針或將它永遠離開它,直到運行時間。編譯時間函數PTR提領

在test.h:

typedef struct array_ { 
    size_t size; 
    void *array; 
    size_t (*get_size)(struct array_ *); 
} array_t; 

static inline size_t 
get_size (array_t *A) { 
    return A->get_size(A); 
} 

在test.c的:

#include <stdio.h> 
#include <stdlib.h> 
#include "test.h" 

static size_t _get_size (array_t *A) { 
    return A->size; 
} 

int main(void) 
{ 
    array_t *A = malloc(sizeof(array_t)); 

    A->size = 3; 
    A->array = (int[]){1,2,3}; 
    A->get_size = _get_size; 

    printf("%llu\n", A->get_size(A)); 
    printf("%llu\n", get_size(A)); 
    return 0; 
} 

我的問題是 - 將A-> get_size(A)在編譯時被解析到_get_size(A)或甚至可能是A->大小?是A-> get_size()總是比get_size(A)更高效,或者它們編譯成幾乎相同的東西?

我知道我要問什麼編譯器會或不會做的是依賴於編譯器和其他的東西(如優化級),但總體上有一個答案還是隻是取決於太多的事情?

編輯:我省略了錯誤檢查的清晰度。

編輯:「gcc -S」代碼。我認爲一個指針正被取消引用。

main: 
.LFB4: 
     .cfi_startproc 
     pushq %rbp 
     .cfi_def_cfa_offset 16 
     .cfi_offset 6, -16 
     movq %rsp, %rbp 
     .cfi_def_cfa_register 6 
     subq $32, %rsp 
     movl $24, %edi 
     call malloc 
     movq %rax, -24(%rbp) 
     movq -24(%rbp), %rax 
     movq $3, (%rax) 
     movl $1, -16(%rbp) 
     movl $2, -12(%rbp) 
     movl $3, -8(%rbp) 
     movq -24(%rbp), %rax 
     leaq -16(%rbp), %rdx 
     movq %rdx, 8(%rax) 
     movq -24(%rbp), %rax 
     movq $_get_size, 16(%rax) 
     movq -24(%rbp), %rax 
     movq 16(%rax), %rax 
     movq -24(%rbp), %rdx 
     movq %rdx, %rdi 
     call *%rax 
     movq %rax, %rsi 
     movl $.LC0, %edi 
     movl $0, %eax 
     call printf 
     movq -24(%rbp), %rax 
     movq %rax, %rdi 
     call get_size 
     movq %rax, %rsi 
     movl $.LC0, %edi 
     movl $0, %eax 
     call printf 
     movl $0, %eax 
     leave 
     .cfi_def_cfa 7, 8 
     ret 
     .cfi_endproc 
+1

正如您正確觀察,「它取決於」。您可以嘗試使用具有各種優化選項的'-S'編譯器選項(編譯爲彙編),並查看生成的彙編代碼以查看編譯器在每種情況下所做的工作。 (編輯:這是海灣合作委員會,我不知道什麼標誌給叮噹)。 – 2014-12-06 19:12:14

+0

我做到了。 「clang -S」與「gcc -S」相同。以上是gcc反彙編(不優化)。鐺產生非常不同的拆卸,與「-O3」更加不同。 – s5s 2014-12-06 19:25:26

回答

3

這裏的鐺如何實現最大優化

; function prologue 
0xa071: pushl %ebp     ; save the base pointer 
0xa072: movl %esp, %ebp    ; setup new base pointer 
0xa074: pushl %esi     ; save esi register 
0xa075: subl $0x14, %esp    ; reserve 20 bytes 

; compute the address of the format string "%llu\n" 
0xa078: calll 0xa07d     ; put the PC on the stack 
0xa07d: popl %eax     ; put the PC into eax 
0xa07e: leal 0x6f8e(%eax), %esi  ; esi points to the format string 

; first call to printf 
0xa084: movl %esi, (%esp)   ; put the format string on the stack 
0xa087: movl $0x3, 0x4(%esp)   ; put the precomputed size on the stack 
0xa08f: calll 0xc674     ; call printf 

; second call to printf 
0xa094: movl %esi, (%esp)   ; put the format string on the stack 
0xa097: movl $0x3, 0x4(%esp)   ; put the precomputed size on the stack 
0xa09f: calll 0xc674     ; call printf 

; function epilogue 
0xa0a4: xorl %eax, %eax    ; return value is 0 
0xa0a6: addl $0x14, %esp    ; clean up the stack 
0xa0a9: popl %esi     ; restore esi register 
0xa0aa: popl %ebp     ; restore the base pointer 
0xa0ab: ret       ; done 

main功能在最大優化,鐺遠遠超越解決在編譯時的函數指針。它刪除了全部與該結構相關的代碼。

  • 它不打擾調用malloc
  • 它不產生任何代碼以初始化結構
  • 它預計算的A->get_size(A)
  • 結果它預計算的get_size(A)
結果

所以在main代碼基本上降低到

int main(void) 
{ 
    printf("%llu\n", 3); 
    printf("%llu\n", 3); 
    return 0; 
} 
3

既然你剛分配上面,你使用它的函數指針一行,良好的編譯器GCC一樣鐺和應該足夠聰明,在編譯時解決。但是一旦你開始在不同文件中的不同函數之間傳遞指向這個結構體的指針,它們幾乎肯定不會在編譯時解決它。

如果你想知道這些細節你的編譯器,你應該開始看反彙編列表。

如果你想使用的語法一樣,在你的示例代碼,並將它有效地編譯,C++是一個很好的語言。