C learning_11 (数组和在内存存储的理解、数组越界、数组作为形参)

news2024/9/27 5:50:08

目录

         数组的理解

数组越界

数组作为函数参数


数组的理解

数组的含义

        在C语言中,用于存储多个相同类型的元素。它可以被简单地定义为包含多个元素的容器。数组中每个元素都可以通过索引来访问,索引从零开始递增。 C语言中的数组可以包含任何基本数据类型,例如整数、字符、浮点数等。要定义一个数组,需要指定数组的类型和元素的数量。

数组的创建

type_t   arr_name   [const_n];

type_t 是指数组的元素类型

const_n 是一个常量表达式,用来指定数组的大小

例如:

int a[5];
char c[2];
double* ptr[10];

数组创建的其他几种实例情况

#include<stdio.h>
#define COUNT 10
enum Count {
	Count = 10,
};
int main()
{
	//写法一
	int count = 10;
	int arr[count];

	//写法二
	const int count = 10;
	int arr[count];

	//写法三
	int arr[COUNT];

	//写法四
	int arr[Count];
	
	return 0;
}

以上代码中包含了4种不同的数组定义方式,分别如下:

1. 使用变量定义数组大小:

```

int count = 10;

int arr[count];

```

此种方式定义数组的长度使用的是一个变量,变量count的值为10,因此定义了一个包含10个元素的数组。

但需要注意的是,在博主的vs编译器中,采用的是C90的标准(数组大小只能是常量表达式),并不支持C99的标准(引入变长数组的概念,使得数组在创建的时候可以使用变量,但是不可以被初始化),所以这样的变量长度的数组会被编译器判定错误。

而在gcc编译器环境下,它是支持C99标准的特性,就允许这样的写法。

 

2. 使用常量定义数组大小:

```

const int count = 10;

int arr[count];

```

此种写法只是将变量count改为了常量count,但是count还是常量,只不过它拥有了常量属性,本质还是变量,原因和第一种方法相同。

3. 使用宏定义定义数组大小:

```

#define COUNT 10

int arr[COUNT];

```

通过宏定义,我们将数组大小定义为一个常量,因此在整个程序中都可以使用宏定义的值。此种方式定义的数组大小,与使用常量定义的数组大小基本相同。

4. 使用枚举定义数组大小:

```

enum Count

{  

        Count = 10,

};

int arr[Count];

```

使用枚举定义数组大小与使用宏定义相似,只不过使用的是枚举类型。其中,我们定义了一个名为Count,值为10的枚举常量,然后通过Count来定义数组大小。最终,以上四种方式定义的数组类型都是相同的,都定义了一个包含10个元素的数组。

总结:数组创建,在C99标准之前,[ ]([ ]内的值必须是整型)中要给一个常量、宏定义或者枚举常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小可以使用变量指定,但是数组不能初始化。

数组的初始化

        数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。

int arr1[5] = {1,2,3,4,5};//完全初始化
int arr2[10] = {1,2,3};//不完全初始化,剩余的元素都是0
int arr3[] = {1,2,3,4};//省略数组的大小,数组会根据初始化的内容来确定
int arr4[3] = {0};//省略数组的内容,就必须要指定数组的大小

//下面解释
char arr5[] = {'a','b','c'};
char arr6[] = "abc";

        数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。

现在我们来看数组中字符数组在创建为什么有两种形式(char arr5[] 和 char arr6[ ] ),以及在内存是怎么分配的。

```

char arr5[] = {'a','b','c'};

```

这种方式就是简单的使用{ }进行初始化 。

```

char arr6[] = "abc"; 

```

这种创建char数组的方式叫做字符串字面值初始化,可以简化char数组的定义和初始化。

由两张图我们可以得出:

第二种写法等价于:

```

char arr6[] = {'a', 'b', 'b', '\0'};

```

注意:两种写法有区别,第二种写法比第一种写法多了'\0',我们以后创建使用的时候不能混淆了。

一维数组的使用

数组访问的操作符: [ ] 

下标从'0'开始

