Visual C++中*号位置和功能详细解说

news2024/11/17 21:43:29

我是荔园微风,作为一名在IT界整整25年的老兵,今天来聊聊Visual C++中*号的位置。

我知道在程序员队伍中有一群特别细心、谨慎的可爱的人,他们经常为一些在别人看来小的不能再小的问题所困惑。比如说,*号的位置,让很多人一直搞不清楚。

加上一些教程把*号印错了位置,引起很大的误解,再加上一些老程序员随意的很,一会把*号放前面,一会把*号放后面,让程序员们很困惑。

我今天这篇文章就来拯救一下细心、谨慎的程序员。

C/C++语言中*号的位置

C/C++语言中*的位置有如下两种书写方式:

int *a;  靠近变量

int* a;  靠近变量类型

两者意思相同且后者看上去更为清楚表明意思,也就是说a被声明为类型为 int* 的指针。

但是这样有时会有一些问题。原因如下:

int* b, c, d;

一般以为这条语句把所有三个变量声明为指向整形的指针, 但事实上并非如此。星号实际上只是表达式 *b 的一部分, 只对这个标识符有用。b 是一个指针, 但其余两个变量只是普通的整形。要声明三个指针, 正确的语句如下:

int *b, *c, *d;

但是,int *a[5]和int(*a)[5]是不一样的。

int *a[5]是指针数组,它的元素是整形指针

int(*a)[5]是指向整形数组的指针

在C/C++语言中,先定义了类型,后有的标识符,这些标识符组成的表达式,用于产生基本类型的变量
​例如 int *a;

​这条语句表示*a表达式产生的结果类型int ,知道*操作符的作用是间接访问操作,就可以知道a是指向int 的指针。

C/C++语言是很自由的语言。​知道编译机制,编译器编译时会将代码中的空格去除掉,链接最后转成二进制机器码,让机器可以识别,​所以有 ​int* a;这种定义形式。​可以看到这样比上面那个更清晰更容易看懂,a被声明为类型为int*类型的指针。

好,我们再来看C/C++语言中*号在不同使用环境下有不同的含义,对于急性子来说,现总结星号的含义有如下几种:

*代表乘法

作为算术运算符,*代表乘法,进行相乘运算

#include<stdio.h>

int main(void){

int a = 10;

int b = 20;

printf("%d",a*b);

return 0;

}

此例中*作为乘法使用。

*定义指针变量

int * p,定义了一个p变量,int *代表变量p是指针变量,只能存放变量地址。

#include<stdio.h>

int main(void){

int a = 100;

int* p;

p = &a;

printf("%p",p);

return 0;

}

此例中,定义了指针变量p,将变量a的地址存储在指针变量p中。

*解引用运算符

*作用是使用指针指向的变量值,引用为引用指针变量的地址,解引用为使用指针变量指向的值。该运算符放在指针变量的前面,表示以该指针变量内容为地址的变量。

如:int * p定义了指针变量p,则*p表示,以p内容为地址的变量

#include<stdio.h>

int main(void){

int a = 100;

int * p;

p = &a;

printf("%p\n",&a);

printf("%p\n",p);

printf("%d\n",*p);

return 0;

}

此例中定义了指针变量int * p,打印时&a与p结果均为变量a的地址,*p为以指针变量p内容为地址的变量,即为变量a的值。

对于急性子来说就是这样,但是实际上,*号的用法很多,有乘法运算符、复合赋值运算符、假读符、注释符、普通符号、指针定义符、指向运算符、行列地址转换符、地址值符。

我们来看代码。

#include <stdio.h>
 
int main()
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int sum = 0;
 
 
    for (int i = 0; i < 10; i++)
    {   
        sum = *(*(&arr + 0) +  i);  
        printf("%d\n",sum);
    }
                                                                       
    printf("\n");
    return 0;
}

从上面代码可以看出,

arr是一个数组,arr表示的是数组首地址,那么&arr表示的就是首地址的地址,*(&arr+0)表示的就是首地址,*(&arr+0)+i得到的是第i个元素地址,那么*(* (&arr+0) + i)得到的就是第i个元素的值。
 

然后我们再综合一些&方面的知识,再看如下内容:

C/C++中*和&的用法详解

