C语言
6.10 .
使用switch实现银行系统,默认用户为A,密码为1234,余额2000如果登录失败,则直接结束
如果登录成功,则显示银行页面
1.查询余额
2.取钱
3.存钱
如果是1,则打印余额如果是2,则输入取钱金额,如果金额大于存款则输出余额不足,否则输出剩余金钱如果是3,则输入存款金额,输出存款后的金额
3.输入一个表达式,实现计算器+-*/%
例如:1+2,则输出35/2,则输出2.5
练习4:下面代码执行后,a\b\c的结果是?
int a=5,b=7,c;
c=a+++b;
解:c=12
c = a++ + b;a先赋值后加加故c=5+7;
练习5:代码如下
void main()
{
int c=9,d=0;
C=C++%5;
d=c;
printf("d=%d\n",d);
}
解:d=4;
练习6.给定一个整数,判断它是否被3、5、7整除,并输出一下信息(笔试)
1.能同时被3,5,7整除(直接输出357,每个数字之间一个空格)
2.只能内其中两个整数整除(输出两个数,小的在前,大的在后,例如35 或者 37 或者57,中间使用空格隔开
3.只能被其中一个整数整除(输出这个除数)
4.不能被任何数整除,输出小写字母
案例如下:
输入:105
输出:357
#include <stdio.h> int main() { int num; printf("请输入一个整数: "); scanf("%d", &num); if (num % 3 == 0 && num % 5 == 0 && num % 7 == 0) { printf("3 5 7\n"); } else if (num % 3 == 0 && num % 5 == 0) { printf("3 5\n"); } else if (num % 3 == 0 && num % 7 == 0) { printf("3 7\n"); } else if (num % 5 == 0 && num % 7 == 0) { printf("5 7\n"); } else if (num % 3 == 0) { printf("3\n"); } else if (num % 5 == 0) { printf("5\n"); } else if (num % 7 == 0) { printf("7\n"); } else { printf("n\n"); } return 0; }
练习7:
a=10;b=10;c=12=a;
d=10*12=120;a=13;
find:b=10;c=12;d=120;
练习8:
c=-75;a=181;d=6;e=5;
练习9:
b:0000 0110 ----> 0001 1000
a:0000 0011
^-----------------
0001 1011
6.11
1.计算公约数,公倍数
#include <stdio.h>
// 使用欧几里得算法计算最大公约数
int gcd(int a, int b)
{
if (b == 0)
{
return a;
}
return gcd(b, a % b);
}
// 计算最小公倍数
int lcm(int a, int b)
{
return (a * b) / gcd(a, b);
}
int main()
{
printf("输入两个数,计算最大公约数,以及最小公倍数--------------------------\n");
int a, b;
printf("请输入两个正整数:");
// 检查输入是否正确
if (scanf("%d %d", &a, &b) != 2 || a <= 0 || b <= 0)
{
printf("输入错误,请确保输入两个正整数。\n");
return 1; // 退出程序
}
// 计算最大公约数和最小公倍数
int temp1 = gcd(a, b);
int temp2 = lcm(a, b);
printf("最大公约数是:%d\n", temp1);
if (temp1 == 1)
{
printf("没有最大公约数\n");
}
else
{
printf("最小公倍数是:%d\n", temp2);
}
return 0;
}
2.各类小题目合集
#include <myhead.h>
void loop1()
{
printf("求一定范围内的偶数和-------------------------------------\n");
int num = 0;
int max = 0;
int sum = 0;
printf("请输入循环最大值:");
scanf("%d", &max);
do
{
if (num % 2 == 0)
{
sum += num;
}
num++;
} while (num <= max);
printf("所有偶数和为%d\n", sum);
}
void loop2()
{
printf("求一定范围内的总数和-------------------------------------------\n");
int sum = 0;
int num = 0;
printf("请输入值:\n");
do
{
scanf("%d", &num);
sum += num;
} while (num);
printf("总和为%d\n", sum);
}
void loop3()
{
printf("输入用户密码判读对错-------------------------------------------\n");
char user = 'A', take_user = 0;
int key = 1234, take_key = 0;
int count = 1;
do
{
take_key = 0;
take_user = 0;
printf("请输入用户名和密码:\n");
scanf("%c%d", &take_user, &take_key);
getchar();
if (user == take_user && key == take_key)
{
printf("登录成功\n");
break;
}
else
{
printf("用户名或密码错误,请重新输入\n");
printf("第%d次登录失败,还剩%d次机会\n", count, 3 - count);
count++;
}
} while (count <= 3);
if (count >= 3)
{
printf("账户被锁\n");
}
}
void loop4()
{
printf("求n的阶乘--------------------------------------\n");
int count, sum = 1;
scanf("%d", &count);
for (int i = 1; i <= count; i++)
{
sum *= i;
}
printf("%d\n", sum);
}
void loop5()
{
printf("求斐波那契数列各项-------------------------------\n");
int num, num1 = 1, num2 = 1, num3 = 0;
printf("请输入要求的斐波那契额数列项数:");
scanf("%d", &num);
if (num <= 2)
{
for (int i = 0; i < num; i++)
{
printf("1 ");
}
}
else
{
printf("1 1 ");
for (int i = 3; i <= num; i++)
{
num3 = num1 + num2;
printf("%d ", num3);
num1 = num2;
num2 = num3;
}
printf("\n");
}
}
void loop6()
{
printf("用for打印1~5--------------------------\n");
int i = 0;
start:
if (i > 5)
{
return 0;
}
printf("%d ", i);
i++;
goto start;
}
void loop7()
{
printf("打印三行五列*--------------------------\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("* ");
}
printf("\n");
}
}
void loop8()
{
printf("打印三角*--------------------------\n");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (j <= i)
{
printf("* ");
}
}
printf("\n");
}
}
void loop9()
{
printf("打印三角*--------------------------\n");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (i <= j)
{
printf("* ");
}
}
printf("\n");
}
}
void loop10()
{
printf("打印三角*--------------------------\n");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (j + i >= 3)
{
printf("* ");
}
else
{
printf(" ");
}
}
printf("\n");
}
}
void loop11()
{
printf("打印三角*--------------------------\n");
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (j >= i)
{
printf("*");
}
else
{
printf(" ");
}
}
printf("\n");
}
}
void loop13()
{
printf("输入一个数,判断是否为素数-----------------------------------------\n");
int a;
printf("请输入一个数:");
scanf("%d", &a);
int i;
for (i = 2; i < a; i++)
{
if (a % i == 0)
{
printf("不是素数\n");
break;
}
}
printf("是素数\n");
}
int main(int argc, const char *argv[])
{
loop1();
loop2();
loop3();
loop4();
loop5();
loop6();
loop7();
loop8();
loop9();
loop10();
loop11();
loop13();
return 0;
6.12
小例题:
#include <myhead.h>
void arr_test1()
{
printf("数组训练循环输入输出求和-----------------------------\n");
float arr[5] = {0};
float sum_vale = 0;
for (int i = 0; i < 5; i++)
{
printf("请输入第%d个数字:", i + 1);
scanf("%f", &arr[i]);
}
for (int i = 0; i < 5; i++)
{
printf("%f\n", arr[i]);
}
for (int i = 0; i < 5; i++)
{
sum_vale += arr[i];
}
printf("平均值为%.2f\n", sum_vale / 5);
}
void arr_test2()
{
/* 初始化数组和最大最小值变量 */
printf("求数组最大最小值-----------------------------\n");
int arr[5] = {0};
int max = 0;
int min = 0;
/* 通过用户输入填充数组 */
for (int i = 0; i < 5; i++)
{
printf("请输入第%d个数字:", i + 1);
scanf("%d", &arr[i]);
}
/* 使用数组的第一个元素作为初始最大最小值 */
max = arr[0];
min = arr[0];
/* 遍历数组以更新最大最小值 */
for (int i = 0; i < 5; i++)
{
if (arr[i] > max)
{
max = arr[i]; // 比当前值大就赋值,得最大
}
if (arr[i] < min)
{
min = arr[i]; // 比当前值小就赋值,得最小
}
}
/* 输出最大和最小值 */
printf("最大值为%d,最小值为%d\n", max, min);
}
void arr_test3()
{
printf("数组的冒泡排序-----------------------------\n");
/* 初始化一个长度为10的整型数组,用于存储用户输入的数字 */
int arr[10] = {0};
for (int i = 0; i < 10; i++)
{
/* 提示用户输入第i+1个数字 */
printf("请输入第%d个数字:", i + 1);
/* 从标准输入读取一个整型数字,存储到数组的第i个位置 */
scanf("%d", &arr[i]);
}
/* 使用temp变量作为临时存储,用于交换数组中的元素 */
int temp = arr[0];
/* 外层循环控制比较的轮数,每轮找到一个最大值 */
for (int i = 0; i < 10; i++)
{
/* 内层循环控制每轮比较的次数,每次比较相邻的两个元素 */
for (int j = i + 1; j < 10; j++)
{
/* 如果当前元素大于后续元素,则交换它们的位置 ,以下方法先确定最小值*/
if (arr[i] > arr[j])
{
temp = arr[i]; /* 先将当前元素存储到temp中 */
arr[i] = arr[j]; /* 将后续元素赋值给当前元素 */
arr[j] = temp; /* 将temp中的值赋给后续元素 */
}
}
}
printf("从小到大排序:");
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void arr_test4()
{
printf("二维数组的输入输出和平均值-----------------------------\n");
int arr[2][3] = {0};
int sum = 0;
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
printf("请输入第%d行第%d列的数字:", i + 1, j + 1);
scanf("%d", &arr[i][j]);
}
}
// 二维输出
// for (int i = 0; i < 2; i++)
// {
// for (int j = 0; j < 3; j++)
// {
// sum += arr[i][j];
// printf("%d ",arr[i][j]);
// }
// printf("\n");
// }
for (int i = 0; i < 2 * 3; i++) // 一维输出
{
sum += arr[0][i];
printf("%d ", arr[0][i]);
}
int len = sizeof(arr) / sizeof(arr[0][0]);
printf("平均值为%.2f\n", (float)sum / len);
}
void arr_test5()
{
printf("二维数组的输入输出和最大最小值-----------------------------\n");
int arr[][4] = {12, 4, 3, 5, 7, 2, 5};
int max = arr[0][0];
int min = arr[0][0];
for (int i = 0; i < 8; i++)
{
if (max < arr[0][i])
{
max = arr[0][i];
}
if (min > arr[0][i])
{
min = arr[0][i];
}
}
printf("最大值为%d,最小值为%d\n", max, min);
}
int main(int argc, char *argv[])
{
arr_test1();
arr_test2();
arr_test3();
arr_test4();
arr_test5();
return 0;
}
6.13
1.杨辉三角问题
2.差值问题
3.查找第二大值问题
输入一个m行n列的二维数组,计算第二大值;
4.元素出现次数问题
输入n个元素的一维数组,输入查找的key值
如果key出现一次,则输出对应的下表
如果key没有出现,则提示不存在
如果key出现多次,则提示出现的次数
5.求数组行列和问题
.输入一个m行n列的二维数组,输出每一行的和,以及每一列的和
.输入一个m行m列的二维数组,输出正对角线和反对角线的差
6.数组行列转换问题
输入一个2行3列的二维数组,请实现转置(行列转换)
#include <stdio.h>
void arr_test12()
{
printf("输入一个2行3列的二维数组,请实现转置(行列转换)-----------\n");
int m=2, n=3;
int arr[m][n];
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
printf("请输入第%d行第%d列的数字:", i + 1, j + 1);
scanf("%d", &arr[i][j]);
}
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
printf("%d ", arr[j][i]);
}
printf("\n");
}
}
7.填空:
#include <myhead.h>
#define MOD(x, y) x % y
int main(int argc, const char *argv[])
{
float a = 2, b = 4, c = 3;
printf("%f\n", (1 / 2) + (a + b) * c);//18
int x = 'f';
printf("%c\n", 'a' + (x - 'a' + 1));//g
int k = 7 >> 1;
printf("%d\n", k);//3
int a1 = 10, b1 = 94;
printf("%d\n", MOD(b1, a1 + 4));//8
int a2 = 1, b2 = 2, x2 = 0;
if (!(--a2))
x2--;
if (!b2)
x2 = 7;
else
++x2;
printf("%d\n", x2);//0
return 0;
}
6.15
printf("输入一个字符串。输出删除空格后的字符串--------------\n"); /* 初始化两个字符数组,用于存储原始字符串和处理后的字符串 */ char str[128] = "";//原始字符串 char str_new[128] = "";//处理后的字符串 /* 初始化计数器,用于跟踪新字符串中的字符位置 */ int count = 0; printf("请输入字符串:"); /* 获取用户输入的字符串,gets函数已弃用最好替换为更安全的函数如fgets */ gets(str);//因为有空格所以无法使用scanf /* 遍历用户输入的字符串,去除空格 */ for (int i = 0; i < strlen(str); i++) { /* 如果当前字符是空格,则跳过,不将其添加到新字符串中 */ if (str[i] == ' ') { continue;//跳过空格赋值,原来的字符串继续再++,而需要的字符串计数器跳过++ } str_new[count] = str[i]; count++; } /* 输出处理后的字符串 */ printf("删除空格后的字符串为:%s\n", str_new);
int main(int argc, const char *argv[]) { printf("输入一个字符串,实现单词的逆置----------------------\n"); char str[128] = ""; char str_new[128] = ""; // 存入删除空格后的字符 int record[100] = {0}; // 记录每个单词的字符个数 int count = 0; // 单词字母个数计数 int num_word = 0; // 单词个数 int num_char = 0; // 每个的字符个数 int i = 0; int sum_char = 0; // 输入的总字符个数 printf("输入要逆置单词的字符串:"); // 最后以空格结尾 gets(str); for (i = 0; i < strlen(str); i++) { if (str[i] != ' ') { num_char++; } else { record[count] = num_char; num_char = 0; count++; num_word++; } } printf("单词个数为:%d\n", num_word); int count2 = 0; for (int i = 0; i < strlen(str); i++) // 删除所有空格 { if (str[i] == ' ') { continue; } str_new[count2] = str[i]; count2++; } printf("删除空格后的字符串为:%s\n", str_new); for (int i3 = 0; i3 < num_word; i3++) { sum_char += record[i3]; printf("第%d个单词的字符个数为:%d\n", i3 + 1, record[i3]); } printf("总字符个数为:%d\n", sum_char); printf("下一步逆置字符串----------------------------------\n"); char temp = 0; int x = 0, j = strlen(str_new); for (int i1 = 0; i1 < j / 2; i1++) { temp = str_new[i1]; str_new[i1] = str_new[j - 1 - i1]; str_new[j - 1 - i1] = temp; } printf("逆置后的字符串为:%s\n", str_new); printf("下一步逆置单词----------------------------------\n"); int rec_num_i = 0; int rec_num_j = -1; for (int count2 = 0; count2 < num_word; count2++) // 几个单词逆置几次 { rec_num_j += record[num_word - count2 - 1]; // 指向每个单词的尾部 for (int i2 = rec_num_i, j2 = rec_num_j; i2 < j2; i2++, j2--) // 单词的个数为record[num_word-i2-1] { temp = str_new[i2]; str_new[i2] = str_new[j2]; str_new[j2] = temp; } rec_num_i += record[num_word - count2 - 1]; // 指向每个单词的头部 } printf("逆置后的字符串为:%s\n", str_new); printf("将逆置的字符串内单词加上空格-------------------------\n"); count2 = 0; char str_latest[128] = ""; // 存放加上空格的字符串 int num_rec = 0; // 判断什么时候需要加空格的条件变量 int j3 = 0; num_rec += record[num_word - count2 - 1]; for (int i3 = 0; i3 < sum_char + num_word; i3++)//sum_char + num_word总字符数+空格数 { if (j3 == num_rec)//判断是否需要加空格 { str_latest[j3++] = ' ';//相同则加空格 count2++; num_rec += record[num_word - count2 - 1]; num_rec++; } str_latest[j3] = str_new[i3]; j3++; } printf("最后字符串为%s\n", str_latest); return 0; }
思路:
输入字符串后,记录输入的单词数,总字符数,以及每个单词的个数;先删除空格,再逆置,根据每个单词字母个数添加空。输出:
穷举算法:“鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?”
int main(int argc, const char *argv[]) { int x, y, z; // 分别表示鸡翁、鸡母、鸡雏的数量 for (x = 0; x <= 100; x++) { // 鸡翁数量从0到100枚举 for (y = 0; y <= 100; y++) { // 鸡母数量从0到100枚举 z = 100 - x - y; // 根据鸡总数计算鸡雏数量 if (5 * x + 3 * y + z / 3 == 100) { // 检查是否满足价值总和条件 printf("鸡翁: %d, 鸡母: %d, 鸡雏: %d\n", x, y, z); } } } return 0; }
输出:
打印99乘法口诀表
// 使用嵌套循环打印9x9乘法口诀表 for (int i = 1; i <= 9; i++) { // 外层循环控制行 for (int j = 1; j <= i; j++) { // 内层循环控制列,只打印到当前行数 // 打印乘法表达式并适当对齐 printf("%d*%d=%-2d ", j, i, i*j); //%-2d是用来保证数字之间的对齐,-2表示至少保留两位宽度,且左对齐。 } // 每打印完一行后换行 printf("\n"); }
输出:
循环输入n个元素,交换数组中最大值和最小值
#include <stdio.h> int main() { int n; printf("请输入数组元素的个数n: "); scanf("%d", &n); int arr[n]; printf("请输入%d个整数:\n", n); for (int i = 0; i < n; i++) { scanf("%d", &arr[i]); } // 初始化最大值和最小值及其索引 int maxVal = arr[0], minVal = arr[0], maxIdx = 0, minIdx = 0; // 遍历数组找到最大值和最小值及其索引 for (int i = 1; i < n; i++) { if (arr[i] > maxVal) { maxVal = arr[i]; maxIdx = i; } else if (arr[i] < minVal) { // 注意这里是else if,避免重复检查已经判断为最大值的元素 minVal = arr[i]; minIdx = i; } } // 直接在数组中交换最大值和最小值,而不是使用单独的swap函数 arr[maxIdx] = minVal; arr[minIdx] = maxVal; printf("交换最大值和最小值后,数组为:\n"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
输出:
输入字符串,输入变量k的值,将字符循环右移k位
#include <stdio.h> #include <string.h> int main() { char str[100]; int k, len; printf("请输入字符串: "); fgets(str, sizeof(str), stdin); // 使用fgets读取,包括空格,相当于gets(str) printf("请输入整数k(循环右移的位数): "); scanf("%d", &k); // 计算字符串长度并处理k值,确保k在字符串长度范围内 len = strlen(str); k %= len; if (k > 0) { // 如果k为0,则不需要移动 // 创建一个临时存储空间来帮助移动字符 char temp[len]; // 将字符串后k个字符复制到临时数组前部 for (int i = 0; i < k; i++) { temp[i] = str[len - k + i]; } // 将字符串前len-k个字符复制到临时数组剩余部分 for (int i = k; i < len; i++) { temp[i] = str[i - k]; } // 将临时数组内容复制回原字符串 for (int i = 0; i < len; i++) { str[i] = temp[i]; } } printf("循环右移后字符串为: %s\n", str); return 0; }
输出:
6.20
用shell指令将网址www.hqyj.com截取出网址的每一个部分(要求,该网址不能存入文件中)
echo "www.hqyj.com" | cut -d "." -f 2
输出
shell指令
6.24
- 写一个shell脚本,将以下内容放到脚本中:
- 在家目录下创建目录文件,dir
- dir下创建dir1和dir2
- 把当前目录下的所有文件拷贝到dir1中,
- 把当前目录下的所有脚本文件拷贝到dir2中
- 把dir2打包并压缩为dir2.tar.xz
- 再把dir2.tar.xz移动到dir1中
- 解压dir1中的压缩包
- 使用tree工具,查看dir下的文件
#!/bin/bash mkdir -p ~/dir mkdir -p ~/dir/dir1/dir2 cp ./* ~/dir/dir1 -r cp ./*.sh ~/dir/dir1/dir2 tar -cvf ~/dir2.tar ~/dir2 tar -cvJf ~/dir2.tar.xz ~/dir2 mv ~/dir2.tar.xz ~/dir/dir1 tar -xvJf ~/dir1.tar.xz tree ~/dir
- 写一个脚本,包含以下内容:
- 显示/etc/group文件中第五行的内容
- 创建目录/home/ubuntu/copy
- 切换工作路径到此目录
- 赋值/etc/shadow到此目录,并重命名为test
- 将当前目录中test的所属用户改为root
- 将test中其他用户的权限改为没有任何权限
#!/bin/bash
head -5 /etc/group | tail -1
mkdir /home/ubuntu/copy
cd /home/ubuntu/copy
cp /etc/shadow /home/ubuntu/copy
mv /home/ubuntu/copy/shadow /home/ubuntu/copy/test
sudo chown root /home/ubuntu/copy/test
chomd o-w,o-r,o-x /home/ubuntu/copy/test
6.25
已知网址www.hqyj.com,使用expr截取出www、hqyj、com,不能使用cut,不能出现数字
#!/bin/bash
str='www.hqyj.com'
len=`expr length "$str"` #获取字符串长度
echo len=$len
num1=`expr index "$str" "."` #获取第一个.的位置
str1=`expr substr "$str" 1 $((num1-1))` #获取第一个.前面的字符串
echo str1=$str1
str2=`expr substr "$str" $((num1+1)) $(($len-$num1))` #获取第一个.后面的字符串
echo str2=$str2
num2=`expr index "$str2" "."`
str3=`expr substr "$str2" 1 $((num2-1))` #获取第二个.前面与第一个.后面的字符串
echo str3=$str3
str4=`expr substr "$str2" $((num2+1)) $(($len-$num1-num2))` #获取第二个.后面的字符串
echo str4=$str4
输出:
6.27
Ubuntu中统计家目录下.c文件的个数
ls -l ~/*.c | wc -l
终端输入一个.sh文件,判断文件是否由可执行权限,如果有可执行权限运行脚本,没有可执行权限添加可执行权限后,再运行脚本
#!/bin/bash read -p "请输入一个.sh文件:" file if [ -x "$file" ] then bash $file else echo $file "没有可执行权限,故添加可执行权限" chmod 0777 "$file" #chmod u+x bash $file fi
输出当前用户uid和gid,并使用变量接收结果
#!/bin/bash num_u=`id -u` num_g=`id -g` echo uid=$num_u echo gid=$num_g
终端输入年月,判断该月有多少天,考虑闰平年的情况
#!/bin/bash read -p "请输入年份:" year read -p "请输入月份:" month flag=0 if [ $((year%400)) -eq 0 ] || [ $((year%4)) -eq 0 ] && [ $((year%100)) -ne 0 ] then echo "闰年" flag=1 else echo "非闰年" flag=0 fi case $month in 12|10|8|7|1|3|5) echo "该月有31天" ;; 11|9|6|4) echo "该月有30天" ;; 2) echo "该月有$((29-$flag))" ;; *) echo "傻X输入错误" ;; esac
使用for循环,输出九九乘法表( printf "%d * %d = %d" $i $j $((i*j)) )
#!/bin/bash for ((i=1;i<10;i++)) do for((j=1;j<=i;j++)) do printf "%d * %d = %d " $j $i $((i*j)) done echo done
使用for循环,找到家目录下的所有.c文件,如果文件有内容编译该文件,如果文件中没有内容,删除文件
#!/bin/bash for i in `ls /home/ubuntu/*.c` do if [ -s "$i" ] then gcc $i ./a.out else rm $i fi done
数据结构简单例题
7.1
在堆区空间连续申请5个int类型大小空间,用来存放从终端输入的5个学生成绩,然后显示5个学生成绩,再将学生成绩升序排序,排序后,再次显示学生成绩。显示和排序分别用函数完成
#include <myhead.h> void my_display(int * p1) { for(int i=0;i<5;i++) { printf("第%d成绩为%d\n",i+1,p1[i]); } } void my_sort(int * p1) { int temp=0; for(int i=0;i<5;i++) { for(int j=i+1;j<5;j++) { if(p1[i]>p1[j]) { temp=p1[i]; p1[i]=p1[j]; p1[j]=temp; } } } } int main(int argc, const char *argv[]) { int * p1 = (int *)malloc(sizeof(int)*5); for(int i=0;i<5;i++) { printf("请输入第%d个学生成绩:",i+1); scanf("%d",&p1[i]); } my_sort(p1); printf("重新排序后的序列为:\n"); my_display(p1); free(p1); p1 =NULL; return 0; }
输出:
在堆区申请两个长度为32的空间,实现两个字符串的比较【非库函数实现】
要求:
1.定义函数,在堆区申请空间;两个申请,主函数需要调用2次
2.定义函数,实现字符串的输入;void input(char *p)
3. 调用函数实现字符串比较,在主函数中输出大小;int my_strcmp(const char *s1,const char *s2)
4. 定义函数,释放空间
#include <myhead.h> /** * 释放内存空间 * @param p 指向要释放的内存空间的指针 */ void my_free(char * p)//释放空间 { free(p); p=NULL; } /** * 从标准输入读取字符串 * @param str 用于存储输入字符串的缓冲区 */ void str_input(char* str)//字符串输入 { printf("请输入字符串:\n"); fgets(str,sizeof(str),stdin); } /** * 申请内存空间 * @param size 需要申请的内存大小 * @return 返回指向申请的内存空间的指针 */ char* my_malloc(int size)//申请空间大小 { char * p = (char *)malloc(size); if (p == NULL) { // 检查是否分配成功 perror("malloc"); return 1; // 返回错误代码 } return p; } /** * 比较两个字符串的大小 * @param str1 第一个字符串 * @param str2 第二个字符串 * @return 返回值0表示两个字符串相等,1表示str1大于str2,2表示str1小于str2 */ int my_strcmp(char *str1,char *str2)//字符串大小比较 { char *p1=str1; char *p2=str2; while(*p1 == *p2) { if(*p1=='\0' || *p2=='\0') { break; } p1++; p2++; } int dif_value=*p1-*p2; if (dif_value > 0) { return 1; } else if(dif_value == 0) { return 0; } else { return 2; } } int main(int argc, const char *argv[]) { char * p1=my_malloc(32); str_input(p1); char * p2=my_malloc(32); str_input(p2); int size=my_strcmp(p1,p2); switch (size) { case 0: printf("str1=str2\n"); break; case 1: printf("str>str2\n"); break; case 2: printf("str<str2\n"); break; default: printf("输出有误!"); break; } //释放空间 my_free(p1); my_free(p2); return 0; }
输出:
7.2
多文件实现编译
#includ <myhead.h>集成包含的所有需要的头文件
main.c
#include <myhead.h> #include "fun.h" int main(int argc, const char *argv[]) { char *p1 = my_malloc(32); str_input(p1); char *p2 = my_malloc(32); str_input(p2); int size = my_strcmp(p1, p2); switch (size) { case 0: printf("str1=str2\n"); break; case 1: printf("str>str2\n"); break; case 2: printf("str<str2\n"); break; default: printf("输出有误!"); break; } // 释放空间 my_free(p1); my_free(p2); return 0; }
fun.c
#include "fun.h" /** * 释放内存空间 * @param p 指向要释放的内存空间的指针 */ void my_free(char *p) // 释放空间 { free(p); p = NULL; } /** * 从标准输入读取字符串 * @param str 用于存储输入字符串的缓冲区 */ void str_input(char *str) // 字符串输入 { printf("请输入字符串:\n"); fgets(str, sizeof(str), stdin); } /** * 申请内存空间 * @param size 需要申请的内存大小 * @return 返回指向申请的内存空间的指针 */ char *my_malloc(int size) // 申请空间大小 { char *p = (char *)malloc(size); return p; } /** * 比较两个字符串的大小 * @param str1 第一个字符串 * @param str2 第二个字符串 * @return 返回值0表示两个字符串相等,1表示str1大于str2,2表示str1小于str2 */ int my_strcmp(char *str1, char *str2) // 字符串大小比较 { char *p1 = str1; char *p2 = str2; while (*p1 == *p2) { if (*p1 == '\0' || *p2 == '\0') { break; } p1++; p2++; } int dif_value = *p1 - *p2; if (dif_value > 0) { return 1; } else if (dif_value == 0) { return 0; } else { return 2; } }
fun.h
#ifndef __FUN_H__ #define __FUN_H__ #include <myhead.h> void str_input(char *str) ; void my_free(char *p); char *my_malloc(int size); int my_strcmp(char *str1, char *str2) ; #endif
输出:
学生管理系统
题目:
代码:
#include <myhead.h> int menu(int *p); struct student // 学生信息数据结构体 { char name[20]; float score; int age; }; /*************************打印学生信息***************/ int print_student(struct student s[], int *p) { if (*p == 0) { printf("\n当前没有学生信息,请先添加学生信息\n"); return 1; } printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < *p; i++) // 循环输出学生信息 { printf("|第%d个学生信息:姓名:%-5s;年龄:%-3d;分数:%-3.2f\n", i + 1, s[i].name, s[i].age, s[i].score); } printf("====================学生共计%d个====================\n", *p); } /************************返回函数**********************************/ int my_return(int *ret) { if (*ret == 0) { printf("\n终止当前操作;返回上一步操作\n"); return 1; } return 0; } /***********************枚举定义*************************************/ enum option { EXIT = 0, ADD, //=1 DELETE, //=2 MODIFY, //=3 FIND, //=4 SHOW }; /************************界面菜单**********************************************/ int menu(int *p) { static int select; start: printf("\n\t------------------------------------------\n"); printf("\t--------------学生信息管理系统-------------\n"); printf("\t--------------1、增添学生信息--------------\n"); printf("\t--------------2、删除学生信息--------------\n"); printf("\t--------------3、修改学生信息--------------\n"); printf("\t--------------4、查找学生信息--------------\n"); printf("\t--------------5、显示学生信息--------------\n"); printf("\t--------------0、 退出系统 --------------\n"); printf("\t-------------------------------------------\n\n"); printf("请输入你选择:\n"); if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } // 检查是否有未读取的字符(例如,用户输入了"1 "后的其他字符) int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (select >= 0 && select <= 5) { // 判断输入是否正确 if (*p == 0 && select != 1 && select != 0) { printf("\n还未添加学生信息,请添加学生信息!\n"); goto start; } return select; // 正确则返回输入的值 } else { printf("\n输入有误,请重新输入:\n"); goto start; } } /*****************************增添学生信息**********************************/ int add_stu(struct student s[], int *p) { static int num; // 添加学生个数 int *ret = # start: printf("\n请输入你要添加学生信息的个数:(输入‘0’返回上一步操作)\n"); if (scanf("%d", &num) != 1) { while (getchar() != '\n') ; printf("\n输入有误,请重新输入:\n"); goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (num < 1 || num > 50) { if (num == 0) { if (my_return(ret)) // 判断是否返回上一步 { return 1; } } printf("\n输入的学生个数应为1到50之间的整数,请重新输入:\n"); goto start; } int j = 1; for (int i = *p; i < num + *p; i++) // 循环输入学生信息 { struct student temp; // 使用临时结构体存储新学生信息,确保信息完全正确后再复制到数组中 printf("\n请按顺序输入%d个学生信息:姓名,年龄,分数:\n", j++); scanf("%5s %3d %3f", temp.name, &temp.age, &temp.score); // 存入临时结构体中 if (strcmp(temp.name, " ") == 0 || temp.age < 10 || temp.age > 100 || temp.score < 0) // 判断输入是否有效 { printf("\n输入无效,请重新输入。\n"); i--; j--; int c; while ((c = getchar()) != '\n' && c != EOF);//清空缓存区 memset(&temp, 0, sizeof(temp));//清空临时结构体 continue; } // 确认无误后存入结构体 strcpy(s[i].name, temp.name); s[i].age = temp.age; s[i].score = temp.score; } printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < num + *p; i++) // 循环输出学生信息 { printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score); } *p += num; // 添加后的总人数重新赋值 printf("====================学生共计%d个====================\n", *p); } /**************************删除学生信息***********************************/ int delete_stu(struct student s[], int *p) { static int sub_num; // 学生下标 int *ret = &sub_num; start: printf("\n请输入你要删除第几个学生:(输入‘0’返回上一步操作)\n"); if (scanf("%d", &sub_num) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } if (my_return(ret)) // 判断是否返回上一步 { return 1; } if (sub_num <= *p && sub_num > 0) { for (int i = sub_num - 1; i < *p; i++) // 循环覆盖学生信息 { s[i] = s[i + 1]; } printf("\n删除成功。\n"); } else { printf("\n输入有误,请重新输入:\n"); goto start; } *p -= 1; printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < *p; i++) // 循环输出学生信息 { printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score); } printf("====================学生共计%d个====================\n", *p); } /**************************修改学生信息***********************************/ int modify_stu(struct student s[], int *p) { static int sub_num; // 学生下标 int *ret = &sub_num; start: printf("\n请输入你要修改第几个学生:(输入‘0’返回上一步操作)\n"); scanf("%d", &sub_num); if (sub_num > *p || *p == 0 || sub_num == 0) // 判断输入是否正确 { if (my_return(ret)) // 判断是否返回上一步 { return 1; } printf("\n输入有误,请重新输入:\n"); while (getchar() != '\n') ; // 吸收垃圾字符,防止死循环 goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } printf("\n请输入修改后的姓名,年龄,分数。\n"); scanf("%s %d %f", s[sub_num - 1].name, &s[sub_num - 1].age, &s[sub_num - 1].score); printf("\n=================打印学生信息列表====================\n"); for (int i = 0; i < *p; i++) // 循环输出学生信息 { printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score); } printf("====================学生共计%d个====================\n", *p); } /**************************查找学生信息***********************************/ int search_stu(struct student s[], int *p) { static int sub_num; // 学生序号 int *ret = &sub_num; start: printf("\n请输入你要查找的学生序号:(输入‘0’返回上一步操作)\n"); scanf("%d", &sub_num); if (sub_num > *p || sub_num <= 0 || sub_num == 0) // 判断输入是否正确 { if (my_return(ret)) // 判断是否返回上一步 { return 1; } printf("\n输入有误,请重新输入:\n"); while (getchar() != '\n') ; // 吸收垃圾字符,防止死循环 goto start; } int c = getchar(); if (c != '\n') { while (getchar() != '\n') ; // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; } printf("\n该学生信息为:\n"); printf("______________________________________\n"); printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f|\n", s[sub_num - 1].name, s[sub_num - 1].age, s[sub_num - 1].score); printf("——————————————————————————————————————\n"); } /**************************申请空间************************************/ struct student *my_malloc() { struct student *p = (struct student *)malloc(sizeof(struct student) * 50); if (p != NULL) // 判断是否申请成功 { return p; } else { perror("malloc:"); return NULL; } } /**********************主函数****************************/ int main(int argc, const char *argv[]) { struct student *ptr = my_malloc(); int sum = 0; int *p = ∑ while (1) { int select = menu(p); switch (select) { case EXIT: printf("\n已退出系统\n"); return 0; case ADD: // add_stu(ptr, p); if (add_stu(ptr, p)) { continue; } break; case DELETE: if (delete_stu(ptr, p)) { continue; } break; case MODIFY: if (modify_stu(ptr, p)) { continue; } break; case FIND: if (search_stu(ptr, p)) { continue; } break; case SHOW: print_student(ptr, p); break; } } free(ptr); ptr = NULL; return 0; }
输出:
(太多放不下,复制代码自己试试)。
- 遇到问题
在遇到吸收垃圾字符时,只能吸收一个的问题,第二个字符会被下一下scanf吸收,如果输入字符,则会出现死循环。
在上述代码 menu函数 中举例:
原代码:
scanf("%d", &select); if (select >= 0 && select <= 4) // 判断输入是否正确 { return select; // 正确则返回输入的值 } else { printf("输入有误,请重新输入:\n"); getchar(); // 吸收垃圾字符,防止死循环 goto start; }
输出:
改进后代码:
解决了不能吸收多个垃圾字符的情况:if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("输入有误,请重新输入:\n"); goto start; } if (select >= 0 && select <= 4) { // 判断输入是否正确 return select; // 正确则返回输入的值 } else { printf("输入有误,请重新输入:\n"); // 无需单独调用 getchar(),上面的清理逻辑已足够 goto start; }
输出:
添加枚举到switch中增加代码可读性:
enum option{ EXIT=0, ADD, DELETE, MODIFY, FIND } ; ………………………… switch (select) { case EXIT: printf("已退出系统\n"); return 0; case ADD: add_stu(ptr, p); break; case DELETE: delete_stu(ptr, p); break; case MODIFY: modify_stu(ptr, p); break; case FIND: search_stu(ptr, p); break; }
适当使用占位符使输出更加美观:
printf("|姓名:%-5s; 年龄:%-3d; 分数:%-3.2f \n", s[i].name, s[i].age, s[i].score);
在未添加学生信息时仍可以进入其它选项,不符合常识:
设置判断学生数为0 的时候不能进去其它选项
if (select >= 0 && select <= 4) { // 判断输入是否正确 if(*p == 0 && select != 1 && select != 0) { printf("\n还未添加学生信息,请添加学生信息!\n"); goto start; } return select; // 正确则返回输入的值 }
在输入选项时。输入“ 1 11”,下一个scanf会读取“11”,导致达不到想要的效果:
如:
if (scanf("%d", &select) != 1) { while (getchar() != '\n') ; // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } if (select >= 0 && select <= 4) { // 判断输入是否正确 return select; // 正确则返回输入的值 } else { printf("\n输入有误,请重新输入:\n"); // 无需单独调用 getchar(),上面的清理逻辑已足够 goto start; }
错误输出:
修改后代码:
if (scanf("%d", &select) != 1) { while (getchar() != '\n'); // 清理输入缓冲区直到遇到换行符 printf("\n输入有误,请重新输入:\n"); goto start; } // 检查是否有未读取的字符(例如,用户输入了"1 "后的其他字符) int c = getchar(); if (c != '\n') { while (getchar() != '\n'); // 如果有其他字符,继续清理缓冲区 printf("\n输入有误,请仅输入单个数字选项:\n"); goto start; }
输出:
输入学生信息错误直接赋值:
printf("\n请按顺序输入:姓名,年龄,分数。\n"); for (int i = *p; i < num + *p; i++) // 循环输入学生信息 { scanf("%5s %3d %3f", s[i].name, &s[i].age, &s[i].score); }
修改后代码:
新建一个结构体,输入正确后再赋值到原结构体
for (int i = *p; i < num + *p; i++) // 循环输入学生信息 { struct student temp; // 使用临时结构体存储新学生信息,确保信息完全正确后再复制到数组中 // int flag = 0;//标记输入是否有效 printf("\n请按顺序输入:姓名,年龄,分数。\n"); scanf("%5s %3d %3f", temp.name, &temp.age, &temp.score); // 存入临时结构体中 if (strcmp(temp.name, "") == 0 || temp.age < 10 || temp.age > 100 || temp.score < 0) // 判断输入是否有效 { printf("\n输入无效,请重新输入。\n"); i--; continue; } // 确认无误后存入结构体 strcpy(s[i].name, temp.name); s[i].age = temp.age; s[i].score = temp.score; }
重新添加“返回上一步操作”,使界面更加人性化:
首先,我们让
my_return
函数返回一个布尔值,表示是否需要返回菜单。为了保持与原有逻辑的一致性,我们可以在调用者函数中根据这个返回值决定是否重新调用menu
。int my_return(int *ret) { if (*ret == 0) { printf("\n终止当前操作;返回上一步操作\n"); return 1; } return 0; }
调整
add_stu
函数以处理my_return
的返回值接下来,在
add_stu
函数中,根据my_return
的返回值决定是否重新调用menu
。这里,我们需要在add_stu
函数中包含对menu
的调用逻辑,以替代原来my_return
直接调用menu
的行为。int add_stu(struct student s[], int *p) { ..... if (my_return(&num, p)) // 如果需要返回到menu { return 1; // 从add_stu返回,由main函数处理是否重新调用menu } ..... }
main
函数处理add_stu
的返回最后,需要在
main
函数中处理add_stu
的返回值,根据返回值决定是否重新调用menu
。...... case ADD: if(add_stu(ptr, p)) { // 如果add_stu返回1,表示需要重新显示菜单,因此跳过switch-case直接重新开始循环 continue; } break; ......
ps:
此代码没有解决内存溢出问题,在添加学生信息时,可能会造成溢出。(最好利用define定义姓名最大长度);
修改学生信息时,只能全部修改,不能单独修改某一项的信息。
函数运用多个goto 语句导致程序不稳定,老板可能看不惯,慎用。
7.8
C语言实现链表
chain.h
#ifndef __CHAIN_H__
#define __CHAIN_H__
#include <myhead.h>
typedef int Datetyp;
typedef struct node//定义链表结构体
{
union
{
int len;
Datetyp date;
};
struct node *next;
}chainList,*chainPtr;
chainPtr chain_creat();//创建链表
int chain_empyt(chainPtr P);//判空
chainPtr chain_applyfor(Datetyp e);
void chain_show(chainPtr H);//遍历数据
int chain_head_add(chainPtr H,Datetyp e);
int chain_tail_add(chainPtr H,Datetyp e);//尾插
int chain_random_add(chainPtr H,int num,Datetyp e);//随插
int chain_head_del(chainPtr H);//头删
int chain_tail_del(chainPtr H);//尾删
int chain_random_del(chainPtr H,int num);//随删
int chain_modify_loc(chainPtr H,int loc,Datetyp e);//按位置修改
int chain_modify_value(chainPtr H,Datetyp e,Datetyp e2);//按值修改
int chain_find_return(chainPtr H,Datetyp e);//查找值返回节点
int chain_value_reverse(chainPtr H);//节点值反转
void chain_free(chainPtr H);//释放链表
#endif
chain.c
#include "chain.h"
/******创建链表*******/
chainPtr chain_creat()
{
chainPtr P = (chainPtr)malloc(sizeof(chainList));
if (P == NULL)
{
printf("创建失败!\n");
return NULL;
}
printf("链表创建成功!\n");
memset(P, 0, sizeof(chainList));
return P;
}
/****判断链表是否为空*****/
int chain_empyt(chainPtr P)
{
if (P == NULL)
{
printf("判空失败!\n");
return -1;
}
return P->len == 0;
}
/*******申请节点 封装数据*********/
chainPtr chain_applyfor(Datetyp e)
{
// 申请结点
chainPtr P = (chainPtr)malloc(sizeof(chainList));
if (NULL == P)
{
printf("申请节点失败!\n");
return NULL;
}
P->date = e; // 封装数据
P->next = NULL;
return P;
}
/****************头插*******************/
int chain_head_add(chainPtr H, Datetyp e)
{
if (H == NULL)
{
printf("头插失败!\n");
return 0;
}
chainPtr P = chain_applyfor(e);
P->next = H->next;
H->next = P;
H->len++;
return 1;
}
/*******遍历数据***************/
void chain_show(chainPtr H)
{
if (H == NULL || chain_empyt(H))
{
printf("遍历失败!\n");
return;
}
chainPtr q = H;
for (int i = 0; i < H->len; i++)
{
q = q->next;
printf("%d ", q->date);
}
printf("\n");
}
/*******************尾插****************/
int chain_tail_add(chainPtr H, Datetyp e)
{
if (H == NULL)
{
printf("尾插失败!\n");
return -1;
}
chainPtr P = chain_applyfor(e);
chainPtr F = H;
while (F->next != NULL)
{
F = F->next;
}
F->next = P;
P->next = NULL;
H->len++;
return 1;
}
/*******************随插*************************/
int chain_random_add(chainPtr H, int num, Datetyp e)
{
if (H == NULL || num <= 0 || num > H->len)
{
printf("随插失败!\n");
return -1;
}
chainPtr P = chain_applyfor(e);
chainPtr F = H;
for (int i = 0; i < num - 1; i++)
{
F = F->next;
}
P->next = F->next;
F->next = P;
H->len++;
return 1;
}
/************头删*************************/
int chain_head_del(chainPtr H)
{
if (H == NULL || chain_empyt(H))
{
printf("头删失败!\n");
return -1;
}
chainPtr F = H->next;
H->next = F->next;
free(F);
F = NULL;
H->len--;
return 1;
}
/************尾删*****************/
int chain_tail_del(chainPtr H)
{
if (H == NULL || chain_empyt(H))
{
printf("尾删失败!\n");
return -1;
}
chainPtr F = H;
while (F->next != NULL)
{
F = F->next;
}
free(F);
F = NULL;
H->len--;
return 1;
}
/********随删*****************/
int chain_random_del(chainPtr H, int num)
{
if (H == NULL || num <= 0 || num > H->len || chain_empyt(H))
{
printf("随删失败!\n");
return -1;
}
chainPtr F1 = H;
chainPtr F2 = H;
for (int i = 0; i < num - 1; i++)
{
F1 = F1->next;
}
F2 = F1->next;
F1->next = F2->next;
free(F2);
F1 == NULL;
H->len--;
return 1;
}
/*****************按位置修改****************/
int chain_modify_loc(chainPtr H, int loc, Datetyp e)
{
if (H == NULL || loc <= 0 || loc > H->len || chain_empyt(H))
{
printf("修改失败!\n");
return -1;
}
chainPtr F = H;
for (int i = 0; i < loc; i++)
{
F = F->next;
}
F->date = e;
return 1;
}
/**************按值修改*********************/
int chain_modify_value(chainPtr H, Datetyp e, Datetyp e2)
{
if (H == NULL)
{
printf("修改失败!\n");
return -1;
}
chainPtr F = H;
for (int i = 0; i < H->len; i++)
{
F = F->next;
if (F->date == e)
{
F->date = e2;
}
}
return 1;
}
/************查找值返回节点******************/
int chain_find_return(chainPtr H, Datetyp e)
{
if (H == NULL || chain_empyt(H))
{
printf("查找失败!\n");
return -1;
}
chainPtr F = H;
int flag = 0;
for (int i = 0; i < H->len; i++)
{
F = F->next;
if (F->date == e)
{
flag = 1;
return i + 1;
}
}
if (flag == 0)
{
printf("未查到该值!\n");
return -1;
}
}
/**************节点值反转***************/
int chain_value_reverse(chainPtr H)
{
if (H == NULL || chain_empyt(H))
{
printf("链表为空,无法反转!\n");
return -1;
}
chainPtr F = H->next;
chainPtr P = NULL;
chainPtr Q = NULL;
while (F != NULL)
{
Q = F->next;
F->next = P;
P = F;
F = Q;
}
H->next = P;
return 1;
}
/*************释放链表****************/
void chain_free(chainPtr H)
{
if(H==NULL)
{
printf("销毁失败!\n");
return;
}
chainPtr P = H;
chainPtr Q = P->next;
while(Q!=NULL)
{
P=Q;
Q=Q->next;
free(P);
}
printf("销毁成功!\n");
}
main.c
//用于检测代码功能是否完成
#include "chain.h"
int main(int argc, const char *argv[])
{
chainPtr P=chain_creat();//创建链表
chain_head_add(P,10);
chain_head_add(P,20);
chain_head_add(P,30);
chain_head_add(P,40);
chain_head_add(P,50);
chain_head_add(P,60);
chain_head_add(P,70);
chain_head_add(P,80);
chain_show(P);
chain_value_reverse(P);
chain_show(P);
chain_free(P);
return 0;
}
7.9 双向循环链表
简单实现双向循环列表的:删除,插入,销毁等操作;
main.c
#include "Two_waylink.h" int main(int argc, const char *argv[]) { Two_waylinkPtr H=creat_twolink(); head_add(H,10); head_add(H,20); head_add(H,30); head_add(H,40); head_add(H,50); head_add(H,60); show(H); tail_del(H); show(H); tail_add(H,100); show(H); tail_add(H,666); show(H); free_twolink(H); return 0; }
Two_waylink.c
#include "Two_waylink.h" /*********创建双向循环链表**************/ Two_waylinkPtr creat_twolink() { Two_waylinkPtr H=(Two_waylinkPtr)malloc(sizeof(Two_waylink)); if(H==NULL) { printf("创建失败!\n"); return NULL; } // memset(H,0,sizeof(Two_waylink)); H->next = H; H->prior = H; H->len = 0; printf("创建成功\n"); return H; } /*********申请节点*****************/ Two_waylinkPtr creat_node(Datetyp e) { Two_waylinkPtr P = (Two_waylinkPtr)malloc(sizeof(Two_waylink)); if(P==NULL) { printf("申请节点失败!\n"); return NULL; } P->next = NULL; P->prior = NULL; P->data = e; return P; } /*********判空*******************/ int empty(Two_waylinkPtr P) { if(P==NULL) { printf("判空失败!\n"); return -1; } return P->prior == P; } /**************头插********************/ int head_add(Two_waylinkPtr H,Datetyp e) { if(H==NULL) { printf("头插失败!\n"); return -1; } Two_waylinkPtr P=creat_node(e); if(H->next==NULL) { H->next=P; P->prior=H; } else { P->next=H->next; P->prior=H; H->next->prior=P; H->next=P; } H->len++; return 1; } /*******遍历节点**************/ void show(Two_waylinkPtr H) { if(H==NULL|| empty(H)) { printf("遍历失败\n"); return; } Two_waylinkPtr P=H; for(int i=0;i<H->len;i++) { P=P->next; printf("%d ",P->data); } printf("\n"); } /*******************随插入***********************/ int random_add(Two_waylinkPtr H,int loc,Datetyp e) { if(H==NULL||loc>H->len+1 ||loc<1) { printf("插入失败!\n"); return -1; } Two_waylinkPtr P=H; Two_waylinkPtr Q=creat_node(e); for(int i =0;i<loc;i++) { P=P->next; } if(P->next==NULL) { P->next=Q; Q->prior=P; } else { Q->next=P; Q->prior=P->prior; P->prior->next=Q; P->prior=Q; } H->len++; return 1; } /*********************随删除**********************/ int random_del(Two_waylinkPtr H,int loc) { if(H==NULL || loc>H->len||loc<1||empty(H)) { printf("删除失败!\n"); return 0; } Two_waylinkPtr F=H; for(int i=0;i<loc;i++) { F=F->next; } if(F->next==NULL) { F->prior->next=NULL; F->prior=NULL; free(F); F=NULL; } else { F->prior->next=F->next; F->next->prior=F->prior; free(F); F=NULL; } H->len--; return 1; } /***************销毁**************/ void free_twolink(Two_waylinkPtr H) { if(H==NULL) { printf("销毁失败!\n"); return ; } for(int i=0;i<H->len;i++) { random_del(H,1); } free(H); H=NULL; printf("销毁成功!\n"); return; } /********尾删****************/ int tail_del(Two_waylinkPtr H) { if(H==NULL||empty(H)) { printf("尾删失败!\n"); return -1; } Two_waylinkPtr P=H; for(int i=0;i<H->len;i++) { P=P->next; } P->prior->next=H; H->prior=P->prior; P->prior=NULL; H->len--; free(P); P=NULL; return 1; } /*******尾插***********/ int tail_add(Two_waylinkPtr H,Datetyp e) { if(H==NULL) { printf("尾插失败!\n"); return -1; } Two_waylinkPtr F=creat_node(e); Two_waylinkPtr p=H; for (int i = 0; i < H->len; i++) { p=p->next; } p->next=F; F->next=H; F->prior=p; H->prior=F; H->len++; return 1; }
Two_waylink.h
#ifndef __TWO_WAYLINK_H__ #define __TWO_WAYLINK_H__ #include <myhead.h> typedef int Datetyp; typedef struct node { union { int len;//链表长度 Datetyp data;//数据域 }; struct node *next;//向后指针 struct node *prior;//前向指针 }Two_waylink,*Two_waylinkPtr; /*****创建双向循环链表*********/ Two_waylinkPtr creat_twolink(); /*********申请节点*****************/ Two_waylinkPtr creat_node(Datetyp e); /*********判空************/ int empty(Two_waylinkPtr P); /**************头插********************/ int head_add(Two_waylinkPtr H,Datetyp e); /*******遍历节点***********/ void show(Two_waylinkPtr H); /*******************随插入***********************/ int random_add(Two_waylinkPtr H,int loc,Datetyp e); /****************随删除****************/ int random_del(Two_waylinkPtr H,int loc); /***************销毁**************/ void free_twolink(Two_waylinkPtr H); /********尾删****************/ int tail_del(Two_waylinkPtr H); /*******尾插***********/ int tail_add(Two_waylinkPtr H,Datetyp e); #endif
输出:
7.10链式队列的简单实现
queueLink.c
#include "queueLink.h" /****申请链表队列*****/ queuePtr create() { queuePtr Q = (queuePtr)malloc(sizeof(queue)); if (Q == NULL) { printf("申请失败!\n"); return NULL; } Q->head = (NodePtr)malloc(sizeof(Node)); if (Q->head == NULL) { printf("申请失败!\n"); free(Q); Q = NULL; return NULL; } Q->head->len = 0; Q->head->next = NULL; Q->tail = Q->head; printf("申请成功!\n"); return Q; } /******申请节点********/ NodePtr create_node(Datetyp e) { NodePtr N = (NodePtr)malloc(sizeof(Node)); if (N == NULL) { printf("申请失败!\n"); return NULL; } N->next = NULL; N->data = e; return N; } /******判空**********/ int empty(queuePtr Q) { if (Q == NULL) { printf("判空失败!\n"); return -1; } return Q->head == Q->tail; } /********入队**********/ int push(queuePtr Q, Datetyp e) { if (Q == NULL) { printf("入队失败!\n"); return -1; } NodePtr N = create_node(e); Q->tail->next = N; Q->tail = N; Q->head->len++; return 1; } /********遍历*********/ void show(queuePtr Q) { if (Q == NULL || empty(Q)) { printf("遍历失败\n"); } NodePtr N = Q->head; while (N->next != NULL) { N = N->next; printf("%d ", N->data); } putchar(10); } /***********出队(尾删)***********/ int pop_tail(queuePtr Q) { if (Q == NULL || empty(Q)) { printf("出队失败!\n"); return -1; } NodePtr N = Q->head; for (int i = 0; i < Q->head->len - 1; i++) { N = N->next; } free(N->next); N->next = NULL; Q->tail = N; Q->head->len--; return 1; } /***********出队(头删)***********/ int pop_head(queuePtr Q) { if (Q == NULL || empty(Q)) { printf("出队失败!\n"); return -1; } NodePtr N = Q->head; N = N->next; Q->head->next = N->next; N->next = NULL; free(N); Q->head->len--; return 1; } /*****链表长度**********/ int len(queuePtr Q) { if (Q == NULL || empty(Q)) { printf("求长度失败!\n"); return -1; } printf("长度=%d\n", Q->head->len); return Q->head->len; } /********销毁链表***********/ int free_queue(queuePtr Q) { if (Q == NULL) { printf("销毁失败!\n"); return -1; } for (int i = 0; i < Q->head->len; i++) { pop_head(Q); } printf("销毁成功!\n"); return 1; }
queueLink.h
#ifndef __QUEUELINK_H__ #define __QUEUELINK_H__ #include <myhead.h> typedef int Datetyp; typedef struct node { union { int len; Datetyp data; }; struct node *next; } Node, *NodePtr; typedef struct queue { Node *head; Node *tail; } queue, *queuePtr; /****申请链表队列*****/ queuePtr create(); /******申请节点********/ NodePtr create_node(Datetyp e); /******判空**********/ int empty(queuePtr Q); /********入队**********/ int push(queuePtr Q, Datetyp e); /********遍历*********/ void show(queuePtr Q); /***********出队(尾删)***********/ int pop_tail(queuePtr Q); /***********出队(头删)***********/ int pop_head(queuePtr Q); /********销毁链表***********/ int free_queue(queuePtr Q); #endif
main.c
#include "queueLink.h" int main() { queuePtr Q = create(); push(Q, 10); push(Q, 20); push(Q, 30); push(Q, 40); push(Q, 50); show(Q); pop_head(Q); show(Q); free_queue(Q); return 0; }
输出:
文件IO
思维导图
7.15
使用 fputc 和 fgetc 实现文件的拷贝功能;
源代码
#include <myhead.h> int main(int argc, const char *argv[]) { FILE *rfp=fopen(argv[1],"r"); FILE *wfp=fopen(argv[2],"w"); if (wfp==NULL||rfp==NULL) { perror("fopen"); return 1; } unsigned char ch=0;//一定要是无符号字符类型,不写unsigned就跳不出循环 while (1) { ch = fgetc(rfp);//读文本 if (ch==255)//结束跳出循环 { break; } fputc(ch,wfp);//写入文本 } fclose(wfp); fclose(rfp); return 0; }
使用 fprintf 和 fscanf实现文件的拷贝功能;
#include <myhead.h> int main(int argc, const char *argv[]) { FILE *fpr=fopen(argv[1],"r"); FILE *fpw=fopen(argv[2],"w"); if (fpr==NULL || fpw==NULL) { perror("fopen"); return 1; } char ch=0; /* while (feof(fpr)!=1) { fscanf(fpr,"%c",&ch); fprintf(fpw,"%c",ch); } */ while (fscanf(fpr,"%c",&ch) != -1) { fprintf(fpw,"%c",ch); } fclose(fpw); fclose(fpr); return 0; }
使用 fprintf 和 fscanf 实现对结构体数组内数据的提取到文件和从文件再赋值过程;
源代码:
#include <myhead.h> typedef struct Student { char name[20]; int chinese; int math; int english; int physics; int chemical; int biology; } su; int main(int argc, char const *argv[]) { su arr[5] = { {"qqq", 11, 11, 11, 11, 11, 11}, {"www", 22, 22, 22, 22, 22, 22}, {"eee", 33, 33, 33, 33, 33, 33}, {"rrr", 44, 44, 44, 44, 44, 44}, {"ttt", 55, 55, 55, 55, 55, 55}}; FILE *wfp = fopen(argv[1], "w"); if (wfp == NULL) { perror("wfp"); return 1; } for (int i = 0; i < 4; i++) { fprintf(wfp, "%s %d %d %d %d %d %d\n", arr[i].name, arr[i].biology, arr[i].chemical, arr[i].chinese, arr[i].english, arr[i].math, arr[i].physics); } memset(arr, 0, sizeof(arr)); FILE *rfp = fopen(argv[1], "r"); if (rfp == NULL) { perror("rfp"); return 1; } char ptr; int num; fclose(wfp); for (int i = 0; i < 4 ; i++) { fscanf(rfp, "%s %d %d %d %d %d %d\n", arr[i].name, &arr[i].chinese, &arr[i].biology, &arr[i].chemical, &arr[i].english, &arr[i].math, &arr[i].physics); } for (int i = 0; i < 4; i++) { printf("%s %d %d %d %d %d %d\n", arr[i].name, arr[i].biology, arr[i].chemical, arr[i].chinese, arr[i].english, arr[i].math, arr[i].physics); } fclose(rfp); return 0; }
使用 fprintf 和 fscanf 实现对链表数据的提取到文件和从文件再赋值过程;
只需要在原链表上加上函数:
/*********将节点内容写入文档中**********/ int fprintf_chain(chainPtr H,const char *filename) { if (H==NULL)//判断链表是否有效 { printf("数据写入文档失败!\n"); return -1; } FILE *wfp=fopen(filename,"w");//将数据写入到文档中 FILE *rfp=fopen(filename,"r");//将文档的数据写入链表 if (wfp == NULL||rfp==NULL) { perror("fopen"); return -1; } chainPtr W=H; chainPtr r=H; while (W->next!=NULL)//写入数据到文档 { W=W->next; fprintf(wfp,"%d ",W->date); } fclose(wfp);//关闭 for(int i=1;i<=H->len;i++)//清空链表数据 { chain_modify_loc(H,i,0); } printf("清空后:\n"); chain_show(H); while (r->next!=NULL)//从文档中得到数据 { r=r->next; fscanf(rfp,"%d ",&(r->date)); if (feof(rfp)==1)//判断是否到文档最后 { break; } } fclose(rfp); printf("重新填入后:\n"); chain_show(H); return 1; }
主函数代码:
#include "chain.h" int fprintf_chain(chainPtr H,const char *filename); int main(int argc, const char *argv[]) { chainPtr P=chain_creat();//创建链表 chain_head_add(P,10); chain_head_add(P,20); chain_head_add(P,30); chain_head_add(P,40); chain_head_add(P,50); chain_head_add(P,60); chain_head_add(P,70); chain_head_add(P,80); chain_show(P); fprintf_chain(P,argv[1]); //将节点内容写入文档中 chain_free(P); return 0; }
输出:
7.16
使用 fwrite和 fread实现文件的拷贝功能;
#include <myhead.h> int main(int argc, char const *argv[]) { // 打开一个文件以写入模式 FILE* wfp=fopen(argv[2],"w"); // 打开一个文件以读取模式 FILE* rfp=fopen(argv[1],"r"); // 检查文件是否成功打开,如果失败,则输出错误信息并返回1 if (wfp==NULL||rfp==NULL) { perror("fopen"); return 1; } // 定义一个字符数组用于临时存储读取的数据,如果读取多个字节就用数组 char ch; while (1) { // 从输入文件读取一个字符,并检查读取是否成功,如果失败,则返回1 if(fread(&ch,1,1,rfp)==0) { return 1; } // 将读取的字符写入输出文件 fwrite(&ch,1,1,wfp); } // 关闭输入文件 fclose(wfp); // 关闭输出文件 fclose(rfp); return 0; }
fflush的利用
在终端的界面上输出:__-__-__-__ 1秒过后,变成 1_-__-__-__ 再1秒过后,变成 12-__-__-__ 依此类推 经过8秒,最终变成 12-34-56-78 \b 是printf里面,光标向左移动的转义符
#include <myhead.h> /* 主函数:程序入口点,接收命令行参数 */ int main(int argc, char const *argv[]) { /* 初始化显示进度条的框架 */ printf("__-__-__-__"); fflush(stdout); /* 确保输出立即刷新 */ sleep(1); /* 延迟1秒,为动态显示进度创造时间间隔 */ /* 逐个替换框架中的字符,模拟进度更新 */ printf("\b\b\b\b\b\b\b\b\b\b\b"); /* 回退到起始位置 */ fflush(stdout); printf("1"); /* 显示第一个数字 */ fflush(stdout); sleep(1); /* 延迟1秒 */ /* 以下类似,逐步显示进度 */ printf("2-"); fflush(stdout); sleep(1); printf("3"); fflush(stdout); sleep(1); printf("4-"); fflush(stdout); sleep(1); printf("5"); fflush(stdout); sleep(1); printf("6-"); fflush(stdout); sleep(1); printf("7"); fflush(stdout); sleep(1); printf("8"); fflush(stdout); sleep(1); /* 输出换行符,结束进度条显示 */ putchar(10); return 0; /* 程序正常结束 */ }
bmp参数的更改
将一张bmp图片的大小更改成原来的4倍,宽度和高度都要对应的变成原来的2倍,多出来的像素点用黑色填充
bmp图片格式:
#include <myhead.h> int main(int argc, char const *argv[]) { /* 打开命令行参数指定的文件,以读写模式 */ FILE *fp = fopen(argv[1], "r+"); int size, hight, weight; /* 定位到文件的第3个字节,读取图像大小 */ fseek(fp, 2, SEEK_SET); fread(&size, 4, 1, fp); printf("大小=%d字节\n", size); // 读大小 size = size * 4; fseek(fp, 2, SEEK_SET); fwrite(&size, 4, 1, fp); // 大小放大四倍 fseek(fp, 2, SEEK_SET); // 再次读取大小 fread(&size, 4, 1, fp); printf("放大后大小=%d字节\n", size); fseek(fp, 18, SEEK_SET); //读取宽度 fread(&weight, 4, 1, fp); weight = weight * 2;//宽度×2 fseek(fp, 18, SEEK_SET); fwrite(&weight, 4, 1, fp);//重新写入 fseek(fp, 18, SEEK_SET); fread(&weight, 4, 1, fp);//重新读取 fseek(fp, 22, SEEK_SET);//读取长度 fread(&hight, 4, 1, fp); hight = hight * 2; fseek(fp, 22, SEEK_SET); fwrite(&hight, 4, 1, fp);//填入长度 fseek(fp, 22, SEEK_SET); fread(&hight, 4, 1, fp);//重新读取长度 /* 输出图像的基本信息 */ printf("像素=%d * %d\n", weight, hight); /* 定位到文件的第55个字节,准备写入RGB数据 */ fseek(fp, 54, SEEK_SET); unsigned char rgb1[3] = {0, 0, 0}; // 黑色 unsigned char rgb2[3] = {38, 27, 173}; unsigned char rgb3[3] = {0, 166, 255}; // 金色 /* 循环遍历每一行,将红色填充到整个图像 */ for (int i = 0; i > hight; i++) { for (int j = 0; j > weight; j++) { if (i > hight / 3) { fwrite(rgb1, 3, 1, fp); } } } /* 关闭文件 */ fclose(fp); return 0; }
写不了一点!!
7.17
dup的使用
使用dup,dup2实现一下功能 按照如下代码顺序输出 printf("hello\n") printf("world\n") 要求,hello输出到文件中,world输出到终端上
#include <myhead.h> int main(int argc, char const *argv[]) { // 复制标准输出文件描述符,以便后续将输出重定向到其他位置 dup(STDOUT_FILENO); //sdtin sdtout sdterr sdtout // 打开命令行参数指定的文件,以读写方式创建(如果不存在)并清空文件内容 // 文件权限设置为0777,表示所有用户都有读写执行权限 int fp=open(argv[1],O_RDWR|O_CREAT|O_TRUNC,0777); //sdtin sdtout sdterr sdtout fp // 将文件描述符fp复制到标准输出,从此标准输出将写入到指定文件中 dup2(fp,1); //sdtin fp sdterr sdtout fp // 输出字符串"hello"到标准输出,此时标准输出已经重定向到文件中 printf("hello\n"); // 强制刷新标准输出缓冲区,确保"hello"被立即写入到文件中 fflush(stdout); // 将文件描述符3复制到标准输出,用于后续的输出重定向 // 注意:这里假设文件描述符3已经在其他地方被正确设置和准备 dup2(3,STDOUT_FILENO); //sdtin fp sdterr sdtout fp // 输出字符串"Word!"到标准输出,此时标准输出被重定向到新的位置 printf("Word!\n"); // 程序正常结束 return 0; }
通过 opendir、readdir、write、read函数实现拷贝一个文件夹中的所有文件的功能
#include <myhead.h> int main(int argc, char const *argv[]) { /* 打开目录“./11” */ DIR *afp = opendir("./11"); /* 检查目录是否成功打开 */ if (afp == NULL) { perror("opendir"); return 1; } struct dirent *dir; char buf[128]; /* 遍历目录中的每个文件 */ while ((dir = readdir(afp)) != NULL) { /* 忽略当前目录“.”和父目录“..” */ if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) { continue; } char file_write[128]; char open_read[128]; /* 构建原文件的完整路径 */ memset(open_read, 0, sizeof(open_read));//清空数组 strcat(open_read, "./11/");//添加路径 strcat(open_read, dir->d_name);//路径和文件名拼接 /* 打开原文件以只读方式 */ int rfp = open(open_read, O_RDONLY); /* 检查文件是否成功打开 */ if (rfp == -1) { perror("open"); return 1;s } /* 构建新文件的名称 */ memset(file_write, 0, sizeof(file_write)); strcat(file_write, open_read); strcat(file_write, "copy"); /* 打开新文件以写入方式,如果不存在则创建 */ int wfp = open(file_write, O_WRONLY | O_CREAT | O_TRUNC, 0644); /* 从原文件读取数据并写入新文件 */ while (1) { int size = read(rfp, buf, 128); int len = strlen(buf); /* 如果读取结束或读取到空字符串,则退出循环 */ if (size <= 0 || len == 0) { break; } write(wfp, buf, size); } /* 关闭原文件和新文件 */ close(wfp); close(rfp); } /* 关闭目录 */ closedir(afp); return 0; }
7.18
Linux终端中实现伪终端
#include <myhead.h> // 包含自定义头文件或标准库,用于声明后续使用的函数 // 函数功能:从标准输入读取一行文本到缓冲区buf中,并移除末尾的换行符 char *getLine(char *buf, int size) { fgets(buf, size, stdin); // 从标准输入读取一行最多size-1个字符到buf中 int len = strlen(buf); // 获取buf中的字符串长度 if (buf[len - 1] == '\n') { // 如果字符串以换行符结束 buf[len - 1] = 0; // 将换行符替换为字符串终止符,移除换行符 } } int main(int argc, const char *argv[]) { while (1) { // 无限循环,模拟伪终端的持续运行 int retval = fork(); // 创建子进程 if (retval > 0) // 父进程 { wait(0); // 等待任意子进程结束 } else // 子进程 { char *username = getlogin(); // 获取当前登录用户名 char hostname[64] = {0}; // 初始化一个64字节大小的主机名缓存区 char pwd[128] = {0}; // 初始化一个128字节大小的当前工作目录缓存区 gethostname(hostname, 64); // 获取主机名并存储在hostname中 getcwd(pwd, 128); // 获取当前工作目录并存储在pwd中 // 打印欢迎信息,使用ANSI转义序列设置字体颜色和样式 /* \033[ 是转义序列的开始,\033 是 ASCII 字符集中的 Escape 字符(ASCII值为27) 1;32;10m 是一系列的代码,它们之间用分号 ; 1 表示启用高亮(通常是加粗)模式。 32 表示设置前景色为绿色。 \033[0m 是重置所有样式和颜色到默认状态的转义序列。 */ printf("\033[1;32;10m%s@%s\033[0m:\033[1;34;10m%s\033[0m$ ", username, hostname, pwd); fflush(stdout); // 刷新标准输出流,确保信息立即显示 char *arg[20] = {0}; // 初始化一个最多20个元素的命令参数指针数组 // 以下代码用于读取用户输入的命令并解析成参数数组 char cmd[128] = {0}; // 初始化一个128字节大小的命令缓存区 getLine(cmd, 128); // 函数功能:从标准输入读取一行文本到缓冲区buf(cmd)中,并移除末尾的换行符(读取用户输入的命令) char *token = NULL; // 初始化用于存储分割出的命令参数 int i = 0; // 初始化参数索引 do { if (token == NULL) { token = strtok(cmd, " "); // 使用空格分割命令,获取第一个参数 } else { token = strtok(NULL, " "); // 继续分割剩余的参数 } arg[i++] = token; // 将参数添加到arg数组中 } while (token != NULL); // 直到所有参数被分割完毕 // 检查并执行命令 if (strcmp(arg[0], "cd") == 0) { // 如果命令是"cd" chdir(arg[1]); // 改变当前工作目录 } else { execvp(arg[0], arg); // 执行外部命令,arg[0]是命令名,arg是参数数组 // 如果execvp返回,表示命令未找到或执行失败 printf("没有找到该指令\n"); // 输出错误信息 } // 子进程执行完命令后会自动退出,无需return 0; } } return 0; }
7.19
测试错误检查锁和递归锁是否会造成死锁状态
#include <myhead.h> // 定义两个互斥锁,用于线程间的同步和互斥访问 pthread_mutex_t mutex1; pthread_mutex_t mutex2; void *run(void *PTR) { while (1) { // 加锁mutex2,确保线程安全地访问共享资源 pthread_mutex_lock(&mutex2); printf("999\n"); sleep(1); // 解锁mutex2,释放对共享资源的锁定 pthread_mutex_unlock(&mutex2); } return NULL; } int main(int argc, char const *argv[]) { pthread_mutexattr_t mutex_attr; // 设置互斥锁属性为错误检查类型 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK_NP); /* 设置互斥锁属性为递归类型类型 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); */ // 初始化mutex1,使用错误检查类型的互斥锁 pthread_mutex_init(&mutex1, &mutex_attr); // 初始化mutex2,使用相同的错误检查类型的互斥锁 pthread_mutex_init(&mutex2, &mutex_attr); pthread_t id; // 创建一个新的线程并运行run函数 pthread_create(&id, 0, run, NULL); // 分离线程,使其在结束时不需要等待 pthread_detach(id); while (1) { // 加锁mutex1,确保线程安全地访问共享资源 pthread_mutex_lock(&mutex1); printf("666\n"); sleep(1); // 解锁mutex1,释放对共享资源的锁定 pthread_mutex_unlock(&mutex1); } return 0; }
误检查锁和递归锁不会造成死锁现象。
火车过隧道问题
#include <myhead.h> // 定义快速隧道和慢速隧道的常量 #define FAST_TUNNEL 1 #define SLOW_TUNNEL 2 // 初始化互斥锁,注意:这里没有使用PTHREAD_MUTEX_INITIALIZER,需要在main函数中初始化 pthread_mutex_t block1 ; pthread_mutex_t block2 ; // 火车结构体 typedef struct { int id; // 火车ID int type; // 火车型号:1表示复兴号,2表示绿皮车 int tunnel; // 当前使用的隧道:0表示未进入隧道 int speed; // 火车速度(用于模拟通过隧道所需时间) int passed; // 是否已经通过隧道:0表示未通过,1表示已通过 } Train; // 火车数组,初始化火车信息 Train trains[] = { {1, 1, 0, 300, 0}, // 复兴号,速度300,未通过隧道 {2, 1, 0, 300, 0}, // 复兴号,速度300,未通过隧道 {3, 1, 0, 300, 0}, // 复兴号,速度300,未通过隧道 {4, 2, 0, 100, 0}, // 绿皮车,速度100,未通过隧道 {5, 2, 0, 100, 0}, // 绿皮车,速度100,未通过隧道 }; // 线程运行函数 void *run(void *arg) { // 从参数中获取火车索引 int index = *(int *)arg; // 根据索引获取火车实例 Train *train = &trains[index]; // 如果火车已经通过隧道,直接返回,不再执行后续逻辑 if (train->passed) { return NULL; } // 用于锁定隧道的互斥锁指针 pthread_mutex_t *tunnel_lock = NULL; // 当前使用的隧道编号 int tunnel = 0; // 判断火车类型 if (train->type == 1) { // 如果是复兴号 // 尝试锁定快速隧道 pthread_mutex_lock(&block1); tunnel_lock = &block1; tunnel = FAST_TUNNEL; // 如果快速隧道不可用,解锁快速隧道并尝试锁定慢速隧道 if (!tunnel_lock) { pthread_mutex_unlock(&block1); pthread_mutex_lock(&block2); tunnel_lock = &block2; tunnel = SLOW_TUNNEL; } } else { // 如果是绿皮车 // 直接锁定慢速隧道 pthread_mutex_lock(&block2); tunnel_lock = &block2; tunnel = SLOW_TUNNEL; } // 如果成功锁定隧道 if (tunnel_lock != NULL) { // 设置火车当前使用的隧道 train->tunnel = tunnel; // 输出火车开始使用隧道的信息 printf("火车 %d 正在使用隧道 %d。\n", train->id, tunnel); // 模拟火车通过隧道所需的时间(usleep参数单位为微秒) usleep(train->speed * 5000); // 输出火车结束使用隧道的信息 printf("火车 %d 已经离开隧道 %d。\n", train->id, tunnel); // 解锁隧道 pthread_mutex_unlock(tunnel_lock); // 标记火车已经通过隧道 train->passed = 1; } // 结束线程 pthread_exit(NULL); } // 主函数 int main(int argc, char const *argv[]) { // 循环创建线程 int i; pthread_t id[5]; // 初始化互斥锁 pthread_mutex_init(&block1, NULL); pthread_mutex_init(&block2, NULL); for (i = 0; i < 5; i++) { // 创建线程,传入火车索引作为参数 pthread_create(&id[i], NULL, run, (void *)&i); } // 循环等待所有线程结束 for (i = 0; i < 5; i++) { pthread_join(id[i], NULL); } // 清理互斥锁 pthread_mutex_destroy(&block1); pthread_mutex_destroy(&block2); // 返回0表示正常退出 return 0; }
7.24
信号
创建一对父子进程,同时死循环
要求使用 ctrl+c 结束子进程的运行。父进程在回收完子进程的资源之后,输出 "资源回收完毕后" 也结束运行
#include <myhead.h> void handl(int signum) { if (signum == SIGINT) { exit(0); } if (signum == SIGCHLD) { while (waitpid(-1,0,WNOHANG)!=-1); printf("资源回收完毕!\n"); sleep(3); exit(0); } } int main(int argc, char const *argv[]) { signal(SIGCHLD, handl); int res = fork(); if (res == 0) { while (1); } else if (res > 0) { signal(SIGINT, SIG_IGN); while (1) ; } else { perror("fork"); return 1; } return 0; }
有名管道间通信
#include <myhead.h> int main(int argc, char const *argv[]) { if (access("./mkfil0", F_OK) == -1) { mkfifo("./mkfifo", 0644); } int res = fork(); if (res > 0) // 父进程写 { int wfp = open("./mkfifo", O_RDWR); if (wfp == -1) { perror("open_wfd"); exit(0); } while (1) { char buf[64] = {0}; printf("请输入:"); fgets(buf, 64, stdin); write(wfp, buf, sizeof(buf)); usleep(1000); } close(wfp); } else if (res == 0) // 子进程读 { int rfp = open("./mkfifo", O_RDWR); if (rfp == -1) { perror("open_rfd"); exit(0); } while (1) { char buf[64] = {0}; read(rfp, buf, sizeof(buf)); printf("获取的数据为:%s\n", buf); } close(rfp); } else { perror("fork"); return 1; } return 0; }
无名管道
/* 创建一对父子进程,
父进程负责输入长方形的两条边长或者三角形的三边长
子进程负责计算长方形 或者 三角形的面积 */
#include <myhead.h> int main(int argc, char const *argv[]) { int arr[2] = {0}; int res = pipe(arr); if (res == -1) { perror("pipe"); return 1; } res = fork(); if (res > 0) // 父进程--写 { close(arr[0]); while (1) { int buf[4] = {0}; printf("请输入三边长或者两边长:"); scanf("%d %d %d", &buf[0], &buf[1], &buf[2]); while (getchar() != 10) ; write(arr[1], buf, sizeof(buf)); usleep(1000); } } else if (res == 0) // 子进程--读 { close(arr[1]); while (1) { int flag = 0; int arg[4] = {0}; int buf[4] = {0}; read(arr[0], buf, sizeof(buf)); if (buf[2] == 0) { printf("输入的长方形面积:%d*%d=%d\n", buf[0], buf[1], buf[0] * buf[1]); } else { int c = (buf[0] + buf[1] + buf[2]) / 2; int s = sqrt(c * (c - buf[0]) * (c - buf[1]) * (c - buf[2])); printf("输入的三角形面积:%d\n", s); } } } else { perror("fork"); return 1; } return 0; }
无名管道+进程替换
/* 创建2个.c 文件
一个.c 文件负责输入三角形或者长方形的数据
在第一个.c文件替换另一个.c 文件负责计算三角形 或者 长方形的面积
*/
写文件
#include <myhead.h> int main(int argc, char const *argv[]) { int arr[2] = {0}; int res = pipe(arr); if (res == -1) { perror("pipe"); return 1; } res = fork(); if (res > 0) // 父进程--写 { close(arr[0]); while (1) { int buf[4] = {0}; printf("请输入三边长或者两边长:"); scanf("%d %d %d", &buf[0], &buf[1], &buf[2]); while (getchar() != 10) ; write(arr[1], buf, sizeof(buf)); usleep(1000); } } else if (res == 0) // 子进程--读 { execl("./child","child","NULL"); } else { perror("fork"); return 1; } return 0; }
计算文件
#include <myhead.h> int main(int argc, char const *argv[]) { close(4); while (1) { int flag = 0; int arg[4] = {0}; int buf[4] = {0}; read(3, buf, sizeof(buf)); if (buf[2] == 0) { printf("输入的长方形面积:%d*%d=%d\n", buf[0], buf[1], buf[0] * buf[1]); } else { int c = (buf[0] + buf[1] + buf[2]) / 2; int s = sqrt(c * (c - buf[0]) * (c - buf[1]) * (c - buf[2])); printf("输入的三角形面积:%d\n", s); } } return 0; }
有名管道+信号
/* 有2个.c文件,每个.c文件都拥有一对父子进程,总共4个进程 A a B b
现在要求实现一个多米诺骨牌的效果:
按ctrl+c结束a进程的运行,a进程结束运行之前,
通过kill函数向b进程发送SIGINT信号,b进程死亡后,B进程回收b进程的资源后,
使用kill函数向A进程发送SIGTSTP信号后,结束运行。
A进程接受到B进程的SIGTSTP信号后,会后a进程的资源后也结束运行*/
A,a进程
#include <myhead.h> int b_pid; int A_pid; void handle(int signum) { if (signum == SIGINT) { printf("a进程死亡\n"); sleep(1); kill(b_pid, signum); exit(0); } if (signum == SIGTSTP) { printf("A进程死亡\n"); exit(0); } } int main(int argc, char const *argv[]) { if (access("./mkfilo", F_OK) == -1) { mkfifo("./mkfifo", 0644); } int fp = open("./mkfifo", O_RDWR); if (fp == -1) { perror("open"); return 1; } int res = fork(); if (res > 0) // A { signal(SIGINT, SIG_IGN); signal(SIGTSTP, handle); while (1) ; } else // a { char buf[20] = {0}; read(fp, buf, 20); sscanf(buf, "%d", &b_pid); memset(buf, 0, 20); A_pid = getppid(); sprintf(buf, "%d", A_pid); write(fp, buf, sizeof(buf)); signal(SIGINT, handle); close(fp); while (1) ; } return 0; }
B,b进程
#include <myhead.h> int A_pid; void handle(int signum) { if (signum == SIGINT) { printf("b进程死亡\n"); sleep(1); exit(0); } } int main(int argc, char const *argv[]) { if (access("./mkfilo", F_OK) == -1) { mkfifo("./mkfifo", 0644); } int fp = open("./mkfifo", O_RDWR); int res = fork(); if (res > 0) // B { char buf[20] = {0}; sprintf(buf, "%d", res); write(fp, buf, sizeof(buf)); memset(buf, 0, 20); sleep(1); read(fp, buf, 20); close(fp); sscanf(buf, "%d", &A_pid); wait(0); printf("B结束运行\n"); sleep(1); kill(A_pid, SIGTSTP); exit(0); while (1) ; } else // b { signal(SIGINT, handle); while (1) ; } return 0; }
输出:
有名管道聊天
/* 使用有名管道,实现2个进程之间的互相聊天 */
文件一
#include <myhead.h> /* 使用有名管道,实现2个进程之间的互相聊天 */ int main(int argc, char const *argv[]) { if (access("./mkfilo", F_OK) == -1) { mkfifo("./mkfifo", 0644); } if (access("./mkfilo2", F_OK) == -1) { mkfifo("./mkfifo2", 0644); } int wfp = open("./mkfifo", O_RDWR); int rfp = open("./mkfifo2", O_RDONLY); if (wfp == -1 || rfp == -1) { perror("open_wfd"); return 1; } int res = fork(); if (res == 0) { while (1) { char buf[64] = {0}; fgets(buf, 64, stdin); int len = strlen(buf); if (buf[len - 1] == '\n') { buf[len - 1] = '\0'; } write(wfp, buf, sizeof(buf)); usleep(1000); } } else { while (1) { char buf[64] = {0}; read(rfp, buf, sizeof(buf)); printf("对方发来消息:%s\n", buf); } } close(wfp); close(rfp); return 0; }
文件二
#include <myhead.h> /* 使用有名管道,实现2个进程之间的互相聊天 */ int main(int argc, char const *argv[]) { if (access("./mkfilo", F_OK) == -1) { mkfifo("./mkfifo", 0644); } if (access("./mkfilo2", F_OK) == -1) { mkfifo("./mkfifo2", 0644); } int rfp = open("./mkfifo", O_RDWR); int wfp = open("./mkfifo2", O_RDWR); if (wfp == -1 || rfp == -1) { perror("open"); return 1; } int res = fork(); if (res == 0) { while (1) { char buf[64] = {0}; fgets(buf, 64, stdin); int len = strlen(buf); if (buf[len - 1] == '\n') { buf[len - 1] = '\0'; } write(wfp, buf, sizeof(buf)); usleep(1000); } } else { while (1) { char buf[64] = {0}; read(rfp, buf, sizeof(buf)); printf("对方发来消息:%s\n", buf); } } close(wfp); close(rfp); return 0; }
输出:
父子进程判断回文字符串
/* 创建一对父子
父进程负责输入一串字符串
子进程负责判断这串字符串是否为回文字符串 */
#include <myhead.h> int ptr(char buf[]) { int len=strlen(buf); char * p=buf; char * q=buf; q=q+len-1; for (int i = 0; i < len-1; i++) { if (*p==*q) { p++; q--; }else { return 0; } } return 1; } int main(int argc, char const *argv[]) { if (access("./mkfil0", F_OK) == -1) { mkfifo("./mkfifo", 0644); } int res = fork(); if (res > 0) // 父进程写 { int wfp = open("./mkfifo", O_RDWR); if (wfp == -1) { perror("open_wfd"); exit(0); } while (1) { char buf[64] = {0}; printf("\n请输入:"); scanf("%s",buf); write(wfp, buf, sizeof(buf)); usleep(1000); } close(wfp); } else if (res == 0) // 子进程读 { int rfp = open("./mkfifo", O_RDWR); if (rfp == -1) { perror("open_rfd"); exit(0); } while (1) { char buf[64] = {0}; read(rfp, buf, sizeof(buf)); printf("获取的数据为:%s\n", buf); if(ptr(buf)) { printf("%s是回文字符串\n",buf); } else { printf("%s不是回文字符串\n",buf); } } close(rfp); } else { perror("fork"); return 1; } return 0; }
输出:
生产者与消费者模型
/*
有一个线程随机生产苹果和橘子
每秒生产8个苹果或者5个橘子
有4个消费者线程
1,2号线程只消费苹果,1,2号线程每次消费5个苹果
3,4号线程只消费橘子,3,4号线程每次消费3个橘子
*/
#include <myhead.h> int apple = 0, orange = 0; pthread_mutex_t m; pthread_mutex_t m2; pthread_cond_t c1; pthread_cond_t c2; /* 消费苹果的线程函数 */ void *run1(void *ptr) { while (1) { /* 加锁以保护共享资源apple */ pthread_mutex_lock(&m); /* 等待生产者信号 */ pthread_cond_wait(&c1, &m); apple -= 5; printf("1号线程消耗了5个苹果,剩余%d个\n", apple); /* 解锁共享资源 */ pthread_mutex_unlock(&m); sleep(2); } } /* 另一个消费苹果的线程函数 */ void *run2(void *ptr) { while (1) { pthread_mutex_lock(&m); pthread_cond_wait(&c1, &m); apple -= 5; printf("2号线程消耗了5个苹果,剩余%d个\n", apple); pthread_mutex_unlock(&m); sleep(2); } } /* 消费橘子的线程函数 */ void *run3(void *ptr) { while (1) { pthread_mutex_lock(&m2); pthread_cond_wait(&c2, &m2); orange -= 3; printf("3线程消耗了3个橘子,剩余%d个\n", orange); pthread_mutex_unlock(&m2); sleep(2); } } /* 另一个消费橘子的线程函数 */ void *run4(void *ptr) { while (1) { pthread_mutex_lock(&m2); pthread_cond_wait(&c2, &m2); orange -= 3; printf("4号线程消耗了3个橘子,剩余%d个\n", orange); pthread_mutex_unlock(&m2); sleep(2); } } /* 主函数,扮演生产者角色 */ int main(int argc, char const *argv[]) { srand(time(0)); pthread_mutex_init(&m, 0); pthread_mutex_init(&m2, 0); pthread_cond_init(&c1, 0); pthread_cond_init(&c2, 0); pthread_t id1, id2, id3, id4; pthread_create(&id1, 0, run1, 0); pthread_create(&id2, 0, run2, 0); pthread_create(&id3, 0, run3, 0); pthread_create(&id4, 0, run4, 0); while (1) { int flag = rand() % 2; pthread_mutex_lock(&m); pthread_mutex_lock(&m2); if (flag == 1) { apple += 8; printf("---------------------------------------------\n"); printf("生产者了8个苹果,现有苹果%d,橘子%d个\n", apple, orange); } else { orange += 5; printf("---------------------------------------------\n"); printf("生产者了5橘子,现有苹果%d,橘子%d个\n", apple, orange); } /* 当苹果数量足够时,通知消费者线程 */ if (apple >= 5) { pthread_cond_signal(&c1); } /* 当橘子数量足够时,通知消费者线程 */ if (orange >= 3) { pthread_cond_signal(&c2); } pthread_mutex_unlock(&m); pthread_mutex_unlock(&m2); sleep(1); } pthread_join(id1,0); pthread_join(id2,0); pthread_join(id3,0); pthread_join(id4,0); return 0; }
输出:
有一个盘子,盘子里面最多放3个苹果,5个橘子 2个生产者线程,一个每秒放1个苹果,另一个每秒2个橘子 放了苹果就不能放橘子,放了橘子就不能放苹果 2个消费者线程,1号消费者线程每秒消费2个苹果,2号消费者线程,每秒消费3个橘子
#include <myhead.h> /* 全局变量定义,用于存储苹果和橘子的数量 */ int apple = 0, orange = 0; /* 定义两个互斥锁,用于保护苹果和橘子数量的访问 */ pthread_mutex_t m; pthread_mutex_t m2; /* 定义两个条件变量,用于线程间的通信 */ pthread_cond_t c; pthread_cond_t c2; /* run1 函数是消费者线程1的入口函数 */ void *run1(void *ptr) { while (1) { /* 加锁以保护共享资源 */ pthread_mutex_lock(&m); /* 等待条件变量满足 */ pthread_cond_wait(&c, &m); /* 消费苹果 */ apple -= 2; printf("1号线程消耗了2个苹果,剩余%d个\n", apple); /* 释放锁 */ pthread_mutex_unlock(&m); /* 暂停1秒,模拟消费过程 */ sleep(1); } } /* run2 函数是消费者线程2的入口函数 */ void *run2(void *ptr) { while (1) { /* 加锁以保护共享资源 */ pthread_mutex_lock(&m2); /* 等待条件变量满足 */ pthread_cond_wait(&c2, &m2); /* 消费橘子 */ orange -= 3; printf("2线程消耗了3个橘子,剩余%d个\n", orange); /* 释放锁 */ pthread_mutex_unlock(&m2); /* 暂停1秒,模拟消费过程 */ sleep(1); } } /* 主函数是程序的入口点 */ int main(int argc, char const *argv[]) { /* 初始化随机数种子 */ srand(time(0)); /* 初始化互斥锁 */ pthread_mutex_init(&m, 0); pthread_mutex_init(&m2, 0); /* 初始化条件变量 */ pthread_cond_init(&c, 0); pthread_cond_init(&c2, 0); /* 创建两个线程 */ pthread_t id1, id2; pthread_create(&id1, 0, run1, 0); pthread_create(&id2, 0, run2, 0); while (1) { /* 随机决定生产苹果或橘子 */ int flag = rand() % 2; /* 加锁以保护共享资源 */ pthread_mutex_lock(&m); pthread_mutex_lock(&m2); /* 根据当前库存决定是否生产苹果或橘子 */ if (apple >= 3 && orange <= 4) { flag = 0; } else if (apple <= 3 && orange >= 5) { flag = 1; } /* 根据flag决定实际生产什么 */ if (flag == 1) { if (apple >= 3) { continue; } apple += 1; printf("---------------------------------------------\n"); printf("生产者了1个苹果,现有苹果%d,橘子%d个\n", apple, orange); } else { if (orange >= 4) { continue; } orange += 2; printf("---------------------------------------------\n"); printf("生产者了2橘子,现有苹果%d,橘子%d个\n", apple, orange); } /* 如果苹果数量足够,通知消费者线程1 */ /* 当苹果数量足够时,通知消费者线程 */ if (apple >= 2) { pthread_cond_signal(&c); } /* 如果橘子数量足够,通知消费者线程2 */ /* 当橘子数量足够时,通知消费者线程 */ if (orange >= 3) { pthread_cond_signal(&c2); } /* 释放锁 */ pthread_mutex_unlock(&m); pthread_mutex_unlock(&m2); /* 暂停1秒,模拟生产过程 */ sleep(1); } /* 等待线程结束 */ pthread_join(id1, 0); pthread_join(id2, 0); return 0; }
C++
8.6
自己封装一个矩形类(Rect),拥有私有属性:宽度(width)、高度(height),
定义公有成员函数:
初始化函数:void init(int w, int h)
更改宽度的函数:set_w(int w)
更改高度的函数:set_h(int h)
输出该矩形的周长和面积函数:void show()
#include <iostream>
using namespace std;
class Rect{
private:
int width;
int height;
public:
void init(int w, int h);
void set_w(int w);
void set_h(int h);
void show();
};
void Rect::init(int w, int h)
{
this->width=w;
this->height=h;
}
void Rect::set_w(int w)
{
this->width=w;
}
void Rect::set_h(int h)
{
this->height=h;
}
void Rect::show()
{
cout << "面积为" << width*height << endl;
cout << "周长为" << 2*(width+height) << endl;
}
int main()
{
Rect r1;
r1.init(10,20);
r1.show();
r1.set_h(5);
r1.set_w(5);
r1.show();
return 0;
}
8.7
设计一个Per类,类中包含私有成员:姓名、年龄、指针成员身高、体重,再设计一个Stu类,类中包含私有成员:成绩、Per类对象p1,设计这两个类的构造函数、析构函数和拷贝构造函数。
#include <iostream> using namespace std; class Per { private: string name; int age; double *tall; double *weight; public: Per()//无参 { tall=nullptr; weight=nullptr; cout << "Per::无参数构造类型" << endl; } Per(string name,int age,double tall,double weight):name(name),age(age),tall(new double (tall)), weight(new double (weight)) //有参构造函数 { cout << "Per::有参构造函数" << endl; } ~Per()//析构 { delete tall; delete weight; cout << "Per::析构函数" <<endl; } Per (const Per &other):name(other.name),age(other.age),tall(new double (*other.tall)),weight(new double (*other.weight))//拷贝构造函数 { cout << "Per::拷贝构造函数" << endl; } void show ()//打印信息 { cout << "name = " << name <<endl; cout << "age = " << age <<endl; cout << "tall = " << *tall <<endl; cout << "weight = " << *weight <<endl; } }; class Stu { private: double score; Per p1; public: Stu()//无参 { cout << "Stu::无参构造类型" << endl; } Stu(double score,string name,int age,double tall,double weight):score(score),p1(name,age,tall,weight)//有参 { cout << "Stu::有参构造类型" << endl; } ~Stu()//析构函数 { p1.~Per(); cout << "Stu::析构函数" <<endl; } Stu(const Stu &sother ):score(sother.score),p1(sother.p1) //拷贝构造函数 { cout<< "Stu::拷贝构造函数" << endl; } void show()//打印信息 { cout << "score = " << score << endl; p1.show(); } }; int main() { Per p1("陶单单",23,184.99,149.99); cout << "p1:" <<endl; p1.show(); Stu s1(99.9,"黄姑娘",27,174.99,140); cout << "s1:" <<endl; s1.show(); Stu s2(s1); cout << "s2:" <<endl; s2.show(); return 0; }