首页 > 其他分享 >指针与引用区别

指针与引用区别

时间:2024-05-30 15:21:39浏览次数:26  
标签:rsi 区别 int rbp 引用 指针 movq

 

=====

来自:https://www.cnblogs.com/honernan/p/12175899.html

每日一问1:指针和引用的区别

 

  指针(pointer)和引用(references)都是对另一个变量的一种关联,即通过指针和引用可以使用另一个变量。而它们最大的区别在于:指针是一个变量,而引用不是。但在c++底层中,引用是通过指针实现的,所以,在实现层面上来说,引用就是指针,但是在c++语法上来说,c++编译器并不为引用类型分配内存,所以引用不能为空,必须被初始化,一旦初始化不能更改引用对象。所有对引用的操作都是对原始对象的操作 。

  指针是一个变量,它所存储的就是它所指向的变量的地址,而它同时也有着自己的地址。引用可以理解为变量的"小名”,它的地址也就是它引用的变量的地址。也可以说,指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。它们更细致的区别总结如下:

 


  后续查阅资料的过程中,发现了自己理解的错误,因为是学习的过程,所以不删除错误,引以为戒。在上面提到引用不是一个变量,这是错误的,引用是一个变量,它在底层的实现就是一个变量,一个常量指针。C++对常量指针进行一些包装后就成了引用。

==========

来自:https://csguide.cn/cpp/memory/difference_of_pointers_and_ref.html#google_vignette

区别
指针和引用在 C++ 中都用于间接访问变量,但它们有一些区别:

指针是一个变量,它保存了另一个变量的内存地址;引用是另一个变量的别名,与原变量共享内存地址。

指针可以被重新赋值,指向不同的变量;引用在初始化后不能更改,始终指向同一个变量。

指针可以为 nullptr,表示不指向任何变量;引用必须绑定到一个变量,不能为 nullptr。

使用指针需要对其进行解引用以获取或修改其指向的变量的值;引用可以直接使用,无需解引用。

下面的示例展示了指针和引用的区别:

#include <iostream>

int main() {
int a = 10;
int b = 20;

// 指针
int *p = &a;
std::cout << "Pointer value: " << *p << std::endl; // 输出:Pointer value: 10

p = &b;
std::cout << "Pointer value: " << *p << std::endl; // 输出:Pointer value: 20

// 引用
int &r = a;
std::cout << "Reference value: " << r << std::endl; // 输出:Reference value: 10

// r = &b; // 错误:引用不能被重新绑定
int &r2 = b;
r = r2; // 将 b 的值赋给 a,r 仍然引用 a
std::cout << "Reference value: " << r << std::endl; // 输出:Reference value: 20

return 0;
}
当然啦,上面说的都是很简单的一些概念,这里继续引用之前公众号发过的一篇文章: 指针和引用有什么区别(opens new window)

这篇文章中从汇编底层角度来解释 C++ 中引用的实现机制。

#从汇编看引用和指针
要是问引用和指针有什么区别,相信大多数学过c++的都能回答上几点: 指针是所指内存的地址,引用是别名,引用必须初始化。。。。。

但是引用是别名这是c++语法规定的语义,那么到底引用在汇编层面和指针有什么区别呢?

没区别。

对,引用会被c++编译器当做const指针来进行操作。

#汇编揭开引用面纱
先分别用指针和引用来写个非常熟悉的函数swap

// 指针版
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 引用版
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
直接gcc -S 输出汇编

# 引用版汇编
__Z4swapRiS_: ## @_Z4swapRiS_
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movq %rdi, -8(%rbp) # 传入的第一个参数存放到%rbp-8 (应该是采用的寄存器传参,而不是常见的压栈)
movq %rsi, -16(%rbp) # 第二个参数 存放到 %rbp-16
movq -8(%rbp), %rsi # 第一个参数赋给 rsi
movl (%rsi), %eax # 以第一个参数为地址取出值赋给eax,取出*a暂存寄存器
movl %eax, -20(%rbp) # temp = a
movq -16(%rbp), %rsi # 将第二个参数重复上面的
movl (%rsi), %eax
movq -8(%rbp), %rsi
movl %eax, (%rsi) # a = b
movl -20(%rbp), %eax # eax = temp
movq -16(%rbp), %rsi
movl %eax, (%rsi) # b = temp
popq %rbp
retq
.cfi_endproc
## -- End function
在来一个函数调用引用版本swap

