四.关于数组那些事

四.数组

https://beryl-licorice-3a8.notion.site/1b5130fd6d1a4bc78d153dbbd81c6514?pvs=25 作者原创地点,可能会看的更明白

1.数组的概念

数组是一组相同类型元素的集合,从这个概念出发我们就可以发现两个有价值的信息:

  • 数组存放的是1个或者多个数据,但是数组元素个数不能为零
  • 数组中存放的多个数据,类型是相同的

数组分为一维数组和多维数组,多维数组比较常见的是二维数组

#include<stdio.h>

#include<memory.h> //声明头文件。

// 什么是数组?
// 数组如何赋值/初始化?

//使用内存赋值的方法 使得数组传递元素。 
// 需要声明函数 memcpy()  和头文件memory.h

int main_11()
{
	int arr1[10];//等价于 int arr1[10] = {};
	int arr2[10] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr1, arr2, sizeof(arr2)); //(内存指向的数组;原始数组;复制的内存字节);
	for (int i = 0; i < 10; i++) {
		printf("%d\n", arr1[i]);
	}

	return 0;}

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

2.1数组创建

基本语法:

type arr_name[常量值]//指定元素类型和数目大小;也可以指定数组名

2.2数组的初始化

int fate [] = {}; 基本结构
完全初始化: int fate [10] = {1,2,3,4,5,6,7,8,9,10,};
不完全初始化:int fate[10] = {1,2,3,}//剩下的元素为0,默认为0
完全初始化括号内的数目大小指定可以省略掉。编译器根据初始化的内容确定初始化的数组大小。

2.3数组的类型

首先要明确,数组的类型不是元素的类型

int arr[4]={};
数组的类型是 int [4] 不是int

3.一维数组的运用

一维数组可以存放数据,存放数据的目的是对数据的操作,那么又该如何使用呢?

3.1数组下标

C语言规定数组有下表从0开始,n个数组元素对应的下标就是0~n-1。为了存储数组中的元素,会向内存申请连续的空间,每个空间以下标区别,通过访问下标来访问数组元素。Eg:a[5]={1,2,3,4,5]

数组下标a[0]a[1]a[2]a[3]a[4]
对应的元素12345

3.2数组元素的打印-遍历数组

这里我们就需要用到循环的知识了。

int arr[10] = {1,2,3,4,5,6};
for(int i =0;i<10;i++){
printf("%d", i);}//这样就会打印出1 2 3 4 5 6 0 0 0 0!

3.3数组的输入

同样我们需要使用循环的知识,这是因为数组不可直接打印,也不可以直接全部访问。

