[C语言] 数组

news2024/11/15 11:03:02
1. 一维数组的创建和初始化
2. 一维数组的使用
3. 一维数组在内存中的存储
4. 二维数组的创建和初始化
5. 二维数组的使用
6. 二维数组在内存中的存储
7. 数组越界
8. 数组作为函数参数
9. 数组的应用实例 1 :三子棋
10. 数组的应用实例 2 :扫雷游戏

目录

1. 一维数组的创建和初始化

1.1 数组的创建

1.2 数组的初始化

1.3 一维数组的使用

1.4 一维数组在内存中的存储

2. 二维数组的创建和初始化

2.1 二维数组的创建

2.2 二维数组的初始化

2.3 二维数组的使用

2.4 二维数组在内存中的存储

3. 数组越界

4. 数组作为函数参数

4.1 冒泡排序函数的错误设计

4.2 数组名是什么?

4.3 冒泡排序函数的正确设计

5. 数据实例:


1. 一维数组的创建和初始化

1.1 数组的创建

数组是一组相同类型元素的集合。
数组的创建方式:
type_t   arr_name   [ const_n ];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
数组创建的实例:
// 代码 1
int arr1 [ 10 ];
// 代码 2
int count = 10 ;
int arr2 [ count ]; // 数组时候可以正常创建?
// 代码 3
char arr3 [ 10 ];
float arr4 [ 1 ];
double arr5 [ 20 ];
注: 数组创建,在 C99 标准之前, [] 中要给一个 常量 才可以,不能使用变量。在 C99 标准支持了变长数组的概念。当前VS2019不支持变长数组。gcc - 对c99标准支持,大部分下默认不支持变长数组。

1.2 数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。
看代码:
int arr1 [ 10 ] = { 1 , 2 , 3 };
int arr2 [] = { 1 , 2 , 3 , 4 };
int arr3 [ 5 ] = { 1 2 3 4 5 }
char arr4 [ 3 ] = { 'a' , 98 , 'c' };
char arr5 [] = { 'a' , 'b' , 'c' };
char arr6 [] = "abcdef" ;
#include<stdio.h>
int main()
{
	//int arr[8];
	//char ch[5];
	
	//int a = 10;//初始化
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//完全初始化
	int arr[10] = { 1,2,3,4,5 };//不完全初始化
	//int arr2[] = { 1,2,3,4,5 };//它会根据数组初始化内容确定数组是几个元素等价于int arr2[5] = { 1,2,3,4,5 };

	char ch1[5] = { 'b','i','t' };
	char ch2[] = { 'b','i','t' };

	char ch3[5] = "bit";//b i t \0 0
	char ch4[] = "bit";//b i t \0

	char ch5[] = "bit";
	//[b i t \0]
	char ch6[] = { 'b','i','t' };
	//[b i t]
	printf("%s\n", ch5);//遇到/0停止打印
	printf("%s\n", ch6);
    printf("%d\n", strlen(ch5));//3
	printf("%d\n", strlen(ch6));//一直找不到/0打印出随机值

	return 0;
}

 

数组在创建的时候如果想不指定数组的确定的大小就得初始化。数组的元素个数根据初始化的内容来确定。
但是对于下面的代码要区分,内存中如何分配。
char arr1 [] = "abc" ;
char arr2 [ 3 ] = { 'a' , 'b' , 'c' };

1.3 一维数组的使用

对于数组的使用我们之前介绍了一个操作符: [] ,下标引用操作符。它其实就数组访问的操作符。
我们来看代码:
#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. 数组的大小可以通过计算得到。
int arr [ 10 ];
int sz = sizeof ( arr ) / sizeof ( arr [ 0 ]);

1.4 一维数组在内存中的存储

接下来我们探讨数组在内存中的存储。
看代码:
#include <stdio.h>
int main ()
{
int arr [ 10 ] = { 0 };
int i = 0 ;
    int sz = sizeof ( arr ) / sizeof ( arr [ 0 ]);
   
for ( i = 0 ; i < sz ; ++ i )
{
printf ( "&arr[%d] = %p\n" , i , & arr [ i ]);
}
return 0 ;
}
输出的结果如下:
仔细观察输出的结果,我们知道,随着数组下标的增长,元素的地址,也在有规律的递增。
由此可以得出结论: 数组在内存中是连续存放的。

