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

我是荔园微风,作为一名在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.hqwc.cn/news/344.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Appium安装部署

目录 一、检查Java环境 二、安装android SDK 一、检查Java环境 Android SDK依赖ava环境&#xff0c;因此需要先安装jdk。在CMD中输入java -version 出现下图的结果&#xff0c;说明当前环境已安装jdk 如果提示java命令无效&#xff0c;请安装后进行下一步。 二、安装androi…

ModaHub AI模型社区:向量数据库CPU 版 Milvus和GPU 版 Milvus 版本比较

目录 CPU 版 Milvus 版本比较 概述 CPU 版 Milvus 支持的索引类型 浮点型向量 二值型向量 GPU 版 Milvus 版本比较 概述 GPU 版 Milvus 支持的索引类型 浮点型向量 二值型向量 CPU 版 Milvus 版本比较 概述 Milvus 提供两个发行版本&#xff1a;CPU 版本和 GPU 版本…

Unix/Linux编程:UDS 流(Stream)

〇、前言 socket 是一种 IPC &#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;方法&#xff0c;它允许位于同一主机&#xff08;计算机&#xff09;或使用网络连接起来的不同主机上的应用程序之间交换数据。通过使用Socket&#xff0c;开发人员可以…

解决不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接的问题

问题概述&#xff1a; 用windows server 2012 r2 vl x64搭了个文件服务器&#xff0c;在使用时有个问题&#xff0c;老是用户登录有问题&#xff0c;提示“不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接”。出现的原因不详&#xff0c;网上也没查到合理的…

路由器的工作原理详解

什么叫路由&#xff1f; 路由器的英文是 Router&#xff0c;也就是「找路的工具」。找什么路&#xff1f;寻找各个网络节点之间的路。 换句话说&#xff0c;路由器就像是快递中转站&#xff0c;包裹会经过一个个的中转站&#xff0c;从遥远的地方寄到你家附近&#xff0c;数据…

基于深度学习的高精度袋鼠检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度袋鼠检测识别系统可用于日常生活中或野外来检测与定位袋鼠目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的袋鼠目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标检测模型…

【软件开发】MyBatis 理论篇

MyBatis 理论篇 1.MyBatis 是什么&#xff1f; MyBatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。…

【机器学习】sklearn数据集的使用,数据集的获取和划分

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 sklearn数据集 二、安装sklearn二、获取数据集三、…

用Docker部署Nginx

部署步骤&#xff1a; 1.拉取镜像 docker pull nginx 2.创建并进入容器 docker run -d --name nginx01 -p 3344:80 nginx #注意nginx01是起的名字&#xff0c;3344是公网访问的端口&#xff0c;80是Nginx的默认端口 3.启动Nginx curl localhost:3344 4.在我的windows系统访…

(贪心) 1221. 分割平衡字符串 ——【Leetcode每日一题】

❓ 1221. 分割平衡字符串 难度&#xff1a;简单 平衡字符串 中&#xff0c;L 和 R 字符的数量是相同的。 给你一个平衡字符串 s&#xff0c;请你将它分割成尽可能多的子字符串&#xff0c;并满足&#xff1a; 每个子字符串都是平衡字符串。 返回可以通过分割得到的平衡字符…

【杨宗宝】Cocos Creator 3.x : 你们要的Label3D来了(升级版)

前沿 宗宝我又回来了&#xff0c;本次给大家带来的分享是基于之前Label3d的升级版&#xff1b;在上次发布了Lable3d的功能后&#xff0c;大家在使用的过程中多多少少的会发现各种问题&#xff1a;微信小游戏真机不显示&#xff0c;字体如何实现描边&#xff0c;引擎版本升级后…

本地部署开源大模型的完整教程:LangChain + Streamlit+ Llama

在过去的几个月里&#xff0c;大型语言模型(llm)获得了极大的关注&#xff0c;这些模型创造了令人兴奋的前景&#xff0c;特别是对于从事聊天机器人、个人助理和内容创作的开发人员。 大型语言模型(llm)是指能够生成与人类语言非常相似的文本并以自然方式理解提示的机器学习模型…