C++中*和&的用法在网上很多贴子里讲这些知识点多数都是分开讲其用法的,没有详细的总结,导致初学者在这方面的知识结构格外混乱,现在我们结合上面说的再深入一层。

C++语言是C语言的超集。几乎所有可以运行的C程序都是可以运行的C++程序。因此,写一个不包含C++特性的C++程序是可能的,尽管cout和引用的使用更好的构成了一个C++程序。C++和C在代码格式上偶尔会有不同。

C语言

∗号用法
乘法运算:x=y*z;
乘法赋值运算:x*=y;相当于x=x*y
注释:/*这里是你的注释*/
指针的声明:int *p 或 int* p; 读法:p是指向一个整数类型的指针。
复合指针: int **p; 或 int** p; 读法: p是一个指向一个指向整数类型的指针的指针。
解引用: x=*p 把指针p指向的值赋值给x。

&号用法
逻辑与:if((a>1)&&(b<0))
位运算与:x=a&b;
逻辑与赋值:x&=y;与 x=x&y含义相同
求地址运算符:p=&x;读法:把x的地址赋给p(指针)

代码详解
注:这里主要讲解关于指针与取地址的问题。

#include <stdio.h>

int main(){
    
int a = 10;
int *b = &a;

printf("%d\n", a);
printf("%d\n", &a);
printf("%d\n", b);
printf("%d\n", *b);
    
return 0;
}

运行结果:
10
6487572
6487572
10

变量a 本质上代表一个存储单元。CPU通过该存储单元的地址访问该存储单元中的数据。所以a本来代表两个值:存储单元的地址和储单元中的数据。C语言规定a表示存储单元中的数据,&a表示存储单元的地址。

a存储单元中的数据可以是一个普通数值,也可以是另一个存储单元的地址,比如:a = &b;语句就是将b的存储单元的地址存入a存储单元中。C语言规定*a代表a中存储的地址对应的存储单元中的数据(也就是解引用的意思),也就是访问*a就等于访问b,于是*a提供了通过a访问b中的数据的手段。

a表示a对应的存储单元中的数据。&a表示a对应的存储单元的地址。

当a声明的类型是int*时,a中存储的是一个存储单元的地址,而该存储单元中存储的数据是一个整数数值;通过*a可以访问(读取或修改)这个数值。a== &*a 都是该存储单元的地址。

当a声明的类型是int**时,a中存储的是一个存储单元的地址,而该存储单元中存储的数据是另外一个存储单元的地址,另外这个存储单元中存储的是一个整数数值;通过**a可以访问(读取或修改)这个数值。

其他说明

简单的来说,在C语言里地址叫指针。在C语言中的数组本质上其实也是指针,即:*a 等同于 a[]。

#include <stdio.h>
int main(){
    int *a;
    int b[2];
    int s;
    char *d="Hello world";
    b[0]=2;
    b[1]=9;
    a=b;
    for(s=0;s<2;s++)
        printf("a[i]=%d,b[i]=%d\n",a[s],b[s]);    
        printf("d[0]=%c,d[1]=%c\n",d[0],d[1]);
    return 0;
}

运行结果:
a[i]=2,b[i]=2
a[i]=9,b[i]=9
d[0]=H,d[1]=e


C++语言

C++中有一种C不存在的变量类型引用变量(简单说来为引用),尽管在C语言中用指针也可以实现类似的功能。引用,指针,地址是联系密切的概念。地址是在电脑内存中的地址(一般是一些变量的值在内存中的储存位置),指针是存地址的变量,所以指针可以“指向”内存地址。概念上讲,引用变量本质上是指针的另一个名字(但是并不能被编译器实例化)

C++中,&和*的用法基本一样。但是,C++中&的补充用法:引用。引用是C++引入的新语言特性,是C++常用的一个重要内容之一。引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

引用的声明方法:[类型标识符] &引用名=目标变量名;

#include <stdio.h>

int main(){
    
int a=3; 
int &ra=a;
printf("a=%d,&ra=%d",a,ra);
    
return 0;    
}

运行结果:
a=3,&ra=3


说明:

&在此不是求地址运算,而是起标识作用。类型标识符是指目标变量的类型。声明引用时,必须同时对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。

#include<iostream>
using namespace std;

void swap(int &p1, int &p2) //此处函数的形参p1, p2都是引用
{     
    int p; 
    p=p1; 
    p1=p2; 
    p2=p; 
}

