八. 容器
1. 一维数组
1.1 什么是一维数组
当数组中每个元素都只带有一个下标(第一个元素的下标为0, 第二个元素的下标为1, 以此类推)时,称这样的数组为一维数组。由一组数目固定、类型相同的数据构成,数组中的每个数据被称为元素,特别需要注意的是数组中的每个元素一定有着相同的数据类型。
1.2 一维数组的定义
1.2.1 语法结构
数据类型 数组名[数组大小] = {初始值1, 初始值2, ... ,初始值N};
1.2.2 示例
// 计算五个学生的平均成绩
#include <stdio.h>
int main()
{
// 方式一: 常规方式计算五个学生的平均成绩
float fScore1, fScore2, fScore3, fScore4, fScore5, fAver;
fScore1 = fScore2 = fScore3 = fScore4 = fScore5 = fAver = 0.0;
scanf("%f", &fScore1);
scanf("%f", &fScore2);
scanf("%f", &fScore3);
scanf("%f", &fScore4);
scanf("%f", &fScore5);
fAver = (fScore1 + fScore2 + fScore3 + fScore4 + fScore5) / 5;
printf("平均成绩为: %.2f", fAver);
// 方式二: 数组的方式计算五个学生的平均成绩
// 定义数组, 下面语句等效于float arrFloatScore[5] = {0.0};
// 可以不初始化, 个人习惯, 喜欢能初始化就初始化
float arrFloatScore[5] = {0.0, 0.0, 0.0, 0.0, 0.0}, fAver = 0.0;
// 用户输入成绩, 数组使用for循环赋值,
// i其实就是数组的下标, 所以从0开始
for (int i = 0; i < 5; i++)
{
scanf("%f", &arrFloatScore[i]);
}
// 计算平均成绩, 这里也是用for循环, 个人喜欢for循环多一点
for (int j = 0; j < 5; j++)
{
fAver += arrFloatScore[j];
}
printf("平均成绩为: %.2f", fAver / 5);
return 0;
}
上面提供了两种方式来解题,单从这里可以看出其实也没有节约代码量,这是因为这个程序还可以进行优化,下面是对第二种方式的优化版本,代码如下:
// 计算五个学生的平均成绩(数组优化版)
#include <stdio.h>
int main()
{
float arrFloatScore[5] = {0.0, 0.0, 0.0, 0.0, 0.0}, fAver = 0.0;
for (int i = 0; i < 5; i++)
{
scanf("%f", &arrFloatScore[i]);
// 在上面的版本中使用的是先存后读的方式计算总成绩
// 这里使用的是在输入阶段就计算总成绩
fAver += arrFloatScore[i];
}
printf("平均成绩为: %.2f", fAver / 5);
return 0;
}
通过优化后代码量缩小,逻辑更为清晰,尽管只是减少了一行,然而如果需要计算的是十个、百个、千个学生的成绩,那么使用这种代码即可看到代码量的极致缩减。
Tips: 针对大批量数据而言, 使用数组进行写入和读取无疑是最佳选择
1.3 数组中元素的修改
1.3.1 修改示例
数组的修改其实非常简单, 代码如下:
// 假定十个学生的成绩分别为90、94、75、83、95、60、99、100、78、80
// 上面的数据已经被输入到了数组中, 但是仔细检查发现其中第6个学生的成绩
// 本该为62, 此时修改第6个学生的成绩
// 方式1:通过下标的方式修改(在知道下标的情况下使用)
#include <stdio.h>
int main()
{
int arrStuScore[10] = {90, 94, 75, 83, 95, 60, 99, 100, 78, 80};
// 修改前
for (int i = 0; i < 10; i++)
printf("%d ", arrStuScore[i]);
printf("\n");
// 修改第6个学生的成绩为62
arrStuScore[5] = 62; // 第6个学生的成绩对应的下标为5哦~
// 修改后
for (int j = 0; j < 10; j++)
printf("%d ", arrStuScore[j]);
return 0;
}
// 假定十个学生的成绩分别为90、94、75、83、95、60、99、100、78、80
// 上面的数据已经被输入到了数组中, 但是仔细检查发现其中第6个学生的成绩
// 本该为62, 此时修改第6个学生的成绩
// 方式2: 通过for循环修改(在不知道下标的情况下使用)
#include <stdio.h>
int main()
{
int arrStuScore[10] = {90, 94, 75, 83, 95, 60, 99, 100, 78, 80};
// 修改前
for (int i = 0; i < 10; i++)
printf("%d ", arrStuScore[i]);
printf("\n");
// 修改第6个学生的成绩为62
for (int j = 0; j < 10; j++)
{
if (arrStuScore[j] == 60)
{
arrStuScore[j] = 62;
break;
}
}
// 修改后
for (int z = 0; z < 10; z++)
printf("%d ", arrStuScore[z]);
return 0;
}
1.3.2 两种修改的对比
上面的两种方式各有优点,第一种方式的缺点是显而易见的,在实际的学校中,成千上万的学生是无法记住每个学生在数组中的准确下标的。第二种方式的缺点也很显然,在实际的开发中成千上万的学生中肯定会有同成绩的情况出现,所以这种方式修改也许会把本不应该修改成绩的学生的成绩给修改了,这个解决办法很简单,利用学号来搜索并修改,这涉及其它知识,后面再叙述咯~
1.4 数组元素的排序
在数组中最需关心的操作中,排序绝对是排得上号的,怎么样排序简单且快在以前是个难题,但是现在却有很多方式了,例如冒泡排序、快速排序、插入排序等等,其中最常用的无疑是冒泡排序,下面的代码实现了冒泡排序,如需获取更多,请查看冒泡排序
// 冒泡排序(从小到大[也称升序])
#include <stdio.h>
int main()
{
int arrStuScore[10] = {90, 94, 75, 83, 95, 60, 99, 100, 78, 80};
int i, j, temp;
// 排序前
for (int i = 0; i < 10; i++)
printf("%d ", arrStuScore[i]);
printf("\n");
// 排序的次数为元素个数减1次, 这是因为最后一个数字无需进行循环比较的
for (i = 0; i < 10 - 1; i++)
{
// 此循环为每次比较的次数,
for (j = 0; j < 10 - 1 - i; j++)
{
// 相邻元素比较左大于右则交换
if (arrStuScore[j] > arrStuScore[j + 1])
{ // 交换两个数的思想牢记,经常用
temp = arrStuScore[j];
arrStuScore[j] = arrStuScore[j + 1];
arrStuScore[j + 1] = temp;
}
}
}
// 排序后
for (int i = 0; i < 10; i++)
printf("%d ", arrStuScore[i]);
return 0;
}
// 冒泡排序(从大到小[也称降序])
#include <stdio.h>
int main()
{
int arrStuScore[10] = {90, 94, 75, 83, 95, 60, 99, 100, 78, 80};
int i, j, temp;
// 排序前
for (int i = 0; i < 10; i++)
printf("%d ", arrStuScore[i]);
printf("\n");
// 排序的次数为元素个数减1次, 这是因为最后一个数字无需进行循环比较的
for (i = 0; i < 10 - 1; i++)
{
// 此循环为每次比较的次数,
for (j = 0; j < 10 - 1 - i; j++)
{
// 相邻元素比较右大于左则交换
// 降序的关键在此判断
if (arrStuScore[j] < arrStuScore[j + 1])
{ // 交换两个数的思想牢记,经常用
temp = arrStuScore[j];
arrStuScore[j] = arrStuScore[j + 1];
arrStuScore[j + 1] = temp;
}
}
}
// 排序后
for (int i = 0; i < 10; i++)
printf("%d ", arrStuScore[i]);
return 0;
}
2. 二维数组
在小学最初学坐标轴的时候用的是数轴, 他就是一根直线,上面每个点用一个数字就能确定,但是到中学时开始接触直角坐标系了,它不再是一根直线了,变成了两根直线,同时变化的还有它上面的每个点也不能用一个数字确定了,得用两个数字确定,也就是x、y的值。其实一维数组对应的就是小学的数轴,而二维数组对应的就是直角坐标系。
2.1 二维数组的定义
数据类型 数组名[行数][列数] = {{}, {}, {}, ..., {}};
用线性代数解释一维数组和二维数组的话可以定义一维数组其实是一个向量,而二维数组其实是一个矩阵(复习高数哦,矩阵和行列式是不一样的哦~矩阵不在乎行数和列数相等,但是行列式要求行数和列数相等哦。)
2.2 示例
// 二维数组的遍历
// 方式1: 先行后列
#include <stdio.h>
int main()
{
// 定义一个3行4列的二维数组
int multArr[3][4] = { // 别忘记外层还有一个中括号哦
{1, 2, 3, 4}, // 里层一个中括号中保存的是一列的内容
{5, 6, 7, 8}, // 所以三行三个中括号
{9, 10, 11, 12},
};
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
// 可以试试%d哦~
printf("%2d ", multArr[i][j]);
}
printf("\n");
}
return 0;
}
// 二维数组的遍历
// 方式2: 先列后行
#include <stdio.h>
int main()
{
// 定义一个3行4列的二维数组
int multArr[3][4] = { // 别忘记外层还有一个中括号哦
{1, 2, 3, 4}, // 里层一个中括号中保存的是一列的内容
{5, 6, 7, 8}, // 所以三行三个中括号
{9, 10, 11, 12},
};
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 3; j++)
{
printf("%2d ", multArr[j][i]);
}
printf("\n");
}
return 0;
}
3. 字符串
在C语言中,字符串其实就是一个变形的一维数组,定义方式如下
3.1 字符串定义的语法结构
// 方式1
char 字符串名[字符串大小] = {'字符1', '字符2', '字符3', ..., '字符N', '\0'};
// 方式2
char 字符串名[估计的大小] = "字符串内容";
在上面的两种方式中推荐使用第二种方式,在使用过程中尽量将估计的大小弄大一点,这样不至于导致内存溢出哦。如果使用第一种方式请注意最后的’\0’, 这个符号标志着字符串的结束,即’\0’后面的不管有没有字符都不会再输出了。
3. 2 字符串的基础操作示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char str1[4] = {'a', 'b', 'c', '\0'};
char str2[4] = "abc";
// 字符串的输出
printf("%s\n", str2);
char str3[55] = "www.baidu.com";
// 字符串的遍历
int i = 0;
while (str3[i] != 0) // 条件表达式与"str3[i] != '\0'"等价
{
printf("%c ", str3[i]);
i++;
}
printf("\n");
// 字符串的长度, 使用strlen()函数需要包含头文件"string.h"
printf("字符串str3的长度为: %d\n", strlen(str3));
return 0;
}
3.3 字符串的其它操作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 字符串的拼接
char str1[50] = "hello ";
char str2[50] = "world!";
strcat(str1, str2);
printf("%s\n", str1);
// 字符串比较
char str3[50] = "My name is SEVEN";
char str4[50] = "My name is XXXXX";
// strcmp():原理是比较ASCII码值, 相等返回0,第一个大于第二个返回值大于0
int nRet = strcmp(str3, str4);
if (nRet > 0)
printf("str3 > str4");
else if (nRet == 0)
printf("str3 = str4");
else
printf("str3 < str4");
printf("\n");
// 除strcmp以外还有以下几种
// a. strncmp(): 按指定的长度做比较
// b. stricmp(): 不区分大小写比较
// c. strnicmp(): 不区分大小写且按指定长度做比较
// 字符串查找
char str5[50] = "file:///G:/C语言/Container/";
// 1. 使用strchr()在字符串查找字符第一次出现的位置,并将该位置后的字符串返回
printf("%s\n", strchr(str5, '///') + 3); // 可以尝试加1、加2、不加的效果
// 2. 使用strstr()可以在字符串1中判断是否包含字符串2。
char str6[50] = "hello world";
char str7[50] = "world";
char str8[50] = "hi";
printf("%s\n", strstr(str6, str7)); // 打印"world"
printf("%s\n", strstr(str6, str8)); // 打印"(null)"
// 可以这样写判断
// 因为strstr()返回了一个字符指针,可以判断这个指针是否为空(NULL)
if (strstr(str6, str7) == NULL)
printf("不包含\n");
else
printf("包含\n");
// 3. 使用strtok()按特定的方式分割字符串
char chIp[] = "192.168.1.100";
char *pChRes = strtok(chIp, ".");
while (pChRes != NULL)
{
printf("%s ", pChRes);
pChRes = strtok(NULL, ".");
}
printf("\n");
return 0;
}
明儿就开始复习指针咯~~