c++ reference 行为浅析
in Solutions with 4 comments

c++ reference 行为浅析

in Solutions with 4 comments

c++ reference 行为浅析

背景

先前看过的参考资料都说c++的reference是一种语法糖,日常写c++时候也广泛应用了引用去代替指针提高代码的可读性。但是在使用上引用还是有点限制:

探究方式

在不开优化的情况下,用clang将c++语句生成为llvm中间代码

clang++ -S -emit-llvm test.cpp -O0

分析

先上最原始的传值

int moo(){
    int a=100;
    return a;
}

得到的IR为

define i32 @_Z3moov() #0 {
  %1 = alloca i32, align 4
  store i32 100, i32* %1, align 4
  %2 = load i32, i32* %1, align 4
  ret i32 %2
}

开辟了一个4B的int对象,把100存到这个对象(a)的地址(&a)指向的玩意中(*&a),从&a这个地方把值拿出来,放到一个新开辟的4B对象上,然后返回这个新的玩意(%2)。

接下来看看指针

int* doo(){
    int a=100;
    return &a;
}
define i32* @_Z3doov() #0 {
  %1 = alloca i32, align 4
  store i32 100, i32* %1, align 4
  ret i32* %1
}

这边在生成a这个对象并赋值之后就直接把a对象的地址返回给caller了。

再看看reference

int& foo(){
    int a=100;
    return a;
}
define dereferenceable(4) i32* @_Z3foov() #0 {
  %1 = alloca i32, align 4
  store i32 100, i32* %1, align 4
  ret i32* %1
}

这边函数体内的行为可以说和指针一毛一样,返回a对象的地址。但是函数的返回名称不一样,reference那边叫做dereferenceable(4) i32*。这两个什么区别呢,得看IR转汇编。

_Z3foov:                                # @_Z3foov
    .cfi_startproc
# %bb.0:
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset rbp, -16
    mov    rbp, rsp
    .cfi_def_cfa_register rbp
    lea    rax, [rbp - 4]
    mov    dword ptr [rbp - 4], 100
    pop    rbp
    ret
.Lfunc_end0:
    .size    _Z3foov, .Lfunc_end0-_Z3foov
    .cfi_endproc
                                        # -- End function
    .globl    _Z3doov                 # -- Begin function _Z3doov
    .p2align    4, 0x90
    .type    _Z3doov,@function
_Z3doov:                                # @_Z3doov
    .cfi_startproc
# %bb.0:
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset rbp, -16
    mov    rbp, rsp
    .cfi_def_cfa_register rbp
    lea    rax, [rbp - 4]
    mov    dword ptr [rbp - 4], 100
    pop    rbp
    ret
.Lfunc_end1:
    .size    _Z3doov, .Lfunc_end1-_Z3doov
    .cfi_endproc
                                        # -- End function
    .globl    _Z3moov                 # -- Begin function _Z3moov
    .p2align    4, 0x90
    .type    _Z3moov,@function

没啥区别啊,愁。

再分析一个例子

void f1(){
    int a=100;
    int& b=a;
}

void f2(){
    int a=100;
    int* b=&a;
}
; Function Attrs: noinline nounwind optnone uwtable
define void @_Z2f1v() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32*, align 8
  store i32 100, i32* %1, align 4
  store i32* %1, i32** %2, align 8
  ret void
}

; Function Attrs: noinline nounwind optnone uwtable
define void @_Z2f2v() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32*, align 8
  store i32 100, i32* %1, align 4
  store i32* %1, i32** %2, align 8
  ret void
}

这两种行为也是一样的。如果要生成整型变量,我们得开辟空间,给这个空间一个符号。如果要生成指针,我们也得开辟一个装得下64位机地址的空间,然后把这个地址放进符号表。如果生成一个引用,我们还得开辟一个装地址的空间,然后把原先对象的地址装进去,然后把这个装地址的空间放进符号表。clang转成llvm之后,指针和引用真没什么区别。

总结下:
指针和引用其实都是符号表里的一个老哥,这个老哥存的是别人的地址。我们拿到的指针拿到的是一个暴露的对象,编译器允许我们对指针变量进行p进行dereference获得其地址,但是编译器禁止我们对&b再进行解引用获得其值的地址。这是一个符号表暴露的问题。

Responses
  1. Dutasteride Baldness Ups Price On Line Highland Kanada Levitra Bestellen buy viagra online Drug Interaction Amoxicillin And Benzodiazepines Kamagra Oral Jelly 100mg Sildenafil Citrate

    Reply
  2. Propecia Rogaine Foam generic cialis from india Baclofene Diarrhee

    Reply
  3. Cialis Sans Ordonnance Belgique Lioresal Acheter En France cialis Viagra Acquistare Farmacia Levitra Professional Reviews Sildenafil Achat 100 Mg

    Reply
  4. Buying Oral Ampicillin http://cialibuy.com - Cialis Order Propecia Online Consultation cialis 20mg price at walmart Cephalexin 500mg For Pets

    Reply