[C语言] 数组

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

[1. 一维数组的创建和初始化](#1. 一维数组的创建和初始化)

[1.1 数组的创建](#1.1 数组的创建)

[1.2 数组的初始化](#1.2 数组的初始化)

[1.3 一维数组的使用](#1.3 一维数组的使用)

[1.4 一维数组在内存中的存储](#1.4 一维数组在内存中的存储)

[2. 二维数组的创建和初始化](#2. 二维数组的创建和初始化)

[2.1 二维数组的创建](#2.1 二维数组的创建)

[2.2 二维数组的初始化](#2.2 二维数组的初始化)

[2.3 二维数组的使用](#2.3 二维数组的使用)

[2.4 二维数组在内存中的存储](#2.4 二维数组在内存中的存储)

[3. 数组越界](#3. 数组越界)

[4. 数组作为函数参数](#4. 数组作为函数参数)

[4.1 冒泡排序函数的错误设计](#4.1 冒泡排序函数的错误设计)

[4.2 数组名是什么?](#4.2 数组名是什么?)

[4.3 冒泡排序函数的正确设计](#4.3 冒泡排序函数的正确设计)

[5. 数据实例:](#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" ;

cpp 复制代码
#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

cpp 复制代码
//%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个字节,所以说数组在内存中是连续存放的!

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

cpp 复制代码
//%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 }}; // 二维数组如果有初始化,行可以省略,列不能省略

cpp 复制代码
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****二维数组的使用

二维数组的使用也是通过下标的方式。
看代码:

cpp 复制代码
#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;
}
cpp 复制代码
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****二维数组在内存中的存储

像一维数组一样,这里我们尝试打印二维数组的每个元素。

cpp 复制代码
#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 语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,
所以程序员写代码时,最好自己做越界的检查。

cpp 复制代码
#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****冒泡排序函数的错误设计

cpp 复制代码
//方法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****数组名是什么?

cpp 复制代码
#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内部单独放一个数组名,数组名表示整个数
    组。
  1. &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。
    除此 1,2 两种情况之外,所有的数组名都表示数组首元素的地址。

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

当数组传参的时候,实际上只是把数组的首元素的地址传递过去了。
所以即使在函数参数部分写成数组的形式: int arr\[\] 表示的依然是一个指针: int *arr 。
那么,函数内部的 sizeof(arr) 结果是 4 。
如果 方法 1 错了,该怎么设计?

cpp 复制代码
//方法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 arr10

char ch35

//

test1(arr)

//

//形参数数组

void test1(int arr10)

{}

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

{}

//

//形参是指针的形式

void test1(int *arr)

{}

相关推荐
不会C语言的男孩3 小时前
C++ Primer 第3章:字符串、向量和数组
开发语言·c++
兰令水3 小时前
leecodecode【反前后指针】【2026.5.31打卡-java版本】
java·开发语言
Dovis(誓平步青云)3 小时前
《QT学习第四篇:常见事件与UDP、TCP、文件系统、(锁、信号量、条件变量》
c语言·开发语言·汇编·qt
isyangli_blog12 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb20081112 小时前
FastAPI APIRouter
开发语言·python
Benszen12 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆12 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木12 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充13 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~13 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言