漫谈:C语言 C++ 数组的迷惑与混乱

        C语言设计很失败,起码数组的设计很失败。

        看看这是什么:

int a[5];

        “这是一个int数组,共有5个元素”——回答正确。

int a[5][3];

        “这是一个int二维数组,第一维为5,第二维为3,一共有5X3=15个元素”——不难对吧。

int a[5][3];a是什么类型?
a+0是什么类型?
*a是什么类型?

        “a是二维数组,a+0是一维数组,*a是int”——你确定吗?

数组名究竟是什么

        我们知道数组名就是数组的首地址——这个说法有没有问题?既然是首地址,为什么不是一个指针?但是数组名确实可以当作指针来用,比如“a+0”就是指针运算。

        有点晕吗?我们用代码来研究一下这些东西。

        作为前提,我们要知道这两个东西:

  • sizeof() 一个类型或对象的字节大小
  • typeid().name() 一个类型或对象的类型名称

        以下代码为VS2022的C++代码(64位编译):

#include <stdio.h>
#include <typeinfo>int main()
{int a[5][3];int* p;printf("sizeof(int)       : %2zd : typeid : %s\n", sizeof(int), typeid(int).name());printf("sizeof(p)         : %2zd : typeid : %s\n", sizeof(p), typeid(p).name());printf("sizeof(a)         : %2zd : typeid : %s\n", sizeof(a), typeid(a).name());printf("sizeof(*a)        : %2zd : typeid : %s\n", sizeof(*a), typeid(*a).name());printf("sizeof(a[0])      : %2zd : typeid : %s\n", sizeof(a[0]), typeid(a[0]).name());printf("sizeof(*a[0])     : %2zd : typeid : %s\n", sizeof(*a[0]), typeid(*a[0]).name());printf("sizeof(a+0)       : %2zd : typeid : %s\n", sizeof(a + 0), typeid(a + 0).name());printf("sizeof(*(a+0))    : %2zd : typeid : %s\n", sizeof(*(a + 0)), typeid(*(a + 0)).name());printf("sizeof(a[0][0])   : %2zd : typeid : %s\n", sizeof(a[0][0]), typeid(a[0][0]).name());printf("sizeof(a[0]+0)    : %2zd : typeid : %s\n", sizeof(a[0] + 0), typeid(a[0] + 0).name());printf("sizeof(*(a[0]+0)) : %2zd : typeid : %s\n", sizeof(*(a[0] + 0)), typeid(*(a[0] + 0)).name());
}

        急性子的先看一下结果:

sizeof(int)       :  4 : typeid : int
sizeof(p)         :  8 : typeid : int * __ptr64
sizeof(a)         : 60 : typeid : int [5][3]
sizeof(*a)        : 12 : typeid : int [3]
sizeof(a[0])      : 12 : typeid : int [3]
sizeof(*a[0])     :  4 : typeid : int
sizeof(a+0)       :  8 : typeid : int (* __ptr64)[3]
sizeof(*(a+0))    : 12 : typeid : int [3]
sizeof(a[0][0])   :  4 : typeid : int
sizeof(a[0]+0)    :  8 : typeid : int * __ptr64
sizeof(*(a[0]+0)) :  4 : typeid : int

        看一下结果应该很清楚了。

        由于是64位编译,指针类型的长度都是8,int是4,数组则是int大小乘以元素总数。

        “*a”是什么?数组名作为对象,代表数组,数组名作为指针,则代表指向数组元素的指针。因为这里a是二维数组,所以a作为指针指向的是一维数组。

        数组名什么时候代表数组、什么时候代表指针呢?当数据名做加减的时候就变成了指针。所以“a”和“a+0”是完全不同的东西,一个是“int [5][3]”,一个是“int[3] *”(非正确写法,示意)。

        上面的示例代码输出仔细搞懂,关于数组你就差不多不会迷惑了。

数组用作函数参数究竟发生了什么

        直接上代码(VS2022,64位):

#include <stdio.h>
#include <typeinfo>void f(int x[6][3])
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
void f2(int x[][3])
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
void f3(int x[5][2])
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
void f4(int x[5][])//无法编译,最后一维不能省略
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
int main()
{int a[5][3];f(a);f2(a);f3(a);//无法编译,参数类型不匹配
}

        有两处不能编译:

        “void f4(int x[5][])”,不允许省略数组最后一维的大小。但是为什么可以省略第一维?这其实暗示了数组名和指针的关系:因为当作指针用的,所以第一维的长度其实无关紧要(所以才可以随便数组越界啊!!!!)。

        “f3(a);”,错误提示:“无法将参数 1 从“int [5][3]”转换为“int [][2]”。 这个信息明确告诉我们,第一维的长度被忽略了,这也能够解释为什么对“void f(int x[6][3])”的调用没有出错,因为第一维的长度根本就被忽略了。

        删除不能编译的代码,运行程序,输出如下:

