int & 的意思是什么?

35

一道C++问题,

我知道

int* foo(void)

foo将返回一个int类型的指针。

那么这个怎么处理?

int &foo(void)

它返回什么?

非常感谢!


还有一点需要知道:https://dev59.com/lnI95IYBdhLWcg3w1BdN - Ben Voigt
8个回答

57

它返回一个指向 int 的 引用。引用类似于指针,但有一些重要的区别。我建议您阅读有关指针、引用、对象和基本数据类型之间差异的相关资料。

"Effective C++" 和 "More Effective C++"(均由 Scott Meyers 写作)对这些差异以及何时使用指针和引用有很好的描述。

编辑:有许多答案表示"引用只是指针更易处理的语法糖"。它们绝对不是

考虑以下代码:

int a = 3;
int b = 4;
int* pointerToA = &a;
int* pointerToB = &b;
int* p = pointerToA;
p = pointerToB;
printf("%d %d %d\n", a, b, *p); // Prints 3 4 4
int& referenceToA = a;
int& referenceToB = b;
int& r = referenceToA;
r = referenceToB;
printf("%d %d %d\n", a, b, r); // Prints 4 4 4

p = pointerToB这行代码改变了p的值,也就是说它现在指向另一个内存地址。

r = referenceToB做的完全不同:它将b的值赋给了原本存放a的位置。它没有改变r本身。 r仍然引用着同一个内存地址。

这个差别微妙但非常重要。

如果你还认为引用只是指针处理的一种语法糖,那么阅读 Scott Meyers 的书籍。他可以比我更好地解释这个区别。


1
+1 推荐 Scott Meyers 的书籍。对于想要深入了解 C++ 的人来说,这些书是必备的。 - Matthieu
1
是的,它们都是很好的书。教会了我所有的知识! - Cameron Skinner
@Bento:不,那不正确。你不能这样做:int* const p = &a; p = &b;,但你可以这样做:int& r = a; r = b;。引用不是指针,无论是否为const。 - Cameron Skinner
考虑以下代码:int& r = a; r = b;。如果您的断言是正确的,那么您可以将int&替换为int* const,并且代码仍应该编译。试一试,你会发现int* constint&不是同一回事。 - Cameron Skinner
@Cameron:我不仅仅是指 int& <=> int* const,而是指其他适当的替换方式——即用解引用 *p = b 替换 r = b。从这个意义上说,这两种语法做相同的工作。 - Bento
显示剩余2条评论

6

请注意…你正在涉及C/C++的领域。虽然两者有明显区别,但有时候并不容易分辨:

C++:通常表示引用。例如考虑以下代码:

void func(int &x)
{
   x = 4;
}

void callfunc()
{
    int x = 7;
    func(x);
}

因此,C++ 可以通过值传递或引用传递。

C 没有引用传递的功能。& 表示“取地址符”,是一种从变量中构建指针的方法。但请考虑以下情况:

void func(int* x)
{
   *x = 4;
}

void callfunc()
{
    int x = 7;
    func(&x);
}

看似相似,但本质不同。在 C 中,你所做的是传递指针的副本。现在这些指针仍然指向同一块内存区域,因此在指向的内存方面,其效果类似于按引用传递,但传递的不是引用,而是指向内存中某个位置的引用。

尝试以下代码(编译为C语言):

#include <stdio.h>

void addptr(int* x)
{
    printf("Add-ptr scope 1:\n");
    printf("Addr: %p\n", x);
    printf("Pointed-to-memory: %d\n", *x);
    *x = *x + 7;
    x++;
    printf("Add-ptr scope 2:\n");
    printf("Addr: %p\n", x);
    printf("Pointed-to-memory: %d\n", *x);
}

int main(int argc, char** argv)
{
    int a = 7;
    int *y = &a;
    printf("Main-Scope 2:\n");
    printf("Addr: %p\n", y);
    printf("Pointed-to-memory: %d\n", *y);
    addptr(y);
    printf("Main-Scope 2:\n");
    printf("Addr: %p\n", y);
    printf("Pointed-to-memory: %d\n", *y);
    return 0;

}

如果C语言有传递引用的机制,那么当指针地址被addptr更改时,这个变化应该在main中反映出来,但实际上并没有。指针仍然是值。
因此,C没有任何传递引用的机制。在C++中,存在这种机制,这就是函数参数中的 & 的含义等等。
编辑:你可能会想知道为什么我不能轻松地在C++中进行这个演示。因为我无法更改引用的地址。根据这个非常好的引用指南

如何重新设置引用以使其引用不同的对象?

没办法。

您无法将引用与所引用的对象分离。

与指针不同,一旦引用绑定到对象上,它就无法“重新设置”以引用另一个对象。引用本身不是对象(它没有标识符;获取引用的地址会给出所引用对象的地址;请记住:引用就是所引用的对象)。

从这个意义上说,引用类似于const指针,例如int* const p(而不是指向const的指针,例如int const* p)。但请不要混淆引用和指针;从程序员的角度来看,它们非常不同。

根据请求,关于返回引用:
#include <iostream>

using namespace std;

int& function(int f)
{
   f=f+3;
   return f;
}

int main(int argc, char** argv)
{
    int x = 7;
    int y;
    y = function(x);
    cout << "Input: " << x << endl;
    cout << "Output:" << y << endl;
    return 0;
}

任何好的编译器都应该以某种形式给您提供此警告信息:

