目录

概述:二维整形数组和二维字符数组在处理过程中有些微妙的差异值得注意,否则容易出错。

1、二维整形数组的初始化

2、常见场景:把二维整形数组的元素初始化为相同值

3、应用场景,二维数组作函数的参数


概述:二维整形数组和二维字符数组在处理过程中有些微妙的差异值得注意,否则容易出错。

1、二维整形数组的初始化

如果未对数组进行初始化,其内部元素的值是未知的。

可以在定义的时候进行初始化:

int A[3][3] = {0};    // 首行的首元素初始化为0
int A[3][3] = {1};    // 首行的首元素初始化为1

初始化后的结果为:

A = {0 0 0

0 0 0

0 0 0}

也可以指定行进行初始化:

int A[3][3] = {{1}, {0, 2}, {1, 2, 3}};

这样初始化之后的结果为:

A = {1 0 0

0 2 0

1 2 3}

这里有个结论:只要对整形数组进行了初始化,对数组内没有指定值的元素,都会自动补零填充,如果没有进行初始化,编译器一般不会自动进行初始化。

2、常见场景:把二维整形数组的元素初始化为相同值

在处理字符数组(字符串)时,我们常常会用到memset这个函数来把某一维或多维字符数组设为某一字符,常见的就是'\n'。

但是对于二维甚至多维整形数组,是否可以这样操作呢?

// 函数原型
void *memset(void *str, int c, size_t n);// 参数
// str -- 指向要填充的内存块。
// c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
// n -- 要被设置为该值的字符数。

先回答上面的问题:除了0和-1可以用这个函数设值外,其他的整数都会发生错误。

分析:

原因:memset函数是按字节对内存进行填充的,第二个参数的值最大127(一字节最大的有符号正整数01111111),如果输入的数长度超过了一字节,高位会被截断。

基于以上原因,对诸如32位正整数用0和-1进行设置时:

int Num;
// 用0
memset(&Num, 0, sizeof(Num));// 用-1
memset(&Num, -1, sizeof(Num));

Num在内存中占4个字节,所以会写4个0的机器码到&Num开始的4个内存单元中,数字在计算机中是以补码的形式存的,一字节有符号整数0的补码为0000 0000,所以在内存中的4字节Num为:

->&Num(起始地址)

0000 0000

0000 0000

0000 0000

0000 0000

该4字节整数依然是0

同理,一字节有符号整数-1的补码为1111 1111,所以调用函数memset后,内存中的4字节Num为:

->&Num(起始地址)

1111 1111

1111 1111

1111 1111

1111 1111

该4字节数恰好也是32位有符号数-1的补码,该数依然是-1

举个反例,如用1来设置,会出现什么情况呢?

int Num;
// 用1
memset(&Num, 1, sizeof(Num));

一字节有符号整数1的补码为0111 1111,所以调用函数memset后,内存中的4字节Num为:

->&Num(起始地址)

0111 1111

0111 1111

0111 1111

0111 1111

该4字节数为0x7f7f 7f7f,十进制为2,139,062,143

可见,结果已经不是我们期待的。

3、应用场景,二维数组作函数的参数

之所以会提出这一条,是因为我在写程序时遇到了这个问题,平时二维整型数组用得少,也自认为对C语言指针融会贯通了,不曾想到真正遇到的时候就出问题了,可见,指针值得去多品一品!

言归正传,简单举个例子,有这样一个函数,负责打印这个二维数组。

void PrintAll(int ???A???, int len)
{for (int i = 0; i < len) {for (int j = 0; j < len; j++) {printf("%d ", A[i][j]);}printf("\n");}
}int main()
{int A[3][3] = {0};int len;len = sizeof(A) / sizeof(int);PrintAll(A, len);return 0;
}

我们需要把二维数组A传给PrintAll这个函数,在main这一侧都没出意外,当时问题出在了函数形参列表中二维数组的表示上。

有如下几种错误写法:

// 1.
void PrintAll(int **A, int len);//2.
void PrintAll(int A[][], int len);//3.
void PrintAll(int *A[], int len)

第一种:这是一个变量,一个指针变量,指向的内容也是一个指针,什么指针,int指针;这个A仅仅表示一个指针,A+1对32位操作系统来说是内存地址加4,对64位来说是内存地址加8。

第二种:这样写看似没问题,看似不应该有问题,但是有一点,定义数组时需要明确数组长度,有同学可能要说了,这里不是定义,这只是形参列表,类型对上就行。但它好歹是个数组,在实参过来的时候,就相当于用实参对其进行初始化,既然是初始化,不妨看看二维数组初始化的写法有哪些。很明显:这位同学是看书不仔细。这个是形式错误,语法错误。

// 1.正确
int A[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};// 2.正确
int A[][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};// 3.错误
int A[3][] = {1, 2, 3, 4, 5, 6, 7, 8, 9};// 4.错误
int A[][] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