int main(){
    int a,b;
    cin>>a>>b; //输入a,b两变量的值
    swap(a,b); //直接以变量a和b作为实参调用swap函数
    cout<<a<< ' ' <<b; //输出结果
return 0;
}

运行结果
输入:10 20
输出:20 10

传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
 

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/700248.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot集成MinIO

简介 对象存储服务OSS&#xff08;Object Storage Service&#xff09;是一种海量、安全、低成本、高可靠的云存储服务&#xff0c;适合存放任意类型的文件。容量和处理能力弹性扩展&#xff0c;多种存储类型供选择&#xff0c;全面优化存储成本。今天我这里主要讲解SpringBoo…

工业物联网解决方案:水轮机PLC数据采集远程监控系统

水轮机是一种能将水的动能转化为旋转机械能的动力设备&#xff0c;是水电站的重要工业设备。随着工业自动化技术与PLC设备的推广应用&#xff0c;水轮机可以通过PLC实现自动化控制&#xff0c;进而通过搭建PLC数据采集远程监控系统&#xff0c;实现水轮机的实时在线监控&#x…

产品再次跳票 法拉第未来的融资价值却渐渐浮现?

从“生态化反”到“极智科技顶奢”&#xff0c;贾跃亭讲故事的出众能力&#xff0c;总是能为旗下企业和产品赚足市场眼球。 但仅有宏大的前景描绘&#xff0c;似乎难以触动理性的资本市场&#xff0c;其创立的新能源车企法拉第未来&#xff08;NASDAQ&#xff1a;FFIE&#xf…

在IDEA中将自定义的配置文件设置到SpringBoot工程中

可以帮助我们生成自定义配置的元数据信息&#xff0c;让配置文件和Java代码之间的对应参数可以自动定位&#xff0c;方便开发。 <!-- 生成自定义配置的元数据信息 --><dependency><groupId>org.springframework.boot</groupId><artifactId>sprin…

Hiera:一个没有Bells-and-Whistles的分层视觉转换器

文章目录 Hiera: A Hierarchical Vision Transformer without the Bells-and-Whistles摘要本文方法实验结果 Hiera: A Hierarchical Vision Transformer without the Bells-and-Whistles 摘要 现代层次视觉转换器在追求监督分类性能的过程中增加了一些特定于视觉的组件。虽然…

PS修改日期(简单两步)

PS修改日期&#xff08;简单两步&#xff09; Q&#xff1a;将图中的日期修改为6月10日 选择画笔工具&#xff0c;把要修改的日期涂抹掉 设置前景色&#xff0c;使用吸管吸附要涂抹的颜色&#xff0c;然后点击确定 使用设置好颜色的画笔&#xff0c;在要修改的日期上涂抹 …

接口自动化测试实操【设置断言思路】

1 断言设置思路 这里总结了我在项目中常用的5种断言方式&#xff0c;基本可能满足90%以上的断言场景&#xff0c;具体参见如下脑图&#xff1a; 在这里插入图片描述 下面分别解释一下图中的五种思路&#xff1a; 1&#xff09; 响应码 对于http类接口&#xff0c;有时开发人…

期末复习【计算机网络】

期末复习【计算机网络】 前言推荐期末复习如何快速阅读电子书重点第1章 概述1.6 计算机网络的性能1.6.2 计算机网络的性能指标√ 1.7 计算机网络体系结构1.7.3 具有五层协议的体系结构√ 第2章 物理层2.3 物理层下面的传输媒体 *2.4 信道复用技术2.4.1 频分复用、时分复用和统计…

天津大学天津市认知计算与应用重点实验室视听觉认知计算团队12篇论文被语音处理顶会Interspeech 2023接收

天津大学天津市认知计算与应用重点实验室视听觉认知计算团队12篇论文被语音技术顶会Interspeech 2023接收&#xff0c;涵盖意图识别、口语理解、声学特征、语音识别、语音分离、情感识别等研究方向&#xff0c;论文简介如下。 01. Rethinking the visual cues in audio-visual…

Harbor 间通过API实现镜像复制

背景&#xff1a; 通过harbor搭建私有镜像仓库时&#xff0c;往往会设计出 研发镜像仓库--》测试镜像仓库--》产品镜像仓库&#xff0c;甚至更多。那个多镜像间的复制将成了一个必须解决的问题。当然可以通过harbor自带的界面操作即可实现需求&#xff0c;也可以通过harbor提供…

