一、一维数组定义
数组是包含给定类型的一组数据,并将这些数据依次存储在连续的内存空间中。,数组可以是多种类型,如 int, char,等,但是数组不可以是void型。
一般数组的定义:类型说明 数组名[常量表达式]
int a[10];
在上述定义中,int就是数组的类型,a是数组的名称,10则是代表数组的长度。我们在定义时一般常量表达式为正整数。而且在数组中的每个元素都有一个序号,这个序号从 0 开始,而不是从我们熟悉的 1 开始,称为下标(Index)。使用数组元素时,指明下标即可,例如:在上述定义的数组中,只有a[9],并没有a[10]。数组名是首元素的地址。
int a[10]; // 整型 数组 a,有 10 个元素。
float b[10]; // 单精度浮点型 数组 b,有 10 个元素。
char c[20]; // 字符数组 ch,有 20 个元素 。
数组有三个特性:单一性、有序性和连续性。单一性是指在你定义的数组中,每个数组的字节长度是一致的。有序性是指每个数组元素都有顺序,例如a[0]在a[1]之前,a[1]在a[2]之前。连续性:是指当你定义了一个数组之后,数组在计算机内部的存储是连续的,中间并没有存放其他的数据。
注意事项:
在数组当中,例如:arr[10] 中只能用 arr[0]、arr[1]、arr[2]···arr[8]、arr[9],而不能使用 arr[10]。若你定义 arr[10] = 123,会导致出现下标越界的情况。但是此时编译器并不会出现报错的情况,因为编译器只会检查类型是否匹配,这就会导致计算机改写其他内存而造成重大错误。
数组不可以整体引用或赋值。在使用“=”进行赋值时,“=”左右两边的数据类型不匹配,此时会编译器报错,而且数组的数组名是作为数组首元素的地址,在计算机中是一个常量存在,不可更改。
错误示范:
int a[];
a = 10;
数组的初始化:
int arr[5] = {1,2,3,4,5}; // 完全初始化
int arr1[5] = {1,2,3}; // 不完全初始化,不完全初始化,剩下没有初始化的元素 默认为0
在定义中[ ]并不是下标,只是说明其是数组类型。
二、一维数组的基本使用
1.统计数组中某类元素个数
下面以某一维数组中能被3整除的变量统计做演示:
int a[] = {0,1,2,3,4,5,6,7,8,9};
int i, counter = 0;;
int len = sizeof(a) / sizeof(a[0]);
for(i = 0;i < len;++i)
{
if(a[i] % 3 == 0)
{
++counter;
// printf("%d\n", a[i]);
}
}
printf("%d\n", counter);
2、对数组求和
int a[] = {1,2,3,4,5,6,7,8,9,0}, i;
int sum = 0;
int len = sizeof(a) / sizeof(a[0]);
for(i = 0;i < len;++i)
{
sum += a[i];
}
printf("%d\n", sum);
3、寻找数组中的最大值(拓展次大值)
int a[] = {10,2,3,4,5,6,7,8,9,0};
int max, i;
int len = sizeof(a) / sizeof(a[0]);
max = a[0];
for(i = 1;i < len;++i)
{
if(max < a[i])
{
max = a[i];
}
}
printf("%d\n", max);
int second = INT_MIN;
for(i = 0;i < len;++i)
{
if(second < a[i] && a[i] < max)
{
second = a[i];
}
}
printf("%d\n", second);
注意:并不是每个数组中都存在最大值与次大值。
4、输出斐波那契数列
int a[10] = {1,1};
int len = sizeof(a) / sizeof(a[0]);
int i;
for(i = 2;i < len;++i)
{
a[i] = a[i - 1] + a[i - 2];
}
for(i = 0;i < len;++i)
{
printf("%d\n", a[i]);
}
在进行代码的测试时需要注意的是:在定义时,数组为整型,所以输出的数列长度不宜过大,否则会引起整型溢出。
5、逆序
int a[] = {1,2,3,4,5,6,7,8,9,0};
int len = sizeof(a) / sizeof(a[0]);
int i;
for(i = 0;i < len / 2;++i)
{
int t;
t = a[i];
a[i] = a[len - i - 1];
a[len - i - 1] = t;
}
for(i = 0;i < len;++i)
{
printf("%d\n", a[i]);
}
三、一维数组中的基本算法
1、选择排序(默认是升序)
int a[] = {1,2,3,4,5,6,7,8,9,0};
int len = sizeof(a) / sizeof(a[0]);
int i, j;
for(i = 0;i < len - 1;++i)
{
for(j = i + 1;j < len;++j)
{
if(a[i] > a[j])
{
int t;
t = a[i];
a[i] = a[j];
a[j] = t;
}
}
}
2、冒泡排序
冒泡排序:数组中相邻的两个元素两两比较,小的在前,大的在后。
int a[] = {1,2,3,4,5,6,7,8,9,0};
int len = sizeof(a) / sizeof(a[0]);
int i, j;
for(j = len - 1;j > 0;--j)
{
for(i = 0;i < j;++i)
{
if(a[i] > a[i + 1])
{
int t;
t = a[i];
a[i] = a[i + 1];
a[i + 1] = t;
}
}
}
3、插入法排序
int a[] = {1,-2,3,-4,5,-6,7,-8,9,0};
int len = sizeof(a) / sizeof(a[0]);
int i, j;
for(i = 1;i < len;++i)
{
int t = a[i];
j = i;
while(j > 0 && a[j - 1] > t)
{
a[j] = a[j - 1];
--j;
}
a[j] = t;
}
4、二分法查找
int a[] = {1,-2,3,-4,5,-6,7,-8,9,0};
int len = sizeof(a) / sizeof(a[0]);
int i, j;
for(i = 1;i < len;++i)
{
int t = a[i];
j = i;
while(j > 0 && a[j - 1] > t)
{
a[j] = a[j - 1];
--j;
}
a[j] = t;
}
int n = -8;
int begin = 0;
int end = len - 1;
int mid;
while(begin <= end)
{
mid = (begin + end) / 2;
if(a[mid] > n)
{
end = mid - 1;
}
else if(a[mid] < n)
{
begin = mid + 1;
}
else
{
break;
}
}
if(begin <= end)
{
printf("found! index = %d\n", mid);
}
else
{
printf("not found!\n");
}
二分法查找要建立在排序的基础之上,而且二分法找到的位置是有序序列位置
四、字符型数组
一、定义
定义一个字符型数组:一维数组:char 函数名[行 常量表达式] 例如:char c[3];
char c[3] = {a,b}
初始化过程中,没有被初始化的数组项编译器会给默认赋值’\0’;(整数数组没有被初始化的数组项被赋值为0)。例如:char c[3] = {a,b}; 中 c[3] = ‘\0’;
对于字符数组来说,要求常量表达式大于或等于数组内字符数;而对于字符串数组来说,常量表达式只能大于 字符串数组字符数,因为编译器会为每个字符串末尾赋值一个字符串结束标志‘\0’,所以在对字符串数组初始化时,我们必须预先为结束标志腾出一个字符空间。所以我们在定义字符数组时给的长度要足够大(>len),否则很容易出现越界访问的情况。
二、几个常用的函数
strlen(对象)函数
注意:这个函数并不像sizeof一样可以测量其他类型数据,只能用来测量字符串数组;
功能:用来测量字符串数组的有效长度(这里的长度指的是字节数);
char s1[100] = "Hello";
strlen(S1);
while(s[i] != '\0')
{
++i;
}
printf("%d\n", i);
程序运行的输出结果为5,而sizeof函数运行结果为6。
puts(对象)函数
puts函数是对某一字符串数组进行遍历输出。
char c[] = "hello";
puts(c);
char s[10] = "Hello";
int i = 0;
while(s[i])
{
putchar(s[i++]);
}
putchar('\n');
return 0;
上述的两端代码起到的输出是相同的。
gets(对象)函数
gets函数是对字符串数组进行整体的赋值。
char s[100];
int i = 0;
gets(s);
在进行对字符串的赋值时还可以使用scanf函数来进行,但是在使用这两种方法是要注意:它们二者并不会在意字符串数组的长度,只是进行简单的赋值操作,所以当字符串数组长度小于输入的长度时,会越界访问,造成严重的后果,所以我们可以使用fgets函数
fgets(s,sizeof(s),stdin);
strcpy(对象2,对象1)函数
此函数的作用是将字符串数数组1的值copy给字符串数组2。
char s1[] = "Hello";
char s2[100];
int i = 0;
strcpy(s2, s1);
while(s1[i] != '\0')
{
s2[i] = s1[i];
++i;
}
s2[i] = '\0';
这两端代码的效果是相同的。
strcat(对象1,对象2)函数
strcat函数的作用是将字符串数组2复制粘贴到字符串数组1的有效长度之后。
char s1[100] = "Hello";
char s2[100] = "World!";
strcat(s1, s2);
int i = 0;
while(s1[i] != '\0')
{
++i;
}
int j = 0;
while(s2[j] != '\0')
{
s1[i] = s2[j];
++i;
++j;
}
s1[i] = '\0';
两端代码运行的结果均为HelloWorld!
strcmp(对象1,对象2)函数
strcmp函数的作用是对字符串数组1与字符串数组2进行大小比较,函数的返回值就是函数运行的结果。
char s1[100] = "Hello";
char s2[100] = "He";
printf("%d\n", strcmp(s1, s2));
char s1[100] = "Hello";
char s2[100] = "He";
int i = 0;
while(s1[i] == s2[i] && s1[i] != '\0' && s2[i] != '\0')
{
++i;
}
printf("%d\n", s1[i] - s2[i]);
两段代码运行结果一致,运行结果为ACSII码相减后得到的值,结果为正说明字符串数组1大于字符串数组2,为负则反之。
注意事项:
1.上述函数须在头文件#include<string.h>下运行
2.在调用strcpy进行copy时不会考虑能否存放的下的问题,所以需要空间足够的大
3.在调用srtrcat函数时,需要对象1足够大,最小长度要大于strlen(s1 + s2) + 1。
函数使用练习:
最大值:
int main(void)
{
char s1[100] = "Hello World, china";
char s2[100] = "World";
char s3[100] = "china";
char max[100];
if(strcmp(s1, s2) > 0)
{
strcpy(max, s1);
}
else
{
strcpy(max, s2);
}
if(strcmp(max, s3) < 0)
{
strcpy(max, s3);
}
puts(max);
return 0;
}
字符串数组的逆序:
char s[100] = "Hello";
int len = strlen(s);
int i;
for(i = 0;i < len / 2;++i)
{
char t;
t = s[i];
s[i] = s[len - i - 1];
s[len - i - 1] = t;
}
puts(s);
五、二维数组
一、定义
二维数组的声明与一维数组相同,一般形式如下类型说明符 数组名 [常量表达式 1] [常量表达式 2]; 其中 "常量表达式1" 被称之为行下标,"常量表达式2" 被称之为是列下标。二维数组下标的取值范围,如下所示: 行下标的取值范围是:0~n-1。 列下标的取值范围是:0~m-1。 二维数组最大元素下标识:a[n-1][m-1]。
int a[3][3];
上述代码声明了一个 3 行 3 列的二维数组,其 数组名 是 a,其下标变量的类型为整形。
在二维数组中同样具有单一性、有序性、连续性3个特点。在 C语言中,二维数组是按行排列的,即按行顺序存放,先存放 a[0] 行,再存放 a[1] 行,接着存放 a[2] 行。每行有 3 个元素,也是其依次存放的。
二维数组元素的引用一般形式如下: 数组名[下标][下标]; 说明:二维数组的下标可以是 整形常量 或 整形表达式。 例如: a[3][4];表示 a 数组三行四列的元素。 下标变量和数组说明在形式中有些相似,但这两者具有完全不同的含义。数组说明的方括号中给出的是某一维的长度,即可取下标的最大值;而数组元素中的下标是该元素在数组中的位置标识。前者只能是常量,后者可以是常量,变量或表达式。 注意:不管是 行下标 还是 列下标,其中的索引值都是从 0 开始的。 需要注意:和一维数组是一样的,我们都需要注意下标越界的问题。
二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可按行分段赋值, 也可按行连续赋值。下面是一个带有 3 行 4 列的数组:
按分段初始化可写为:
int a[3][4] = {
{0, 1, 2, 3} , //0行
{4, 5, 6, 7} , //1行
{8, 9, 10, 11} //2行 };
也可以整体赋值:
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
起到的效果是一样的
在进行初始化时要注意:
对二维数组初始化还需注意几点 1.可以只对部分元素赋值,未赋值的元素自动取"0"值。例如:
int a[3][4] = {0,1,2,3,4,5}
在对数组全部元素进行数组的初始化时可以省略行数,但是不能省略列数。
int a[][4] = {1,2,3,4, 5,6,7,8, 9, 10,11,12, 13};
对二维数组的简单使用:求数组元素平均值。
int main(void)
{
int a[][4] = {1,2,3,4, 5,6,7,8, 9, 10,11,12, 13};
int i, j;
int rows = sizeof(a) / sizeof(a[0]);
int cols = sizeof(a[0]) / sizeof(a[0][0]);
int sum = 0;
for(i = 0;i < rows;++i)
{
for(j = 0;j < cols;++j)
{
sum += a[i][j];
}
}
printf("%f\n", sum / (double)(rows * cols));
本文中所有代码均是在Ubuntu18.04中编译运行