第三种:这是对指针变量种常见困惑梳理不清导致的,即老生常谈的指针数组和数组指针问题:

在这里,int *A[],*和int结合,表示指向int类型的指针,而A是和[]结合的,表示这个变量是个数组,合在一起就是,定义了一个数组A,A的元素是什么呢,是指针,说具体点,是指向int类型数据的指针。

究其本质,A在这里就是一个一维的指针数组,A表示这个数组的首地址,同样,由于它的元素是指针,所以和第一种一样,A+1对32位操作系统来说是内存地址加4,对64位来说是内存地址加8。

再看main函数中的实参:

int A[3][3] = {0};
int len;len = sizeof(A) / sizeof(int);
PrintAll(A, len);

我们期望传入的A是一个二维数组。

其实回归本源,还是看我们要传的是啥,A到底表示个啥。

知识点也正在这里:A是啥?二维数组名。

二维数组名的含义?对于这个问题,先问数组名是啥?首元素的地址。再问二维数组的“元素”是啥?是每一行一维数组。

所以,二维数组名的含义就是首行地址,首行地址就是首行在内存中的起点的地址,它虽然和首行第一个元素的地址及其二维数组首元素的地址是同一个地址,但区分首行和首元素在这里很有必要。

这里扩充一点:指针变量

对所有基本数据类型乃至自定义数据类型,我们都可以定义该类型的指针变量。这里想补充啥呢?——一个指针变量p,它到底包含了哪些信息:

1、变量的值:表示被指向对象在内存中的地址

2、指向对象的类型:决定了p+1时在内存单元的指向情况

回归正题,我们把二维数组A传入到函数中,归根结底,就是想通过对这个指针的递增操作,逐个访问到所指向的数据(注:下标操作p[i]和指针*(p+i)等价)。

回到上述例子,传入的A应该是首行的地址,A+1应该变成下一行:*(A+1) == A[1]

所以,形参应该是一个指向一行数组的指针,即应该是一个指向一维数组的指针,是数组的指针,数组指针的定义:

// 定义数和数组(数)
int a;
int a[3];// 定义指针和指针数组
int *p;
int *p[];// 定义数组指针
int (*p)[3];

可能有点绕,对比着数和数组的定义再看就会容易理解有些。为方便理解,不得不提一下数组,得先对数组是什么,组是什么有个清晰的认识,再看复杂的,也就不会头晕了。

数组,数的组合,本质上是组合,是一种东西的组合,里面的东西有相同的类型。

XXX的组

XXX的指针

我TM也晕了。。。。

来来来,这样看,忽略类型int,定义变量时就一个varName:int varName,这是一个整形变量

要它是一个指针,我们需要加上星号,即星号要修饰这个变量,即星号修饰的变量编译器认为是指针:* varName,即要得到指针,星号需要修饰变量名。

然而,对于数组指针,如果不加括号,星号*修饰了这个数组,即所有元素都成了指针,为了让它之修饰数组名,所以加上括号:int (*p)[]。

(*p)[]:这样,p是指针变量,p是数组名,所以这个指针是数组指针

*p[]:这样,p[i]是指针变量,所有p[i]组成一个数组,数组名是p,这个数字是指针数组

此外,由于实参传给函数形参的,归根结底还是一个地址,只是首元素的地址,所以区分这个地址在使用时按首行地址来处理还是按首元素地址来处理,就由形参类型定义,int (*p)[]虽然定义了这个形参是指向一个一维数组的指针,但是这还不够,还需要指明它指向的一维数组的大小:

// 1
void PrintAll(int (*A)[3], int len)
{for (int i = 0; i < len) {for (int j = 0; j < len; j++) {printf("%d ", A[i][j]);}printf("\n");}
}int main()
{int A[3][3] = {0};int len;len = sizeof(A) / sizeof(int);PrintAll(A, len);return 0;
}

