前言
在之前学习了一下基本的数据类型,当学完后突然有一个疑问,我想储存一堆同一个数据,那我不能写一堆int变量来存放吧,这样显得很冗余。
那我们该如何用不冗余的方法来创建能存放一堆同一个数据的方法呢?这个就得使用到下面要说的数组了。
一、什么是数组
数组其实装一些特定的数据的容器,而这个容器是有规定的长度的。并且特定数据和长度都是我们规定的。
二、一维数组
1.一维数组创建
类型名 数组名[数组的长度]; 类型名 数组名[] = {value, value}; 类型名 数组名[数组的长度] = {value, value};
第一种方式是动态创建。数组的长度有 [] 里的参数决定。
第二种创建方法是静态创建。数组的长度由后面的初始值的个数决定。
第三种方法是不完全初始化,也有可能是完全初始化。得看数组的长度和初始值的内容长度。如果说是使用不完全初始化,那么剩下的内容以0来占位。
2.一维数组的使用
2.1 索引值
如果我们需要查看数组中里面元素的内容,可以使用索引值来进行查看。
索引值从0开始,一直到数组长度-1。
使用方法:
数组名[索引值];
例如:查看数组arr里面索引值为2的内容
1. int arr[] = {1,2,3,4,5,6,7}; 2. printf("%d\n", arr[2]);
2.2 遍历数组
本质上就是把数组中的所有内容全部输出一遍。
利用的是索引值的变化,找到索引值的最大值,然后索引值进行变化,从而找到数组中的所有元素。
int main(){ int arr[] = {1,2,3,4,5,6,7}; int i; for (i = 0; i < 7; i++){ printf("%d\t", arr[i]); } }
2.3 如何使用sizeof()计算出数组的长度
首先需要知道数组在空间中所占的大小为多少,数组在空间中所占的大小由数组的类型和数组的长度组成,也就是说:
数组所占字节数=数组的类型所占字节数*数组的长度
而 sizeof(数组名) 可以直接得到数组所占的字节数,而数组只能存放一种类型的数据,而数组索引值为0的这个元素是必须存在的,无论你的数组长多少,这个索引值为0的元素必须存在,如果这个索引值不存在,那么这个数组就没有存在的意义了。
而我们可以通过索引值为0这个位置的元素得到这个数组它每一个空间所占的字节数,所以我们可以通过sizeof()计算出这个数组的长度。
数组的长度=数组所占字节数/数组的类型所占字节数
代码表示如下:
int arr[] = {1,2,3,4,5,6,7}; printf("%d\n", sizeof(arr)/sizeof(arr[0]));
三、二维数组
二维数组其实本质是在一个数组中存放一个数组。
1.二维数组的创建
类型名 数组名[外部数组的长度][内部数组的长度]; 类型名 数组名[][内部数组的长度] = {value,value}; 类型名 数组名[][内部数组的长度] = {{value}, {value}};
第一种创建方法是动态创建。
第二种创建方法是静态创建,只需要规定内部数组的长度,然后计算机会自动计算出外部数组的长度,但是不能只写外部数组的长度,这样是不可以的。
第三种创建方法是完全初始化或者不完全初始化,在初始化值中,如果有大括号,就代表着一个数组,而如果你这个大括号里面元素的个数和内部数组的长度不匹配,那么就会补上0。
这种方法创建的时候,外部数组的长度由初始值中的大括号来决定。
2.二维数组的使用
2.1 找到数组中的内容
其实也是使用索引值,这里的索引值要注意,需要写上两个。
比如说需要找到二维数组中第1个数组里的第2个元素:
int arr[][3] = {1,2,3,4,5,6,7,8,9,10}; printf("%d\n", arr[1][2]);
2.2 遍历二维数组
这里不能直接使用sizeof()
直接计算出数组的长度。除非你知道外部数组的长度或者内部数组的长度。
(内层数组的长度 * 内层数组每块空间的类型所占的字节数)*外层数组的长度 = 这个二维数组所占的字节数
如果你得到了其中一个才可以使用sizeof()进行推断。
//假如知道外部数组的长度=5 for (i = 0; i < 5; i++){ for (j = 0; j < sizeof(arr)/(5*arr[0][0])){ printf("%d\t", arr[i][j]); } }
//假如知道内部数组的长度=3 for (i = 0; i < sizeof(arr)/(3*arr[0][0]); i++){ for(j = 0; j < 3; j++){ printf("%d\t", arr[i][j]); } }
上面的方法是知道一个值才能使用上面的方式,如果不知道一些已知条件就不能使用上面的方式进行计算。
四、数组深层理解
数组其实是一块连续的空间,所以我们想要为数组规定一下它的长度,如果不规定会导致数组在开辟空间的时候不知道开辟多大的空间。
1.数组的地址
数组的地址是首元素的地址值,而输出数组的地址可以直接写数组名
1. int arr[5]; 2. printf("%d", arr);
不需要使用&
的方式。需要记住:数组的地址是首元素的地址。下面的代码可以验证
int main(){ int arr[] = {1,2,3,4,5}; if (arr == &arr[0]) printf("地址一样"); else printf("地址不一样"); return 1; }
输出的内容是一样的。
2.&数组名
我们知道数组名得到的就是数组的地址,而数组中第一个元素的地址也是数组的地址,那么直接&数组名得到的内容是什么呢?
直接&数组名得到的是整个数组的地址,虽然说这个地址和我们的数组首元素的地址一样,但是代表的内容确是不一样的,因为它是代表整个数组的长度,如果使用这个地址+1,那跳过的是一个数组的长度。
例如现在有一个数组arr,它的长度为5
int arr[5]; printf("&arr=%p\n", &arr); printf("&arr+1=%p\n", &arr + 1);
首先先输出一下这个数组的地址,得到的结果为:0061FE8C,后面的输出是将整个数组的地址+1,得到的结果为:0061FEA0,会发现上面的结果和下面的结果相差了20个字节,而我整个数组是20个字节,所以可以知道当我们&数组名后得到的是整个数组的地址,并且使用这个地址加一个常数,那跳过的是整个数组。
3.数组作为函数的参数
在使用数组的时候需要将数组作为函数的参数传入进函数中,所以就得对传入的数组进行操作,这里有两种方式
3.1 数组作为函数的参数
这种方式的写法如下:
void test(int arr[]){}
而调用这个函数的时候是直接写数组名
int main(){ int arr[5]; test(arr); }
然后就可以进行使用了。这种方式是将你传入的这个数组中第一个元素的地址传入进test中的形参arr中,也就是arr是数组中的第一个元素,然后对这个数组进行操作本质上也是操作main函数中的arr。
因为函数中操作的地址和这个数组的地址是一样的,所以操作的时候也是操作数组的空间。
3.2 数组以指针的形式
其实使用的数组连续空间的特征和地址的加运算。
现在有下面的这个函数
void test(int* a){}
调用的时候直接传入这个数组的地址,但不是整个数组的地址,这种方法和上面的方法是一样的,使用方法也是一样的。都是第一个元素的地址。
4.数组使用非索引的方式
在使用数组的时候也可以不使用索引的方法得到元素,因为知道,数组名是首元素的地址,而地址可以使用解引用的方式,所以这里可以使用数组地址加上地址的加运算和解引用来操作,如下代码:
int main(){ int arr[] = {1,2,5,4,5}; printf("%d", *(arr + 2)); }
通过数组名进行加操作,因为数组名就是地址,所以直接用地址进行加操作,操作完之后需要使用解引用,将地址所对应的内容解出来。
5.数组表达字符串
字符串这个并不是一个类型,在其它语言中会为其封装,而C语言中并没有这个类型,那如何表示字符串呢?
首先我们要知道字符串是一堆字符连起来,所以叫做字符串,那通过一些数据类型将字符连在一起就可以变成字符串了。
那么如何做到呢?这里就需要使用到数组了,因为数组是一个连续的空间,可以很好的让这些字符串连在一起。
5.1 创建字符串
创建的方式很简单,就是创建数组的方式
1. char str1[] = {'H', 'e', 'l', 'l', 'o'}; 2. char str2[] = "Hello";
str1
和str2
这两种创建方法都是可以的,但是他们之间是有区别的,str1
中的字符使用%s
进行输出的时候后面会出现一些乱码(乱码不固定,不一定会出现)
1. char str1[] = {'H', 'e', 'l', 'l', 'o'}; 2. printf("%s", str1);
而str2使用%s输出的时候并不会出现乱码,因为直接使用""括起来的字符的后面会默认加一个\0,而%s进行输出的时候遇到\0就会结束。
这两种方法创建出来的长度也不同,str1创建出来的字符串长为5,而str2创建出来的字符串长度为6,因为str2的后面有一个\0。
总结
数组的内容其实很多,数组、函数、指针是C语言中最复杂也是最灵活也是很多新手都搞不明白的知识,大家需要多看看这些文章并且要多练习练习,只有这样大家才可以学会这些内容。