编译时间函数ptr dereference

时间:2014-12-06 19:04:48

标签: c gcc clang

您好我有以下设计,我想知道一般来说C编译器(gcc或clang)是否会在编译时尝试解析函数指针,或者它会一直保留到运行时。

在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-&gt; get_size(A)在编译时解析为_get_size(A),甚至可能解析为A-&gt;大小? A-> get_size()总是比get_size(A)更有效,还是编译成几乎相同的东西?

我意识到我在询问编译器将会做什么或者不会做什么,这取决于编译器和其他事情(例如优化级别),但一般来说是答案还是只依赖于太多东西了?

编辑:为了清楚起见,我省略了错误检查。

编辑:&#34; gcc -S&#34;码。我认为指针被取消引用了。

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

2 个答案:

答案 0 :(得分:4)

由于您刚刚将函数指针分配到使用它的上方一行,因此gcc和clang等优秀的编译器应该足够聪明,可以在编译时解析它。但是一旦你开始在不同文件中的不同函数之间传递指向该结构的指针,它们几乎肯定不会在编译时解决它。

如果您想详细了解编译器,则应该开始查看反汇编列表。

如果你想在你的示例代码中使用这样的语法并有效地编译它,那么C ++就是一种很好的语言。

答案 1 :(得分:3)

以下是clang如何在最大优化时实现main函数

; 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

在最大化优化时,clang远远超出了在编译时解析函数指针的能力。它删除与结构相关的代码的所有

  • 无需拨打malloc
  • 它不会生成任何初始化结构的代码
  • 它预先计算A->get_size(A)
  • 的结果
  • 它预先计算get_size(A)
  • 的结果

因此main中的代码基本上缩减为

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