嗨,我有以下的设计,我想知道一般来说,C编译器(gcc或clang)是否会试图在编译时解析函数指针,或者总是将其保留到运行时。
在测试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);
}

在测试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)是在编译时解析为获取大小(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

最佳答案

下面是CLAN如何在最大优化下实现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

在最大优化时,CLAN远远超出编译时函数指针的解析。它删除了与结构相关的所有代码。
不必打电话给malloc
它不会生成任何代码来初始化结构
它预先计算A->get_size(A)
它预先计算get_size(A)
因此main中的代码本质上被简化为
int main(void)
{
    printf("%llu\n", 3 );
    printf("%llu\n", 3 );
    return 0;
}

关于c - 编译时间函数ptr取消引用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27335308/

10-11 15:15
查看更多