sizeof(x)         :  8 : typeid : int (* __ptr64)[3]
sizeof(x)         :  8 : typeid : int (* __ptr64)[3]

        哈哈,函数参数其实是个指针!

        但是出错信息用的是数组表示法啊,写编译器的人自己把自己都绕进去了?

传递数组给指针参数究竟发生了什么

#include <stdio.h>
#include <typeinfo>void f(int* x)
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
void f2(int(*x)[3])
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
void f3(int* x[3])
{printf("sizeof(x)         : %2zd : typeid : %s\n", sizeof(x), typeid(x).name());
}
int main()
{int a[5][3];f(a[0] + 0);f(a + 0 + 0);//无法将参数 1 从“int (*)[3]”转换为“int *”f((a + 0) + 0);//无法将参数 1 从“int (*)[3]”转换为“int *”f2(a);f3(a);//无法将参数 1 从“int [5][3]”转换为“int *[]”
}

        这个代码有三处不能编译。

        前两处错误信息相同。“二维数组的数组名a做加减就成了指向一维数组的指针,一维数组的指针再做加减不就是指向int的指针吗?”……啊,我再想想……是我想错了啊,原来“指向一维数组的指针”做加减还是“指向一维数组的指针”,指针加减不会改变指针类型,数组名变成指针的时候脱去了一层,再做加减不会继续脱了。

        第三处明显不对的,这样写这是为了显示“int(*x)[3]”和“int* x[3]”的区别,前一个是指向数组的指针,后一个是指针数组,区别要细细品味一下。

        删除不能编译的代码,执行结果如下:

sizeof(x)         :  8 : typeid : int * __ptr64
sizeof(x)         :  8 : typeid : int (* __ptr64)[3]

        输出的是f和f2的参数类型,因为定义的就是指针,所以显示出来没什么区别。

总结:数组长度压根不存在

  •         数组名确实就是指向数组元素的指针。
  •         不仅如此,作为函数的参数,直接就是解释为指向数组元素的指针,数组长度(对于多维数组,就是第一维的长度)根本就被完全无视了!
  •         C语言压跟没想过控制数组越界。
  •         除了sizeof(数组名)会判断一下数组长度,数组长度也就是初始化的时候用一下。

(这里是结束)

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

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

相关文章

MyCat实现分库分表技术

目录 一、分库分表 1.1介绍 1.1.1问题分析 1.1.2拆分策略 1.1.3垂直拆分 1.1.3.1垂直分库 1.1.3.2垂直分表 1.1.4水平拆分 1.1.4.1水平分库 1.1.4.2水平分表 1.1.5实现技术 二、MyCat概述 2.1介绍 2.2下载 2.3安装 2.4目录介绍 2.5概念介绍 三、MyCat入门 3.…

MyBatisPlus(八)范围查询

说明 范围查询&#xff0c;包括&#xff1a; 大于大于等于小于小于等于在范围内在范围外 大于&#xff1a;gt 代码 Testvoid gt() {LambdaQueryWrapper<User> wrapper new LambdaQueryWrapper<>();wrapper.gt(User::getAge, 20);List<User> users mapp…

MyBatis-plus快速代码生成,使用MybatisX插件

概述 MyBatis提供逆向工程&#xff0c;可以快速生成代码。 而MyBatis-plus也提供了一个代码生成器&#xff0c;它需要执行一些代码来实现。 而MybatisX 是一款基于 IDEA 的快速开发插件&#xff0c;为效率而生&#xff0c;你不需要写代码&#xff0c;直接界面化操作&#xf…

python 学习随笔 4

列表list 将序列前几个进行替换&#xff08;数量可以不同&#xff09; 将序列进行间隔替换&#xff08;必须保证数量相同&#xff0c;否则报错&#xff09; 删除序列内元素 向序列后新增一个元素 向序列后新增多个元素 将序列进行数乘&#xff08;不是产生几个序列哦&#xff0…

三、git的安装和配置

一、安装 1.官网下载&#xff1a;https://git-scm.com/download 下载最新版本&#xff0c;点击红框或篮筐处即可 2.点击下载好的安装包安装这个软件 3.一直点击next&#xff0c;直到出现install&#xff0c;点击install&#xff0c;安装完成后点击finish&#xff1a; 下载完成…

【Mysql专题】视图介绍及其基本操作

前言 前段时间&#xff0c;跟客户开个线上会议&#xff0c;在和对方技术人员讨论到如何把数据给我们的时候&#xff0c;对方说【丢给你们一个视图&#xff0c;你们查视图就好了】我一下子懵了。当时现场贼尴尬&#xff0c;我只能假装断线了&#xff0c;然后又模棱两可的说了几…

