数组
对应教学日历:讲次 11-16(4月23日-5月14日)
一、数组的概念
数组:一组相同类型数据的集合,在内存中连续存放。
- 数组名代表首元素地址(即
a等价于&a[0]) - 下标从 0 开始
- C 语言不检查数组下标越界,程序员需要自己保证
二、一维数组
定义
类型 数组名[常量表达式];int a[10]; // 定义 10 个 int 的数组,下标 0~9
float b[5]; // 定义 5 个 float 的数组
char c[26]; // 定义 26 个 char 的数组常量表达式中不能含变量(C99 之前),如
int a[n];是错误的(n 为变量)。
初始化
int a[5] = {1, 2, 3, 4, 5}; // 全部初始化
int b[5] = {1, 2}; // 部分初始化,剩余为 0
int c[] = {1, 2, 3}; // 不指定长度,自动定为 3
int d[5] = {0}; // 全部初始化为 0注意:
int a[5] = {0};是将全部元素初始化为 0 的简洁写法。
引用
a[0] = 10; // 给第一个元素赋值
scanf("%d", &a[i]); // 读入第 i 个元素,注意要加 &
printf("%d", a[i]); // 输出第 i 个元素,不加 &遍历
for (i = 0; i < n; i++)
printf("%d ", a[i]);三、一维数组常见操作
1. 排序
冒泡排序(Bubble Sort)
// 从小到大
for (i = 0; i < n-1; i++)
for (j = 0; j < n-1-i; j++)
if (a[j] > a[j+1])
{
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}- 每轮将最大值”冒泡”到末尾
- 外层循环 n-1 轮
- 内层
n-1-i:每次比较的终点递减
选择排序(Selection Sort)
// 从小到大
for (i = 0; i < n-1; i++)
{
min = i;
for (j = i+1; j < n; j++)
if (a[j] < a[min]) min = j;
temp = a[i]; a[i] = a[min]; a[min] = temp;
}2. 查找
顺序查找(线性查找)
for (i = 0; i < n; i++)
if (a[i] == target)
{
printf("找到,位置:%d\n", i);
break;
}
if (i == n) printf("未找到\n");二分查找(折半查找,要求数组有序)
int low = 0, high = n-1, mid;
while (low <= high)
{
mid = (low + high) / 2;
if (a[mid] == target) { printf("找到\n"); break; }
else if (a[mid] < target) low = mid + 1;
else high = mid - 1;
}3. 插入与删除
- 插入:从末尾开始向后移动元素,腾出位置
- 删除:从删除位置开始向前移动覆盖
四、二维数组
定义
类型 数组名[行数][列数];
int a[3][4]; // 3 行 4 列,元素总数 = 3×4 = 12
float b[5][10];初始化
int a[2][3] = {{1,2,3}, {4,5,6}}; // 按行初始化
int a[2][3] = {1,2,3,4,5,6}; // 按存储顺序
int a[2][3] = {{1}, {4}}; // 部分初始化,未指定的为 0
int a[][3] = {1,2,3,4,5,6}; // 只能省略行数,不能省略列数重要:二维数组定义时可以省略行数,不能省略列数。
遍历
for (i = 0; i < row; i++)
for (j = 0; j < col; j++)
printf("%d ", a[i][j]);矩阵常见运算
// 对称矩阵判断
for (i=0; i<n; i++)
for (j=0; j<n; j++)
if (a[i][j] != a[j][i])
{ isSym = 0; break; }
// 矩阵转置
for (i=0; i<row; i++)
for (j=0; j<col; j++)
b[j][i] = a[i][j];五、字符数组与字符串
字符数组
char s[10]; // 字符数组
char s[10] = {'H','e','l','l','o'}; // 不是字符串(没有 \0)字符串
C 语言中字符串以 空字符
'\0'结尾。定义时要多留一个位置给\0。
char s[10] = "Hello"; // 等价于 {'H','e','l','l','o','\0', 0,0,0,0}
char s[] = "Hello"; // 自动分配 6 个空间(5个字符 + 1个\0)字符串与字符数组的区别
| 字符数组 | 字符串 | |
|---|---|---|
| 结尾 | 不一定要 \0 | 必须 \0 |
| 输出 | 逐个字符 | printf("%s", str) 或 puts(str) |
| 长度 | sizeof(str) | strlen(str)(不含 \0) |
常用字符串函数(<string.h>)
| 函数 | 格式 | 作用 |
|---|---|---|
strlen(s) | len = strlen(s); | 返回字符串长度(不含 \0) |
strcpy(dst, src) | strcpy(a, b); | 将 b 复制到 a(含 \0) |
strncpy(dst, src, n) | strncpy(a, b, 5); | 最多复制 n 个字符 |
strcat(dst, src) | strcat(a, b); | 将 b 连接到 a 末尾 |
strcmp(a, b) | strcmp(a, b) | 比较大小:0 相等,>0 a 大,<0 b 大 |
strchr(s, ch) | strchr(s, 'a') | 查找字符首次出现位置 |
strstr(a, b) | strstr(a, b) | 查找子串 |
strcmp 返回值记忆:a 大于 b → 正数;a 小于 b → 负数;相等 → 0。
字符串输入
gets(str); // 读入一行(含空格),遇换行结束,不安全
fgets(str, 100, stdin); // 安全的带空格输入
scanf("%s", str); // 遇空格/换行结束数字字符串转整数
// 手工转换(atoi 的原理)
int n = 0, i = 0, sign = 1;
if (s[0] == '-') { sign = -1; i++; }
while (s[i] >= '0' && s[i] <= '9')
{
n = n * 10 + (s[i] - '0');
i++;
}
n = n * sign;作业示例
ex9-1:一维数组——跳水评分(去掉最高最低)
#include <stdio.h>
int main()
{
double scores[7];
double difficulty;
double sum = 0, max, min;
double result;
int i;
printf("请输入7个裁判的打分:\n");
for (i = 0; i < 7; i++) {
scanf("%lf", &scores[i]);
}
printf("请输入动作的难度系数:\n");
scanf("%lf", &difficulty);
max = scores[0];
min = scores[0];
for (i = 0; i < 7; i++) {
sum += scores[i];
if (scores[i] > max) max = scores[i];
if (scores[i] < min) min = scores[i];
}
sum = sum - max - min; // 去掉最高最低分
result = sum * difficulty / 5 * 3;
printf("该动作实得分为: %lf\n", result);
return 0;
}要点:
- 一维数组的遍历和元素访问
- 同时求最大值和最小值
- 先给
max/min赋第一个元素的值作为初值
ex9-3:数组筛选——埃拉托色尼筛法求素数
#include<stdio.h>
int main()
{
int a[101],i,j,n=0;
for(i=2;i<=100;i++)
a[i]=i;
for(i=2;i<=100;i++)
for(j=i-1;j>1;j--)
if(a[i]%j==0)
a[i]=0; // 标记非素数
for(i=1;i<=100;i++)
if(a[i]!=0)
{
printf("%5d",a[i]);
n++;
if(n%5==0) printf("\n");
}
return 0;
}要点:
- 利用数组下标直接作为数值
- 将非素数标记为 0(筛除)
- 最后输出非 0 元素
ex9-4:字符串转整数(手工 atoi)
#include<stdio.h>
int main()
{
char s[10];
int n=0;
int i=0;
printf("Enter a string:\n");
gets(s);
if(s[0]=='-') i++; // 处理负号
while(s[i] >= '0' && s[i] <= '9')
{
n=n*10+(s[i] - '0'); // 关键公式
i++;
}
if(s[0]=='-') n=-n;
printf("%1d\n",n);
return 0;
}要点:
s[i] - '0'将数字字符转为数值n = n*10 + digit逐位移入- 负数处理:跳过首字符
-,最后取反
ex10-1:二维数组——对称矩阵判断
#include<stdio.h>
int main(){
int jieguo=1,a[5][5],i,j;
printf("请输入二维数组的数据:\n");
for(i = 0; i < 5; i++)
for(j = 0; j < 5; j++)
scanf("%d", &a[i][j]);
for(i = 0; i < 5; i++)
{
for(j = 0; j < 5; j++)
{
if(a[i][j] != a[j][i])
{
jieguo = 0;
break;
}
}
if(jieguo == 0) break; // 提前退出
}
if(jieguo == 1)
printf("该矩阵是对称矩阵!\n");
else
printf("该矩阵不是对称矩阵!\n");
return 0;
}要点:
- 对称矩阵的定义:
a[i][j] == a[j][i] - 使用标志变量
jieguo(非 0 表示是对称的) - 发现不对称后立即
break跳出
ex10-2:循环矩阵生成
#include <stdio.h>
int main()
{
int n;
int a[100][100];
int i, j;
scanf("%d", &n);
for(j = 0; j < n; j++)
scanf("%d", &a[0][j]); // 输入第一行
for(i = 1; i < n; i++)
for(j = 0; j < n; j++)
a[i][j] = a[i-1][(j+1) % n]; // 每一行 = 上一行循环左移 1 位
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
printf("%d ", a[i][j]);
printf("\n");
}
return 0;
}要点:
- 二维数组作为矩阵
(j+1) % n实现循环索引(模运算)- 先用第一行推导后续行
ex10-3:字符串加密(凯撒密码变体)
#include<stdio.h>
#include<string.h>
int main()
{
char a[80],b[80];
int k=0,i;
gets(a);
k=strlen(a);
for(i=0;i<k;i++)
if(a[i]>='A'&&a[i]<='Z')
b[i]=(a[i]+3)>'Z'?a[i]+3-26:a[i]+3; // 大写右移3
else if(a[i]>='a'&&a[i]<='z')
b[i]=(a[i]-3)<'a'?a[i]-3+26:a[i]-3; // 小写左移3
else b[i]=a[i];
b[i]='\0'; // 字符串结束符不能忘!
puts(b);
return 0;
}要点:
- 凯撒加密:大写字母后移 3,小写字母前移 3
- 越界处理:
> 'Z'则回绕(-26) - 非字母字符原样保留
- 最后必须加
\0
ex10-4:字符串比较函数(模拟 strcmp)
#include<stdio.h>
#include<string.h>
int main()
{
char a[80],b[80];
int i,j,k,m;
gets(a); gets(b);
j=strlen(a), k=strlen(b);
if(j>=k) m=j; else m=k;
for(i=0;i<m;i++)
if(a[i]!=b[i]){
printf("第%d个字符不相等,ASCII码相差:%d",i+1,a[i]-b[i]);
return 0;
}
if(i==m)
printf("两个字符串相等");
return 0;
}要点:
- 逐字符比较 ASCII 码
- 比较次数取两串长度的较大值
a[i]-b[i]得到 ASCII 差值
ex10-5:字符串统计——元音字母计数
#include<stdio.h>
#include<string.h>
int main()
{
char a[1000];
int i,k,am=0,em=0,im=0,om=0,um=0;
gets(a);
k=strlen(a);
for(i=0;i<k;i++){
if(a[i]=='a'||a[i]=='A') am++;
if(a[i]=='e'||a[i]=='E') em++;
if(a[i]=='i'||a[i]=='I') im++;
if(a[i]=='o'||a[i]=='O') om++;
if(a[i]=='u'||a[i]=='U') um++;
}
printf("%d %d %d %d %d",am,em,im,om,um);
return 0;
}要点:
- 逐字符检查,大小写均需统计
- 多个独立计数器同时工作
||连接大小写判断
作业重点
- 数组下标从 0 开始,定义长度必须为常量表达式
- 数组名即首元素地址(
a与&a[0]等价) - 字符串以
\0结尾,定义时留一个空间给\0 strlen()不含\0,sizeof()含\0- 二维数组初始化可省略行数不可省略列数
- 排序(冒泡、选择)、查找(顺序、二分)必须手写
scanf("%s")遇空格停止,gets可读带空格的字符串- 用
gets读取后手动处理的字符串最后要加\0