指针变量和引用变量之间的区别是什么?
当前回答
此外,作为内联函数的参数的引用的处理方式可能与指针不同。
void increment(int *ptrint) { (*ptrint)++; }
void increment(int &refint) { refint++; }
void incptrtest()
{
int testptr=0;
increment(&testptr);
}
void increftest()
{
int testref=0;
increment(testref);
}
许多编译器在内联指针版本1时实际上会强制写入内存(我们显式地获取地址)。然而,他们会将引用保留在更优化的寄存器中。
当然,对于未内联的函数,指针和引用生成相同的代码,如果函数未修改和返回内部函数,则通过值传递内部函数总是比通过引用传递内部函数更好。
其他回答
除了语法糖,引用是常量指针(而不是指向常量的指针)。在声明引用变量时,必须确定它所指的内容,以后不能更改它。
更新:现在我再考虑一下,有一个重要的区别。
常量指针的目标可以通过获取其地址并使用常量转换来替换。
引用的目标不能以UB以外的任何方式替换。
这应该允许编译器对引用进行更多优化。
可以重新分配指针:int x=5;整数y=6;int*p;p=&x;p=&y;*p=10;断言(x==5);断言(y==10);引用不能重新绑定,必须在初始化时绑定:int x=5;整数y=6;整数&q;//错误int&r=x;指针变量有它自己的标识:一个独特的、可见的内存地址,可以用一元&运算符获取,还有一定的空间,可以用sizeof运算符测量。对引用使用这些运算符将返回与引用绑定到的任何对象相对应的值;引用自身的地址和大小是不可见的。由于引用以这种方式假定原始变量的身份,因此可以方便地将引用视为同一变量的另一个名称。int x=0;int&r=x;int*p=&x;int*p2=&r;断言(p==p2);//&x==&r断言(&p!=&p2);可以将任意嵌套的指针指向提供额外间接级别的指针。引用仅提供一个间接级别。int x=0;整数y=0;int*p=&x;int*q=&y;int**pp=&p;**pp=2;pp=&q;//*pp现在是q**pp=4;断言(y==4);断言(x==2);指针可以指定为nullptr,而引用必须绑定到现有对象。如果您足够努力,您可以将引用绑定到nullptr,但这是未定义的,并且行为不一致。/*以下代码未定义;你的编译器可以优化它*不同的是,发出警告,或者干脆拒绝编译*/int&r=*static_cast<int*>(nullptr);//在GCC 10下打印“空”标准::cout<<(&r!=空指针? “not null”:“null”)<<std::endl;bool f(int&r){return&r!=nullptr;}//根据GCC 10打印“非空”标准::cout<<(f(*static_cast<int*>(nullptr))? “not null”:“null”)<<std::endl;但是,可以引用值为nullptr的指针。指针可以遍历数组;您可以使用++转到指针指向的下一个项目,使用+4转到第五个元素。这与指针指向的对象的大小无关。指针需要用*解引用以访问它指向的内存位置,而引用可以直接使用。指向类/结构的指针使用->访问其成员,而引用使用。。引用不能放入数组,而指针可以(由用户@litb提及)Const引用可以绑定到临时项。指针不能(不是没有间接指向):常量int&x=int(12);//法定C++int*y=&int(12);//取临时地址是非法的。这使得const&更便于在参数列表等中使用。
我对引用和指针有一个类比,将引用看作对象的另一个名称,将指针看作对象的地址。
// receives an alias of an int, an address of an int and an int value
public void my_function(int& a,int* b,int c){
int d = 1; // declares an integer named d
int &e = d; // declares that e is an alias of d
// using either d or e will yield the same result as d and e name the same object
int *f = e; // invalid, you are trying to place an object in an address
// imagine writting your name in an address field
int *g = f; // writes an address to an address
g = &d; // &d means get me the address of the object named d you could also
// use &e as it is an alias of d and write it on g, which is an address so it's ok
}
指针和引用之间有一个根本的区别,我没有看到任何人提到过:引用支持函数参数中的引用传递语义。指针,虽然它最初不可见,但它不可见:它们只提供值传递语义。这在本文中得到了很好的描述。
当做&rzej公司
引用是常量指针。int*const a=&b与int&a=b相同。这就是为什么没有const引用,因为它已经是const,而const的引用是const int*consta。当使用-O0编译时,编译器将在这两种情况下将b的地址放在堆栈上,并且作为类的成员,它也将出现在堆栈/堆上的对象中,与您声明了常量指针时相同。使用-Ofast,可以免费优化此功能。常量指针和引用都被优化了。
与常量指针不同,无法获取引用本身的地址,因为它将被解释为它引用的变量的地址。因此,在Ofast上,表示引用的常量指针(被引用变量的地址)将始终在堆栈外进行优化,但如果程序绝对需要实际常量指针的地址(指针本身的地址,而不是指针指向的地址),即您打印常量指针的位置,那么const指针将被放置在堆栈上,以便它有一个地址。
否则它是相同的,即当您打印它指向的地址时:
#include <iostream>
int main() {
int a =1;
int* b = &a;
std::cout << b ;
}
int main() {
int a =1;
int& b = a;
std::cout << &b ;
}
they both have the same assembly output
-Ofast:
main:
sub rsp, 24
mov edi, OFFSET FLAT:_ZSt4cout
lea rsi, [rsp+12]
mov DWORD PTR [rsp+12], 1
call std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<void const*>(void const*)
xor eax, eax
add rsp, 24
ret
--------------------------------------------------------------------
-O0:
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-12], 1
lea rax, [rbp-12]
mov QWORD PTR [rbp-8], rax
mov rax, QWORD PTR [rbp-8]
mov rsi, rax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(void const*)
mov eax, 0
leave
ret
指针已经在堆栈外进行了优化,在这两种情况下,指针甚至都没有在-Ofast上取消引用,而是使用编译时值。
作为对象的成员,它们在-O0到-Ofast上是相同的。
#include <iostream>
int b=1;
struct A {int* i=&b; int& j=b;};
A a;
int main() {
std::cout << &a.j << &a.i;
}
The address of b is stored twice in the object.
a:
.quad b
.quad b
mov rax, QWORD PTR a[rip+8] //&a.j
mov esi, OFFSET FLAT:a //&a.i
当通过引用传递时,在-O0上,传递被引用变量的地址,因此它与通过指针传递相同,即常量指针包含的地址。On Ofast如果函数可以内联,则编译器会在内联调用中对其进行优化,因为动态范围是已知的,但在函数定义中,参数总是作为指针(期望引用引用的变量的地址)被解引用,其中它可能被另一个转换单元使用,而编译器不知道动态范围,当然,除非函数声明为静态函数,否则它不能在转换单元之外使用,然后它通过值传递,只要它没有在函数中通过引用进行修改,那么它将传递您传递的引用所引用的变量的地址,如果调用约定中有足够多的易失性寄存器,则将在一个寄存器中传递,并保持在堆栈之外。
推荐文章
- 为什么我的程序不能在Windows 7下用法语编译?
- 如何获取变量的类型?
- 什么是奇怪的重复模板模式(CRTP)?
- 连接两个向量的最佳方法是什么?
- 在c++中,是通过值传递更好,还是通过引用到const传递更好?
- 在STL中deque到底是什么?
- 如何在xml文档中引用泛型类和方法
- Windows上最好的免费c++分析器是什么?
- 如何自动转换强类型枚举为int?
- 在一个类中使用具有成员函数的泛型std::function对象
- 'for'循环中的后增量和前增量产生相同的输出
- 虚函数和纯虚函数的区别
- c++中的_tmain()和main()有什么区别?
- 内存泄漏是否正常?
- 当启用c++ 11时,std::vector性能回归