数组介绍
什么是数组?
数组就是把相同数据类型的变量组合在一起而产生的数据集合。从数组定义中可以看出数组主要有两个方面:
对于第一点比较好理解,对于第二点简单来说就是把这些相同数据类型的变量按某种关系联系起来,这也是数据结构的定义。因此数组可以说是一个存储数据的数据结构,这种关系就是这些相同数据类型的变量在内存中必须是连续存储的。
数组的定义
前面说数组是相同数据类型变量连续存储的集合,因此在定义数组的时候需要给定数组的数据类型以及数组存放的变量个数。因此数组的定义格式如下:
数据类型 数组名[数组大小];
这里简单介绍定义数组的三个部分:
例如定义一个数组:
int a[3];
上面定义了一个整型数组,数组名为a,此时数组a中有3个整型变量,由于一个整型变量占4个字节的内存空间,那么3个整型变量占3 * 4 = 12个字节的内存空间,并且这12个字节的空间是连续的。习惯把数组中的变量称为元素。
实际上面数组的定义是一维数组的定义。当然也会有二维数组、三维数组等多维数组,其本质都是一样的。二维数组可以理解成一维数组中的元素还是一维数组,同理三维等多维数组的理解。
二维数组定义格式如下:
数据类型 数组名[第一维大小][第二维大小];
数组的初始化
为了方便接介绍,接下来以一维数组为例,当然对于二维三维等更高维度的数组同样适用。数组的初始化可以使用下面三种方法实现:
接下来分别介绍:
#include <stdio.h>
int main(){
int a[3] = {1, 2, 3}; // 完全初始化
int i;
// 打印输出数组
for(i = 0; i < 3; i++){
printf("a[%d] = %d\n", i, a[i]);
}
return 0;}
a[0] = 1 a[1] = 2 a[2] = 3
#include <stdio.h>
int main(){
int a[3] = {1, 2}; // 不完全初始化
int i;
// 打印输出数组
for(i = 0; i < 3; i++){
printf("a[%d] = %d\n", i, a[i]);
}
return 0;}
a[0] = 1 a[1] = 2 a[2] = 0
定义数组a中的3个元素时,只对第一个以及第二个元素值进行初始化,最后一个元素即a[2]没有被初始化。在"不完全初始化",没有被初始化的元素自动为0。
#include <stdio.h>
int main(){
int a[3]; // 完全不初始化
int i;
// 打印输出数组
for(i = 0; i < 3; i++){
printf("a[%d] = %d\n", i, a[i]);
}
return 0;}
a[0] = 21909 a[1] = 1871924176 a[2] = 32765
同样没有对数组中的元素初始化,但是最终和"不完全初始化"只对部分初始化的结果不同:
02
对数组中每个元素赋相同值的memset函数
在实际使用中可能需要对数组中的每一个元素赋以相同的值。当想要把整个数组元素都赋初值为0的话,可以使用"不完全初始化"的方式:
int a[3] = {0}; // 后面再写一些0也是可以的
int a[3] = {}; // 必须加上大括号,这样就和完全不初始化区分开
虽然上面的为数组每一个元素赋0初始值很方便,但是如果想要赋除0以外的其他初始值就需要使用其他方式。一般来说,给数组中每一个元素赋相同初始值的方法有两种:
memset函数的格式为:
memset(数组名, 值, sizeof(数组名));
如果想要使用memset函数,需要在程序的开头添加string.h头文件。介绍memset函数是因为这个函数不是按照常规赋予一个初始值即可,memset函数使用的是按字节赋值,即对每个字节赋同样的值。
在计算机所有数值都是以二进制的方式进行存储的,这种二进制叫做机器数,这是计算机内部的数据表示形式,而在计算机中就是通过这些二进制来进行运算的。为了方便进行运算,机器数有三种常用的表示方法:
在计算机中原码、反码以及补码的转换规则如下:
接下来看一看memset函数是如何按字节赋值:
#include <stdio.h>
#include <string.h> // 想要使用memset函数,必须加上string.h头文件
int main(){
int i;
int a[3] = {1, 2, 3};
// 赋值为0
memset(a, 0, sizeof(a));
for(i = 0; i < 3; i++){
printf("a[%d] = %d\n", i, a[i]);
}
// 赋值为-1
memset(a, -1, sizeof(a));
for(i = 0; i < 3; i++){
printf("a[%d] = %d\n", i, a[i]);
}
return 0;}
a[0] = 0 a[1] = 0 a[2] = 0 a[0] = -1 a[1] = -1 a[2] = -1
在C/C++中int数据类型占4个字节,memset函数按字节赋值,memset函数中的值即为对字节赋值的数值。将字节赋值为0,0为正数因此原码、反码以及补码都是一样的,1个字节的0补码表示如下:
00000000
int有4个字节,每个字节都是0的补码:
00000000 00000000 00000000 00000000
上面得到的是4个字节的补码,由于符号位为0为正值,正数反码补码以及原码都是一样的,转换成原码结果为0。即0就是数组需要为每个元素赋的值。
而对于-1而言,-1在1个字节中的原码表示为:
10000001 -1在1个字节中的原码表示方式
在计算机中参与运算的都是补码,因此还需将上面的原码转换成补码:
10000001 -1在1个字节中的原码表示方式 11111110 -1在1个字节中的反码表示方式 11111111 -1在1个字节中的补码表示方式
int有4个字节,每个字节都是-1的补码:
11111111 11111111 11111111 11111111
上面得到的是4个字节的补码,由于符号位为-为负值,正数反码补码以及原码是不一样的,补码转换成原码:
11111111 11111111 11111111 11111111 补码 11111111 11111111 11111111 11111110 反码(减1) 10000000 00000000 00000000 00000001 原码(按位取反),结果为-1
最终将数组a中的每一个元素都赋值为-1。需要注意,转换为4个字节的时候,最左边的最高位依然是符号位。
接下来,为一个字节赋值为-121进行分析(只要数值能够在一个字节范围中即可,超出会抛出异常):
由于-121是负数,因此需要计算转换成补码:
11111001 -121原码 10000110 -121反码 10000111 -121补码
上面只是1个字节,对于int数据类型的4个字节结果为:
10000111 10000111 10000111 10000111 补码形式
现在并不知道上面的补码具体是什么,接下来可以将上面的补码转换成原码来看看具体的数值。
10000111 10000111 10000111 10000111 补码形式 10000111 10000111 10000111 10000110 反码形式(减1) 11111000 01111000 01111000 01111001 原码形式(按位取反,最左边是符号位,符号位不要变)
通过计算,上面原码值为-2021161081。有了结果,通过代码来验证。
#include <stdio.h>
#include <string.h> // 想要使用memset函数,必须加上string.h头文件
int main(){
int i;
int a[3] = {1, 2, 3};
// 赋值为-1
memset(a, -121, sizeof(a));
for(i = 0; i < 3; i++){
printf("a[%d] = %d\n", i, a[i]);
}
return 0;}
a[0] = -2021161081 a[1] = -2021161081 a[2] = -2021161081
实验的结果和我们分析结果一致。这种按字节赋值的方式还是比较复杂的,因此还有一种fill方法,这里不详细介绍。这种方式赋值虽然比较复杂,但是效率比较高。