#include <stdio.h>
int main()
{
 int arr[10] = {0};//数组的不完全初始化

 //计算数组的元素个数
 int sz = sizeof(arr)/sizeof(arr[0]);

 //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以:
 int i = 0;//做下标
 for(i=0; i<10; i++)//这里写10,好不好? -  可以清晰的看到元素的总个数
 {
     arr[i] = i;
 } 

 //输出数组的内容
 for(i=0; i<10; ++i)
 {
     printf("%d ", arr[i]);
 }
 return 0;
}

总结:

        1. 数组是使用下标来访问的,下标是从0开始。

        2. 数组的大小可以sizeof关键字通过计算得到。

一维数组在内存中的存储

        上面我们打印的是数组每个元素的内容,现在我们来打印一下数组每个元素的地址

//输出数组每个元素的地址
	for (i = 0; i < 10; ++i)
	{
		printf("[%d]==%p\n", arr[i], &arr[i]);
	}

总结:数组在内存中是连续存储的。

二维数组的含义

        二维数组是一种数组类型,它可以存储具有相同数据类型的元素值,

但是以二维网格的形式存储。

例如:

 

二维数组由行和列组成,其中每个元素可以通过其行和列的索引来访问。通常,我们使用两个下标来访问二维数组的元素,第一个下标表示要访问的行数,第二个下标表示要访问的列数。

二维数组的创建

int arr[3][4];

char arr[3][5];

double arr[2][4]

二维数组的初始化

int arr[3][4] = {1,2,3,4};

int arr[3][4] = {{1,2},{4,5}};

int arr[][4] = {{2,3},{4,5}};//二维数组如果有初始化,行可以省略,列不能省略

二维数组的使用

        二维数组的使用也是通过下标的方式,行下标和列下标都从0开始。

#include <stdio.h>
int main()
{
	int arr[3][4] = { 0 };
	int i = 0;
	for (i = 0; i < 3; i++)//行下标
	{
		int j = 0;
		for (j = 0; j < 4; j++)//列下标
		{
			arr[i][j] = i * 4 + j;
		}
	}
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
	}
	return 0;
}

二维数组在内存中的存储

//输出数组每个元素的地址
for (i = 0; i < 3; i++)
{
	int j = 0;
	for (j = 0; j < 4; j++)
	{
		printf("&arr[%d][%d] = %p\n", i,j, &arr[i][j]);
	}
}

 二维数组初始化为什么可以省略行,不能省略列

        二维数组是由多个一维数组按照行排列而成的,每一个一维数组被称为一行,它们在内存中是连续存放的,行与行之间是相互独立的。当我们初始化一个二维数组时,需要指定其行数和列数。

对于指定行数,我们可以采用以下两种方式:

1. 显示指定行数

```

int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};

```

这种方式明确指定了行数为2。

2. 隐式指定行数

```

int arr[ ][3] = {{1, 2, 3}, {4, 5, 6}};

```

这种方式没有指定行数,但是编译器会根据初始化中元素的个数来计算出行数,这里由于有两个一维数组,每个一维数组包含3个元素,所以行数为2。

对于指定列数,我们需要显式指定数组列数,因为在分配内存空间时,需要先知道每一行应该分配多少个元素。如果省略了列数,则编译器无法确定每一行应该分配多少个元素。因此,初始化二维数组时可以省略行数,但必须指定列数。

数组越界

        数组的下标是有范围限制的。 数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。 所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。 C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就 是正确的, 所以我们在写代码时,最好自己做越界的检查。

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    for(i=0; i<=10; i++)
   {
        printf("%d\n", arr[i]);//当i等于10的时候,越界访问了
   }
 return 0;
}

        这段代码定义了一个长度为10的整型数组,并将其初始化为1到10的连续数字。然后通过循环遍历数组,依次输出数组中的元素,但是需要注意的是,在循环中判断的条件是 `i<=10`,这意味着循环将会执行11次,而数组下标从0开始,最大只能取到9。

        因此,在循环的最后一次迭代中,`i`等于10,此时访问了数组下标为10的元素,即超出了数组的范围,这个错误我们称之为数组越界访问

        越界访问是一种常见的编程错误,它可能导致程序崩溃、产生不可预知的结果或安全隐患等问题。为了避免越界访问,我们需要在访问数组元素时,保证数组下标的合法性,即数组下标不能小于0,且不能大于等于数组长度。所以代码中应该把循环判断条件修改为 `i<10`。