int arr[10] = {0};
for(int i = 0; i <10; i++{
scanf("%d ",&arr[i]);}

4.一维数组在内存中的存储

我们最好了解一下数组在内存中的存储。我们用%p 打印地址,用& 取地址。

int arr[10] = { 0 };
for (int i = 0; i < 10; i++) {
	printf("&arr[%d]=%p\n", i, &arr[i])}
00CFF8400
00CFF8441
00CFF8482
00CFF84C3
00CFF8504
00CFF8547
00CFF8488

计算机处理的数据,都要加载到内存中处理

内存会被划分为一个个的内存单元-1个字节

然后给每个内存单元都编上号–通过编号就可以找到内存单元

如左图所示,编号为0~9 我们称之为地址 地址则与指针关系密切

我们通过&获得地址,地址打印出来为16进制

我们发现地址是连续递增的

5.sizeof计算数组中的元素个数

sizeof 计算的是数组的总大小,单位是字节。如果要计算元素的数量,需要 sizeof(arr) / sizeof(int).也就是数组总大小/数组元素类型的大小
在这里插入图片描述

这启示了我们:我们可以在索引数组的时候:

for(i=0;i<sizeof(arr)/sizeof(int); i++)//用sizeof来计算需要索引的数组数目

6.二维数组的创建

6.1二维数组的概念

数组的元素都是内置类型的,如果我们把一维数组作为数组的元素,这时候就是二维数组,二维数组作为数组元素的数组被称为三维数组,二维数组以上被称为多维数组。

  int a[3][5] = {
		{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5},
	};
	int i, j;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 5; j++)
			printf("%d", a[i][j]);
	}
	return 0;
}
//二维数组的遍历、赋值
for (i = 0; i < 3; i++) {
	for (j = 0; j < 5; j++) {
	a[i][j] = i * j;}

6.2二维数组的创建

我们是怎么定义二维数组的呢?语法如下:

type arr_name [行常量值][列常量值];//我知道这很像矩阵Matrix,线性代数很重要
例:double arr_1 [3][5]; //创建了一个三行五列的二维数组

7.二维数组的初始化

在创建变量和数组的时候 - 给定初始值就是初始化

二维数组的初始化比较复杂,我们结合实例分析:

7.1二维数组的完全初始化

int main()
{
	int a[3][5] = {
		{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5},//中间的大括号甚至可以省略
	};                                    // 因为二维数组从行开始依次录入元素

7.2二维数组的不完全初始化

//二维数组的不完全初始化
int arr[3][5] = { 0,1,2,3 };
依次初始化每一行,剩余元素为0

7.3**********************************************************************二维数组按照行进行初始化

这里我不得不承认,{}是非常有必要且非常有用的

//二维数组按行进行初始化
int arr[3][5] = { {1,2},{3,4},{5,6} };
每一个内置的{}表明这是第几行的元素

8.二维数组的运用

8.1二维数组的下标与访问

同样,二维数组的行和列都是从0开始的,因为要从0开始访问。比如int a [3] [5];

行 \ 列01234
0a [0][0]a [0][1]a [0][2]a [0][3]a [0][4]
1a [1][0]a [1][1]a [1][2]a [1][3]a [1][4]
2a [2][0]a [2][1]a [2][2]a [2][3]a [2][4]

我要找一个元素:a[0][0] 我们就找到了第一行第一列的元素

8.2二维数组的数据输入和打印 首先定义 int a[3][5];

int i, j;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 5; j++)
			printf("%d", a[i][j]);}
int i, j;
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 5; j++)
			scanf("%d", &a[i][j]);}

我们最好先行再列 按照行优先的方式访问数组(即先行后列)可能更符合内存的存储方式,从而获得更好的缓存局部性,这可能会导致更快的访问速度。

9.二维数组的内存存储

二维数组在内存中也是连续存放的!二维数组是一个一维数组的数组,存放一维数组的数组

也就是说:二维数组的每一个元素都是一个一维数组。arr[]就是二维数组的存储数组。

在这里插入图片描述

二维数组的每一个元素也都是连续存储的;且按行连续存储。

10.C99之后-引入“变长数组“,可以使用变量来指定数组的大小

我无比欣喜地接受它,又无比心灰的认识到VS不支持!但是oj网站一般是支持的。