%p - 是按地址格式打印 - 十六进制的打印                                              32位 / 4 =8

//%p - 是按地址格式打印 - 十六进制的打印
int main()
{
	
	int arr[10] = { 0 };//第一个元素初始化值为0,其余元素默认为0
	//int arr2[10] = { 1 };//第一个元素初始化值为1,其余元素默认为0
	
	int i;
	
	for (i = 0; i < 10; i++) 
	{
		printf("&arr[%d]=%p\n", i, &arr[i]);  //  &取地址
	}
		
	return 0;

}

运行结果:

int 占4个字节,所以说数组在内存中是连续存放的!

随着数组下标的增长,地址是由低到高变化的

//%p - 是按地址格式打印 - 十六进制的打印
int main()
{
	
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	
	int * p=arr;//数组名是数组首元素的地址
	int i;
	
	for (i = 0; i < 10; i++) 
	{
		printf("%d ", *p);//*解引用输出1 2 3 4 5 6 7 8 9 10
		p++;  //一个整型指针加1跳过一个int
	}
		
	return 0;

}

2. 二维数组的创建和初始化

2.1 二维数组的创建

//数组创建

int arr [ 3 ][ 4 ];
char arr [ 3 ][ 5 ];
double arr [ 2 ][ 4 ];

2.2 二维数组的初始化

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

int main()
{
	/*int arr[3][4];
	char ch[3][10];*/
	//初始化 - 创建的同时给赋值
	//int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	//int arr[3][4] = { 1,2,3,4,5,6,7 };//不完全初始化 - 后面补0
	int arr[][4] = { {1,2},{3,4},{4,5} };//行可以省略,但是列不可以省略,因为{}里面有3个{}所以有3个行
	return 0;
}

2.3 二维数组的使用

二维数组的使用也是通过下标的方式。
看代码:
#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;
}
int main()
{
	/*int arr[3][4];
	char ch[3][10];*/
	//初始化 - 创建的同时给赋值
	//int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	//int arr[3][4] = { 1,2,3,4,5,6,7 };//不完全初始化 - 后面补0
	int arr[][4] = { {1,2},{3,4},{4,5} };//行可以省略,但是列不可以省略,因为{}里面有3个{}所以有3个行,4决定一行有几个元素
	int i, j;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

结果:
1 2 0 0
3 4 0 0
4 5 0 0

2.4 二维数组在内存中的存储

像一维数组一样,这里我们尝试打印二维数组的每个元素。
#include <stdio.h>
int main()
{
int arr[3][4];
int i = 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]);
}
}
return 0;
}
输出的结果是这样的:

 *p 指针解引用

通过结果我们可以分析到,其实二维数组在内存中也是连续存储的。  

一行内部连续,跨行也是连续的

3. 数组越界

数组的下标是有范围限制的。
数组的下规定是从 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;
}
二维数组的行和列也可能存在越界。

4. 数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序(这里要讲算法思想)函数将一个整形数组排序。
那我们将会这样使用该函数:
冒泡排序的思想:两两相邻的元素进行比较,并且可能的话需要交换!
9 8 7 6 5 4 3 2 1 0
8 9 7 6 5 4 3 2 1 0
8 7 9 6 5 4 3 2 1 0
8 7 6 9 5 4 3 2 1 0
...
8 7 6 5 4 3 2 1 0 9
一趟冒泡排序搞定一个

4.1 冒泡排序函数的错误设计

//方法1:
#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-i-1; 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);//是否可以正常排序?
   for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
  {
       printf("%d ", arr[i]);
  }
   return 0;
}
方法 1 ,出问题,那我们找一下问题,调试之后可以看到 bubble_sort 函数内部的 sz ,是 1
难道数组作为函数参数的时候,不是把整个数组的传递过去?

4.2 数组名是什么?

#include <stdio.h>
int main()
{
   int arr[10] = {1,2,3,4,5};
printf("%p\n", arr);
   printf("%p\n", &arr[0]);
   printf("%d\n", *arr);
   //输出结果
   return 0;
}
结论:
数组名是数组首元素的地址。(有两个例外)
如果数组名是首元素地址,那么:
int arr [ 10 ] = { 0 };
printf ( "%d\n" , sizeof ( arr ));
为什么输出的结果是: 40
补充:
1. sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数
组。
2. &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。
除此 1,2 两种情况之外,所有的数组名都表示数组首元素的地址。