void call() {
int a = 10;
int b = 3;
int &ra = a;
int &rb = b;
swap(ra, rb);
}
对应汇编:

__Z4callv: ## @_Z4callv
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $32, %rsp
leaq -8(%rbp), %rax # rax中是b的地址
leaq -4(%rbp), %rcx # rcx中是a的地址
movl $10, -4(%rbp)
movl $3, -8(%rbp) # 分别初始化a、b
movq %rcx, -16(%rbp) # 赋给ra引用
movq %rax, -24(%rbp) # 赋给rc引用
movq -16(%rbp), %rdi # 寄存器传参, -16(%rbp)就是rcx中的值也就是a的地址
movq -24(%rbp), %rsi # 略
callq __Z4swapRiS_
addq $32, %rsp
popq %rbp
retq
清楚了! 我们可以看到给引用赋初值也就是把所绑定对象的地址赋给引用所在内存,和指针是一样的。

再来看看指针的汇编吧

__Z4swapPiS_: ## @_Z4swapPiS_
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rsi
movl (%rsi), %eax
movl %eax, -20(%rbp)
movq -16(%rbp), %rsi
movl (%rsi), %eax
movq -8(%rbp), %rsi
movl %eax, (%rsi)
movl -20(%rbp), %eax
movq -16(%rbp), %rsi
movl %eax, (%rsi)
popq %rbp
retq
.cfi_endproc
## -- End function
汇编我就不注释了,真的是完全一样!并不是我直接复制的引用汇编而是真的是一种方式实现的

指针版调用

void pointer_call() {
int a = 10;
int b = 3;
int *pa = &a;
int *pb = &b;
swap(pa, pb);
}
这次我特意改了下函数名,对应汇编:

__Z12pointer_callv: ## @_Z12pointer_callv
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $32, %rsp
leaq -8(%rbp), %rax
leaq -4(%rbp), %rcx
movl $10, -4(%rbp)
movl $3, -8(%rbp)
movq %rcx, -16(%rbp)
movq %rax, -24(%rbp)
movq -16(%rbp), %rdi
movq -24(%rbp), %rsi
callq __Z4swapPiS_
addq $32, %rsp
popq %rbp
retq
还是几乎完全一样...。也没再注释

#简单总结
引用只是C++语法糖,可以看作编译器自动完成取地址、解引用的指针常量
引用区别于指针的特性都是编译器约束完成的,一旦编译成汇编就喝指针一样
由于引用只是指针包装了下,所以也存在风险,比如如下代码:
int *a = new int;
int &b = *a;
delete a;
b = 12; // 对已经释放的内存解引用
引用由编译器保证初始化,使用起来较为方便(如不用检查空指针等)
尽量用引用代替指针
引用没有顶层const即int & const,因为引用本身就不可变,所以在加顶层const也没有意义; 但是可以有底层const即 const int&,这表示引用所引用的对象本身是常量
指针既有顶层const(int * const--指针本身不可变),也有底层const(int * const--指针所指向的对象不可变)
有指针引用--是引用,绑定到指针, 但是没有引用指针--这很显然,因为很多时候指针存在的意义就是间接改变对象的值,但是引用本身的值我们上面说过了是所引用对象的地址,但是引用不能更改所引用的对象,也就当然不能有引用指针了。
指针和引用的自增(++)和自减含义不同,指针是指针运算, 而引用是代表所指向的对象对象执行++或--

作者: 编程指北
链接: https://csguide.cn/cpp/memory/difference_of_pointers_and_ref.html#%E5%8C%BA%E5%88%AB
来源: https://csguide.cn
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

==

 

参考:

