c中指针和引用之间的区别?

use*_*929 11 c

c中的指针,引用和取消引用有什么区别?

wil*_*ell 21

这是一张记忆图; 将记忆表示为一系列块:

    address    01   02   03
             +----+----+----+...
data within  | 23 | 6f | 4a |
             +----+----+----+...
Run Code Online (Sandbox Code Playgroud)

现在假设我们创建了一个角色:

char c = 'z';  // 'z' is 7a in hex
Run Code Online (Sandbox Code Playgroud)

进一步假设c存储在地址01,所以我们的内存看起来像这样:

    address    01   02   03
             +----+----+----+...
data within  | 7a | 6f | 4a |
             +----+----+----+...
Run Code Online (Sandbox Code Playgroud)

现在,让我们创建一个指针:

char* p = &c;  // point at c
Run Code Online (Sandbox Code Playgroud)

p可以存储在地址02:

    address    01   02   03
             +----+----+----+...
data within  | 7a | 01 | 4a |
             +----+----+----+...
Run Code Online (Sandbox Code Playgroud)

这里指针p位于地址02,它指向地址01.那是意思p = &c;.当我们取消引用指针p(在地址处02)时,我们会查看指向的地址中的内容p.也就是说,p指向地址01,因此解除引用p意味着查看内部地址01.

最后,让我们创建一个参考:

char& r = c;
Run Code Online (Sandbox Code Playgroud)

这里的内存布局不会改变.也就是说,没有内存用于存储r.r是一种的别名c,所以当我们提到r我们实际上指代c.r并且c在概念上是一个.变化r意味着变化c,变化c意味着变化r.

创建引用时必须初始化,一旦初始化,就无法使用其他目标重新初始化它.也就是说,高于参考r手段并永远意味着c.

还有相关的const引用.这些与引用相同,除了它们是不可变的:

const char& r = c;
r = 'y';  // error; you may not change c through r
c = 'y'   // ok. and now r == 'y' as well
Run Code Online (Sandbox Code Playgroud)

当我们有兴趣阅读数据时我们使用const引用,但是在更改数据时皱眉.通过使用const引用,编译器不会复制数据,因此这为我们提供了理想的性能,但也禁止我们更改数据,以确保正确性.

从某种意义上说,您可以说引用是编译时功能,而指针是运行时功能.因此,引用比指针更快,更便宜,但具有某些约束和含义.与其他编译时vs运行时替代方案一样,我们有时会选择一个用于性能,有时用于静态分析,有时用于灵活性.


小智 6

是时候进行抨击狂热了,因为这些事情总是引起混乱.

  • 指针本身就是一个内存地址.提示在内存中如何发生的花哨图:

    | Address  | Value          |
    |----------|----------------|
    |0x1111    |0x1112          | <-- Pointer!
    |0x1112    |42              | <-- Pointed value
    |0x1113    |42              | <-- Some other value
    
    Run Code Online (Sandbox Code Playgroud)

    为简单起见,我使用了更小的地址大小.基本上,0x1111是一个指针,因为它的内容是另一个值的地址.

  • 解除引用意味着检查指针值中保存的地址的值.这种奇特的语言可能令人困惑; 基本上,如果我取消引用,0x1111我会查看0x1112并从该地址中获取值.为什么?因为它非常有用,因为汇编让我们也这样做,

    mov rax, [r8]
    
    Run Code Online (Sandbox Code Playgroud)

    用于"查看r8中的nasm/intel语法,找到该内存地址,按照它查找该内存地址的值并将其放入rax中".

  • 通过价值.传递值意味着当您创建一个函数堆栈帧(即函数周围的堆栈内容)时,您每个作为参数的值复制到任何位置.寄存器,堆栈,无论在哪里.当然,如果复制指针的值,则复制内存地址,从而创建指向同一内存的另一个指针.这就是这样的功能:

    void add(int* x)
    {
        *x = *x + 7;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    工作.

  • 通过引用传递.上面的函数基本上是通过引用语义传递的,因为你会在C++中看到它们.在程序集级别实现可能相同的关键且可能唯一的区别是引用是C++编译器理解的内容.由于编译器语言,这很重要.C理解指针和操作内存,C编译器也是如此,但它们会让你做任何你喜欢的事情.例如,您无法重新分配参考

    void cppadd(int& x)
    {
        int a = 7;
        x = &a; // doesn't work.
    }
    
    Run Code Online (Sandbox Code Playgroud)

因此,总而言之,引用在一个层面上是一种语言特性,其中编译器可以理解源存储器的位置并防止修改该源存储器地址.它理解你想要玩这个价值.指针就是这样,内存地址保存其他内存地址.

维基百科很好地总结了它:

在C++编程语言中,引用是一种简单的引用数据类型,它比从C继承的指针类型更强大但更安全.名称C++引用可能会引起混淆,因为在计算机科学中引用是一般概念数据类型,带有指针和C++引用是特定的引用数据类型实现.

是的,当这个问题只是C时,我已经提到了C++,但我觉得澄清一个术语如何与后面的语言的添加有些混淆是明智的.


Nik*_*sov 5

在C++中,C中没有明确的引用类型.任何人在C语言的上下文中都说"引用"你可以假设一个指针.