数组不可以直接赋值,为什么结构体中的数组却可以?
liuian 2024-12-23 12:10 60 浏览
- 一、前言
- 二、数组的各种操作
- 1. 错误方式
- 2. 利用结构体来复制数组
- 3. 其他复制方式
- 三、语言标准和编译器
- 1. 数组和指针的关系
- 2. 为什么不能对数组赋值
- 3. 函数形参是数组的情况
- 4. 为什么结构体中的数组可以复制
- 5. 参数传递和返回值
- 五、总结
一、前言
在 C/C++ 语言中,数组类型的变量是不可以直接赋值的。但是如果把数组放在结构体中,然后对结构体变量进行赋值,就可以实现把其中的数组内容进行复制过去。
很多朋友对这个不是特别理解,只是强制记忆,下面我尝试用自己的理解来描述一下,希望对你有所帮助!
二、数组的各种操作
1. 错误代码
int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a;
对于上面的赋值语句,编译器会报错 error: assignment to expression with array type,即:不能对一个数组类型的变量进行赋值。
那么编译器此时是如何来解释 a 和 b 的?下面会说到这个问题。
有一个地方提一下:第一条语句中的 = 操作,不是赋值,而是初始化。C/C++ 语法规定在定义变量的时候,是可以使用 操作符 = 来进行初始化操作的。
2. 利用结构体来复制数组
typedef struct {
int arr[5];
} array_wrap;
array_wrap a = {{1, 2, 3, 4, 5}};
array_wrap b;
b = a;
这里的赋值操作是针对结构体变量,C 语言标准允许这种行为,是合法的,变量 a 中的所有内容(也就是这个变量占用过的那一块内存空间中的内容)会原样的复制到变量 b 中。
3. 其他复制方式
既然不能直接对数组类型的变量进行赋值,只能寻求其他的替代方式,例如:
利用 memcpy(b, a, sizeof(int) * 5); 复制一整段内存空间中的内容;
利用 for/while 等循环语句,逐个复制数组中每一个元素: b[i] = a[i];
三、语言标准和编译器
C/C++ 只是一门高级语言,是被标准委员会从无到有设计出来的,因此我们编程时需要严格遵守这些规则。
这些规则中,就包括这么一条:只有标量和结构体,才能出现在赋值操作符=的左侧。
但是数组类型并不是一个标量,因此不能对结构体执行赋值操作。
理论上,如果 C/C++ 语言愿意的话,是"可以"对数组直接赋值的(那就要修改语法标准),只不过标准委员会在经过各种场景的权衡利弊之后,做出了目前这样的规定,这是对各种考虑到的因素进行权衡之后的结果。
也就是说,目前标准中对于数组操作的方式,是利大于弊。
既然标准已经是制定成这样的了,我们就来分析一下编译器是如何来遵循、实现这个标准的。
1. 数组与指针的暧昧关系
很多人都这样记忆:数组名就是数组开始地址的指针。这是不对的,或者说不严谨的。
在 C/C++ 中,数组就是数组,指针就是指针。数组在内存中有确定的空间(每个元素的大小 x 元素个数)。
只不过在表达式中,数组名会“临时的”表示数组中第一个元素的常量指针(前提条件:在没有操作符 sizeof 和 & 的情况下)。
对于下面这段代码,打印结果是相同的:
int a[5] = {1, 2, 3, 4, 5};
printf("a = %p \n", a);
printf("&a = %p \n", &a);
第一个 printf 中,a 会“临时的”代表指向第一个元素的常量指针。
第二个 printf 中,a 就表示一个数组,与指针没有半毛钱的关系,前面加上取地址符 &,就表示获取这个数组所在的地址,这个地址与第一个元素的地址是重合的。
注意:代码在被编译成二进制文件之后,没有任何变量的概念,全部是用地址来“传递” C/C++ 代码中的变量。
2. 为什么不能对数组变量赋值
有了上面的基础理解就好办了,对于下面的这段代码:
int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a;
在赋值语句 b = a 中,左侧的 b 是一个数组类型,右侧的 a 被编译器“临时的”代表第一个元素的常量指针,但是数组不是一个标量,不可以放在赋值运算符=的左侧,因此编译器就抱怨:非法!
既然在一个表达式中,数组名被临时的表示第一个元素的常量指针,那么就说明我们不能对数组名本身进行计算,例如:不能进行 a++, a-- 等操作。
例如:下面这的遍历方式是非法的:
int a[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++)
{
// 常量指针,不可以进行递增操作
printf("a[%d] = %d \n", i, *a++);
}
3. 函数形参是数组的情况
考虑下面这个函数:
void func(int arr[5])
{
for (int i = 0; i < 5; ++i)
{
printf(*arr++); // 合法!
}
}
形参 arr 在形式上好像是一个数组,实际上被编译器当做指针,也就是相当于:void func(int *arr),因此,在 printf 打印语句中,可以对 arr 进行递增操作。
PS: 这种场景下都需要额外的传递一个参数,来告知元素的个数。
调用这个函数的代码如下:
int a[5] = {1, 2, 3, 4, 5};
fun(a);
数组名临时代表第一个元素的常量指针,在传参的时候,形参 arr 的值就是数组中第一个元素的内存地址。
4. 为什么结构体中的数组可以复制
有了前面的语法标准,这个问题似乎不用再讨论了~~
赋值的目的是什么?就是让一块内存空间的内容,与另一块内存空间中的内容完全相同。如果想要完成复制操作,那么就需要知道这块内存空间的大小。
编译器是知道一个结构体变量所占用的空间大小的,所以当复制的时候,类似于memcpy 一样,把一个结构体变量所占空间按照 byte to byte 的方式复制过去。
5. 参数传递和返回值
在调用函数时,实参到形参的传递;
函数执行结束后的返回值;
这两个场景中都涉及到变量的赋值问题。
关于参数传递,上面已经说了:编译器是把形参当做普通的指针类型的。
对于函数返回值来说,同样的道理,也不能直接返回一个数组,因为它仅仅是临时性的代表第一个元素的常量指针。
当然,可以利用结构体的可赋值特性,把数组包裹在其中,以此达到复制的效果。
五、总结
记住这两句话:
1.数组就是数组,指针就是指针,它们各不相干。
2.在表达式中,数组名会“临时的”表示数组中第一个元素的常量指针(前提条件:在没有操作符 sizeof 和 & 的情况下)
转自 IOT物联网小镇。
相关推荐
- 128键盘键位图高清图(128键机械键盘键位图)
-
“Fn”键通常是功能键的简称。在惠普128fn键盘上,按下“Fn”键可以启用键盘上的其他功能按键。这些功能按键通常印有其他标志,如调节亮度、音量、飞行模式、触控板开关等。惠普128fn使用说明。首先需...
- 给电脑设置开机密码(电脑开关机密码设置方法)
-
方法如下1.建立开机密码。进入BIOS系统界面,点击键盘的Del按键,点击选项中的设置用户密码。设置完毕进入高级设置,点击密码选项列表的系统密码,点击保存并推出即可;2.设置系统密码。进入系统界...
- 用u盘怎么安装系统到电脑上(从u盘怎么安装系统)
-
首先将要安装的电脑系统下载到u盘里面。然后将u盘插入电脑,确保电脑识别成功。最后打开u盘,双击里面的系统安装包,点击安装即可。以下是重装电脑系统的一般步骤:在正常可用的电脑上下载并安装一个制作启动U盘...
- 百度输入法下载免费下载(百度输入法安卓版免费下载)
-
不同的车载导航系统的添加方法:1、车载导航为安卓系统:在电脑中下载第三方安卓输入法安装包,用u盘拷贝安装包,传入车载导航中,在导航中选择安装即可。2、车载导航为ce系统:此系统不支持额外安装输入法,只...
- hp电脑如何进入bios(hp电脑如何进入u盘启动界面)
-
请看下文在重装电脑或是需要进行硬件设置的时候,就需要进入BIOS进行设置,那么怎么样进入电脑的BIOS呢?下面就以HP电脑来说明进入BIOS的方法吧。1.按电源键启动电脑在屏幕刚亮时不停按下F10...
-
- flash下载电脑版下载(flash软件电脑版下载)
-
AdobeFlashPlayer,是一种广泛使用专有的多媒体程序播放器,今天来分享一下电脑如何安装flashplayer,希望对大家有所帮助;1、首先打开电脑桌面【浏览器】,搜索【AdobeFlashPlayer】,2、点击第一个网址进入【...
-
2025-11-07 19:05 liuian
- 无线网设置步骤(无线网设置步骤怎么设置)
-
任意的打开一个浏览器,最好是自己比较常用的浏览器。我们在地址栏上面输入指定的路由器网站的内容。02输入网站便会弹出这样的对话框。03在账号中输入admin,密码同样如此。04回车后,即可进入到无线路由...
- u盘里面装系统 可以直接用吗
-
可以。因为下载到U盘里的系统是可启动的,可以直接插入需要安装系统的电脑中启动安装程序,进行系统的安装。但是需要注意的是,不同类型的系统(如Windows和MacOS)需要不同的方法进行安装,而且在安...
- 一个win10密钥能激活几台电脑
-
零售版的密钥只能激活一台电脑,VOL版的能够批量激活。切实而今根基上用东西的人比较多,那样比较便当,提议也能够碰运气。软件可以正确辨认用户计较机上布置的悉数office版本和windows版本,包括w...
- 电脑城买电脑(电脑城买电脑装了盗版系统)
-
不太靠谱。首先电脑城的电脑同个款式配置很凌乱,要么来个阉割版、要么来个升级版,而所谓升级往往会以次充好,为的就是让你觉得少花了钱还买到了更好的配置。其次电脑城的销售人员大部分都是那种半懂的非专业人员,...
- win11很多游戏不兼容(win11不兼容的游戏)
-
据我们了解,Windows11系统和传奇游戏之间没有直接的冲突或不兼容问题。然而,可能有一些间接的原因导致此问题。首先,传奇游戏是一款老游戏,可能需要在Windows11系统上运行一些兼容性设置或...
- 华为路由器登录(华为路由器登录入口手机版)
-
华为路由器的登录地址是192.168.3.1,电脑/手机连接到华为路由器的网络后,在浏览器中输入192.168.3.1,就能进入登录入口。然后输入登录密码,可以进入华为路由器的设置页面。华为wifi设...
- 固态硬盘的缺点(固态硬盘缺点和坏处)
-
1.价格与容量 固态硬盘的容量和价格都要比机械硬盘贵 2.物理特性 固态硬盘无噪音,抗震动,体积小,发热量低,功耗也非常低,工作温度范围很大!固态硬盘的内部并没有任何机械活动部件,没有马达和风...
- 联想自带系统怎么重装(联想电脑自带系统如何重装系统)
-
联想电脑重装系统步骤:1、制作好U盘启动盘,然后把下载的联想win7系统iso文件直接复制到U盘的GHO目录下:2、在联想电脑上插入U盘,重启后不停按F12或Fn+F12快捷键打开启动菜单,选择U盘项...
- 一周热门
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)
