初级C语言之【数组】

news2024/11/25 17:20:39

🦖作者:学写代码的恐龙
🦖博客主页:学写代码的恐龙博客主页
🦖专栏:【初级c语言】
🦖语录:❀未来的你,一定会感谢现在努力奋斗的自己❀
在这里插入图片描述

初级C语言之【数组】

  • 一:一维数组的创建和初始化
    • 1.1:数组的定义
    • 1.2:数组的创建
      • 1.2.1:数组创建举例:
    • 1.3:数组的初始化
      • 1.3.1:完全初始化
        • 1.3.1.1:整型数组的完全初始化
        • 1.3.1.2:字符数组的完全初始化
      • 1.3.2:不完全初始化
        • 1.3.2.1:整型数组不完全初始化
        • 1.3.2.2:字符型数组不完全初始化
    • 1.4:一维数组的使用
    • 1.5:一维数组在内存中的存储
  • 二:二维数组的创建和初始化
    • 2.1:二维数组的创建
    • 2.2:二维数组的初始化
    • 2.3:二维数组的使用
    • 2.4:二维数组在内存中的存储
      • 2.4.1:二维数组行数和列数的计算
  • 三:数组越界
  • 四:数组作为函数的参数
    • 4.1:冒泡排序函数
    • 4.2:数组名是什么?

一:一维数组的创建和初始化

1.1:数组的定义

数组是一组相同类型元素的集合

1.2:数组的创建

type_t   arr_name   [const_n];
//type_t 是指数组的元素类型
//arr_name是数组名
//const_n 是一个常量表达式,用来指定数组的大小

注意:数组创建,在C99标准之前, [ ] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,数组的大小可以使用变量指定,但是数组不能初始化。

1.2.1:数组创建举例:

int arr1[10];
double arr2[5+5];

//支持c99的编译环境
int n=10;
char arr3[n];//变长数组,数组的大小是由变量n来指定的

1.3:数组的初始化

在数组创建的同时给数组一些值,就叫初始化

1.3.1:完全初始化

1.3.1.1:整型数组的完全初始化

int a[10] = {1,2,3,4,5,6,7,8,9,10};//这叫完全初始化,将数组里的全部10个元素都进行了初始化
int arr4[] = { 1,2,3,4,5,6,7,8,9,10 };//这里没有指定数组元素个数,编译会根据初始化的内容来确定数组的元素个数,这里的arr4数组就有10个元素

下面两个数组是有差异的

int arr1[]={1,2,3};//只有三个元素
int arr2[10]={1,2,3};//有十个元素

1.3.1.2:字符数组的完全初始化

char arr3[3] = {'a','b','c'};
char arr4[ ] = {'a','b','c'};
//这两种初始化效果一样

用字符串进行初始化

注意:字符串的后面默认有’\0’,也要算在字符个数里面

char arr5[4] = "abc";//arr5数组里面一共有4个元素分别是'a','b','c','\0'
char arr6[ ] = "abc";//arr6数组里面一共有4个元素分别是'a','b','c','\0'
//在这两种初始化的结果相同
char arr7[] = "abc";//arr7数组有4个元素分别是'a','b','c','\0'
char arr8[] = { 'a','b','c' };//arr8数组有3个元素分别是'a','b','c'
//这两种数组的初始化结果不同

arr7数组和arr8数组打印出来的结果有所不同,arr7打印出来的是abc,而arr8数组打印出来的是abc烫烫烫烫蘟bc,这时因为字符串在打印的时候只有遇到’\0’才会停下来,arr7数组是用字符串进行的初始化,数组最后一个元素默认是’\0’,因此arr7数组会打印出abc,而arr8数组是用单个字符进行初始化的,数字最后一个元素是’c’,此时打印arr8数组,会从字符’a’开始往后打印直到遇到了’\0’才会停下来。

1.3.2:不完全初始化

1.3.2.1:整型数组不完全初始化

int arr2[10] = { 1,2,3 };//不完全初始化,只对数组的前三个元素进行了初始化,后七个元素默认是0

1.3.2.2:字符型数组不完全初始化

char arr3[10] = {'a','b','c'};//只把前三个元素分别初始化成'a','b','c',后七个元素默认初始化成'\0'

1.4:一维数组的使用

数组是有下标的,下标是从0开始的。[ ] ,下标引用操作符。它其实就数组访问的操作符。