int main(){
	int n = 0;
	scanf("%d",&n);
	int arr[n];
int main(){
	int n = 10;
	int arr[n];
	如果硬上必报错

在这里插入图片描述

11.数组练习

11.1练习一:多个字符从两端移动,向中间汇聚

  • 开始之前,我们需要补充一些字符数组的知识

    1. 定义字符数组:
      在 C 语言中,你可以定义一个字符数组来存储一个字符串。例如:

      char name[5];
      
    2. 初始化字符数组:
      你可以在声明字符数组的时候就对其进行初始化。例如:

      char name[] = "John"; // 这会自动为字符串 "John" 分配足够的空间,包括末尾的 '\\0'。
      
    3. 空字符:
      C 语言中的字符串是通过一个空字符 ‘\0’ 结束的。这意味着如果你有一个长度为 4 的字符串 “John”,在内存中的实际长度是 5,因为它包含一个末尾的 ‘\0’。\0是字符0,与常量区分。

    4. 访问字符数组:
      你可以使用数组索引来访问字符数组中的每一个字符。例如:

      char firstLetter = name[0]; // 这会得到 'J'
      
    5. 字符串函数:
      C 标准库 <string.h> 提供了很多有用的函数来操作字符串,例如 strcpy(), strlen(), 等等。

    • 字符串会用到的两个特殊函数

      1.strlen()

      在 C 语言中,字符串是以字符数组的形式存储的,并以 '\0' 空字符作为结束标志。strlen() 函数就是通过查找这个结束标志来计算字符串的长度的。

      下面是一个使用 strlen() 函数计算字符数组长度的例子:

      #include <stdio.h>
      #include <string.h>
      
      int main() {
          char message[20] = "Hello, World!";
          printf("The length of the message is: %zu\\n", strlen(message));
          return 0;}
      

      这段代码会输出:

      The length of the message is: 13
      

      需要注意的是,strlen() 只返回到 '\0' 字符为止的字符数,它不计算 '\0' 本身。所以,尽管 message 的数组长度是 20,但实际存储的字符串 “Hello, World!” 的长度只有 13。

      当使用 strlen() 时,要确保传递的字符数组确实包含了结束标志 '\0',否则 strlen() 可能会读取超出数组边界的内存,从而导致未定义的行为。

      2.strcpy()

      strcpy() 是 C 语言 <string.h> 库中的一个函数,用于将一个字符串复制到另一个字符串。

      函数原型:

      char *strcpy(char *dest, const char *src);
      

      参数:

      • dest:目标字符串的指针。这是您要复制到的字符串。
      • src:源字符串的指针。这是您要从中复制的字符串。

      返回值:

      • 返回指向 dest 的指针。

      功能:

      • strcpy() 函数会将 src 字符串(包括末尾的 ‘\0’)复制到 dest 字符串。

      注意事项:

      • 确保 dest 有足够的空间来存储 src 的内容。否则,超出 dest 边界的写入可能会导致未定义的行为。
      • srcdest 不应该重叠,因为结果是未定义的。

      示例:

      #include <stdio.h>
      #include <string.h>
      
      int main() {
          char source[] = "Hello, World!";
          char destination[20];
      
          strcpy(destination, source);
      
          printf("Source: %s\\n", source);
          printf("Destination: %s\\n", destination);
      
          return 0;}
      

      输出:

      Source: Hello, World!
      Destination: Hello, World!
      

      在上述示例中,source 字符串被复制到 destination 字符串。两者现在都包含相同的内容。

      总的来说,strcpy() 是一个非常有用的函数,但使用时需要小心,确保目标字符串有足够的空间来容纳源字符串,以避免潜在的缓冲区溢出问题。


    1. 注意事项:
      • 当你为字符数组分配空间时,确保考虑到末尾的 ‘\0’。
      • 不要超出字符数组的边界。这可能会导致未定义的行为。
      • 打印字符数组可以用printf(”%s”,数组名); 这告诉我们字符数组和字符串有某种联系
    2. 与指针的关系:
      字符指针经常被用来指向字符串。例如,char *str = "Hello"; 在这里,str 是一个指针,指向字符串 “Hello” 的第一个字符。

确定两个字符串,使其输出动画的效果:b的元素从两边逐渐取代a的元素

#define  _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<Windows.h> //Sleep
#include<stdlib.h> //system
int main()
{
	char a[] = "*****************";
	char b[] = "welcome to leshan";
	int i = 0;
	int length = sizeof(a) / sizeof(a[0])-1;
	int j = length-1;
	printf("%c\n", b[length]);
	while(i<=j) {
		 a[i] = b[i];
		 a[j] = b[j];
		 j--;
		 i++;
		 for (int k = 0; k <length ; k++) {//对与字符数组也可以使用
			                               //printf("%s",a); 进行打印
			 printf("%c", a[k]);
	}
		 Sleep(500);//使用Sleep函数进行休眠处理
		 system("cls");//清楚控制台屏幕 system是库函数
		// printf("\n");
	}
	printf("%s", a);

	return 0;
}

11.2练习二:二分查找的运用

二分查找就是折半查找,目的是简化运算量。

比如我们想再一个递增数列中寻找某个值:

#include<stdio.h>
int main()
{ 
	int a[] = { 1,2,3,4,5,6,7,8,9,10 };
	int left = 0;
	int right = sizeof(a) / sizeof(a[1]) - 1;
	
	int k = 0;
	scanf("%d", &k);
	while (left <= right) {
		int middle = (left + right) / 2;
		if (a[middle] == k) {
			printf("%d", middle);
			break;
		}
		else if (a[middle] > k) right = middle-1 ;
		else left = middle+1 ;
	}
	if (left > right) printf("我找不到");
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值