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++,但我觉得澄清一个术语如何与后面的语言的添加有些混淆是明智的.