diff --git a/README.md b/README.md index 28f80e1..cf8c40a 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,5 @@ #### day11: 动态内存申请,内存泄漏,字符串处理函数 #### day12: 字符串处理函数,const,结构体 + +#### day13: 结构体,位域,共用体,枚举,链表操作 diff --git a/day13/ANSI_TMP.c b/day13/ANSI_TMP.c new file mode 100644 index 0000000..58fd156 --- /dev/null +++ b/day13/ANSI_TMP.c @@ -0,0 +1,99 @@ +/* +ANSI转义序列可以用来设置终端中文本的颜色、背景色和其他属性。以下是一些常见的ANSI颜色代码及其对应的颜色值: + +- 前景色(文本颜色): + + - 黑色:`\033[30m` + - 红色:`\033[31m` + - 绿色:`\033[32m` + - 黄色:`\033[33m` + - 蓝色:`\033[34m` + - 紫色:`\033[35m` + - 青色:`\033[36m` + - 白色:`\033[37m` + +- 背景色: + + - 黑色:`\033[40m` + - 红色:`\033[41m` + - 绿色:`\033[42m` + - 黄色:`\033[43m` + - 蓝色:`\033[44m` + - 紫色:`\033[45m` + - 青色:`\033[46m` + - 白色:`\033[47m` + + + +除了前景色和背景色,ANSI转义序列还可以用来设置其他文本属性,例如加粗、下划线、闪烁等。以下是一些常见的ANSI属性代码及其对应的属性: + +- 加粗:`\033[1m` +- 下划线:`\033[4m` +- 闪烁:`\033[5m` +- 反显(交换前景色和背景色):`\033[7m` +- 隐藏(使文本不可见):`\033[8m` + +你可以将这些属性代码与前景色、背景色代码组合使用,以实现更多的效果。例如,要设置红色加粗文本,可以使用`\033[31;1m`。 + + + +除了设置文本颜色、背景色和其他属性,ANSI转义序列还可以用来控制终端的显示,例如清空屏幕、移动光标等。以下是一些常见的ANSI控制代码及其对应的操作: + +- 清空屏幕:`\033[2J` +- 将光标移动到屏幕左上角:`\033[H` 或 `\033[1;1H` +- 将光标向上移动n行:`\033[nA` +- 将光标向下移动n行:`\033[nB` +- 将光标向右移动n列:`\033[nC` +- 将光标向左移动n列:`\033[nD` +- 隐藏光标:`\033[?25l` +- 显示光标:`\033[?25h` + + + +除了常见的ANSI转义序列,还有一些特殊的控制代码,可以用来实现一些高级的终端操作。以下是一些常见的特殊控制代码及其对应的操作: + +- BEL(响铃):`\a` +- ESC(转义字符):`\e` 或 `\033` +- CSI(控制序列引导):`\033[` +- OSC(操作系统命令):`\033]` + +这些特殊控制代码通常用于与终端外部环境进行交互,例如向终端发送提示音、设置终端标题等。其中,OSC代码可以用来向终端发送自定义的命令,以实现一些特殊的功能。例如,可以使用OSC代码设置终端的图标、背景图片等。 + +需要注意的是,这些特殊控制代码可能在不同的终端程序或操作系统中有所不同。如果你想要在程序中使用这些代码,最好先查看终端程序或操作系统的文档,确保代码的兼容性和正确性。 +*/ + +#include +#include + +typedef enum front_color_e +{ + BLACK = 30, // 黑色 + RED, // 红色 + GREEN, // 绿色 + YELLOW, // 黄色 + BLUE, // 蓝色 + PURPLE, // 紫色 + CYAN, // 青色 + WHITE, // 白色 +} Front_Color; // 前景色 + +typedef enum back_color_e +{ + BLACK_B = 40, // 黑色 + RED_B, // 红色 + GREEN_B, // 绿色 + YELLOW_B, // 黄色 + BLUE_B, // 蓝色 + PURPLE_B, // 紫色 + CYAN_B, // 青色 + WHITE_B, // 白色 +} Back_Color; // 背景色 + +typedef enum attr_e +{ + BOLD = 1, // 加粗 + UNDERLINE = 4, // 下划线 + BLINK, // 闪烁 + REVERSE = 7, // 反显 + HIDE, // 隐藏 +} Attr; // 文本属性 \ No newline at end of file diff --git a/day13/d1.c b/day13/d1.c new file mode 100644 index 0000000..e0d40cc --- /dev/null +++ b/day13/d1.c @@ -0,0 +1,19 @@ +// 结构体内存分配 +#include + +struct stu +{ + char sex; + int age; +} lucy; + +int main() +{ + // 是 8 而不是 5,因为结构体内存分配是按照最大的数据类型来分配的 + // 即使是 char 类型,也会分配 4 个字节 + // 但是如果是数组,就会按照数组的大小来分配 + // 例如:char name[10],那么就会分配 10 个字节 + // 本题中,8 = 4 + 4 + printf("lucy size: %lu\n", sizeof(lucy)); + return 0; +} \ No newline at end of file diff --git a/day13/d2.c b/day13/d2.c new file mode 100644 index 0000000..476aea6 --- /dev/null +++ b/day13/d2.c @@ -0,0 +1,19 @@ +#include + +struct +{ + // short m; + char c[13]; + int x[4]; + // long y[10]; + // int y; + // short c; + // char x; +} p1; + +int main() +{ + printf("p1 size: %lu B\n", sizeof(p1)); + printf("%p %p\n", &p1.c, &p1.x); + return 0; +} \ No newline at end of file diff --git a/day13/d3.c b/day13/d3.c new file mode 100644 index 0000000..efcd98e --- /dev/null +++ b/day13/d3.c @@ -0,0 +1,15 @@ +#include +#pragma pack(1) // 手动指定内存对齐,优点:可以减少内存的浪费,缺点:会降低内存的读取速度 +struct +{ + char a; + short b; + int c; +} p1; + +int main() +{ + printf("p1 size: %lu B\n", sizeof(p1)); + printf("%p %p %p\n", &p1.a, &p1.b, &p1.c); + return 0; +} \ No newline at end of file diff --git a/day13/d4.c b/day13/d4.c new file mode 100644 index 0000000..5604a5a --- /dev/null +++ b/day13/d4.c @@ -0,0 +1,24 @@ +// 位段: 是啥? 为啥要用? +// 位段: 用来节省内存空间的 +// 位段: 用来存储多个不同的数据 +// 位段: 用来存储多个不同的数据, 但是这些数据的取值范围都比较小 +// 位段的定义: struct 结构体名 +// { +// 数据类型 变量名: 位数; +// 数据类型 变量名: 位数; +// }; +// 位段的使用: 位段的使用和结构体的使用是一样的 +// 位段的注意事项: 位段的位数不能超过数据类型的位数 +#include +struct +{ + char a : 3; + short b : 9; + short : 1; // 下一个变量的位段从新的存储单元开始 + int c : 19; +} p1; +int main() +{ + printf("p1 size: %lu B\n", sizeof(p1)); + return 0; +} \ No newline at end of file diff --git a/day13/d5.c b/day13/d5.c new file mode 100644 index 0000000..ed2709f --- /dev/null +++ b/day13/d5.c @@ -0,0 +1,33 @@ +#include +#include +#include + +struct +{ + unsigned char a : 2; + unsigned char : 1; // 占位符 + unsigned char b : 2; + unsigned char : 1; // 占位符 + unsigned char c : 2; +} REG; + +int main() +{ + REG.a = 3; + REG.b = 1; + REG.c = 2; + + printf("%ld B\n", sizeof(REG)); // 1 B + char reg = *((char *)®); // 原理: 位段的内存存储是从低位到高位的 + printf("%#hhx\n", reg); // 0x11 + + // 打印二进制 + printf("reg = 0b"); + for (int i = 7; i >= 0; i--) + { + printf("%d", (reg >> i) & 1); + } + printf("\n"); + + return 0; +} \ No newline at end of file diff --git a/day13/d6.c b/day13/d6.c new file mode 100644 index 0000000..727de2f --- /dev/null +++ b/day13/d6.c @@ -0,0 +1,39 @@ +// 共用体 +// 特点: +// 1. 共用体中的所有成员共用一块内存空间 +// 2. 共用体的大小是最大成员的大小 +// 3. 共用体的成员可以是不同的数据类型 +// 只初始化第一个成员 +// 不能同时使用其他的成员 +// 变量中起作用的只有最后一次赋值 +// 在存入一个新的值之前, 会把上一个值覆盖掉 +#include + +union data1_uni +{ + unsigned char a; + short b; + int c; +} data1, data2; + +int main() +{ + printf("%ld\n", sizeof(union data1_uni)); + data1.a = 10; + data1.c = 20; + printf("a = %d, b = %d, c = %d\n", data1.a, data1.b, data1.c); + union data1_uni data2 = {14}; + printf("a = %d, b = %d, c = %d\n", data2.a, data2.b, data2.c); + union data1_uni data3 = {257}; + printf("a = %d, b = %d, c = %d\n", data3.a, data3.b, data3.c); + + union data1_uni data4; + data4.b = 300; + printf("a = %d, b = %d, c = %d\n", data4.a, data4.b, data4.c); + + union data1_uni data5; + data5.c = 32769; + printf("a = %d, b = %d, c = %d\n", data5.a, data5.b, data5.c); + + return 0; +} \ No newline at end of file diff --git a/day13/d7.c b/day13/d7.c new file mode 100644 index 0000000..d0b3b5e --- /dev/null +++ b/day13/d7.c @@ -0,0 +1,30 @@ +// 共用体和结构体结合使用 +// 可以解决不同类型的数据在内存中的存储问题 +#include + +struct REG +{ + unsigned char a : 1; // 低位 + unsigned char b : 1; + unsigned char c : 1; + unsigned char d : 1; + unsigned char e : 1; + unsigned char f : 1; + unsigned char g : 1; + unsigned char h : 1; // 高位 +}; + +union REG_UNI +{ + struct REG reg; + unsigned char ch; +}; + +int main() +{ + union REG_UNI reg1 = {{1, 0, 0, 0, 1, 0, 0, 0}}; + // reg1.ch 可以把每一位的值都取出来,但是不能修改,读取顺序是从低位到高位,倒序读取 0b00001001 + printf("%#x\n", reg1.ch); // 0x11 + + return 0; +} \ No newline at end of file diff --git a/day13/d8.c b/day13/d8.c new file mode 100644 index 0000000..55186c2 --- /dev/null +++ b/day13/d8.c @@ -0,0 +1,43 @@ +// 枚举 enum +#include + +typedef enum front_color_e +{ + BLACK = 30, // 黑色 + RED, // 红色 + GREEN, // 绿色 + YELLOW, // 黄色 + BLUE, // 蓝色 + PURPLE, // 紫色 + CYAN, // 青色 + WHITE, // 白色 +} Front_Color; // 前景色 + +typedef enum back_color_e +{ + BLACK_B = 40, // 黑色 + RED_B, // 红色 + GREEN_B, // 绿色 + YELLOW_B, // 黄色 + BLUE_B, // 蓝色 + PURPLE_B, // 紫色 + CYAN_B, // 青色 + WHITE_B, // 白色 +} Back_Color; // 背景色 + +typedef enum attr_e +{ + BOLD = 1, // 加粗 + UNDERLINE = 4, // 下划线 + BLINK, // 闪烁 + REVERSE = 7, // 反显 + HIDE, // 隐藏 +} Attr; // 文本属性 + +int main() +{ + // 彩色字体测试 + printf("\033[%d;%d;%dmHello, \033[0m\033[%d;%d;%dmworld!\033[0m \n", BOLD, RED, CYAN_B, GREEN, WHITE_B, UNDERLINE); + + return 0; +} \ No newline at end of file diff --git a/day13/d9.c b/day13/d9.c new file mode 100644 index 0000000..42754e8 --- /dev/null +++ b/day13/d9.c @@ -0,0 +1,92 @@ +#include +#include + +// 定义学生信息的数据结构,链表的节点 +typedef struct stu_s +{ + char sid; + char name[32]; + int age; + + struct stu_s *next; +} STU; + +void shows(STU *head) +{ + STU *pi = head; + printf("学号\t姓名\t年龄\n"); + + while (NULL != pi) + { + printf("%d\t%s\t%d\n", pi->sid, pi->name, pi->age); + pi = pi->next; + } +} + +STU *insert_head(STU *head, STU *item) +{ + printf("2 %p\n", head); + + // 实现头部插入 + if (NULL == head) // 如果是第一个节点(空链表) + { + head = item; + } + else + { + item->next = head; // 新节点指向原来的头节点 + head = item; // item 变成新的头节点 + } + printf("3 %p\n", head); + + return head; // 返回头节点,头指针不能改变 +} + +STU *insert_end(STU *head, STU *item) +{ + // 实现尾部插入 + if (NULL == head) // 如果是第一个节点(空链表) + { + head = item; + } + else + { + STU *pi = head; + while (NULL != pi->next) + { + pi = pi->next; + } + pi->next = item; + } + + return head; // 返回头节点,头指针不能改变 +} + +int main() +{ + // 静态生成学生信息的链表 + STU s1 = {1, "张三", 18, NULL}; // 头节点: 参数说明: 学号, 姓名, 年龄, 下一个节点的地址 + STU s2 = {2, "李四", 19, NULL}; + STU s3 = {3, "王五", 20, NULL}; + + STU *head = &s1; // 头节点 + s1.next = &s2; // 头节点指向第二个节点 + s2.next = &s3; // 第二个节点指向第三个节点 + s3.next = NULL; // 第三个节点指向空 + + STU s4 = {4, "赵六", 21}; + head = insert_head(head, &s4); // 头插法,地址会改变,需要返回头节点,主函数需要接收 + + printf("1 %p\n", head); + + shows(head); + + // insert_end(head, &(STU){5, "孙七", 22, NULL}); + head = insert_head(head, &(STU){5, "孙七", 22, NULL}); // 临时变量,存在栈中,函数结束后会被释放,需要返回头节点,主函数需要接收 + + printf("1 %p\n", head); + + shows(head); + + return 0; +} \ No newline at end of file diff --git a/day13/d9_2.c b/day13/d9_2.c new file mode 100644 index 0000000..0470c86 --- /dev/null +++ b/day13/d9_2.c @@ -0,0 +1,107 @@ +#include +#include + +// 定义学生信息的数据结构,链表的节点 +typedef struct stu_s +{ + char sid; + char name[32]; + int age; + + struct stu_s *next; +} STU; + +void shows(STU *head) +{ + STU *pi = head; + printf("学号\t姓名\t年龄\n"); + + while (NULL != pi) + { + printf("%d\t%s\t%d\n", pi->sid, pi->name, pi->age); + pi = pi->next; + } +} + +STU *insert_head(STU *head, STU new_stu) +{ + // 动态创建新节点 + STU *new_node = (STU *)malloc(sizeof(STU)); + *new_node = new_stu; + + // 实现头部插入 + if (NULL == head) // 如果是第一个节点(空链表) + { + head = new_node; + } + else + { + // 头结点变第二个节点,新节点变头结点 + new_node->next = head; // 新节点指向头节点 + head = new_node; // 头指针指向新节点 + } + + return head; // 返回头节点,头指针不能改变 +} + +STU *insert_end(STU *head, STU *item) +{ + // 实现尾部插入 + if (NULL == head) // 如果是第一个节点(空链表) + { + head = item; + } + else + { + STU *pi = head; + while (NULL != pi->next) + { + pi = pi->next; + } + pi->next = item; + } + + return head; // 返回头节点,头指针不能改变 +} + +STU *insert_sort(STU *head, STU *item) +{ + if (NULL == head) + return item; + + if (head->age > item->age) + { + item->next = head; + head = item; + } + else + { + STU *pi = head; + while (NULL != pi->next && pi->next->age < item->age) + { + pi = pi->next; + } + item->next = pi->next; + pi->next = item; + } + + return head; +} + +int main() +{ + // 静态生成学生信息的链表 + STU s1 = {1, "张三", 18, NULL}; // 头节点: 参数说明: 学号, 姓名, 年龄, 下一个节点的地址 + STU s2 = {2, "李四", 19, NULL}; + STU s3 = {3, "王五", 20, NULL}; + + STU *head = NULL; // 头节点 + head = insert_sort(head, &s1); + head = insert_sort(head, &s2); + head = insert_sort(head, &s3); + head = insert_sort(head, &(STU){4, "赵六", 17, NULL}); + + shows(head); + + return 0; +} \ No newline at end of file diff --git a/day13/d9_3_delete.c b/day13/d9_3_delete.c new file mode 100644 index 0000000..5df62f1 --- /dev/null +++ b/day13/d9_3_delete.c @@ -0,0 +1,88 @@ +#include +#include + +// 定义学生信息的数据结构,链表的节点 +typedef struct stu_s +{ + char sid; + char name[32]; + int age; + + struct stu_s *next; +} STU; + +void shows(STU *head) +{ + STU *pi = head; + printf("学号\t姓名\t年龄\n"); + + while (NULL != pi) + { + printf("%d\t%s\t%d\n", pi->sid, pi->name, pi->age); + pi = pi->next; + } +} + +STU *delete_stu(STU *head, char sid) +{ + if (NULL == head) + { + printf("链表为空,无法删除\n"); + return NULL; + } + + if (head->sid == sid) + { + // 如果 head 节点的内存是在堆区分配的,那么需要释放 + // STU *tmp = head; + head = head->next; + // free(tmp); + // return head; + } + else + { + STU *pi = head; + while (NULL != pi->next && pi->next->sid != sid) + { + pi = pi->next; + } + + // 如果没有找到删除的节点 + if (NULL == pi->next) + { + printf("没有找到要删除的节点\n"); + return head; + } + // 找到了要删除的节点 + STU *pn = pi->next; // 要删除的节点 + pi->next = pn->next; // 要删除的节点的前一个节点指向要删除的节点的后一个节点 + free(pn); // 释放要删除的节点 + + printf("删除成功\n"); + // return head; + } + return head; +} + +int main() +{ + // 静态生成学生信息的链表 + STU s1 = {1, "张三", 18, NULL}; // 头节点: 参数说明: 学号, 姓名, 年龄, 下一个节点的地址 + STU s2 = {2, "李四", 19, NULL}; + STU s3 = {3, "王五", 20, NULL}; + + STU *head = &s1; // 头节点 + s1.next = &s2; // 头节点指向第二个节点 + s2.next = &s3; // 第二个节点指向第三个节点 + s3.next = NULL; // 第三个节点指向空 + + printf("1 %p\n", head); + shows(head); + + head = delete_stu(head, 1); + + printf("1 %p\n", head); + shows(head); + + return 0; +} \ No newline at end of file diff --git a/day13/d9_4_find.c b/day13/d9_4_find.c new file mode 100644 index 0000000..380bd37 --- /dev/null +++ b/day13/d9_4_find.c @@ -0,0 +1,86 @@ +#include +#include + +// 定义学生信息的数据结构,链表的节点 +typedef struct stu_s +{ + char sid; + char name[32]; + int age; + + struct stu_s *next; +} STU; + +void shows(STU *head) +{ + STU *pi = head; + printf("学号\t姓名\t年龄\n"); + + while (NULL != pi) + { + printf("%d\t%s\t%d\n", pi->sid, pi->name, pi->age); + pi = pi->next; + } +} + +STU *find_stu(STU *head, char sid) +{ + if (NULL == head) + { + printf("链表为空,无法查找\n"); + return NULL; + } + + if (head->sid == sid) + { + return head; + } + else + { + STU *pi = head; + while (NULL != pi->next && pi->next->sid != sid) + { + pi = pi->next; + } + + // 如果没有找到删除的节点 + if (NULL == pi->next) + { + printf("没有找到要查找的节点\n"); + return NULL; + } + // 找到了要删除的节点 + STU *pn = pi->next; // 要删除的节点 + return pn; + } +} + +int main() +{ + // 静态生成学生信息的链表 + STU s1 = {1, "张三", 18, NULL}; // 头节点: 参数说明: 学号, 姓名, 年龄, 下一个节点的地址 + STU s2 = {2, "李四", 19, NULL}; + STU s3 = {3, "王五", 20, NULL}; + + STU *head = &s1; // 头节点 + s1.next = &s2; // 头节点指向第二个节点 + s2.next = &s3; // 第二个节点指向第三个节点 + s3.next = NULL; // 第三个节点指向空 + + shows(head); + + STU *item = find_stu(head, 2); + if (NULL != item) + { + printf("找到了\n"); + printf("%d\t%s\t%d\n", item->sid, item->name, item->age); + } + else + { + printf("没有找到\n"); + } + + // shows(head); + + return 0; +} \ No newline at end of file