C语言:批量初始化二维整型数组及二维整型数组作函数参数的重要知识点[C_006]相关推荐

  1. C语言编程>第七周 ⑧ 请编一个函数void fun(int a[M][N],int b[N]),c指向一个M行N列的二维数组,求出二维数组每列中最大元素,并依次放入b所指一维数组中。

    例题:请编一个函数void fun(int a[M][N],int b[N]),c指向一个M行N列的二维数组,求出二维数组每列中最大元素,并依次放入b所指一维数组中.二维数组中的数己在主函数中赋予. ...

  2. 如何批量制作扫描后即可在线阅读的二维码

    由于二维码在日常生活中应用比较广泛,因此受到很多行业的青睐.随着手机功能的增强,我们平时会使用手机上网,甚至连一些报纸行业也开始进军互联网行业,而它们的媒介就是二维码.读者可以通过手机对报纸上的二维码 ...

  3. 【C 语言】二级指针 内存模型图 ( 指针数组 | 二维数组 | 自定义二级指针内存 )

    文章目录 前言 一.指针数组 二.二维数组 三.自定义二维指针内存 前言 绘制如下 333 种二级指针的内存模型 : // I. 指针数组 char *p1 []= {"12", ...

  4. Java二维数组详解:二维数组的声明和初始化,以及获取二维数组的值

    为了方便组织各种信息,计算机常将信息以表的形式进行组织,然后再以行和列的形式呈现出来.二维数组的结构决定了其能非常方便地表示计算机中的表,以第一个下标表示元素所在的行,第二个下标表示元素所在的列.下面 ...

  5. java二维数组的创建,java二维数组创建方法

    java动态创建二维数组,从零学java笔录-第31篇 图解二位数组在内存中存储,java二维数组动态赋值,java二维数组创建方法 二维数组的定义 type arrayName[ ][ ]; typ ...

  6. 使用前端QrCode.js实现根据输入内容生成二维码,并提供二维码下载功能(已使用在交付项目中)

    前端QrCode 根据输入内容生成二维码,并提供二维码下载功能 简介:这是一个使用前端QrCode自动生成二维码并下载二维码图片的项目. 项目背景:在某个项目的交付阶段,客户提出需要一个可手动生成二维 ...

  7. 免费在线生成二维码网站,支持二维码自定义

    ToolBaox平台 工具盒子,一个专注于为用户提供 免费.工具.在线 服务的网站. ToolBaox平台:二维码生成器,免费在线生成二维码.可以支持二维码颜色.码眼.内容.logo自定义功能.支持批 ...

  8. C 二维数组,以及自定义二维数组

    C 二维数组,以及自定义二维数组 我们通常情况下是这样定义一个二维数组的: int a[10][15]; 我们分别查看一下a,a[0],*a 都是一样的值吧 我们可以这么理解: a是一个数组的数组 a ...

  9. 计算机二维动画实验原理,浅析计算机二维动画制作

    摘 要:最初的二维动画在制作上需要大量的人力还有较长的时间,相对的就是财力的消耗,在修改上也极度不便.计算机动画技术的发展提高了二维动画的制作效率,然而效率的提高却也让二维动画的质量变得不如以前.不过 ...

最新文章

  1. 验证视图状态 MAC 失败。如果此应用程序由网络场或群集承载,请确保 配置指定了相同的 validationKey 和验证算法。不能在群集中使用 AutoGenerate...
  2. Spring Cloud Sleuth 服务跟踪 将跟踪信息存储到数据库
  3. Python程序设计题解【蓝桥杯官网题库】 DAY11-算法训练
  4. springboot启动不了_七款高Star的开源SpringBoot扩展,助你的代码水平更上一层楼
  5. SpringCloud微服务架构,Config 分布式配置中心,Bus 消息总线, Stream 消息驱动,Sleuth+Zipkin 链路追踪
  6. spring boot 启动卡住_SpringBoot 居然有 44 种应用启动器
  7. 51单片机并行I/O口工作原理
  8. Python——EM(期望极大算法)实战(附详细代码与注解)(一)
  9. Python chapter 2amp;3 learning notes
  10. 入门级详细USB移植教程——致正在为USB烦恼的朋友
  11. 输入罗马数字1到12输出阿拉伯数字C语言,用C语言古罗马数字转化阿拉伯数字
  12. 正斜杠(左斜杠)和反斜杠(右斜杠)
  13. OC中__kindof的用法
  14. 蓝牙无线自制串口模块连接穿越机配置工具
  15. 山东理工ACM【1147】求绝对值最大值
  16. ISO/IEC 27018公有云中个人身份信息管理体系认证概述
  17. html2canvas给图片添加水印,小程序用Canvas给图片加水印,拼接图片,制作名片
  18. 树的递归与非递归遍历算法
  19. 架设传奇私服时提示此服务器满员的解决方法
  20. 计算机新建文件夹的步骤打开,电脑怎样为自己新建的文件夹加密,只能自己打开. 爱问知识人...

热门文章

  1. java轮训算法_负载均衡算法WeightedRoundRobin(加权轮询)简介及算法实现
  2. 内网摄像机(RTSP/IPC/NVR)如何能在公网进行RTSP、RTMP、HLS(m3u8)、HTTP-FLV互联网H5网页直播
  3. LBP和HOG特征算子
  4. 【2022年度系列工作总结】「国内软件质量调查问卷」针对于本年度软件质量分析总结报告
  5. c语言下标法改为指针变量法,C语言指针教学的改革探析.doc
  6. 4g通信模块怎么连接sim卡_车载模块 4G LTE通信模块
  7. python世界你好的输出便携电源适配器_65W PD 输出,thinkplus USB-C 便携电源适配器(PA65)开箱评测...
  8. 作业三:高温预警系统项目中的观察者模式解析
  9. java printwriter 文件_关于java:如何将PrintWriter转换为String或写入文件?
  10. 恩,super man