4.3 冒泡排序函数的正确设计

当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。
所以即使在函数参数部分写成数组的形式: int arr[] 表示的依然是一个指针: int *arr
那么,函数内部的 sizeof(arr) 结果是 4
如果 方法 1 错了,该怎么设计?
//方法2
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{
//代码同上面函数
}
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);//是否可以正常排序?
   for(i=0; i<sz; i++)
  {
       printf("%d ", arr[i]);
  }
   return 0;
}

5. 数据实例:

5.1 数组的应用实例 1 :三子棋
5.2 数组的应用实例 2 :扫雷游戏

注意:数组作为函数传参的时候:本质上是指针只是语法展示,数组传参本质上传递过去的是首元素地址

形参可以写成2种形式:

1.数组形式

2.指针形式

int arr[10]

char  ch[3][5]

//

test1(arr)

//

//形参数数组

void test1(int arr[10])

{}

void test1(int arr[])  //数组大小可以省略

{}   

//

//形参是指针的形式

void test1(int *arr)

{}

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

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

相关文章

初阶数据结构——二叉树题目

文章目录 一、单值二叉树二、检查两颗树是否相同三、另一棵树的子树四、二叉树的前序遍历五、对称二叉树 一、单值二叉树 单值二叉树 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff…

AD原理图检查ERC(编译检查)

原理图的编译&#xff1a; 原理图界面选择菜单栏的&#xff1a;工程---->Compile PCB Project 也可以项目管理栏右击对应项目文件 在右下角&#xff0c;点击panels---->Message&#xff0c;就可以打开Message界面 原理图的常用检测&#xff1a; 原理图界面选择菜单…

kv键值对快速转换为json串(字典类型) | 批量添加双引号