标签:rsi,区别,int,rbp,引用,指针,movq
From: https://www.cnblogs.com/rebrobot/p/18222448

相关文章

  • WCDMA LTE FDD TDD 区别
     首先,这四种都是移动网络通讯的标准 WCDMAWCDMA是一种让手机可以更快上网和打电话的技术。它有几个关键特点:宽带:就像一条很宽的高速公路,可以让更多的数据(信息)同时通过。这意味着你上网、看视频或者下载东西的速度会更快。码分多址:这是WCDMA的一种特殊技术,让很多人可以同......
  • Java中Comparable接口和Comparator接口的区别(如果想知道Java中Comparable接口和Compar
        前言:在Java中,Comparable接口和Comparator接口都用于对象之间的比较和排序,但它们在使用和设计上存在一些关键的区别。✨✨✨这里是秋刀鱼不做梦的BLOG✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客        本篇文章主要讲解的是J......
  • 深入理解指针(5)
    在之前的深入理解指针(4)中我们学习了回调函数相关知识,并且学会了如何使用库函数qsort,以及模拟实现了qsort,接下来在本篇中将对srtlen和sizeof进行细致的讲解,并对相关的题型进行讲解,一起加油吧!!!1.strlen与sizeof的区别1.sizeof在之前学习操作符的时候就已经了解过了sizeof......
  • JS中?? 与 || 的区别
     1)相同点:??和||的用法相同,都是前后是值,中间用符号连接,根据前面的值来判断最终是返回前面的值还是后面的值。One??TwoOne||Two2)不同点:判断的方法不同:使用??时,只有One为null或者undefined时才会返回two;使用||时,One会先转化为布尔值判断,为true时返回One......
  • 你知道列存储的定义和优势以及行存储的区别?--数据仓库基本概念
    列存储(ColumnarStorage)是一种数据库存储数据的方式,它将每一列的数据存储在一起,而不是按行存储。这与传统的行存储(RowStorage)相反,后者将一行中的所有数据存储在一起。列存储的定义:列存储数据库或文件系统会将表中的每一列数据分别存储在不同的位置。例如,如果有一个表包含......
  • #和 $ 在 MyBatis 中的区别
    哈喽,大家好,我是木头左!MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。在MyBatis中,#{}和${}都可以用来表示参数,但是它们之间有一些区别。本文将从以下几个方面介绍这两种符号的区别:1.#{}和${}的用法在MyBatis中,#{}用于预编译的SQL语句......
  • 指针(2),迭代,快速排序,单词倒置
    指针运算:& * +N-Np++ //往后跳了一个元素 p-- //往前一个元素 p-q //相同类型的指针减出的来的结果为,地址之间相差的元素个数 关系运算:p>q  p<q >>=<<=!= 迭代:迭代其实就是一种特殊的循环,迭代根据上一次循环得到的运算结果来进行下......
  • 记一次go项目循环引用问题
    1.问题背景界面歌曲列表需要添加收藏icon。后端需要返回对应歌曲的用户收藏信息。allmusic-api-server/service/favoriatesongservice包下依赖函数:commonservice.GetRelatedInfos()用于查询歌曲相关信息与本次需求无关。allmusic-api-server/service/commonservice包下......
  • C++ 跨线程 传递指针
    目录在C++中跨线程传递指针时,需要注意线程安全和生命周期管理的问题。以下是一些常见的方法,用于在C++中安全地跨线程传递指针:使用智能指针和线程安全队列结合使用std::shared_ptr和线程安全的队列(如std::queue配合互斥锁)是一种常见的方法。#include<iostream>#include<t......
  • 视频汇聚EasyCVR平台视图库GA/T 1400协议与GB/T 28181协议的区别
    在公安和公共安全领域,视频图像信息的应用日益广泛,尤其是在监控、安防和应急指挥等方面。为了实现视频信息的有效传输、接收和处理,GA/T1400和GB/T28181这两个协议被广泛应用。虽然两者都服务于视频信息处理的目的,但它们在实际应用、功能特性和适用范围上却存在显著的区别。今天我......