目录
1. 数组的定义和初始化
- 数组的作用
- 保存大量同类型的相关数据
- 数组的定义
- int a[10]
- 数组名代表首地址
- 定义一个有10个int类型元素的一维数组
- 在内存中分配连续的存储空间给此数组
- 为什么数组下标从0开始
- 使编译器的实现简化一点,且下标的运算速度少量提高
-
数组的数据类型——每一元素占内存空间的字节数
- 数组的存储类型——内存的动态、静态存储区或CPU的寄存器
- int a[10]
- 一维数组的初始化
- 静态数组和全局数组自动初始化为0值,否则,是随机数
-
赋值初始化
-
int a[5] = {62, 74, 56, 88, 90};
-
-
更高效的数组初始化方法
- memset(a, 0, sizeof(a));
-
用sizeof(a)来获得数组a所占的内存字节数
-
#include <string.h>
- 一维数组元素的访问
- 数组名[下标]
- 引用时下标允许是int型变量或表达式
-
一维数组元素的赋值
-
通过循环语句遍历赋值
-
memcpy(b, a, sizeof(a));
- 数组a复制给数组b
-
#include <string.h>
-
数组的逻辑存储结构
- 多维数组存放顺序:按行存放,线性存储
-
已知每行的列数才能正确读出数组元素
-
二维数组的定义和初始化
- 讨论
- memcpy(b, a, sizeof(a));
-
使用这条语句时,如果数组a和b的长度不一样,那么会导致什么结果,是否存在安全隐患?
2. 数组元素的访问与螺旋矩阵
- 螺旋矩阵代码
#include<stdio.h>
#define N 10
void PrintArray(int a[][N], int m, int n);
void SetArray(int a[][N], int len, int n);
int main()
{
int a[N][N], n;
printf("Input n:");
scanf("%d", &n);
SetArray(a, 1, n);
PrintArray(a, n, n);
return 0;
}
//迭代算法
void SetArray(int a[][N], int len, int n)
{
//level为走过的圈数
int m, k, level;
level = n>0 ? (n+1)/2 : -1;
printf("%d\n", level);
for (m=0; m<level; m++)
{
//top
for(k=m; k<n-m; k++)
a[m][k] = len++;
//right
for(k=m+1; k<n-m-1; k++)
a[k][n-m-1] = len++;
//bottom
for(k=n-m-1; k>m; k--)
a[n-m-1][k] = len++;
//left
for(k=n-m-1; k>m; k--)
a[k][m] = len++;
}
}
//递归算法
void SetArray(int a[][N], int len, int start, int border)
{
int k;
if (start > border) return;
else if (start == border)
{
a[start][start] = len;
return ;
}
else
{
//top
for(k=start;k<border;k++)
a[start][k]=len++;
//right
for(k=start;k<border;k++)
a[k][border]=len++;
//bottom
for(k=border;k>start;k--)
a[border][k]=len++;
//left
for(k=border;k>start;k--)
a[k][start]=len++;
SetArray(a, len, start+1, border-1);
}
}
void PrintArray(int a[][N], int m, int n)
{
int i, j;
for (i=0; i<m; i++)
{
for (j=0; j<n; j++)
{
printf("%d\t", a[i][j]);
}
printf("\n");
}
}
3. 向函数传递数组
-
简单变量和数组作函数参数的区别
- 键盘输入数组
-
计数控制—键盘输入数组元素个数
for(i=0; i<n; i++) { scanf("%d", &score[i]); }
-
标记控制—负值作为输入结束标记
do{ i++; printf("Input Score:"); scanf("%d", &score[i]); }while(score[i]>=0);
-
- 向函数传递一维数组
-
int Average(int score[], int n);
-
通常不指定数组的长度,用另一个形参来指定数组的大小
-
- 向函数传递二维数组
-
void Average(int score[][COURSE_N], float aver[], int n);
-
向函数传递二维数组的首地址
-
在声明函数的二维数组 形参 时,不能省略数组第二维的长度
- 通常不指定数组的长度,用另一个形参n来指定二维数组的行数
- 元素地址:首地址+偏移量
-
a[i][j]在数组中相对于第一个元素的偏移位置:i * 3 + j
-
-
4. 函数实现
4.1 计算最大值的函数实现
-
用一个函数能同时返回 最大值及其所在数组的下标两个 值吗?
-
可以,可以用形参进行返回。
-
4.2 查找算法的函数实现
- 线性查找
- 平均情况:查找次数是数据量的一半
- 二分查找
-
要求数据表是已排好序的
-
先将表的中间位置记录的关键字与查找关键字比较
-
mid = (high + low) / 2
- 如果数组很大,low和high之和大于有符号整数的极限值INT_MAX(在limits.h中定义)。就会发生数值溢出,使mid成为一个负数
-
防止溢出的解决方案:mid = low + (high - low) / 2;(修改计算中间值的方法,用减法代替加法)
-
最多所需的比较次数是第一个大于表中元素个数的2的幂次数
- 14个数,最多比较的次数是4
- 缺点
- 要求待查表按关键字有序排列,否则需要先进行排序操作
-
必须采用顺序存储结构,插入和删除数据需移动大量的数据
-
把逻辑上相邻的结点存储在物理位置上相邻的存储单元中,结点之间的逻辑关系由存储单元的邻接关系来体现
-
-
适用于不经常变动而查找频繁的有序表
-
4.3 排序算法的函数实现
- 交换法排序
- 每次将待排序中第一个数放在其最终的位置上
- 选择法排序
-
在每一遍比较中,在剩余的待比较的数中选择一个最小的数与这个剩余序列的第1个数交换位置
-
-
冒泡算法
-
比较相邻的两个数据,若顺序不对,则将其位置交换
-
#include<stdio.h>
void ChangeSort(int score[], int n);
void Display(int score[], int n);
void SelectionSort(int score[], int n);
void BubbleSort(int score[], int n);
int main()
{
int score1[5] = {84, 83, 88, 87, 61};
// ChangeSort(score1, 5);
// SelectionSort(score1, 5);
BubbleSort(score1, 5);
Display(score1, 5);
}
void Display(int score[], int n)
{
int i;
for(i=0; i<n; i++)
printf("%d ", score[i]);
printf("\n");
}
//交换法排序
void ChangeSort(int score[], int n)
{
int i, j, temp;
for(i=0; i<n-1; i++)
{
for(j=i+1; j<n; j++)
{
if(score[i]>score[j])
{
temp = score[i];
score[i] = score[j];
score[j] = temp;
}
}
}
}
//选择法排序
void SelectionSort(int score[], int n)
{
int i, j, k, temp;
for(i=0; i<n-1; i++)
{
k = i;
for(j=i+1; j<n; j++)
{
if(score[j] < score[k])
{
k = j;
}
if(k != i){
temp = score[i];
score[i] = score[k];
score[k] = temp;
}
}
}
}
//冒泡法排序
void BubbleSort(int score[], int n)
{
int i, j, temp;
for(i=0; i<n-1; i++)
{
for(j=0; j<n-1-i; j++)
{
if(score[j]>score[j+1])
{
temp = score[j];
score[j] = score[j+1];
score[j+1] = temp;
}
}
}
}
5. 一维数组下标越界问题分析
-
访问数组元素时,下标越界是大忌
-
编译器通常不检查下标越界,导致程序运行时错误
-
下标越界,将访问数组以外的空间,可能带来严重后果
-
- 对于数组定义 int a[2][3],虽然a[0][3]和a[1][0]指的是同一元素,但隐患严重
-
使用数组的基本原则
-
永远清楚每个数组有多大,永远不要让下标越界
-
字符数组永远留意'\0'
-
筛选求素数
#include<stdio.h>
#include<math.h>
#define N 100
void SiftPrime(int a[], int n);
void PrintPrime(int a[], int n);
int main()
{
int a[N+1];
SiftPrime(a, N);
PrintPrime(a, N);
}
void SiftPrime(int a[], int n)
{
int i, j;
for(i=2; i<=n; i++)
{
a[i] = i;
}
for(i=2; i<=sqrt(n); i++)
{
for(j=i+1; j<=n; j++)
{
if(a[i]!=0 && a[j]!=0 && a[j]%a[i]==0)
{
a[j] = 0;
}
}
}
}
void PrintPrime(int a[], int n)
{
int i;
for(i=2; i<=n; i++)
{
if(a[i] != 0)
{
printf("%d\t", a[i]);
}
}
// printf("\n");
}
6. 小结
- 不能使用数组名对数组元素进行初始化
- 不能使用变量定义数组的长度
- 形参、实参的数量和返回值不一致导致编译错误
- 形参、实参的类型和顺序不一致导致运行时错误
- 函数传递多维函数时,需要定义第一维以外的后面所有维的长度声明
- 在函数中定义的静态局部数组元素不会被初始化为零