//访问数组中的元素,将其打印出来
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr)/sizeof(arr[0]);//sz的值代表了数组元素个数
	int i = 0;
	//顺序打印
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	//逆序打印
	for (i = sz - 1; i >= 0; i--)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

1.5:一维数组在内存中的存储

//将数组中每一个元素的地址打印出来
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	//顺序打印
	for (i = 0; i < sz; i++)
	{
		printf("&arr[%d]=%p\n",i, &arr[i]);
	}
	printf("\n");
	return 0;
}

//打印结果:
&arr[0]=007CF730
&arr[1]=007CF734
&arr[2]=007CF738
&arr[3]=007CF73C
&arr[4]=007CF740
&arr[5]=007CF744
&arr[6]=007CF748
&arr[7]=007CF74C
&arr[8]=007CF750
&arr[9]=007CF754

可见相邻两个数组元素地址之间相差4个字节,也就是一个整型,这说明数组里面元素的地址是连续的,随着数组下标的增长,地址是由低到高变化的

在这里插入图片描述

//打印数组元素的另一种方式
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr)/sizeof(arr[0]);
	int* p = &arr[0];//把首元素的地址给p
	int i = 0;
	//顺序打印
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p+i));//p+i就是第i个元素的地址
	}
	printf("\n");
	return 0;
}

二:二维数组的创建和初始化

2.1:二维数组的创建

int arr1[3][4];//一个三行四列的整型数组,共12个元素
char arr2[3][5];//一个三行五列的字符型数组,共15个元素
double arr3[2][4];//一个两行四列的双精度浮点型数组,共8个元素

2.2:二维数组的初始化

int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };//完全初始化
int arr2[3][4] = { 1,2,3,4,5 };//不完全初始化
int arr3[3][4] = { {1,2},{3,4},{5,6} };//按行进行初始化,第一行的四个元素被初始化为1,2,0,0,第二行的四个元素被初始化成3,4,0,0,第三行的四个元素被初始化成5,6,0,0

注意:二维数组的行可以省略,但是列不能省略。列决定了一行可以放几个,所以只要列不省略,编译器就会自动计算出行数。

int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
int arr1[ ][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
//这两个数组初始化的效果相同

2.3:二维数组的使用

二维数组中的每一个元素都有其对应的行号和列号,行号和列号都是从0开始的,故可以通过下标来使用二维数组

//通过下标访问数组的每一个元素并将其打印出来
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

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

//打印二维数组中每一个元素的地址
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	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;
}
//打印结果如下:
&arr[0][0]=00AFF860
&arr[0][1]=00AFF864
&arr[0][2]=00AFF868
&arr[0][3]=00AFF86C
&arr[1][0]=00AFF870
&arr[1][1]=00AFF874
&arr[1][2]=00AFF878
&arr[1][3]=00AFF87C
&arr[2][0]=00AFF880
&arr[2][1]=00AFF884
&arr[2][2]=00AFF888
&arr[2][3]=00AFF88C

可见二维数组中每相邻两个元素的地址是连续的

在这里插入图片描述

//因为二维数组中元素的存储是连续的,因此我们也可通过下面这种方式来打印数组中的每一位元素
int main()
{
	int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int* p = &arr[0][0];
	int i = 0;
	for (i = 0; i < 12; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	return 0;
}

如果把二维数组的每一行都看成是一个以为数组,那么:
arr[0]可以看作是第一行元素的数组名
arr[1]可以看作是第二行元素的数组名
arr[2]可以看作是第三行元素的数组名

2.4.1:二维数组行数和列数的计算

//计算二维数组由多少行
sizeof(arr)/sizeof(arr[0])//二维数组总的字节数除以每一行的字节数就是行数
//计算二维数组的列数
sizeof(arr[0])/sizeof(arr[0][0])//二维数组一行的字节数除以每一个元素的字节数就是列数

三:数组越界

int arr[10];//下标的取值范围是0~9,超出这个范围就是越界

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

int main()
{
	int arr[10] = { 0 };
	//下标的取值范围是0~9,超出这个范围就是越界
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		printf("%d ", arr[i]);
	}
}
//结果:
0 0 0 0 0 0 0 0 0 0 -858993460
//通过最后一个打印结果(随机值)可以看出,发生了数组越界
int main()
{
	int arr[10] = { 0 };
	//下标的取值范围是0~9,超出这个范围就是越界
	arr[10] = 11;//数组已越界
}