从浏览器中复制的以下数据, 并不能直接使用 refresh:0 start:0 count:20 selected_categories:%7B%7D uncollect:false playable:true tags:在python中需转为字典类型 {refresh: 0,start: 0,count: 20,selected_categories: %7B%7D,uncollect: false,playable: true,tags: , …

SIMD系列-GATHER/SCATTER操作

SIMD系列-GATHER/SCATTER操作 众所周知&#xff0c;SIMD寄存器可以使用LOAD/STORE操作与标量域&#xff08;或者更准确的说是内存&#xff09;进行通信。这些操作的缺点是&#xff1a;只允许移动内存中连续的数据元素。然而&#xff0c;我们代码中&#xff0c;经常需要访问非连…

浅谈大数据软件的功能性分析

在当今时代的潮流中&#xff0c;工作中遇到大数据处理的时候非常多&#xff0c;因此需要一些大数据分析软件帮助人们进行工作。由于这些软件针对的对象不同&#xff0c;因此使用方法也不同&#xff0c;那么为了帮助更多的人了解大数据分析软件&#xff0c;我们就对这些软件的功…

【Linux命令200例】mc一个十分实用的文件管理器

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

ARM裸机-4

1、什么是交叉编译 1.1、两种开发模式 非嵌入式开发&#xff0c;A&#xff08;类&#xff09;机编写&#xff08;源代码&#xff09;、编译得到可执行程序&#xff0c;发布给A&#xff08;类&#xff09;机运行。 嵌入式开发&#xff0c;A&#xff08;类&#xff09;机编写&am…

【PWN · Stack Smash】[2021 鹤城杯]easyecho

花式栈溢出——Canary保护是吧&#xff1f;接化发&#xff0c;拿来吧你 目录 前言 一、代码分析 0.保护 1.main函数 2.sub_CF0()函数 &#xff08;v9指向的函数&#xff09; 二、Stack Smash过程 0.原理简述 1.条件与准备 2.地址泄露 ①真实地址泄露 ②flag地址泄露…

解决问题:python PermissionError: [WinError 5]拒绝访问

重要&#xff1a;关闭PyCharm Community Edition 2022.3等与python相关的编程程序找到按照python解释器的位置python->右键>属性>安全->点击组或用户名"中的Users->编辑点击"组或用户名"中的Users->把"完全控制"打钩->应用->…

【Java】使用JDBC操作MySQL(快速入门+详解)

文章目录 1. JDBC概述2. JDBC快速入门2.1 下载驱动jar包2.2 数据准备2.3 创建工程2.4 编写代码 3. JDBC API详解3.1 DriverManager3.2 Connection3.2.1 获取执行SQL对象3.2.1 管理事务 3.3 Statement3.3.1 执行DML语句3.3.2 执行DDL语句 3.4 ResultSet3.4.1 ResultSet对象方法3…

ChatGPT 实现前一天

提出需求 个人输入需求&#xff1a; Java实现键盘输入日期 输出前一天&#xff0c;需要考虑润年和非润年&#xff0c;2月是否有29号&#xff0c;大月小月的区分等细节处理&#xff0c;不符合的有对应提示&#xff0c;不使用java包里的封装好的类 ChatGPT4分析出的语义&#xff…

人工智能安全-2-非平衡数据处理

0 提纲 现象与原因非平衡数据处理方法概览数据预处理层面特征层算法层面1 现象与原因 非平衡数据分类问题:在网络信息安全问题中,诸如恶意软件检测、SQL注入、不良信息检测等许多问题都可以归结为机器学习分类问题。这类机器学习应用问题中,普遍存在非平衡数据的现象。 产…

哈希函数如何工作 ?

动动发财的小手&#xff0c;点个赞吧&#xff01; 作为一名程序员&#xff0c;您每天都会使用哈希函数。它们在数据库中用于优化查询&#xff0c;在数据结构中用于使速度更快&#xff0c;在安全性中用于保证数据安全。几乎每次与技术的交互都会以某种方式涉及哈希函数。 哈希函…

生命在于学习——APP渗透学习笔记

一、app渗透篇 1、Android 简介 自从 Android 被谷歌收购&#xff08;2005 年&#xff09;&#xff0c;谷歌已经完成了整个开发&#xff0c;在过去的 9 年里&#xff0c;尤其是在安全方面&#xff0c;有很多变化。 现在&#xff0c;它是世界上最广泛使用的智能手机平台&#…

代码、低代码、无代码开发触手可及的低代码平台源码

基于moleculer微服务架构开发的低代码平台源码&#xff0c;代码、低代码、无代码开发触手可及。 一、低代码平台系统功能 【公司信息】 管理员可通过页面顶部设置菜单或者应用程序中设置应用进入到后台设置页面。 在公司信息页面可进行基础信息修改&#xff0c;启用用户自助…

我在CSDN创作的第五十天

这篇文章主要是写给自己的&#xff0c;是对自己现在阶段的一个认识&#xff0c;6月10号&#xff0c;我在CSDN发布了第一篇文章&#xff0c;距离现在不多不少&#xff0c;刚刚好是50天&#xff0c;期间创作的都是C语言的一些内容&#xff0c;我创作的文章也都是我现在所学的知识…

Django系列之DRF简单使用

基于ModelViewSets的简单使用 models.py from django.db import modelsclass AuthorDetail(models.Model):gender models.CharField(max_length8)birthday models.DateField()telephone models.BigIntegerField()addr models.CharField(max_length64)class Author(models…

手机的python怎么运行文件,python在手机上怎么运行

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;手机上的python怎么运行程序&#xff0c;手机的python怎么运行文件&#xff0c;今天让我们一起来看看吧&#xff01; 1、python程序怎么在手机上运行 python语言应用很广泛&#xff0c;自己也很喜欢使用它&#xff0c;其…

自学网络安全(黑客)入门

自学网络安全入门可以按照以下步骤进行&#xff1a; 确定学习目标&#xff1a;网络安全是一个广泛的领域&#xff0c;包括密码学、网络防御、漏洞利用等方面。确定自己想要学习的具体方向&#xff0c;可以更好地规划学习路线。 学习基础知识&#xff1a;网络安全的基础知识包括…

Leetcode周赛 | 2023-7-30--我真是个废物

2023-7-30--我真是个废物 题1体会我的代码 题2体会我的代码 题3体会我的代码 题1 体会 根本没想到用双指针。原因是&#xff0c;没想到还要用一个字典去维护子数组中各个数字的出现频次&#xff0c;以及出现频次不小于1 (也就是大于0) 的数字个数。 这里的双重循环也很巧妙&am…