exp.cpp:7:11: 警告:返回与本地变量“f”关联的堆栈内存的引用

这是什么意思?我们知道函数参数被推入堆栈(注意:在x64上实际上不是,它们进入寄存器然后进入堆栈,但它们在x86上确实在堆栈上),而这个警告的意思是创建对这样一个对象的引用不是一个好主意,因为不能保证它会留在原地。它留在原地只是运气而已。

那怎么办呢?试试这个修改过的版本:

#include <iostream>

using namespace std;

int& function(int& f)
{
    f=f+3;
    return f;
}

int main(int argc, char** argv)
{
    int x = 7;
    int y;
    y = function(x);
    cout << "Input: " << x << endl;
    cout << "Output:" << y << endl;
    return 0;
}

运行此代码,您会看到两个值都得到了更新。为什么呢?因为它们都指向同一个被编辑的对象。

1
非常感谢!我知道在C语言中,&foo表示foo的地址,如果foo是一个整数,则可以将其传递给int*类型。但我困惑的是,当它用于函数声明时,int &foo(...),它是什么意思? - Alfred Zhong
你在 C 中所做的是传递指针的副本。不,你传递的是本地变量 x 的地址的值作为指针参数给 func。此时没有其他指针,因此不能复制指针。在 C++ 示例中,您将变量 x 传递给一个带有 引用 参数的函数,因此您得到对 x 的引用。这些的实现(可能)相同:x 的地址被推送到堆栈上。换句话说,C 确实 允许您使用引用语义,但它不提供支持它们的语法。 - dajames
是的,你传递的是指针值的副本 - 指针的副本。指针本身就是一个内存地址(因此是“变量”),包含另一个内存地址。将gdb附加到进程中,您将能够看到这一点。因此,复制它的行为是分配一个新的内存空间并将值放入其中。这就是所谓的按值传递。 - user257111
按引用传递参数是一种明确的语言特性,语言本身只是工具。最终结果非常相似,但强调不同语言的工作方式差异很重要。 - user257111

1

它返回一个指向 int 变量的引用。


不需要是一个变量(这意味着它没有命名)。它返回一个 int 对象的引用。 - Martin York

1

这个问题与C/C++无关,因为C没有引用,只有指针。int&是对int的引用。此外,您不需要使用void,可以直接使用int& foo();


这是文档上的内容,德州仪器的TMS320C28x C/C++编译器内部函数,http://focus.ti.com/lit/ug/spru514c/spru514c.pdf,第122页,int&__byte(int, unsigned int),我猜它和PC不同。 - Alfred Zhong
@Alfred:不,实际上并没有。C和C++语言没有特殊的嵌入式版本。手册是错误的,就是这样。 - Puppy
@Alfred Zhong。文档上说的和这里其他人一样。 - Martin York

1

来自阿尔弗雷德的评论:

这是文档上的内容,德州仪器 TMS320C28x C/C++编译器内置函数,第122页,int&__byte(int, unsigned int)。我猜它和个人电脑不同。- 阿尔弗雷德

来自手册:

int &__byte(int *array, unsigned int byte_index);

MOVB array[byte_index].LSB, src

C28x 中可寻址的最小单元为 16 位。因此,通常情况下无法访问 8 位 MOVB dst,array[byte_index]。该内置函数可以帮助从内存位置中获取 8 位数据,并可如下调用:

__byte(array,5) = 10;
b = __byte(array,20);

这意味着该函数返回一个整数的引用,它的行为类似于8位数量。因为该值是一个引用,修改将会修改目标对象(就像MOVB指令一样),而将值赋给b将会复制(就像MOVB指令一样)到目标。


谢谢Martin,我很困惑,因为我完全按照手册上的指示操作,__byte(array,5)=10,但编译器一直报错“错误:表达式必须是可修改的左值”。 - Alfred Zhong

0

int& 是一个引用。

更准确地说,引用变量只是现有变量的替代名称。

例如:考虑以下代码

main(){
    int x = 4;
    int& ref_of_x = x;
    // Now, modifying x modifies ref_of_x too and vice-versa
    x = 5;
    cout<<ref_of_x; //This prints 5
    
    ref_of_x = 15;
    cout<<x;  // This prints 15
    }

0

只是在玩弄变量来展示其含义

int i = 5;
int * pI = &i;
int & referenceToI = * pI;
referenceToI = 4; // now i == 4

编辑:参考文献只是一种更易于处理指针的语法糖。在汇编级别上,编译器生成的代码会返回给您一个地址指针。


谢谢!我知道这个。让我困惑的是当它用在函数声明中时,比如 int &foo(...),这个函数返回什么? - Alfred Zhong
它返回一个对return后面放置的内容的引用。 - Matteo Italia
我认为这是汇编语言,MOVB array[byte_index].LSB,src,我不知道它的含义。 - Alfred Zhong
1
参考文献只是指针的一种语法糖。在汇编级别上,我确定函数会返回一个指针。我要进行编辑。 - Stephane Rolland

0

https://isocpp.org/wiki/faq/references

这个页面简明扼要地说明了它!引用是一个别名。它就是对象,只是另一个名称而已。以前的名称仍然有效。引用不是独立的对象。它是以不同的名称表示的对象。你不能对引用进行操作,因为它本身不是一个东西。它只是原始对象的别名。

就是这么简单。现在你可以轻松地理解它,而不是像上面的帖子一样绕来绕去。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接