执行上面这段代码,会报出如下的数组越界错误
/在这里插入图片描述

四:数组作为函数的参数

4.1:冒泡排序函数

冒泡排序的思想是:从左到右,相邻的两个元素进行比较,并且进行适当的交换。每次比较一轮,就会找到序列中最大的或者最小的数,这个数就会来到序列的最右边。
以从小到大排序为例:从左到右,让相邻的两个元素进行比较,如果左边的元素大于右边的元素就将这两个元素进行交换,从左到右,两两相邻的元素比较结束,最大的那个数就会来到序列的最右边,第一轮比较就结束了。接着进行第二轮,从左边第一个数一直比较到倒数第二个数(最后一个数就是序列里面最大的,无需再进行比较),如下图的序列:9 8 7 6 5 4 3 2 1.第一轮冒泡共进行了9次两两相互比较,第一轮冒泡排序结束后序列里面最大的数字9的位置就确定了下来,即在序列的最后面(因为是从小到大排序)。接着进行第二轮冒泡排序,找出9前面的序列:8 7 6 5 4 3 2 1 0里面的最大值让他来到9的前面,第二轮冒泡排序共进行了8次两两比较,最终序列:8 7 6 5 4 3 2 1 0中的最大值8来到了他应该出现的位置,即在9的前面。第三轮冒泡会让7去到他应该去到的位置,第四轮冒泡会让6去到他应该去到的位置。大家猜猜这十个数字要进行几轮冒泡呢?既然一轮搞定一个数字,那十个数字不就需要十轮嘛?答案是需要九轮,因为十个数经过九轮冒泡,那一定有九个数都去到了他们应该去的位置,既然九个数的位置都确定下来了,那最后剩的那个数肯定就在他应该出现的位置上,就不用再进行第十轮冒泡了。因此,如果有n个数就要进行n-1轮冒泡才能得到一个有顺序的序列。

在这里插入图片描述

void Sort(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//冒泡轮数
		int j = 0;
		for (j = 0; j < sz - 1 - i; j++)//第一轮要进行九次两两比较,第二轮要进行八次两两比较,所以这里的判断条件是sz-1-i
		{
			//每一轮冒泡要进行多少次两两比较
			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,5,4,7,6,9,8,0,2 };//一共十个数
	//写一个函数对数组排序
	int sz = sizeof(arr) / sizeof(arr[0]);//计算数组中元素的个数,数组总的字节数除以每一个元素的字节数就是数组中元素的个数
	Sort(arr, sz);
	int i = 0;
	//将排完序的数组打印出来
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

注意:计算数组元素个数的这段代码“int sz = sizeof(arr) / sizeof(arr[0]);”一定得放在主函数里面,将计算出的个数sz传到Sort函数里面,切不可在Sort函数里面进行计算。为什么只能这样呢?这就是我们接下来要讨论的问题-数组名是什么了?

4.2:数组名是什么?

数组名是首元素的地址有两个例外
例外1:sizeof(数组名),这里的数组名是表示整个数组,计算的是整个数组的大小,单位是字节
例外2:&数组名,这里的数组名也表示整个数组,&数组名取出的是数组的地址

//通过这段代码可证明:数组名就是首元素的地址
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	printf("%p\n", arr);//打印数组名
	printf("%p\n", &arr[0]);//打印首元素地址
	return 0;
}
//执行结果:
004FFEAC
004FFEAC
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	printf("%p\n", arr);//打印数组名
	printf("%p\n", &arr[0]);//打印首元素地址
	printf("%p\n", &arr);//这里&arr取的是数组的地址
	return 0;
}
//执行结果:
00EFFBF8
00EFFBF8
00EFFBF8

虽然上面的这段代码打印出来的结果相同,但是意义却不相同,前两个仅仅代表首元素的地址,而第三个代表的是数组的地址
通过下面的代码便可得以验证

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	printf("%p\n", arr);
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);

	printf("%p\n", &arr);
	printf("%p\n", &arr+1);

	return 0;
}
//执行结果:
006FFB5C
006FFB60
006FFB5C
006FFB60
006FFB5C
006FFB80

可见arr+1和&arr[0]+1的结果一样,都是跳过了四个字节,而&arr+1则是跳过了四十个字节

