第二章 数组
2.1 数组的概念
- 数组是一种数据结构;
- 可以存储多个数据;
- 每个数据的类型相同;
- 每个数据是一个变量;
- 变量存放在一块连续的内存中。
2.2 数组的定义及初始化
2.2.1 数组的定义
- 一维数组
定义格式:数据类型 数组名[数组元素个数]。
int array01[10]; // 定义了一个存放 10 个 int 类型变量的数组 array01
short array02[20]; // 同一作用域不允许出现相同数组名
array02 = 5; // 错误, 数组名是常量, 不能直接赋值
/*
定义的时候, [] 内的是数组元素个数, 使用的时候, [] 内的是下标
数组下标是从 0 开始计算, 如果要输出数组第五个元素的值, 只需要 printf("%d\n", a[4]);
*/
- 二维数组
定义格式:数组类型 数组名[行的个数][列的个数]
int array01[5][4]; // 定义了一个行为 5, 列为 4, 存放 int 类型变量的数组 array01
int array02[][3];
/*
二维数组只是逻辑层面的二维, 在物理层面上依然是一维;
二维数组在定义的时候可以不给出行数, 但必须给出列数;
二维数组的大小根据初始化的行数来定。
*/
2.2.2 数组分类
- 按元素分类
字符数组,短整型数组,整形数组,长整型数组,浮点型数组,指针数组,结构体数组。
int *a[10]; // 每个元素存的是 int * 类型的变量
typedef struct stu STU; // 为 struct stu 定义一个别名 STU
STU b[10]; // 每个元素存的是 struct stu 类型的变量
- 按纬度分类
一维数组,二维数组,多维数组。
int array[4][3][5]; // 定义了一个存 int 类型变量的三维数组
2.2.3 数组初始化
- 一维数组初始化
int main()
{
//int a[5] = {1, 2, 3, 4, 5};
//int a[5] = {1, 2}; // 初始化部分元素, 其他元素为 0
//int a[5] = {[2] = 4}; // 初始化 a[2], 其它元素为 0
//int a[5] = {a[2] = 4};// 初始化 a[0] a[2] 为 4
//int a[5] = {0};
int a[] = {1, 2, 3}; // 数组的元素个数由{}元素个数决定
//a = {3, 4, 5}; // 不可以, 这种赋值只能在初始化时使用
// 在定义之后, 给数组赋值只能一个一个赋值
//int a[] = {}; // 不可以, 编译器不知道几个元素
for (unsigned int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{ // sizeof(a) / sizeof(a[0]) = 数组元素个数
printf("a[%d] = %d ", i, a[i]);
}
printf("\n");
a[2] = 5; // 赋值
for (unsigned int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("a[%d] = %d ", i, a[i]);
}
return 0;
}
- 二维数组初始化
int main()
{
int a[2][2] = {{1, 2}, {3, 4}}; // 允许 int a[2][2] = {1, 2, 3, 4};
int b[][3] = {{1, 2, 3}, {4}};
int c[3][3] = {{1}, {2}};
int d[2][2] = {{2, 2}, {2}};
d[1][1] = 1;
//d[2] = 4; // 不允许
for (unsigned int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{ // sizeof(a) / sizeof(a[0]) = 数组行
for (unsigned int j = 0; j < sizeof(a[0]) / sizeof(a[0][0]); j++)
{ // sizeof(a[0]) / sizeof(a[0][0]) = 数组列
printf("a[%d][%d] = %d ", i, j, a[i][j]);
}
printf("\n");
} // sizeof(a) / sizeof(a[0][0]) = 数组元素个数
printf("\n");
for (unsigned int i = 0; i < sizeof(b) / sizeof(b[0]); i++)
{
for (unsigned int j = 0; j < sizeof(b[0]) / sizeof(b[0][0]); j++)
{
printf("b[%d][%d] = %d ", i, j, b[i][j]);
}
printf("\n");
}
printf("\n");
for (unsigned int i = 0; i < sizeof(c) / sizeof(c[0]); i++)
{
for (unsigned int j = 0; j < sizeof(c[0]) / sizeof(c[0][0]); j++)
{
printf("c[%d][%d] = %d ", i, j, c[i][j]);
}
printf("\n");
}
printf("\n");
for (unsigned int i = 0; i < sizeof(d) / sizeof(d[0]); i++)
{
for (unsigned int j = 0; j < sizeof(d[0]) / sizeof(d[0][0]); j++)
{
printf("d[%d][%d] = %d ", i, j, d[i][j]);
}
printf("\n");
}
return 0;
}
2.2.4 数组名
数组名是一个常量,代表的是数组首元素地址,不可以更改。
- 一维数组名
int a[3];
a[0]; // 代表数组的首元素
&a[0]; // 数组首元素地址
&a; // 数组的地址, 也是数组首元素的地址
&a[0] + 1; // 跨过一个元素, 即相当于 &a[1]
a + 1; // 跨过一个元素
&a + 1; // 跨过数组 a
int main()
{
int a[3];
printf("%p %p %p\n", &a[0], a, &a); // 得到的地址相同, 假设为 x
printf("%p %p %p\n", &a[0] + 1, a + 1, &a + 1); // x + 4 x + 4 x + 12
//a 有三个 int 型元素, 每一个元素占 4 个字节, 数组在内存中占 12 个字节
//%p 将地址以十六进制的方式打印出来
return 0;
}
- 二维数组名
int a[2][2];
int a[2][2];
&a[0][0]; // 数组第 0 行, 第 0 列元素的地址
a[0]; // 数组第 0 行, 也是数组第 0 行, 第 0 列元素的地址
&a[0]; // 数组第 0 行的地址
&a; // 数组的地址, 也是数组首元素的地址
&a[0][0] + 1; // 跨过一个元素
a[0] + 1; // 跨过一个元素
&a[0] + 1; // 跨过一行
a + 1; // 跨过一行
&a + 1; // 跨过数组 a
int main()
{
int a[2][2];
printf("%p %p %p %p %p\n", &a[0][0], a[0], &a[0], a, &a);
printf("%p %p %p %p %p\n", &a[0][0] + 1, a[0] + 1, &a[0] + 1, a + 1, &a + 1);
return 0;
}
2.2.5 字符数组
字符每一个元素都是字符型的数组。
- 一维字符数组
int main()
{
//char a[5];
//char a[5] = {1, 2, 3, 4, 5};
//char a[5] = {'a', 'b', 'c', 'd', 'e'};
//char a[5] = {'a'};
char a[6] = {'a', 'b', 'c', 'd', 'e', '\0'};
//char a[6] = "abcde"; // 在 "" 后面自动加上一个\0
char b[] = "hehe"; // sizeof(b) = 5
char c[128] = "Hello world"; // sizeof(c) = 128
//字符数组中含有 \0 字符 这种字符数组称为字符串
/*
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%c ", a[i]);
}
*/
printf("%s\n", a); // %s 是用来打印字符串
printf("[%6s]\n", b); // [ hehe] %6s 代表输出一个长度为 6 的字符串, 不足左补空格, 超过 6 则全部输出
printf("[%-6s]\n", b); // [hehe ] 右补空格
printf("[%12.5s]\n", c); // [ Hello] 选择 12 个字符, 从左开始截取 5 个字符, 其余位补空格
printf("[%-12.5s]\n", c); // [Hello ]
printf("[%6.7s]\n", c); // [Hello w]
printf("[%5s]\n", c); // [Hello world]
printf("%d\n", sizeof("hello")); // "hello"字符串常量存在常量区
return 0;
}
- 二维字符数组
int main()
{
char a[2][128] = {"hello", "world"};
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
printf("%s\n", a[i]);
}
return 0;
}
2.3 字符串处理函数
#include <stdio.h>
#include <string.h>
int main()
{
char a[16] = "[abcd]";
char b[16] = "[abcde]";
char c[16] = "";
printf("strlen(a) = %d\n", strlen(a)); // 6
printf("strcpy(c, a) = %s\n", strcpy(c, a)); // [abcd]
printf("strcat(c, b) = %s\n", strcat(c, b)); // [abcd][abcde]
printf("strcmp(a, b) = %d\n", strcmp(a, b)); // -1
printf("strupr(c) = %s\n", strupr(c)); // [ABCD][ABCDE]
printf("strlwr(c) = %s\n", strlwr(c)); // [abcd][abcde
printf("stricmp(\"[AbCd]\", a) = %d\n", stricmp("[AbCd]", a)); // 0
printf("strncmp(a, b, 5) = %d\n", strncmp(a, b, 5)); // 0
printf("strrev(a) = %s\n", strrev(a)); // ]dcba[
printf("strchr(a, 'o') = %s\n", strchr(a, 'o')); // dcba[
printf("strstr(c, \"abc\") = %s\n", strstr(c, "abc")); // abcd][abcde]
return 0;
}
int main()
{
char str[] = "100;86#110#150;7944;5667#101#he;lo";
char *p[10] = {0};
p[0] = strtok(str, "#;");
int i = 0;
do
{
i++;
p[i] = strtok(NULL, "#;");
}
while(p[i] != NULL);
for (int i = 0; p[i] != NULL; i++)
{
printf("%s ", p[i]);
}
return 0;
}
int main()
{
int year = 2021;
int month = 10;
int day = 1;
char buf[128] = "";
char str[] = "20221001";
/*
%d 输出整数
%nd 占 n 位
%0nd 占 n 位, 不足补 0
%-nd 占 n 位左对齐
%*d 跳过所有数字
%*nd 跳过前 n 个数字
%*s 跳过所有字符
%[a] 取字符 a
%*[a] 跳过字符 a
%[a-z] 取字符 a ~ z
%*[a-z] 跳过字符 a ~ z
%[0-9] 取所有数字
%*[0-9] 跳过所有数字
%[^x] 取不是字符 x 的字符, 遇到 x 退出
%*[^x] 跳过不是字符 x 的字符
*/
sprintf(buf, "%4d:%02d:%02d %d %s", year, month, day, 5, "hello");
/*
sprintf(buf, "格式", "参数") 是把参数以某种格式写入到 buf 中
*/
sscanf(str, "%4d%2d%2d", &year, &month, &day);
/*
sscanf(buf, "格式", "参数") 是把 buf 以某种格式写入到参数中
*/
printf("%s\n", buf); // 2021:10:01 5 hello
printf("%d:%2d:%02d\n", year, month, day); // 2022:10:01
return 0;
}
/**
* @header #include <string.h>
* @brief 测量字符串长度, 忽略 '\0'
* @param size_t: unsigned int
* @param Str: 字符串
* @return 返回字符串长度
*/
size_t strlen(const char *Str);
/**
* @header #include <string.h>
* @brief 将源字符串拷贝给目标字符串
* @param Dest: 目标字符串
* @param Source: 源字符串
* @return 返回指向目标字符串的指针
*/
char *strcpy(char *Dest, const char *Source);
/**
* @header #include <string.h>
* @brief 将源字符串连接到目标字符串末尾
* @param Dest: 目标字符串
* @param Source: 源字符串
* @return 返回指向目标字符串的指针
*/
char *strcat(char *Dest, const char *Source);
/**
* @header #include <string.h>
* @brief 将 Str1 和 Str2 的相同位按 ASCII 码值比较
* @param Str1: 字符串 1
* @param Str2: 字符串 2
* @return Str1 > Str2, 返回 1;
* Str1 = Str2, 返回 0;
* Str1 < Str2, 返回 -1。
*/
int strcmp(const char *Str1, const char *Str2);
/**
* @header #include <string.h>
* @brief 将字符串所有小写字母转换成大写字母
* @param Str: 目标字符串
* @return 返回指向 Str 的指针。
*/
char *strupr(char *Str);
/**
* @header #include <string.h>
* @brief 将字符串所有大写字母转换成小写字母
* @param Str: 目标字符串
* @return 返回指向 Str 的指针。
*/
char *strlwr(char *Str);
/**
* @header #include <string.h>
* @brief 功能类似于 strcmp, 只是它无视英文字母的大小写。
* @param Str1: 字符串 1
* @param Str2: 字符串 2
* @return Str1 > Str2, 返回 1;
* Str1 = Str2, 返回 0;
* Str1 < Str2, 返回 -1。
*/
int stricmp(const char *Str1, const char *Str2);
/**
* @header #include <string.h>
* @brief 类似于 strcmp, 只是它只比较前 MaxCount 位的字符串大小。
* @param Str1: 字符串 1
* @param Str2: 字符串 2
* @param MaxCount: 字符串比较的位数
* @return Str1 > Str2, 返回 1;
* Str1 = Str2, 返回 0;
* Str1 < Str2, 返回 -1。
*/
int strncmp(const char *Str1, const char *Str2, size_t MaxCount);
/**
* @header #include <string.h>
* @brief 将字符串逆置。
* @param Str: 字符串
* @return 返回指向 Str 的指针。
*/
char *strrev(char *Str);
/**
* @header #include <string.h>
* @brief 在 Str 中找到第一次匹配 Val 的字符串, 并从此处开始到结束的字符串返回。
* @param Str: 源字符串
* @param Val: 目标字符
* @return 成功匹配, 返回指向新地址的指针, 否则返回 0。
*/
char *strchr(const char *Str, int Val);
/**
* @header #include <string.h>
* @brief 在 Str 中找到第一次匹配 SubStr 的字符串, 并从此处开始到结束的字符串返回。
* @param Str: 源字符串
* @param SubStr: 目标字符串
* @return 成功匹配, 返回指向新地址的指针, 否则返回 0。
*/
char *strstr(const char *Str, const char *SubStr);
/**
* @header #include <string.h>
* @brief 分割 Str
* @param Str: 被分割的字符串
* @param Delim: 分隔符
* @return 返回分割好的字符串
*/
char *strtok(char *Str, const char *Delim);
2.4 字符串输入
int main()
{
char str[16] = "";
scanf("%s", str); // 不建议
/*
scanf 遇到空格不读取
scanf 不会读取 '\n'
scanf 不安全
*/
gets(str); // 不建议
/*
gets 不会读取 '\n'
gets 不安全
*/
fgets(str, sizeof(str), stdin); // 推荐 stdin: 标准输入, 键盘输入
return 0;
}
/**
* @header #include <stdio.h>
* @brief 从标准输入获得一个字符串
* @param Buffer: 字符串
* @return 返回指向 Buffer 的指针
*/
char *gets(char *Buffer);
/**
* @header #include <stdio.h>
* @brief 从 File 获得一个长度为 MaxCount 的 Buf。
* @param Buf: 字符串
* @param MaxCount: 可获得的字符数
* @param FILE: 一种结构体类型
* @param File: 获得字符串的地址
* @return 返回指向 Buf 的指针。
*/
char *fgets(char *Buf, int MaxCount, FILE *File);
2.5 练习
2.5.1 求数组最大值
#include <stdio.h>
int main()
{
int tmp;
size_t i;
int arr[5] = {5, 9, 66, 7, 55};
tmp = arr[0];
for (i = 1; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (tmp < arr[i])
{
tmp = arr[i];
}
}
printf("The arr MaxNum is %d。\n", tmp);
return 0;
}
2.5.2 数组逆置
#include <stdio.h>
int main()
{
int end; // 末尾
int tmp;
size_t i;
int begin; // 开始
int arr[5] = {5, 9, 66, 7, 55};
begin = 0;
end = sizeof(arr) / sizeof(arr[0]) - 1;
while (begin < end)
{
tmp = arr[begin];
arr[begin] = arr[end];
arr[end] = tmp; // 这三句语句用于交换两数的值
begin++;
end--;
}
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("arr[%u] = %d ", i, arr[i]);
}
printf("\n");
return 0;
}
2.5.3 数组排序
#include <stdio.h>
int main()
{
int tmp;
size_t i;
size_t j;
int arr[5] = {5, 9, 66, 7, 55}; // 从小到大排序
for (i = 0; i < sizeof(arr) / sizeof(arr[0]) - 1; i++)
{
for (j = i; j < sizeof(arr) / sizeof(arr[0]); j++)
{
if (arr[i] > arr[j])
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("arr[%u] = %d ", i, arr[i]);
}
printf("\n");
return 0;
}
2.5.4 找出一维数组的最大值和次大值
/*
给定一个数组, 找到这个数组的第一大值和第二大值, 并输出第一大值和第二大值以及对应的出现次数
第一大值和第二大值不能相等,
如果数组所有元素相同, 则输出
printf("All elements of this array are the same, MaxNum = %d, have no second largest.\n", max_fir);
*/
#include <stdio.h>
int main()
{
size_t i;
int max_fir;
int max_sec;
int cnt_fir = 0;
int cnt_sec = 0;
int arr[10] = {14, 22, 22, 16, 18, 18, 18, 15, 12, 10};
//int arr[10] = {14, 14, 13, 4, 13, 13, 15, 13, 4, 13};
//int arr[10] = {2, 2, 2, 2, 20, 2, 16, 2, 16, 2};
//int arr[10] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
//int arr[10] = {1, 2, 2, 2, 2, 2, 2, 2, 2, 2};
//int arr[10] = {4, 4, 2, 2, 2, 2, 2, 2, 2, 2};
max_fir = arr[0];
max_sec = arr[0];
for (i = 1; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (arr[i] == max_fir || arr[i] == max_sec)
continue;
else
{
if (arr[i] >= max_sec)
max_sec = arr[i];
else if (max_fir == max_sec)
max_sec = arr[i];
if (max_sec > max_fir)
{
max_sec = max_fir ^ max_sec;
max_fir = max_fir ^ max_sec;
max_sec = max_fir ^ max_sec;
}
}
}
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (arr[i] == max_fir)
cnt_fir++;
else if (arr[i] == max_sec)
cnt_sec++;
}
if (max_sec == max_fir)
printf("All elements of this array are the same, MaxNum = %d, have no second largest.\n",
max_fir);
else
printf("MaxNum = %d, MaxCnt = %d\nSecNum = %d, SecCnt = %d\n",
max_fir, cnt_fir, max_sec, cnt_sec);
return 0;
}
2.5.4 找出各科平均值和各科不及格人数
/*
有 5 个人, 他们考试科目有: 语 数 外, 成绩如下
float a[5][3] = {{80, 75, 56}, {59, 65, 71}, {59, 63, 70}, {85, 45, 90}, {76, 77, 45}};
计算各科的平均分和各科不及格的人数
*/
#include <stdio.h>
int main()
{
size_t i;
size_t j;
int failcnt[3] = {0};
float avg[3] = {0.0f};
float a[5][3] = {{80, 75, 56}, {59, 65, 71}, {59, 63, 70}, {85, 45, 90}, {76, 77, 45}};
for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
{
for (j = 0; j < sizeof(a[0]) / sizeof(a[0][0]); j++)
{
if (a[i][j] < 60)
failcnt[j]++;
avg[j] += a[i][j];
}
}
printf("The average score for Chinese is %.2f, the number of failed students is %d\n",
avg[0] / 5, failcnt[0]);
printf("The average score for Math is %.2f, the number of failed students is %d\n",
avg[1] / 5, failcnt[1]);
printf("The average score for Foreign Languages is %.2f, the number of failed students is %d\n",
avg[2] / 5, failcnt[2]);
return 0;
}
2.5.5 猜数字游戏
/*
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main()
{
//rand() 产生随机数, 但是每次产生的随机数一样
//int t = time(NULL); // 时间戳, 现在时间 - 1970.1.1.0.. 秒
srand((unsigned int)time(NULL)); // 设置随机种子
int a = rand();
printf("%d\n", a);
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
*/
/*
随机生成一个四位数, 与自己输入的四位数比较, 如果相等, 就打印猜测成功,
如果某一位数字比生成的数字的相同位小了, 就打印某某位小了,
如果大了就打印某某位大了, 否则打印某某位相等。
*/
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int rand_num[4] = {0};
int input_num[4] = {0};
int flag = 0;
int sum = 0;
srand((unsigned int)time(NULL));
for (int i = 0; i < 4; i++)
{
rand_num[i]= rand() % 10;
}
while (1)
{
flag = 0;
printf("Please enter a random 4-digit-number: ");
scanf("%d", &sum);
for (int i = 3; i >= 0; i--)
input_num[i] = sum / (int)(pow(10, 3 - i)) % 10;
for (int i = 0; i < 4; i++)
{
if (input_num[i] == rand_num[i])
{
printf("The %dth digit is correct\n", i + 1);
flag++;
}
else if (input_num[i] > rand_num[i])
printf("The %dth digit is bigger\n", i + 1);
else
printf("The %dth digit is smaller\n", i + 1);
}
if (4 == flag)
{
printf("Congratulations on your correct answer\n");
break;
}
}
return 0;
}
/**
* @header #include <math.h>
* @brief 计算 X ^ Y 的值
* @param X: 底数
* @param Y: 指数
* @retval 返回 X ^ Y 的值。
*/
double pow(double X, double Y);
2.5.6 实现字符串处理函数
/* strlen */
size_t my_strlen(const char *str)
{
size_t len = 0;
while (*str++)
len++;
return len;
}
/* strcpy */
char *my_strcpy(char *DstStr, const char *SrcStr)
{
char *str = DstStr;
while (*SrcStr)
*DstStr++ = *SrcStr++;
*DstStr++ = 0;
return str;
}
/* strcat */
char *my_strcat(char *DstStr, const char *SrcStr)
{
char *str = DstStr;
while (*DstStr)
DstStr++;
while (*SrcStr)
*DstStr++ = *SrcStr++;
*DstStr = 0;
return str;
}
/* strcmp */
int my_strcmp(const char *Str1, const char *Str2)
{
while (*Str1)
{
if (*Str1 > *Str2)
return 1;
else if (*Str1 < *Str2)
return -1;
Str1++;
Str2++;
}
if (*Str2 == 0)
return 0;
else
return -1;
}
/* strrev */
char *my_strrev(char *str)
{
char p[16] = {0};
char *sstr = str;
for (unsigned int i = 0; i < my_strlen(str) / 2; i++)
{
*p = *(str + i);
*(str + i) = *(str + my_strlen(str) - i - 1);
*(str + strlen (str) - i - 1) = *p;
}
return sstr;
}