【C语言数据结构】:数组
数组本质上只是编译器在内存空间上开辟的【一连串的内存】
而代表数组的【变量】其实只是这一连串内存空间的【第一个元素的内存地址】。
所以当你给编译器看一个数组时,他并不是像人一样能看到这个数组的全貌,他只能看到这个数组的第一个元素,并且知道这个元素的内存地址。
【看了这段话很蒙圈?问题不大!接着看下去就行了】
一、定义数组
数组的普通定义非常简单,你只需要这样
int numList[10];
就可以定义一个【含有10个空间大小的整型数组】,里面可以存放十个整型元素。
在刚定义并未初始化的时候,你可以理解为里面什么都没有,是空的,就像你拿了一个袋子,里面没有装东西一样。
1. 【延伸知识】数组的理解
毕竟数组你们在学C语言的时候也都学了,那么我们再学数据结构的时候,肯定还需要再深入一点点的
当然上面这个例子不是十分恰当,或许这个例子更好一点:
在你们学校开会的时候,【第一排的座位】规定只能由【领导】坐
这个【第一排座位】就可以理解为这个【数组】,有【10个座位】就是数组的【长度为10】,【领导】就是这个【int类型】。
如果这个你还不理解,那就…那就…去看视频理解吧 [手动狗头]
2. 【延伸知识】数组的初始元素
当然了,虽然可以理解为空的
但是其实不是空的,他是由数组长度个(上面的数组就是10个)自由内存地址组成,诶,你别不信,我们可以打印一下试试,看看我说的到底是真的还是假的。
#include <stdio.h> int main() { int a[10]; for (int i=0;i<10;i++) { printf("%d\n", a[i]); } }
看到这,可能有些Java选手受不了了,明明没有定义,你就打印,肯定会报错的!
pass:Java中需要定义,声明
但是会吗?很明显是不会的,它是有元素的
打印结果:
1997818734 0 1 44 7673468 6422376 4200059 4199952 0 44
二、数组的类型强制
——带着问题学知识——
- 下标取值是怎么做到的?
- 下标为什么从0开始?
- 为什么数组会强制类型?
————————————
【p1】下标取值是怎么做到的?
我们知道,数组其实是在内存空间中开辟的一串连续的内存地址,而在编译器中只有数组第一个元素的内存地址。
【下标】,其实是在操作数组第一个元素的内存地址,令其【加下标】,就能得到第【几个】元素。
我们假设第一个数组元素的内存地址为:00000001(不要当真),而这个数组变量为【a】(数组为整型),那么【a[0]】,其实就是令这个内存地址【+0】,得到第一个元素的内存地址,再得到这个内存地址所存储的值。
而因为这个数组是整型数组,而整型在内存中占用4个字节,所以,第二个元素应该这么表示:【a[1] =》(00000001+(1*4))】 得到第二个元素的内存地址,再得到第二个元素。
展示数组的地址与数组第一个元素的地址相同
#include <stdio.h> int main(int argc, char const *argv[]) { int a[] = {1, 2, 3, 4}; // 一个数组 printf("a的内存地址%p\na[0]的内存地址%p", &a, &a[0]); return 0; }
运行结果:
a的内存地址0061FEC0 a[0]的内存地址0061FEC0
【p2】下标为什么从0开始?
想必看完上面的问题,你也对这个问题有了认知。
因为数组的内存实际上是第一个元素的内存,而通过下标进行计算是需要对内存地址进行相加操作的,所以就必须从0开始。
【p3】为什么数组需要强制类型?
看看刚刚的式子:【a[1] =》(00000001+(1*4))】 转成通用的式子就是:
数组第一个元素的内存地址+(下标*类型在内存中所占用的字节数)
所以数组要强制类型。
这个内存地址在C语言中,是可以通过【&】直接取到的,有兴趣的,可以通过这个符号去对数组进行一些操作。
比如:
#include <stdio.h> int main() { int a[10]; for (int i=0;i<10;i++) { printf("%p\n", &(a[i])); } }
自己打印一下看看数组的内存地址看看是不是相邻的,看看相邻是不是4
运行结果:
0061FEA4 0061FEA8 0061FEAC 0061FEB0 0061FEB4 0061FEB8 0061FEBC 0061FEC0 0061FEC4 0061FEC8
三、数组使用
增改查的操作。
增其实也是在未初始化的时候,,,而不是长度增加。
数组在使用的时候,切忌要记得数组的长度,不要超出数组长度,会报错的。
1. 增
遍历赋值的操作
#include <stdio.h> int main() { int a[10]; for (int i=0;i<10;i++) { a[i] = i; printf("元素的内存地址:%p\n\n元素的值:%d\n", &(a[i]), a[i]); } }
运行结果:将0-9的值放进数组中
元素的内存地址:0061FEA4 元素的值:0 元素的内存地址:0061FEA8 元素的值:1 元素的内存地址:0061FEAC 元素的值:2 元素的内存地址:0061FEB0 元素的值:3 元素的内存地址:0061FEB4 元素的值:4 元素的内存地址:0061FEB8 元素的值:5 元素的内存地址:0061FEBC 元素的值:6 元素的内存地址:0061FEC0 元素的值:7 元素的内存地址:0061FEC4 元素的值:8 元素的内存地址:0061FEC8 元素的值:9
2. 改
因为定义的时候,数组不是空的,也是有元素的,我们在进行我们的操作的时候,就将原来的值替换掉了,这个改也是同样的道理,其原理还是通过下标。
3. 查
通过下标查找某个元素
#include <stdio.h> int main() { int a[10]; for (int i=0;i<10;i++) { a[i] = i; printf("元素的内存地址:%p\n元素的值:%d\n\n", &(a[i]), a[i]); } }
再说别的就是数组的相关算法了,什么二分枚举,双指针,三分枚举…
四、练习题
学编程,最忌讳的就是光看不练,动起来,做点题
p1 数组串联
给你一个长度为 n 的整数数组 nums 。请你构建一个长度为 2n 的答案数组 ans ,数组下标 从 0 开始计数 ,对于所有 0 <= i < n 的 i ,满足下述所有要求:
ans[i] == nums[i]
ans[i + n] == nums[i]
具体而言,ans 由两个 nums 数组 串联 形成。
返回数组 ans 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/concatenation-of-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
p2 基于排列构建数组
给你一个 从 0 开始的排列 nums(下标也从 0 开始)。请你构建一个 同样长度 的数组 ans ,其中,对于每个 i(0 <= i < nums.length),都满足 ans[i] = nums[nums[i]] 。返回构建好的数组 ans 。
从 0 开始的排列 nums 是一个由 0 到 nums.length - 1(0 和 nums.length - 1 也包含在内)的不同整数组成的数组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/build-array-from-permutation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
p3 执行操作后的变量值
存在一种仅支持 4 种操作和 1 个变量 X 的编程语言:
++X 和 X++ 使变量 X 的值 加 1
–X 和 X-- 使变量 X 的值 减 1
最初,X 的值是 0
给你一个字符串数组 operations ,这是由操作组成的一个列表,返回执行所有操作后, X 的 最终值 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/final-value-of-variable-after-performing-operations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
关注下方公众号,领取更多福利 - python基础教程 - python知识脑图 - python入门100例 - C语言数据结构教程 - C语言算法100题