在这里插入图片描述

这下我们就知道了:数组作为函数的参数,传递的是首元素的地址,既然是地址那形参就应该用一个指针来接收。此时我们再看这段代码“int sz = sizeof(arr) / sizeof(arr[0]);”如果放在函数内部,arr此时仅仅是一个整型指针,而一个指针的字节数在×86的环境下就是四个字节,所以sizeof(arr)的结果就是4而不是整个数组所占的字节数,而sizeof(arr[0])也是4,最终相除的结果就是1了,并不是我们想得到的数组元素个数,因此求数组的元素个数应该在主函数中求

//数组作为函数的参数
void Sort(int* arr)//形参是对应的指针类型
{

}

int main()
{
	int arr[] = { 1,2,3 };
	Sort(arr);//传的是首元素的地址
	return 0;
}

形参得到了数组的首元素地址,接着顺藤摸瓜就能找到数组中的其他元素

到这里,函数栈帧的有关分享就结束啦,喜欢的话可以点赞、评论和收藏哟!

在这里插入图片描述

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

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

相关文章

Swagger在线API文档

Swagger 解决的问题 随着互联网技术的发展&#xff0c;现在的网站架构基本都由原来的后端渲染&#xff0c;变成了前后端分离的形态&#xff0c;而且前端技术和后端技术在各自的道路上越走越远。前端和后端的唯一联系&#xff0c;变成了 API 接口&#xff0c;所以 API 文档变成…

C++进阶---C++11

本篇主要是介绍C11中新添加的一些特性。 文章目录 1.C11简介2.列表初始化3.变量类型推导4.新增容器---静态数组array5.右值引用6.lambda表达式7.包装器8.新的类功能9.可变参数模板一、C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字…

Python高阶函数装饰器

“ 从CANoe vTESTstudio版本7开始&#xff0c;支持使用python编辑器编写python脚本。其中CANoe提供了许多API接口给python使用&#xff0c;大大扩展了python的可用性。在python中使用装饰器定义capl中的事件处理程序&#xff08;on key/on timer等&#xff09;。对此我们有必要…

C语言—宏定义

宏定义的作用是替换&#xff0c;再复杂也只能替换&#xff0c;不能用做计算&#xff1b; 一个源文件将另一个源文件的全部内容包含进来&#xff1b; 条件编译&#xff1b; 不带参数的宏定义&#xff1a; #include <stdio.h> #define PI 3.14int main() {printf("…

聊聊Redis消息队列-实现异步秒杀

一、前言 消息队列&#xff08;Message Queue&#xff09;, 字面意思就是存放消息的队列&#xff0c;最简单的消息队列模型包括3个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理&#xff08;Message Broker&#xff09;;生产者&#xff…

Shell编程补充

Shell编程补充shell的变量定义变量的单双引号的不同输出变量父子shellshell子串BASHshell子串的用法shell统计变量长度输出程序运行时间结论:shell扩展变量用于处理变量值的创建子shell(进程列表)查看是否开启子shell在运行内置命令,外置命令shell编程总结shell的变量 定义变量…

小黑实习debug中遇到了函数式编程的混乱,特此进行的日常积累:python函数积累1

函数参数中有默认值&#xff0c;在函数内部会创建一块区域并维护这个默认值 # 在函数内存中会维护一块区域存储 [1,2,666,666,666] 100010001 def func(a1,a2[]):a2.append(666)print(a1,a2)func(100) func(1000)100 [666] 1000 [666, 666] def func(a1,a2[]):a2.append(666…

【nowcoder】笔试强训Day7

目录 一、选择题 二、编程题 2.1Fibonacci数列 2.2合法括号序列判断 一、选择题 1.JAVA属于&#xff08; &#xff09;。 A 操作系统 B 办公软件 C 数据库系统 D 计算机语言 计算机软件主要分为系统软件与应用软件两大类。系统软件主要包括操作系统、语言处理系统、数…

three.js之形状缓冲几何体

文章目录简介例子解释其他圆弧矩形专栏目录请点击 简介 Shape用来定义一个二维形状平面 官网常常与ShapeGeometry(形状缓冲几何体)搭配使用 官网&#xff0c;我们可以下运行下面的例子 例子 <!DOCTYPE html> <html lang"en"><head><meta cha…

玩转GPT--在线文本生成项目[可入坑~科普系列]