数组作为函数参数

        在C语言中,我们可以将数组作为函数的参数来传递。当我们将一个数组作为参数传递给函数时,实际上传递的是数组的地址。在函数中,我们可以通过传递进来的地址来访问数组中的元素,对数组进行读取、修改等操作。

数组名怎么理解

 数组名通常情况下就是数组的首元素的地址。
但是有两个例外:
        1.sizeof(数组名),数组名单独放在sizeof()内部,这里的数组名表示整个数组,计算的是整个数组的大小。
        2.&数组名,这里的数组名表示整个数组,这里取出的是整个数组的地址。

我们来用函数作为参数写一个冒泡排序实现整形数组排序。

#include <stdio.h>
void bubble_sort(int arr[])
{
	int sz = sizeof(arr) / sizeof(arr[0]);//这样对吗?
	int i = 0;
	for (i = 0; i < sz - 1; i++)//比较的趟数
	{
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)//两两比较的次数
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
	bubble_sort(arr);//是否可以正常排序?
	int i = 0;
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

        在这段代码中,定义了一个名为 `bubble_sort` 的函数,它接受一个整型数组 `arr` 作为参数。在函数内部,使用双重循环实现了冒泡排序算法,将传入的数组进行排序。

        在 `main` 函数中,定义并初始化了一个整型数组 `arr`,然后将其作为参数传递给 `bubble_sort` 函数进行排序。

        然而,这段代码存在一个问题:在 `bubble_sort` 函数中,计算数组长度的方式不确。 在 C 语言中,数组作为函数参数时,我们只能传递数组的地址,也就是指向数组第一个元素的指针。因此,当 `bubble_sort` 函数被调用时,传递给它的是数组的地址,而不是数组本身的大小。因此,`sizeof(arr)` 将会返回数组指针的大小,而不是整个数组的大小。我们试着打印一下发现结果确实是4,并且程序也提示警告。

 

为了解决这个问题,我们可以在调用函数时将数组的长度一起传递给函数,或者在函数内部使用另一个参数来表示数组的长度。在这段代码中,我们可以在定义函数时加上一个表示数组长度的参数 `int len`,然后在调用函数时将数组的长度传递给它。

修改后的冒泡排序算法:

#include <stdio.h>
//void bubble_sort(int *arr,int sz)
void bubble_sort(int arr[],int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)//比较的趟数
	{
		int j = 0;
		int flag = 1;//表示有序
		for (j = 0; j < sz - 1 - i; j++)//两两比较的次数
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;//表示无序
			}
		}
		if (flag == 1)//本轮比较的过程没有元素交换
			break;
	}
}
int main()
{
	int arr[] = { 3,1,7,5,8,9,0,2,4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr,sz);
	int i = 0;
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

         在代码中,用一个flag变量来表示当前的数组是否已经有序了。在每一轮比较之前,将flag值赋为1,表示当前的数组已经有序。如果在比较的过程中发现有元素需要进行交换,那么就将flag值改为0,表示当前的数组无序。 在每一轮比较结束之后,检查flag的值。如果flag值还是1,说明这一轮比较过程中没有进行任何元素的交换,也就是整个数组已经有序了。此时就可以直接退出排序循环,不再进行无用的比较,从而提高排序的效率。这个优化方法能够有效地减少排序循环的次数,尤其在处理大数据量时效果更为明显。因为如果数组已经有序,那么不必再进行多余的比较操作,这样可以大大减少算法的时间复杂度

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

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

相关文章

力扣---LeetCode141/142. 环形链表 (I)和(II) (代码详解+流程图+数学逻辑拓展)

文章目录 前言141. 环形链表 I1.1 链接&#xff1a;1.2 思路&#xff1a;1.3 代码&#xff1a;快慢指针1.4 流程图&#xff1a; 142. 环形链表 II2.1 链接&#xff1a;2.2 思路&#xff1a;2.3 代码&#xff1a;2.4 流程图&#xff1a; 拓展问题及证明(面试常问)&#xff1a;3.…

【雅特力】单片机AT32F421系列入门资料

1. 命名规则 AT32 全系列MCU选型手册.PDF AT32F421F8P7 AT32F421C8T7 (雅特力厂商送样的两个芯片版本) 2. 数据手册 【 数据手册】AT32F421系列引脚定义、电气特性与封装特性.PDF 3. 技术手册 【技术手册】AT32F421系列各外设(Peripheral)完整说明与各寄存器(Register)定…

[架构之路-190]-《软考-系统分析师》-4-据通信与计算机网络-5-图解CRC计算方法与步骤

目录 一、概述&#xff1a; 二、实战演示 假设&#xff1a; 第1步&#xff1a;把多项多项式转化为除数 第2步&#xff1a;把发送数据转换为被除数&#xff1a;在信息序列后加0 第3步&#xff1a;信息序列除以多项式序列 第4步&#xff1a;获得余数&#xff08;CRC校验值…

OpenCV4.x图像处理实例-搭建身份识别系统

搭建身份识别系统 文章目录 搭建身份识别系统1、人脸识别系统介绍2、人脸特征数据提取3、人脸识别模型训练4、从静态图像进行身份识别5、从视频流识别身份在本文中,将介绍如何使用 OpenCV 搭建一个人脸检测与身份识别系统。 为了构建我们的人脸识别系统,我们将首先执行人脸检…

对称加密与非对称加密、证书、SSL/TLS握手过程

文章目录 对称加密(Symmetrical Encryption)&#xff1a;非对称加密(Asymmetric Encryption)&#xff1a;区别&#xff1a;SSL证书TLS1.2握手过程 对称加密(Symmetrical Encryption)&#xff1a; 对称加密&#xff0c;是一种既简单速度又快的加密方式&#xff0c;加密与解密使用…

大数据之PySpark的RDD介绍

文章目录 前言一、RDD简介二、RDD的特性三、RDD的特点总结 前言 #博学谷IT学习技术支持# 之前的文章主要介绍Spark基础知识&#xff0c;例如集群角色、Spark集群运行流程等&#xff0c;接下来会进一步讨论Spark相对核心的知识&#xff0c;让我们拭目以待&#xff0c;同时也期待…

Linux安装Harbor亲测成功

Harbor简介 Harbor 是为企业用户设计的容器镜像仓库开源项目&#xff0c;包括了权限管理(RBAC)、LDAP、审计、安全漏洞扫描、镜像验真、管理界面、自我注册、HA 等企业必需的功能&#xff0c;同时针对中国用户的特点&#xff0c;设计镜像复制和中文支持等功能。 虽然Docker官方…

Android framework学习指南之Launcher启动过程原理分析

前言 Launcher是一个用来显示系统中已经安装的应用程序的应用程序&#xff0c;Launcher 在启动过程中会请求PackageManagerService 返回系统中已经安装的应用程序的信息&#xff0c;并将这些信息封装成一个快捷图标列表显示在系统屏幕上&#xff0c;这样用户可以通过点击这些快…

Flask框架的入门使用

Flask框架的入门使用 Flask框架Flask概述常用扩展包 Flask的基本使用环境准备创建helloworld.py文件启动运行访问 参数配置Flask对象配置应用程序配置读取配置信息加载配置信息配置对象与环境变量结合 app.run参数 开发服务器启动方式在命令行中运行Python程序中手动启动Pychar…

2.2.3开机流程中的BIOS与UEFI开机检测程序

操作系统的系统软件产生&#xff0c;是为了计算机所有硬件系统的资源合理分配。操作系统会控制所有的硬件并且提供核心功能&#xff0c;因此我们的计算机就能够认识硬盘内的文件系统&#xff0c;并且进一步的读取硬盘内的软件文件与执行该软件来达成各项软件的执行目的。 基本上…

【Redis】封装Redis缓存工具解决缓存穿透与缓存击穿问题

基于StringRedisTemplate封装一个缓存工具&#xff0c;主要有一下几个方法 方法1&#xff1a;将任意Java对象序列化为json并存储在String的指定key中且设置TTL 方法2&#xff1a;将任意Java对象序列化为json并存储在String的指定key中&#xff0c;并可以设置逻辑过期时间&…

【python可视化】常用数据类型

&#x1f64b;‍ 哈喽大家好&#xff0c;本次是python数据分析、挖掘与可视化专栏第二期 ⭐本期内容&#xff1a;常用数据类型 &#x1f3c6;系列专栏&#xff1a;Python数据分析、挖掘与可视化 &#x1f44d;欢迎大佬指正&#xff0c;一起学习&#xff0c;一起加油&#xff01…

【Frida-实战】EA游戏平台的文件监控(PsExec.exe提权)

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 代码编写开源代码搜索自己撸代码procexp确定句柄对应的文件名并过滤 2️⃣ PsExec.exe提权定位找不到EABackgroundService.exe的问题 PsExec.exe提权PsExec.exe原理 &#x1f6ec; 结论&#x1f4d6; 参考资料 &#x1f6eb; 问题…

4年Android开发,面试通过全靠狂刷这份面试题,从11K涨到25K+(内含答案)

在博主认为&#xff0c;对于Android面试以及进阶的最佳学习方法莫过于刷题博客书籍总结&#xff0c;前三者博主将淋漓尽致地挥毫于这篇博客文章中&#xff0c;至于总结在于个人&#xff0c;实际上越到后面你会发现面试并不难&#xff0c;其次就是在刷题的过程中有没有去思考&am…

【Python】序列类型③-集合

文章目录 1.集合(set)简介2.集合的定义3.集合的遍历4.集合的常用方法 1.集合(set)简介 集合是一种无序可变的容器对象 集合最大的特点:同一个集合内元素是不允许有重复的,因此集合自带"去重"效果 2.集合的定义 集合的定义有两种方式: 使用{}进行定义,这种方式不能定…

【TCP 重传、滑动窗口、流量控制、拥塞控制】

文章目录 重传机制超时重传快速重传SACK方法Duplicate SACK 滑动窗口流量控制那操作系统的缓冲区&#xff0c;是如何影响发送窗口和接收窗口的呢&#xff1f;窗口关闭 拥塞控制慢启动拥塞避免拥塞发生快速恢复 重传机制 TCP 实现可靠传输的方式之一&#xff0c;是通过序列号与…

chatgpt可以降重论文吗-chatgpt降重论文软件

chatgpt可以降重论文吗 ChatGPT是一种自然语言处理技术&#xff0c;可以生成符合指定条件的文本。因此&#xff0c;理论上可以使用ChatGPT来降重论文。但是&#xff0c;需要注意以下几点&#xff1a; 是否符合学术道德要求&#xff1a;学术论文的降重需要严格遵守学术道德准则…

mfc140u.dll丢失怎么解决?,哪种方法更简单?

如果您在运行 Windows 操作系统时遇到了“mfc140u.dll 丢失”或“找不到 mfc140u.dll”等错误提示&#xff0c;那么这意味着您的计算机遗失了该文件。mfc140u.dll 文件是 Microsoft Visual C 的一部分&#xff0c;是支持应用程序运行所必需的。无论是什么原因导致了 mfc140u.dl…

PointNetGPD<论文>

摘要 提出了一种端到端的抓取位置预测模型&#xff0c;能够从点云中估计出机器人的抓取位姿。网络以原始点云作为输入&#xff0c;能够捕捉到抓取器闭合区域点云的复杂几何结构&#xff0c;即使这些点云很稀疏。 PointNetGPD是一种轻量级的网络模型&#xff0c;能够处理抓取器…