【网络安全---ICMP报文分析】Wireshark教程----Wireshark 分析ICMP报文数据试验

一&#xff0c;试验环境搭建 1-1 试验环境示例图 1-2 环境准备 两台kali主机&#xff08;虚拟机&#xff09; kali2022 192.168.220.129/24 kali2022 192.168.220.3/27 1-2-1 网关配置&#xff1a; 编辑-------- 虚拟网路编辑器 更改设置进来以后 &#xff0c;先选择N…

10.1 调试事件读取寄存器

当读者需要获取到特定进程内的寄存器信息时&#xff0c;则需要在上述代码中进行完善&#xff0c;首先需要编写CREATE_PROCESS_DEBUG_EVENT事件&#xff0c;程序被首次加载进入内存时会被触发此事件&#xff0c;在该事件内首先我们通过lpStartAddress属性获取到当前程序的入口地…

阿里云免费服务器无法领取限制说明

阿里云提供免费服务器供用户申请&#xff0c;但是领取免费服务器是有条件的&#xff0c;并不是有所的阿里云用户均可领取免费云服务器&#xff0c;免费服务器领取条件为&#xff1a;账号从未使用过阿里云服务器的用户&#xff0c;阿里云百科来举例说明免费服务器领取说明&#…

STM32复习笔记(六):STM32远程升级BootLoader相关

目录 Preface&#xff1a; &#xff08;一&#xff09;STM32上电启动流程 &#xff08;二&#xff09;BootLoader相关 &#xff08;三&#xff09;Clion配置 Preface&#xff1a; 有关STM32的BootLoader主要还是参考了许多大佬的文章&#xff0c;这里只是简单地列举一下&am…

基于SpringBoot的体育馆场地赛事预约管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

【C++】基础入门

万字复习C基础入门语法&#xff0c;适合学过C的朋友用来复习查阅&#xff0c;可能不太适合0基础的朋友。 一.c初识 (1) 第一个c程序 最简单的格式&#xff1a; // 导入头文件 #include<iostream> // 简化对命名空间std下函数和对象的使用 using namespace std; // …

Java基于SSM的校园一卡通系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

go语法入门2

字符串 使用双引号或反引号引起来的任意个字符。它是字面常量。 func main() {var a "abc\n测试" // \n换行fmt.Println(a) } abc 测试func main() {var a "abc\n\t测试" \\换行后在tabfmt.Println(a) } abc测试func main() {var a abc测试 …

好奇喵 | Surface Web ---> Deep Web ---> Dark Web

前言 我们可能听说过深网(deep Web)、暗网(dark Web)等名词&#xff0c;有些时候可能会认为它们是一个东西&#xff0c;其实不然&#xff0c;两者的区别还是比较大的。 什么是deep web&#xff1f; 深网是网络的一部分&#xff0c;与之相对应的是表层网络&#xff08;surface …

SketchUp Pro 2023 for Mac——打造你的创意之城

SketchUp Pro 2023 for Mac是一款专业级的3D建模软件&#xff0c;为你提供最佳的设计和创意工具。不论你是建筑师、室内设计师&#xff0c;还是爱好者&#xff0c;SketchUp Pro都能满足你对于创意表达的需求。 SketchUp Pro 2023拥有强大而直观的界面&#xff0c;让你轻松绘制…

C++入门-day01

一、认识C C融合了三种不同的编程方式 C代表的过程性语言在C基础上添加的类、结构体puls代表的面向对象语言C模板支持泛型编程 C完全兼容C的特性 Tips&#xff1a;侯捷老师提倡的Modren C是指C11、C14、C17和C20这些新标准所引入的一系列新特性和改进。在我们练习的时候也应当去…

PG 多表连接查询

写法&#xff1a; 使用 select 表名.键名 from 表1 join表2 on 相同的主键 构造出来一张新表 多表要用表名.键名 才能知道是哪一张表 传统写法也行 类型&#xff1a; 内 而外的要这样写

复习C语言数组的用法

实验内容 1.1设计一个函数fun&#xff0c;功能是有N*N的矩阵&#xff0c;根据给定的m值&#xff0c;m<N,将每行元素中的值&#xff0c;均往右移m个位置&#xff0c;左边置0 #include<stdio.h> void fun(int (*a)[3],int m){int n,j,i,k,num;int p2;//右移位置列数nu…

Debezium日常分享系列之:使用数据库中的数据流进行在线机器学习

Debezium日常分享系列之&#xff1a;使用数据库中的数据流进行在线机器学习 一、背景介绍二、数据集准备三、使用 Apache Flink 进行分类四、使用 Debezium 和 Kafka 作为源数据流五、构建 Flink 流 k-means六、评估模型七、使用 Apache Spark 进行分类八、定义数据流九、定义和…