文章目录前言效果页面说明文字个数top_KTop_Ptemperature聊天上下文关联记忆项目部署获取项目获取模型运行彩蛋总结前言 没办法&#xff0c;最近ChatGPT杀疯了&#xff0c;没忍住&#xff0c;还是想look&#xff0c;look。没办法&#xff0c;哪个帅小伙能够忍受的了一个可以和…

数学知识---数论(质数和约数)

文章目录 1.质数1.1质数的判定---试除法1.2分解质因数---试除法1.3筛质数2.约数2.1试除法求约数2.2约数个数2.3约数之和2.4最大公约数---欧几里得算法(辗转相除法)1.质数 质数是针对所有大于1的自然数定义的,在大于1的整数中,如果只包含1和本身这两个约数,就被定义成为质…

【SpringCloud Alibaba】 初始化Sentinel

Sentinel 概念 特征 主要特征 开源生态​ 开启控制台 初始化工程 Sentinel 概念 分布式系统的流量防卫兵。随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、流量路由、熔断降级、系统自适应过载保护…

Biotin-PEG-NH2,Biotin-PEG-amine,生物素-PEG-氨基材料改性用化学试剂

英文名称&#xff1a;Biotin-PEG-NH2&#xff0c;Biotin-PEG-amine 中文名称&#xff1a;生物素-聚乙二醇-氨基 Biotin-PEG-NH2是氨基化PEG中的一种&#xff0c;他可以用于材料改性&#xff1b;氨基和很多基团可以反应&#xff0c;如&#xff1a;羧基&#xff0c;活性酯&…

JavaScript:封装单向链表10种常见的操作方法

链表的优势 1.要存储多个元素的时候&#xff0c;除了数组还可以选择链表。 2.与之数组不同的是&#xff0c;链表中的元素在内存中不必是连续的空间。 3.链表中的每个元素由一个存储元素本身的节点和一个指向下一个节点的引用(指针或者连接)组成。 相比于数组&#xff0c;链表有…

QGIS基础:根据字段属性值或基于规则进行分类符号化显示

以下操作是对数据进行分类符号化&#xff0c;下面是原始操作数据&#xff1a; 基于分类符号化的字段是如下所示&#xff08;ZDTZM&#xff09;: A B C D 找到数据图层&#xff0c;右键属性&#xff0c;找到【符号化】&#xff0c;点击如下所示的分类&#xff1a; 在【valu…

mysql数据库完整实例-“汽车维修”

mysql数据库创建&#xff0c;编写&#xff0c;查询&#xff0c;自定义函数实战案例 创建汽车修理数据库&#xff0c;并完成数据库编写&#xff1a; 本文分三个部分&#xff0c;第一部分为数据库的创建编写和基础查询&#xff0c;第二部分为关联查询等复杂查询方法&#xff0c…

实验四:完整性实验

【实验目的】 掌握实体完整性、参照完整性和用户自定义完整性的定义的维护方法 【实验内容】 要实现这样一个功能&#xff1a;医生根据药品价表选择处方药品&#xff0c;录入数量和使用天数&#xff0c;系统根据医生选择的药品和录入的信息自动生成处方主表和处方明细表。功能…

Flink-按键分区状态-算子状态-广播状态

文章目录1. 按键分区状态&#xff08;Keyed State&#xff09;1.2基本概念和特点1.3 支持的结构类型1.4 代码实现1.5 状态生存时间&#xff08;TTL&#xff09;2 算子状态&#xff08;Operator State&#xff09;2.1 基本概念和特点2.2 状态类型2.3 代码实现3. 广播状态&#x…

2022年安徽建筑施工塔式起重机(建筑特种作业)模拟题库及答案

百分百题库提供特种工&#xff08;塔式起重机&#xff09;考试试题、特种工&#xff08;塔式起重机&#xff09;考试真题、特种工&#xff08;塔式起重机&#xff09;证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 87.塔式起重机滑轮上…

基于历史对比学习的时序知识图谱推理

时序知识图谱 知识图谱&#xff08;KGs&#xff09;作为人类知识的集合&#xff0c;在自然语言处理、推荐系统和信息检索等领域显示展现了很好的前景。传统的KG通常是一个静态知识库&#xff0c;它使用图结构数据拓扑&#xff0c;并以三元组&#xff08;s, p, o&#xff09;的…