目录
– 定义数组
– 语法
– 本质和特征
– 数组的特点
– 数组的单元、下标
– 有效的下标范围
– 数组的集成初始化
– 定义
– 写法注意
– 集成初始化时的定位
– 数组操作
– 数组长度
– 数组赋值
– 数组的应用
– 搜索
– 素数判断
– 二维数组
– 内存中的排布
– 遍历
– 初始化
定义数组
语法
语法: <类型> 变量名称[元素数量]
+ 类型是指数组中每个元素的类型
+ 元素数量必须是整数
+ 元素数量应当在编译时就是确定的字面量, 在C99之后可以是变量
本质和特征
数组的本质是一个容器 (放东西的东西)
就现代编程语言而言, 评判其能力大小的一项标准, 就是该语言提供容器的能力
数组的特点
- 所有元素都具有相同的数据类型
- 数组的大小一旦创建就不得改变
- 在内存中, 数组中的元素是紧密、连续、依次排列的
数组的单元、下标
数组的每个单元就是数组类型的一个变量
使用数组时放在[]
中的数字称为 下标 or 索引
下标是从0
开始计数的
虽然数组并不是C语言发明的, 但下标从
0
开始计数是从C语言开始的
重要的原因是, C语言的编译器可以简化很多
有效的下标范围
编译器和运行环境不会检查数组的下标是否越界(无论读写)
可能会造成报错/警告: segmentation fault
int a[0]
长度为0
的数组可以存在, 但毫无用处, 因为不存在有效的下标范围
数组的集成初始化
定义
int a[] = {1,22,333}
这种写法被称为 数组的集成初始化
+ 直接用大括号给出数组中所有元素的初始值
+ 不需要给出数组的大小, 编译器会自动计数
写法注意
注意: 如果是这种写法
int a[5]={2,3};
其效果等同于
int a[]={2,3,0,0,0};
集成初始化时的定位
在C99中, 允许用[n]
在初始化数据中给出定位
没有定位的数据接在前面的位置后面
其他位置填补零
如果不给出数组大小, 编译器会把涉及到的最大的位置定为数组的长度
这种写法特变适合初始数据稀疏的数组
int a[8] = {
[0]=2, [2]=3, 6,
};
上面的写法等同于
int a[8] = {2,0,3,6,0,0,0,0};
int a[] = {[3] = 1, 2, 3, [5] = 6, 7, 8};
等同于 “`int a[] = {0, 0, 0, 1, 2, 6, 7, 8};
数组操作
数组长度
int a[]={1,2,3,};
int length_a = sizeof(a)/sizeof(a[0]);
这个小技巧对于所有数组都成立
有趣的是在定义数组时, 最后多出来的那个
,
这是为了后来人不用再打上逗号而准备的, 对程序来说毫无影响
这么做可以显得自己读过70/80年代的编程书
数组赋值
int a[]={1,2,3,};
int b[]=a;
这么做无法将数组a
赋值给数组b
唯一方法是遍历数组
数组的应用
搜索
在一个数组中找出要找的数字
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int search(int key, int a[], int length);
int main()
{
int a[] = {2, 4, 6, 82, 455, 1, 10, 54, 4, 3, 5, 17, 14, 32, 8};
int x;
int loc;
printf("请输入一个数字: ");
scanf("%d", &x);
loc = search(x, a, sizeof(a) / sizeof(a[0]));
if (loc != -1)
{
printf("%d在第%d个位置上\n", x, loc);
}
else
{
printf("%d不存在\n", x);
}
return 0;
}
int search(int key, int a[], int length)
{
int ret = -1;
for (int i = 0; i < length; i++)
{
if (a[i] == key)
{
ret = i;
break;
}
}
return ret;
}
这里注意: 数组作为函数的参数时, 往往要求用另一个参数来传入数组的大小
这是因为当数组作为参数时:
+ 不能再利用sizeof来计算数组的元素个数
+ 也就无法计算数组的大小
素数判断
如果使用素数去判断数字是否是素数, 时间复杂度最低
但仅仅在构造素数表的时候有用
以下代码用于找出前100个素数
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int isPrime(int x, int knownPrimes[], int length);
int main()
{
const int number = 100;
int prime[number];
prime[0] = 2;
int count = 1;
int i = 3;
while (count < number)
{
if (isPrime(i, prime, count))
{
prime[count++] = i;
}
i++;
}
for (i = 0; i < number; i++)
{
printf("%d", prime[i]);
if ((i + 1) % 5)
printf("\t");
else
printf("\n");
}
return 0;
}
int isPrime(int x, int knownPrimes[], int length)
{
int ret = 1;
for (int i = 0; i < length; i++)
{
if (x % knownPrimes[i] == 0)
{
ret = 0;
break;
}
}
return ret;
}
如果说想构造n
以内的素数表, 可以采用挖空法
即, 挖掉所有倍数
以下求出25以内的素数
int main()
{
const int number = 25;
int isPrime[number];
for (int i = 0; i < number; i++)
{
isPrime[i] = 1;
}
for (int x = 2; x < number; x++)
{
if (isPrime[x])
{
for (int i = 2; i * x < number; i++)
{
isPrime[i * x] = 0;
}
}
}
for (int i = 2; i < number; i++)
{
if (isPrime[i])
{
printf("%d\t", i);
}
}
printf("\n");
return 0;
}
要多多考虑时间复杂度, 有时候不能被人的思维所局限
二维数组
int a[3][5]
通常被理解为一个3行5列的矩阵
内存中的排布
| | | | | |
| :—–: | :—–: | :—–: | :—–: | :—–: |
| a[0][0] | a[0][1] | a[0][2] | a[0][3] | a[0][4] |
| a[1][0] | a[1][1] | a[1][2] | a[1][3] | a[1][4] |
| a[2][0] | a[2][1] | a[2][2] | a[2][3] | a[2][4] |
遍历
for (i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)
{
a[i][j] = i * j;
}
}
平时数学上的 $a[i,j]$ 表示坐标系中的一个点
但在C语言中, 由于,
是一个运算符
a[i,j]
和a[j]
是等价的, 这显然不是正确的表达二维数组的方式
初始化
int a[][5] = {
{0, 1, 2, 3, 4},
{2, 3, 4, 5, 6},
};
列数必须给出, 行数可以省略
其他一些细节和一位数组一致
其实可以用一长串数字初始化
因为这与内存中的结构实质上一致
为了人类读者阅读方便, 还是用{}
来写较好