详解20个常见的模拟电路

桥式整流电路 二极管的单向导电性&#xff1a;二极管的PN结加正向电压&#xff0c;处于导通状态&#xff1b;加反向电压&#xff0c;处于截止状态。其伏安特性曲线&#xff0c;如下图。 理想开关模型和恒压降模型&#xff1a;理想模型指的是在二极管正向偏置时&#xff0c;其管…

乳腺癌预测的黑科技!一起探索 R 语言和随机森林算法的奇妙之处!

一、引言 乳腺癌是全球最常见的女性恶性肿瘤之一&#xff0c;也是导致女性死亡的主要原因之一。根据世界卫生组织的数据&#xff0c;每年有约200万例乳腺癌新病例的发生&#xff0c;导致60多万人死亡。乳腺癌的早期发现和精准治疗对于患者的生存率和预后至关重要。 然而&…

uniapp封装门票等票务样式

先看效果图 ticketpage组件引用后&#xff0c;根据父级背景颜色改变镂空的颜色,空组件只有中间镂空的样式&#xff0c;上面是插槽heaer,下面内容是插槽content&#xff0c;可以自定义自己的内容和样式。我实现的最终效果是用的uview组件&#xff0c;如果复现需要项目引入。可…

pytorch量化库使用(2)

FX Graph Mode量化模式 训练后量化有多种量化类型&#xff08;仅权重、动态和静态&#xff09;&#xff0c;配置通过qconfig_mapping &#xff08; prepare_fx函数的参数&#xff09;完成。 FXPTQ API 示例&#xff1a; import torch from torch.ao.quantization import (ge…

ACL2023 | 赔了?引入GPT-3大模型到智能客服,竟要赔钱?

作者 | 小戏、Python 关于大模型的商业落地&#xff0c;一个非常容易想到的场景就是智能客服&#xff0c;作为不止是大模型也是 NLP 领域的一个最主要的应用场景&#xff0c;由于人工客服的高昂成本&#xff0c;AI 客服伴随着模型技术的发展也逐步走进我们的生活&#xff0c;在…

vue iview table Tooltip内容过多闪屏解决

vue的项目&#xff0c;框架是iview 客户反应&#xff0c;指令描述的内容有几百个字&#xff0c;鼠标悬浮&#xff0c;浏览器开始闪烁 解决思路是加宽度限制&#xff0c;滚动&#xff0c; 后面发现像是transfer属性的bug&#xff0c;触碰浏览器底部&#xff0c;距离不够造成 …

重新理解 RocketMQ Commit Log 存储协议

最近突然感觉&#xff1a;很多软件、硬件在设计上是有 root reason 的&#xff0c;不是 by desgin 如此&#xff0c;而是解决了那时、那个场景的那个需求。一旦了解后&#xff0c;就会感觉在和设计者对话&#xff0c;了解他们的思路&#xff0c;学习他们的方法&#xff0c;思维…

C#可视化 国产热剧信息查询(具体做法及全部代码)

目录 题目&#xff1a; 做法&#xff1a; 代码部分&#xff1a; DBHelper类 From1主窗体代码 题目&#xff1a; 1. 首次打开页面&#xff0c;展示所有汽车信息列表&#xff0c;如图 1 所示。 2.双击第二行右边内容全部发生改变 数据库设计及内容 做法&#xff1a; 首先设置d…

React hooks文档笔记(五)useEffect——解决异步操作竞争问题

1.开发环境下组件加载两次&#xff1f; 非bug&#xff0c;重新安装组件仅在开发过程中发生&#xff0c;帮助找到需要清理的效果。在生产环境中只会加载一次。 React 将在 Effect 下次运行之前以及卸载期间调用您的清理函数。return () > {}; 2. &#x1f330;订阅事件情况…

Python连接MySQL数据库(简单便捷)

&#x1f412;&#xff0c;本文中&#xff0c;使用到的工具有&#xff1a;Pycharm&#xff0c;Anaconda&#xff0c;MySQL 5.5&#xff0c;spyder(Anaconda) 什么是 PyMySQL&#xff1f; PyMySQL 是在 Python3.x 版本中用于连接 MySQL 服务器的一个库&#xff0c;Python2 中则…