1. 数组的基本用法
1.1 概念
数组(array)是有序的元素序列
若将有限个类型相同的变量的集合命名,那么这个名称为数组名。
重点必须类型相同和连续的变量的集合
1.2 格式
存储类型 数据类型 数组名[元素个数];
例如:(auto) int a[5]; // 这个auto的其中一种存储类型,通常省略不写,后面会学到
数组名:代表数组的首地址(即第一个元素的地址),a是地址常量,不能为左值,不能被赋值。
数组的定义方法:
1. 数组名定义规则跟变量名相同,遵循标识符定义规则
2. 数组名后使用方括号括起来的常量表达式表示元素个数
3. 常量表达式中可以包含常量和宏定义,不能包含变量
1.3 访问元素
数组名[下标]; // 这里需要注意的是下标从0开始
例如:访问第一个元素就是:a[0]
注:int a[5]; int b = a[5];
//这里的a[5]就属于数组越界错误,因为数组是从0开始的,数组下标只有{0,1,2,3,4}
数组的特点:数据类型相同 内存连续
总结注意:
1. 数组的数据类型就是元素的数据类型
2. 数组名要符合标识符命名规则
3. 在同一个函数中,数组名不要与变量名相同
int a;
int a[5]; //错误
4. 下标从0开始,到n-1结束
2. 一维数组
2.1 一维数组概念
只有一个下标的数组
格式: 存储类型 数据类型 数组名[元素个数];
访问元素: 数组名[下标], 下标从0开始
数组名: 首地址,也就是第一个元素的地址(等价于&a[0])
2.2 初始化
1. 定义时全部初始化
int a[5] = {1, 2, 3, 4, 5};
2. 定义时部分初始化,未初始化的元素值为0
int a[5] = {1, 2}; // 只初始化前两个元素,后面的未初始化元素为0
3. 未初始化:只定义未赋值,需要单个元素赋值。(不然局部变量是随机值)
int a[5];
a[0] = 1;
a[1] = 2;
a[2] = 3;
a[3] = 4;
a[4] = 5;
注意定义时不能数组越界!!!
int a[3]={1,2,3,4}; //错误,数组越界!!!
2.3 定义空数组
int a[5]={0,0,0,0,0};
int a[5]={0};
int a[5]={};
2.4 数组引用
例如:
#include <stdio.h>
int main(int argc[], char const *argv[])
{
int a[5] = {1, 2, 3, 4, 5};
for(int i = 0; i < 5; i++)
printf("%d\\n",a[i]);
printf("%p %p\\n", a, &a[0]);
// 这里的%p是为了验证看一下数组名==首元素地址
return 0;
}
2.5 数组大小
例如:
int a[5]; //20
double b[2]; //16
char c[32]; //32
计算方式:
1. 数组元素个数 * 数据类型大小
// 数组元素个数 = sizeof(数组名) / sizeof(数据类型)
2. sizeof(数组名)
可以用下面的程序自己运行试一下:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[5];
double b[2];
char c[32];
printf("%ld\\n", sizeof(a)); // 20
printf("%ld\\n", sizeof(b)); // 16
printf("%ld\\n", sizeof(c)); // 32
int n = sizeof(a) / sizeof(int);
printf("%d\\n", n); // 5
return 0;
}
这个地方也有一个非常经典的题目:
计算斐波那契数列前15项并逆序输出 // 1 1 2 3 5 8 13 21…
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[15] = {1, 1}; // 赋值前两个元素
for (int i = 2; i < 15; i++) // 从第三个元素到最后一个元素遍历赋值
a[i] = a[i – 1] + a[i – 2];
for (int i = 14; i >= 0; i–) // 逆序打印
printf("%d ", a[i]);
printf("\\n");
return 0;
}
2.6 清零函数
2.6.1 bzero()函数
#include <strings.h>
void bzero(void *s, size_t n);
功能:将内存空间设置为0
参数:
s:要清空的空间首地址
n:字节大小
返回值:无
2.6.2 memset()函数
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:将指定内存空间内容设置为0
参数:s:指定内存首地址
c:要设置的数,一般为0
n:要设置的内存大小
返回值:指定内存首地址(一般不用)
这两个函数暂时用不着但是也很重要在后面学习单片机的时候
2.7 数组的内存分配
int a[5];
a是数组名,是数组的首地址,也就是第一个元素的地址即&a[0]
a+1是第二个元素的地址,即&a[1]
注意:这里的地址+1表示下一个元素的地址,而不是数值上+1
| int a[5]; | 地址 | 地址 |
| a[0] | &a[0] | a |
| a[1] | &a[1] | a+1 |
| a[2] | &a[2] | a+2 |
| a[3] | &a[3] | a+3 |
| a[4] | &a[4] | a+4 |
可以用下面的代码看一下两者表示的地址是一样的
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[5];
for(int i = 0; i < 5; i++)
printf("%p %p\\n", &a[i], a + i);
return 0;
}
3. 字符数组
因为C语言没有字符串类型,所以可以用字符数组的形式表示字符串。
类型为字符的数组就是字符数组,也就是说数组中每个元素是字符类型,可以表示字符串。
3.1 定义时初始化表示形式
1. 单个字符:
char s1[5] = {'h','e','l','l','o'}; //sizeof(s1)=5
char s2[] = {'h','e','l','l','o'}; //sizeof(s2)=5
2. 用字符串表示,结尾会自动加上'\\0'
char s1[]={"hello"}; //sizeof(s1)=6 因为结尾会自动添加'\\0'
char s2[]="hello"; //sizeof(s2)=6
char s3[6]={"hello"}; //sizeof(s3)=6
char s4[6]="hello"; //sizeof(s4)=6
char s5[]={}; //错误
3.2 输入输出
1. 用 scanf / printf
char s[32] = "hello";
printf("%s\\n", s); // 打印字符串
scanf("%s", s); // 输入字符串,输入完会自动添加'\\0'作为字符串结尾的标志,
// 但是该写法不能接受空格的输入(空格、回车、Tab都会当成输入标志)
printf("%s\\n", s); // 打印到\\0就停了
/*验证*/
for (int i = 0; i < 32; i++)
printf("%d: %c\\n", i, s[i]);
由于scanf("%s\\n", s);不能输入空格,如果有空格,空格后面的内容就不会存入到数组
如果需要有空格
scanf("%[^\\n]",str);
//正则表达式,可以输入除了\\n以外的字符,遇到\\n结束
2. 用for循环遍历输入输出
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[6] = "";
for (int i = 0; i < 6; i++)
scanf("%c", &str[i]);
for (int i = 0; i < 6; i++)
printf("%c", str[i]);
return 0;
}
例题:输入一个字符串,计算其空格个数
#include <stdio.h>
int main(int argc, char const *argv[])
{
int n = 0;
char s[32] = "";
scanf("%[^\\n]", s);
// for (int i = 0; i < 32; i++)
for (int i = 0; s[i] != '\\0'; i++)
{
if (s[i] == ' ')
n++;
}
printf("%d\\n", n);
return 0;
}
易错点:
char s[10] = {}; //对的
s[10] = "hello"; //错误,不能将字符串赋值给数组元素
//做题非常易错
s = "hello"; //错误,s是地址常量,不能为左值
strcpy(s, "hello"); //正确,这是一个函数可以了解一下
3.3 计算字符串长度
3.3.1 用循环统计
这个时候用while是更加方便的
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[32] = "hello world";
int i = 0;
while (str[i] != '\\0')
{
i++;
}
printf("%d\\n", i);
return 0;
}
3.3.2 用strlen()
#include <string.h>
size_t strlen(const char *s);
功能:计算字符串长度
参数:字符串的首地址
返回值:返回字符串实际长度,不包括‘\\0’在内。
3.3.3 sizeof 和 strlen 的区别
4. 排序(重中之重!!!)
4.1 冒泡排序
排序过程:
外层循环轮数i=个数-1 = N-1(0到<N-1)
内存循环比较次数j = 个数-1-轮数(从0开始) = N-1-i
#include <stdio.h>
#define N 5
int main(int argc, char const *argv[])
{
int a[N] = {5, 4, 3, 2, 1}, t;
for (int i = 0; i < N – 1; i++) // 外层循环轮数
{
for (int j = 0; j < N – 1 – i; j++) // 内层循环比较次数
{
if (a[j] > a[j + 1]) // 比较
{
// 交换
t = a[j];
a[j] = a[j + 1];
a[j + 1] = t;
}
}
}
for (int i = 0; i < N; i++)
printf("%d ", a[i]);
printf("\\n");
return 0;
}
4.2 选择排序
排序过程:

轮数=个数-1=N-1(同冒泡)
每轮比较的j: 从i+1开始到最后一个元素N-1
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[5] = {76, 56, 1, 45, 10};
int min = 0, t;
for (int i = 0; i < 4; i++) // 外层循环轮数(同冒泡)
{
min = i; // 拿着下标为i的元素开始比较
for (int j = i + 1; j < 5; j++) // 从i的后一个元素开始一直比较到最后一个元素
{
if (a[j] < a[min]) // 如果有更小的数
min = j;
} // 循环结束后最小元素的下标就被min保存了
// 交换最小的元素和前面的元素
if (a[min] != a[i])
{
t = a[min];
a[min] = a[i];
a[i] = t;
}
}
for (int i = 0; i < 5; i++)
printf("%d ", a[i]);
printf("\\n");
return 0;
}
5. 二维数组
二维数组下标为两个的数组,下标分别是行下标和列下标。
5.1 定义格式
存储类型 数据类型 数组名[行数][列数];
5.2 访问元素
数组名[行下标][列下标]; (下标都从0开始)
a[0][0]; //第一行第一列
a[n-1][m-1]; //第n行第m列元素
注意:
5.3 二维数组大小
元素个数 = 行数 * 列数
二维数组大小:
5.4 初始化二维数组
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,2},{4,5}}; //按行赋值
int a[2][3] = {1,2,3,4,}; //顺序赋值
5.5 遍历二维数组
循环嵌套来遍历二维数组,外层循环行数,内层循环列数。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[2][3] = {{1, 2}, {4, 5}};
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
printf("%d ", a[i][j]);
printf("\\n");
}
return 0;
}
5.6 内存分配
| int[2][3]为例 | |||
| 行地址 | 列地址 | ||
| a[0][0] | &a[0][0] | *a | a[0] |
| a[0][1] | &a[0][1] | *a+1 | a[0]+1 |
| a[0][2] | &a[0][2] | *a+2 | a[0]+2 |
| a[1][0] | &a[1][0] | *(a+1) | a[1] |
| a[1][1] | &a[1][1] | *(a+1)+1 | a[1]+1 |
| a[1][2] | &a[1][2] | *(a+1)+2 | a[1]+2 |
| a[i][j] | &a[i][j] | *(a+i)+j | a[i]+j |
| 行地址前面加*代表降级为列地址(该行第一列的地址) | |||
| 数组名[行下标]代表这一行第列的地址 | |||
1. 行地址
二维数组中,数组名a表示第一行的地址,其数值和第一行第一列的地址即&a[0][0]一样,但是含义不一样。
a+1表示第二行的地址,其数值和第二行第一列的地址即&a[1][0]一样,但是含义不一样。
2. 列地址
在二维数组中a[0]表示第一行第一列的地址,即等价于&a[0][0],或者在数组名前面加*表示降级为列地址,也就是*a表示第一行第一列的地址,那么第一行第二列的地址就是*a+1
可以记住以下三个列地址的方法:&a[i][j]、a[i]+j、*(a+i)+j
网硕互联帮助中心



评论前必须登录!
注册