C学习历程的总汇

张开发
2026/4/11 13:07:49 15 分钟阅读

分享文章

C学习历程的总汇
C学习历程的总汇前言在学习C时信息闭塞 没有接触到还有博客这么一个广阔的复习、学习平台 也就没有提交相关博文 但是电子笔记还是有很多的包括 每天的学习笔记 基础数据结构像顺序表 单向链表 双向链表 栈 队列 堆 均进行了模拟实现 小型游戏扫雷 小型通讯录项目 小型游戏贪吃蛇 以及 常用函数的模拟实现像 str系列函数 strcpy strcat strcmp strstr下面是我的每日学习记录笔记12-13—12-1712-13int a0栈自动分配空间直接用链表节点指针只是地址必须手动在堆上申请内存才能让指针指向合法空间进而存储数据。 //作为手动管理的空间//开平方根sqrt()开立方根主要用 cbrt()要表示 “left和right的绝对值等于 1”需要用abs()函数stdlib.h写法是abs(left - right) 1源文件–头文件源文件多文件通过按功能拆分模块如链表操作单独写在list.c工具函数写在utils.c让每个文件只负责一类功能修改 / 定位问题时能直接找到对应文件维护成本大幅降低头文件函数的声明只写函数名、参数、返回值必须放在头文件里。链表的实现主要是 多级指针的移动变化 与 极端节点 情况的处理故其要求指针的知识十分牢靠尤其是多级指针的指向 空指针的理解 避免野指针的出现// 第一行memset (arr, ‘‘, sizeof(char) * 13 * 13);// 第二行memset (arr, (unsigned char)’’, sizeof(char) * 13 * 13);第一行’‘是char类型假设是 ASCII 码42若你的编译器中char是有符号类型默认情况’‘的二进制是00101010转成int后还是42此时memset会用0x2A即’‘填充内存 → 实际效果是对的但存在 “类型不匹配” 的隐患。第二行(unsigned char)’‘显式把’*转成unsigned char再隐式转成int → 保证了传递给memset的是无符号的42完全符合memset的参数要求 → 更规范、更安全。阶段性调试——测试代码别太自信使用例子模拟的过于狭隘没有抓到题眼导致无法兼顾多种情况#includestring.hchar arr[13][13];memset(arr, (unsigned char)‘*’, sizeof(char) * 13 * 13);及时复习真乃神技也图片memset的使用文档多个额外函数的使用规则12-14将一天内学到的记录在记事本里。包括图片内容反正就是达到提醒第二天记得复习的目的bool #if 0 使用文档听讲作业可以补充新的想法与代码运用接下来几天试着使用while循环体来解决问题-----用几天后思考与for的区别听作业可以补漏自己答案不全却显示正确之处图片死循环打草决定十分正确有价值 但不能局限于此----更应该改进尽量使得打草少些就能将题目分析完全 仅打草关键部分其余部分可以自己想到如何编写 ----多思考----。12-15#define的使用无分号#define LLL intLLL main(){return 0;}在链表中释放 “一级指针” 本身退出作用域自动销毁并不会影响它指向的“节点数据”但如果是释放指针指向的节点内存 free§则节点数据会变成 “无效内存”即野指针 / 悬空数据危险内存丢失位操作符 ~ | ^ 按位与| 按位或^ 按位异或~ 按位取反重难点 文档 原反补图片 强制转换 回绕类比思想32位二进制中1的个数 通过类比思想10 进制中 1的个数 从而获得思路12-16指针关于表达式求值有了优先级和结合性表达式也有可能有不同的计算路径导致计算结果的差异。ret (i)(i)(i);只有操作符的优先级和结合性没法确定唯一计算路径所以这个表达式可能因为计算顺序的差异导致结果是不一致的所以表达式是错误的表达式。可以在VS和Linux gcc测试结果可能有差异。全局变量在所有函数外 包括‘主函’ 没有给初始值时编译其会默认将其初始化为0 ------- 局部变量没有给予初始值那么只会是随机的垃圾值。算术表达式 运算符的先后执行顺序变量副作用指针变量的大小一致的原因x86 作为32位机器假设有32根地址总线每根地址线出来的电信号转换成数字信号后是1或者0那我们把32根地址线产生的2进制序列当做一个地址那么一个地址就是32个bit位需要4个字节才能存储。x64 64位机器假设有64根地址线一个地址就是64个二进制位组成的二进制序列存储起来就需要8个字节的空间指针变量的大小就是8个字节。void* p_; //未初始化 野指针int* pa a;printf(“pa %p\n”pa);printf(“pa1 %p\n”,pa1); //地址加4int a 0;void* p a; //int* /void* 用于接收任何类型的指针变量但是不能*p 20解引用赋值反汇编解释作用是将机器码计算机能直接执行的二进制指令逆向转换为汇编指令的过程与 “汇编” 过程相反汇编是把汇编指令转机器码。它的作用是穿透高级语言如 C的语法糖 解释 程序在 CPU / 内存层面的真实执行逻辑—— 比如变量的内存寻址、指针的地址操作、数据的字节级修改对应你之前的short*修改int数组的场景。const int a 10://a具有了常属性(不能被修改了)//a 是不是常量呢?虽然a是不能被修改的但是本质上还是变量//常变量const在的左右侧的区别在左侧p无法更改在右侧p无法更改之外还有注意局部指针的返回自动销毁学至word页面2关于static的局部变量探究性质 微信仅剩文字没有探究12-17二级指针int a 10;int* pa a;int** ppa pa;//二级指针printf(“%d”, **ppa);//两个*解引用指针数组里面存指针为什么指针复习效果这么显著答语言形式构成的时空复杂度在算术表达式中************************注意 问题表达式 的判断************************12-19Size_t的使用size_t file_size 1024 * 1024; // 1MB// strlen 返回 size_tsize_t len strlen(str);// memcpy 的第三个参数是 size_tsize_tcopy_size sizeof(buffer) len ? sizeof(buffer) : len;memcpy(buffer, str,copy_size);在做菜单时使用转移表附有tp解析除法避免除以0perror仅仅打印错误原因不会中止常在顺序表/结构体中用到的技法1.用于大小变动的非定向存在所以以#define 来规则大小如:#define NAME_MAX 20;#define GENDER_MAX 10;2.perror公式化打印错误场景如// 场景1fopen打开不存在的文件 FILE \*fp fopen(text.txt, r); if (fp NULL) { perror(fopen failed); // 自定义提示错误信息 ***return 1;*** }3.释放节点需要指针移动要十分注意内存泄漏指针移动而导致指针先前指向的内存无指针指向应该先释放后移动。原因原先指向的内存数据依然保留但是无法找到const int* p 和 int const * p —— 两者等价12-20m left ((right - left) 1)使用必须加上括号再次复习转移表 图片O1指空间复杂度不额外开辟空间链表访问的关键在于基于分支与循环语句----指针移动与边界处理0.增 删 查 改 插 的情况判断1.逻辑严谨语法正确所有堆内存malloc/calloc/realloc的分配、释放需要手动控制free§; p NULL;if (fp NULL){perror/strerror(fopen failed); return/exit -1; // 终止当前逻辑避免后续错误}豆包快慢多种指针收藏对话12-21#define 较于typedef 前者仅仅是替换如#define pint int*pint p1,p2 ; //err typedef int\* pint pint p1,p2 ;//ok单链表不带头单向不循环链表双链表带头双向循环链表phead:无论链表是空、还是有数据节点phead 本身的指向始终固定指向头节点变化的是头节点的prev/next 成员而非 phead 本身。“node1-next phead; // node1的next指向哨兵”为什么不是“node1-next phead-prev; // node1的next指向哨兵”这是因为“phead-prev”是指针 指向尾节点****************************************************************图片双向链表尾‘插’ 的图解与代码优先改变newnode向外的指针***优先更改指针的的顺序十分重要可能导致 难以找到 内存泄漏等*******************************************************************等到实践链表的重点是 1. 增删查改插 中的删 插增可以用插代替2.创建新链表实现新链表中数据的链接 3.思考是否存在特定技法像先前那样)12-22双向链表中假如说 phead NULL ; // 这不是一个有效的双向链表 所以说双向链表中如果仅仅只有一个哨兵位 称其为“空的双向链表”有的时候有没有必要故意难为自己分情况像switch while 就应该要求自己去多使用 练习好吧while循环体 与switch分支语句太不灵活了而且条件难以控制 尤其是边界条件不明确 导致代码的混乱 可读性同时也会大大下降12-23char str[10] { 0 };虽然说char的类型但是可以初始化—结果为str字符数组内部存储 10个‘ \0 .及时反思自己要做的与自己目标的影响复习了一轮后应当试着 打草 与 写代码时都应该思考—适当减少打草时的付出strncpy(tmp, arr1, a);tmp[a] ‘\0’;注意strcpy不会在字符后自动添加‘\0’因此特定情况下需要手动添加strcmp中除了abc abf之间的比较如果 abc ab那么仍然第一个字符串更大strcpytmp,arr1 2);字符串常量char str “nacbs”) 传入函数后无法修改”库方法“指直接利用特定函数实现strcat 字符串追加函数的前提是目标字符串有足够的空间所以不能str[] { 0 } ;来初始化。strstr(arr_long[ ] , str_short[ ] );for (char killer ’ a ’ ; killer ’ d ’ ; killer //循环体的执行if( (killer ’ a ) (killer ! ’ d ) 2 ) //正确语句12-24试着两天复习一次AddSubMulDiv图片计算器的实现反思做什么任务就按照什么心态 什么顺序 去完成 。不是为了完成某个既定目的去猛冲 目的 只是完成任务前提前制定的粗略行动方向。等到自己真正熟悉了再去规划下一个目标int* parr[11] { NULL }; //将指针数组中的11个元素全部初始化为空指针12-25%lp用于格式化输出 / 输入指针以十六进制显示地址是 64 位系统下对 %p 的扩展是部分编译器对%p的扩展如Gccscanf( %c)读取一个字符%c 前的空格用于跳过输入流中的空白字符避免读取残留的换行 / 空格。但是scanf( %d) 中的空格字符是冗余 无用的在 C 语言中函数参数arr1的声明是void* arr1所以即使你在函数内写了arr1 (int*)arr1;arr1的编译期类型仍然是void*因为参数的类型是由函数声明决定的局部赋值不会改变参数的类型解释如下变量的 “类型” 是由声明决定的一旦声明后就固定了赋值操作无法改变变量本身的类型 —— 你只是把一个 “int*类型的指针值” 存进了 “void*类型的变量” 里但变量的类型依然是void*。图片qsort 函数整形排序模拟实现12-26函数中形参 若是数组要加 ”[ ] “void无法定义变量函数传参 直接写int cmp_ft(const void*, const void*)作为my_qsort的参数有什么问题核心结论是这个写法在语法上不完整、语义上不明确编译器无法识别这是一个 “函数指针参数”会直接报语法错误。C经典址交换char *elmen1 (char *)base j * size;char *elmen2 (char *)base (j 1) * size;if (cmp_all(elmen1elmen2)e){//循环实现址交换 char temp; for(size\_t k 0;k sg\_size;k) { tempelmen1\[K]; elmen1\[k]elmen2\[k]; elmen2\[k]temp; {}本质void* base***[ ]*** 字面是 “存储 void* 指针的数组”每个元素都是能指向任意类型的通用指针函数参数特性C 语言中数组作为函数参数时不会传递整个数组只会传递数组首元素的地址因此 void* base[] 会自动 “退化” 为 void**即指向 void* 类型指针的指针1. 错误写法int* cmp_all(…) 表示“返回int*的函数”而非“函数指针”void my_qsort(…, int* cmp_all(const void*, const void*))2. 正确写法int (*cmp_all)(…) 才是“返回int的函数指针”void my_qsort(…, int (*cmp_all)(const void*, const void*))my_qsort(arr01, (size_t)3, …) → 3本身是整型无需强制转size_tVS2022 会自动隐式转换。图片字符串后自动加‘\0’的情况 1.用双引号 包裹的字符串常量初始化字符数组时编译器会自动在末尾追加’\0’即使数组长度大于字符串长度剩余位置也会初始化为’\0’。2.字符串常量本身在内存中会被编译器自动追加’\0’赋值给char*时指针指向的是**包含’\0’**的内存区域3.大部分字符串库函数string.h会保证结果字符串末尾有’\0’前提是目标数组空间足够像 strcpy strcat4.全局 // 静态字符数组若未初始化编译器会自动将所有元素初始化为’\0’:// 全局数组自动初始化为全\\0 char str5\[10]; static char str6\[5]; // 静态数组同样全\\05.即使是字符串常量初始化但数组长度严格等于字符数编译器不会添加’\0’空间不足总结只有 “双引号字符串常量初始化” 且空间足够时编译器才自动加’\0’其余场景需手动处理。sizeof ( arr 0 ) 首元素地址sizeof(arr[0])首元素 *arr 0)char arr[] “abcdef”;strlen( arr ); 从首元素开始找0x00000000 明显是6char *p “abcdef”;strlen(p);二级指针函数模拟要求 绝对的精准模拟函数实现时不要落下const限制1.使用const体现了一种声明 : 无法更改只读。2.加了const后函数可以同时接收 只读数据比如常量字符串和 可写数据比如普通数组兼容性更好。模拟函数 实现的核心目标是 “展示函数的核心逻辑”教学场景而非打造工业级健壮性的生产级代码arr02[5] ‘\0’; //将数组下标为5的元素赋值‘ \0 ’;12-27数组指针的 “使命” 就是存储数组的内存起始位置是操作整个数组的指针工具。函数模拟实现在C中最重要 无论对于哪个方面strstr返回值为char*而非intstrstr的核心功能是查找子串在字符串中首次出现的起始地址内存地址必须用指针char*表示char* s1, s2; //仅仅s1类型为char* s2类型为char #define PCHAR char效果与其类似 但是 typedef charPCHAR 使用的话则s1 s2均会为char* 类型。明天 模拟实现函数重新学习使用deepseek辅助函参检验强转时考虑类型范围形参传入void* des:指针赋值 无效 (char*)des (char*)des1;这是最核心的错误本质是对 “强制类型转换后的临时指针” 赋值无法真正移动原指针。正确((char*)des);模拟函数的实现1.明确函数功能2.定参量及参量类型3.形参合法性校验4.主题功能的实现注意返回原地址的话需要额外变量存储若用来接收的形参void*若需要指针移动我们必须创建指针变量来进行移动。12-30字符串拷贝strcpy):while( (*dest *src) ) { ; }这段代码是 C 语言中字符串拷贝的经典实现方式核心逻辑是逐字节拷贝 src 指向的内容到 dest直到遇到字符串结束符 ‘\0’数组指针的 “使命” 就是存储数组的内存起始位置是操作整个数组的指针工具。1.sizeof(arr);//整个数组的大小–因为数组名单独放在sizeof内部–虽然指向的仍然是数组首元素地址。2.sizeof(arr);//整个数组的大小3.除此之外所有的数组名都表示首元素地址对二维数组arr[3][4]sizeof(arr[0] 1);//因为数组名没有单独放在sizeof内部—表示地址 4/8—拿到的是第一列第二个元素的地址sizeof(arr);//因为数组名单独放在sizeof内部—第一行地址计算所有元素大小*arr[1]就是访问第二行的所有元素sizeof(arr[3])//计算第四行的大小–因为sizeof仅关注数据类型所有就算越界也可正确算作第四行的地址int a[5] { ····}a 表示:int (*)[ ]一个指针地址为0x1000 0000 其中”0“表示四个字节 那么加20个字节—20 16^0 * 416^1 * 1. 所以得到0x1000 0014若一个整形值1那么就是加1long整形没有固定值但是在VS2022中为4个字节算数 指针 两种计算题型的第一步永远是将变量类型分析明白int (*p)[4] ; //p是数组指针 每个元素是4个整形元素1-1在判断参数类型时使用代换法—防止被变量名字干扰 如void print_arr(int* arr, int row, int col); 其中arr是单个指针类型为int * 没有说明为数组。12-30—1-6void print_arr(int* arr, int row, int col); //arr 是int * 类型 是单个指针scanf( %d,arr[ i ]); //落下了逻辑不充分重新遍历寻找 i -1不是i 0找不到自然直接打印 目的是为了找到需要删除的数字 后 打印#define Add AB //表示Add代替A B 无括号对齐数恒指类型 union中的short[ 7 ]是申请了14个字节的空间监视的特殊情况在后续如果不使用则会将变量优化掉导致监视不到为避免这种情况发生导致误判 可以在后面加一个调用的语句来不让其优化1-4—1-111-4图片联合体的使用memset(puc,0,4) void *memset(void *ptr, int value, size_t num);ptr要填充的内存起始地址此处是 pucvalue填充的字节值此处是 0 若是char 则全部为’ \ 0 注意memset 按单个字节填充即 使传 int 也只会取低 8 位num要填充的字节数此处是 4。结构体中 #pragma pack(1) //设置默认对⻬数为1—“后无’ 结尾”算数 指针 两种计算题型的第一步永远是将变量类型分析明白在判断参数类型时使用代换法—防止被变量名字干扰 如void print_arr(int* arr, int row, int col); 其中arr是单个指针类型为int * 没有说明为数组。位段如果硬要存储过大的数会发生“截断”如1比特大小的位段存放‘3’二进制 0000 0011 中仅保留一个比特的最后一个‘1’存放进去后续的存放凭借类型为单位是从右向左放置union与struct 的重要不同union Un{short s[7];int n;};申请大小为2*7 14个字节1-6瓶颈思路变种水仙花数for突破限制次数的局限for(int j 10;j10000 ; j*10){sum (i/j) * (i%j) ;}enummalloc(100)默认开辟100个字节大小的空间//下列代码的printf使用正确char *GetMemory(void){char *p[ ] “hello world”;return p;}void Test(void){char *str NULL;str GetMemory();printf(str);}1-7以后都要实现结构体对齐断点条件如i 10即时窗口修改变量调试思维训练遇到Bug先做“现场保护”不要急着改代码。先定位到出错的确切行和那一刻的所有相关变量值。问自己三个问题程序执行到这里时我预期的状态应该是什么理论值实际的状态是什么监视窗口值为什么会产生这个差异是前序逻辑错误还是本行理解错误作业题的深度解剖策略将每一道作业题“吃干榨净”第一步完成 按老师要求解出题目。第二步调试与测试 刻意练习调试技巧。 即使代码AC通过也故意制造几个错误如修改边界条件然后用调试器跟踪观察哪里出问题。为自己设计边界测试用例空输入、最大值、最小值、负数等这是克服“漏情况”的最佳训练。第三步归纳 将这道题归纳到你的“个人算法/问题分类库”中。可以是一个简单的笔记软件分类或者就在Gitee仓库里建一个NOTE.md文件。例[链表-反转] 方法1迭代三指针方法2递归。关键点头结点处理、指针修改顺序。相关易错题链接。第四步联想 思考这道题和之前做过的哪道题相似不同点在哪里例如同样是数组操作这道是双指针靠近那道是滑动窗口。这能有效打破“思维固化”。应对牛客社群题目“旁观者学习法” 不需要每题都做。每天花5分钟看群里讨论的题目只思考解题思路如果思路清晰且属于你学过的知识范围可以快速写一下。如果涉及未学知识如复杂DFS/BFS只需要知道“这个问题属于XX算法我将在后续课程中学到”即可然后放过它。你的主航道是你的付费系统课程不要被带偏。学习时段拆分在你能学习的任何6-7小时内模块A主课学习约60%时间 跟随课程进度。模块B调试强化约20%时间 在做作业时强制自己必须使用至少一种高级调试功能条件断点、内存窗口等。模块C深度复习约20%时间 翻阅《C专家编程》中与当前学习章节相关的部分或自测“提问清单”。建立类型错题集按照错误类型进行汇总与每周错题汇总并行待实践格式最短3行就够了坑xxx现象xxx检查点xxx1-8应对牛客社群题目“旁观者学习法” 不需要每题都做。每天花5分钟看群里讨论的题目只思考解题思路如果思路清晰且属于你学过的知识范围可以快速写一下。如果涉及未学知识如复杂DFS/BFS只需要知道“这个问题属于XX算法我将在后续课程中学到”即可然后放过它。你的主航道是你的付费系统课程不要被带偏。建立类型错题集按照错误类型进行汇总与每周错题汇总并行待实践格式最短3行就够了坑xxx现象xxx检查点xxx图片字符串初始化操作malloc是为指针变量分配地址存在分配失败的情况所以需要检查操作 但是若指针变量直接进行存储变量的地址则一定成功复习malloc calloc realloc free assert 的使用realloc变化长度需要强制转换 realloc返回值必需接收枚举值只能为整数非枚举类型为整数 打开文件的文件名字也需要被双引号包括将内存中的数据转换成ASCII码值的形式并以ASCII码值的形式存储的文件就是文本文件数据在内存中以二进制的形式存储在不加以转换的条件下输出到外存就是二进制文件fclose(pfile); // 关键fclose( )会// 1. 刷新缓冲区将缓冲数据写入内核// 2. 关闭文件描述符// 3. 释放FILE结构体在此代码进行完毕之后才可以打开目标文件进行查看若以”只读“的形式打开一个原本拥有内容的文件内容会清空最后操作关闭文件fclose置为空NULLint fputc(int character,FILE* stream)例子fputc(‘a’,pfile);int fgetc(FILE* stream)例子int ch fgetc(pfile);printf(“%c”,ch);char* fgets(char* str,int num,FILE* stream)返回的是str的起始地址 代表将stream指向的文件中的内容转化为字符串存储注意num的值将自动补齐的‘\0’也计算进去了 也就是说只能读取num-1个字符到str中。例子fgets(arr,10,pfile);习惯养成以后不单独写char 而是signed char / unsigned char虽说signed char表示有符号用于代替单个字符但是由于其单个字节大小的范围规范仍然可以用来定义一些变量如temperature**********************unsigned char的0-255表示原始字节的完整范围用于二进制数据处理 **********************颜色/像素值网络协议硬件寄存器signed char的-128~127表示需要正负号的小整数用于有符号的小范围计算某些特定的字符编码关键认知转变char不是字符类型而是1字节整数类型字符只是整数的一种解释方式。类型只是对内存中二进制模式的解释方式。1-91.char a1[ ] “zhang”;int s 18;fprintf(pf, “%s %d”, a1, s);2.char a ‘a’;fprintf(pf, “%c”, a);fprintf 转换为字符串f 开头操作对象为文件 fprintf 写入文件 fscanf 读文件s 开头操作对象为字符串 sprintf 将数据写入到某个字符串 sscanf 字符串解析–后常使用printf 打印特定字符串变量类型fwrite : 写入数据size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);ptr: 指向要写入数据的指针size: 每个数据项的字节大小count: 要写入的数据项数量stream: 文件指针返回值: 成功写入的数据项数量fwrite( buf , sizeof(int) , 4 , pf )fread : 读取数据顺序与写入时的顺序一致size_t fread(void *ptr, size_t size, size_t count, FILE *stream);ptr: 指向存储数据的内存指针size: 每个数据项的字节大小count: 要读取的数据项数量stream: 文件指针返回值: 成功读取的数据项数量fread( buf ,sizeof(int),4,pf );数据的写入与读取要分开操作 输入后关闭 读取时打开读取完关闭 主要原因是指针的位置在写入时发生了改变 \ 反斜杠作为续行符#define MAX 999#define SQUARE(x) (x*x)int main( ){printf(“file:%s line: %d time:%s\n”,FILE,LINE,TIME);printf(“%d\n”, MAX);printf(“%d”, SQUARE(MAX));return 0;}//“FILE”等称为预定义符号习惯养成无论什么时候使用1.#define时都要在其参数上加入()2.避免使用具有副作用的宏参数 如(a)3.宏的定义名称全部大写文件名不一定包含后缀名。文件可以有扩展名后缀名但不是必须的。例如在 Unix/Linux 系统中很多文件没有后缀名但仍然是有效文件。我试着总结一下文本操作fgetsrewind( FILE* stream) ;fgetcfclose先fclose后置为NULL背预处理只会处理 #开头的语句编译阶段只校验语法链接时才会去找实体。·每个步骤的具体操作方式预处理相当于根据预处理指令组装新的C/C程序。经过预处理会产生一个没有头文件都已经被展开了、宏定义都已经替换了没有条件编译指令该屏蔽的都屏蔽掉了没有特殊符号的输出文件这个文件的含义同原本的文件无异只是内容上有所不同。编译将预处理完的文件逐一进行一系列词法分析、语法分析、语义分析及优化后产生相应的汇编代码文件。编译是针对单个文件编译的只校验本文件的语法是否有问题不负责寻找实体。链接通过链接器将一个个目标文件或许还会有库文件链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在此过程中会发现被调用的函数未被定义。需要注意的是链接阶段只会链接调用了的函数/全局变量如果存在一个不存在实体的声明函数声明、全局变量的外部声明但没有被调用依然是可以正常编译执行的。1-10作业笔记stdlib.hrand函数生成0~32767之间的整数rand((unsigned int)time())函数的形参在 栈 中保存整”型“1-14顺序表学习习惯养成无论什么时候使用1.#define时都要在其参数上加入()2.避免使用具有副作用的宏参数 如(a)3.宏的定义名称全部大写4.以后不单独写char 而是signed char / unsigned char5.结构体内存对齐6.3i 防止误用赋值符号 顺序表为什么使用两个源文件一个头文件而不是两个头文件一个源文件头文件.h/.hpp核心作用是声明告诉编译器 “有这个函数 / 变量 / 类它的样子是什么”不负责实现是 “接口说明”源文件.c/.cpp核心作用是定义 / 实现告诉编译器 “这个函数具体怎么执行”是 “接口实现”。初始化传值不行 怎样传地址答psfree需要包含stdlib.hvoid* Fun( )//可以返回任何指针但是会隐式转换为void*问号表达式不直接赋值需要接收者申请空间的监视中误将第一次的申请看成第二次的申请以为仍然丢失了空间导致a内的数据变为随机值long long int 打印使用%l ld函数体void SLFind(Con* s, char name[10]) 我却在char name[10]处传入了int类型的1strcmp返回0表示相等0表示第一个字符串大于第二个传址调用—改变实参转移表的使用1.再开一个.c文件定义转移表2.在.h文件中typedef转换替代 并 声明extern转移表报错可能误报需要编译测试重新运行代码修改字符串if (strcmp(s-a[i].name, name) 0){printf(“此联系人的名字 性别 年龄 电话 地址分别是\n%s %s %d %lld %s\n”,s-a[i].name, s-a[i].gender, s-a[i].age, s-a[i].tel, s-a[i].addres);printf(“请分别输入要修改成的项1.名字 2.性别\n”);//基于原字符串进行scanf的直接修改scanf(“%s%s”, s-a[i].name, s-a[i].gender);return 0;}没有从“暴力拆解”转向“抓核心规律”能用下标还用什么指针1-16单链表笔记复习解释节点的内容分别是什么解释节点之间的相互连接关系解释*pphead解释assert(pphead)尾插的实践练习解释p-next想要脱离自己发挥关键是脱离的程度 不是完全脱离而是基于正确的前提知识脱离1-165.结构体内存对齐解题的代码优化方法的进阶测试才是真正的解题switch中的case语句执行后不会自动中止需要在执行的case主体语句后加上 break .单链表typedef struct Sgle_lian_node{SLDatatype x;Sln* next; //Sln使用错误}Sln;int main(){Sln n;SLInit(n);}与int main(){Sln* n;SLInit(n);}一个函数返回值仅为一个本质为指针变量的类型与指向数据的存储位置间的矛盾单链表重点1.pphead的创建与理解2.节点之间的链接画图3.节点的创建函数遇到代码的轻错误需要逐字逐句的看项目学习方式1.听课时笔记写下核心逻辑的实现—写完后再复盘进行补充后续第二天进行复盘—实现项目的实现2.跟随自己的笔记进行项目实现遇到无法解决的问题查仓库 问AI 答疑老师3.课堂复习看重点不可在基础不牢的情况下凭自己的感觉写代码。关照重点 指针的创建 与 实现函数体基础0.空链表空链表的 “空”核心是链表中没有任何节点表现为链表的头指针比如你代码里的head、plist的值为 NULL没有分配任何节点的堆内存不存在 “有节点但 val 为空” 的情况。1.创建新链表的节点不能强制转换原节点成新节点应当–值复制—空指针2.解决遍历无法到末尾的方式while (cur ! NULL ) //不再是cur-next ! NULL{SLPushFront(cur); //遍历原链表 头插构建新链表cur cur-next;}//链表的最后一个节点的next指针是NULL链表的终止标志。找尾while(ptail-next ! NULL){ptail ptail-next ;}3.“慢一步指针的创建”SLTNode* prev pphead;SLTNodeptail *pphead;while(ptail - next ! NULL){prev ptail;//“慢一步”ptail ptail-next;}assert(pphead);作用确保传入的pphead头指针的地址不是NULL避免后续解引用*pphead时触发空指针错误。画图真乃神技也~*pphead (pphead)-next;//- 优先级高于1-19双向链表修改指向的原则先修改newnode的两个指向再修改其他exit(-1); //使用exit(-1)将警告去除1-23—1-30冒泡排序 N-1 N-2 ……1 时间复杂度O(N^2)while(k–)//进行k次k_–;副作用没有考虑导致循环次数变少系数指固定的倍数或常数2n 中2 是 n 的系数3n² 5n 中3 是 n² 的系数5 是 n 的系数n/2 中1/2 是 n 的系数递归的归不记作时间复杂度递归函数的调用栈是 “先递后归”但每个函数调用本身就是一次基本操作我们统计的是 “调用次数”而不是区分 “递” 或 “归”。函数调用需要建立栈帧你可以把函数调用建立栈帧的过程理解成程序员在办公桌上为临时任务 “开辟专属工作区” —— 每个函数调用都需要独立的空间存放自己的变量、参数、返回地址等struct List* p0 phead, *p1 phead ;写作业怎么遇到稍微一个弯就错1-24假设法针对于两种类似情况的相似代码处理 使用假设让为一种处理方式解决问题的关键在于找到具体矛盾点 然后试着采用对应的方法集如路程差 就采用快慢指针/下标对称 就应该想到倒置缺失 就应该想到相减循环 就让它在适当的位置断开未知量过多 就应该大胆假设未知数又分作变量 常量 两类合理地选择可以快速解决问题函数内部为解决问题而额外申请的空间 称作空间复杂度 此空间复杂度为固定值检查不仔细导致没有检查到创建额外链表建议使用一个节点结构体创建—方便转化写if§ 习惯了导致 写p为空时写成了if§优先更改newnode思想1-25力扣报错位置玄妙 花费2小时修改空指针-访问避免空指针解引用1-26if语句后误加分号初始化后加循环 循环条件没有排除初始化时的情况realloc的扩容分作 原地扩容 与 异地扩容return pst-top 0 ; //简便写法如果正确返回1 反之 返回01-271.预处理 //进行适当的删除后 进行纯代码替换2.编译 将代码转化为CPU能识别的指令3.链接 解决函数与变量引用的过程4.执行 操作系统加载可执行文件到内存CPU 执行机器指令一定要注意好free和访问的先后顺序习惯养成将单独情况纳入群体不要单独处理 因为可能出现难以调和的解决方法字符串s[ 3 ] :while(*s){s;//就可以实现下标的移动}line114 既然使用了Pop那么就应该考虑栈空情况但依然访问line114的情况。所以有if判断空情况1-28QNode* tmp (QNode*)malloc(sizeof(QNode));if (tmp NULL){perror(“QueuePop::malloc”);return;}tmp pg-phead;此代码中的问题是内存泄漏原因既然为tmp分配以堆内存空间却没有继续使用反而使tmp改变指向 指向pg-phead那么原本的堆内存空间就会泄漏写完后一定要喂给AI检查代码数据结构的作业题非常值得做两遍什么类型的数据需要销毁只有通过malloc/calloc/realloc在「堆内存」上分配的数据才需要手动销毁释放栈内存、全局 / 静态内存由系统自动管理无需手动销毁。QueuePop::free(tmp); // 关键释放出队节点避免内存泄漏tmp NULL;以后循环体尽量使用while for的话难以避免副作用1-29switch多语句加{ }default后也要加break default与case的相对位置无关。感觉我目前的复习如课件 方式太低效了没有重点不如去抓重点去复习从每天开始在复习日常后需要找到自己的痛点复习函数栈帧Excel文档1-30试着先学习新知识再复习旧知识链表实现队列的出队操作对头尾指针的影响出队操作一定会修改头指针如果出队之后队列为空需要修改尾指针。1-31在进行字符及字符串的操作时不要只想到%c与%s了还有字符操作函数strcpy strcat……在对字符串学习的部分时仅仅进行了字符串函数的模拟实现未进行实践练习 并且在后续实践练习之中没有想到使用。分母不为0//无论你要创建的什么堆大/小堆 必须执行堆有序的底层逻辑 否则创建了一个错误堆以后不要使用自动窗口了。听模块课 抓重点2-2等差数列求和公式(a1an)n/2读题要仔细2月2日重大错误反思错误位置向下调整算法逻辑不严密我的修改没有毛病 但是基于我创建的逻辑下 不是难以进行修改而是修改后仍然存在逻辑偏差难以准确达到目的导致花费大量时间 其中我秉持以改一下马上成功的思想进行调试 但是改完后依然存在偏差错误原因1逻辑粘连 边界模糊以后应该怎样进行避免前提只要我惯行以后应该怎么做遇到复杂问题别用画图来打草了 用纸缺少刷题练习2-3类比思想32位二进制中1的个数 通过类比思想10 进制中 1的个数 从而获得思路C网站的阅读练习学至C时试着跟写博客“w”读写 为了读和写建议⼀个新的⽂件 建⽴⼀个新的⽂件试着先预习对于绝对距离和的最小化问题中位数比平均数更优//因为中位数对极端值不敏感 如001002-4力扣函数作为接口实现递建立栈帧 归回收栈帧 所以归后如果再次调用相同函数那么栈帧与上一次建立的一致static 静态区非堆区 特别关注多次调用仍保留数据的情况 所以在解题时可以适当使用但在一些特殊情境下就要考虑一下了char arr[18] { “ABD##E#H##CF##G##” };2-5递归忌全类将所有情况细分忌重肯定要处理全部情况 如空树 所以你的代码要在处理特殊情况的情境下实现额外情况分作两部分一条件判断与处理二向下调用调试一深度思考矛盾点 纳入样例进行分析二按顺序进行排雷模板化一由于解决问题就只有那么几种类型所以可以想到以前的解决方式。二某些代码群是固定的不要去改变 变的是单条件型 比如leftresult maxDepth(root-left);rightresult maxDepth(root-right);return 1(leftresultrightresult ? leftresult:rightresult);递归的图是为了找到极点调试调用堆栈(作用仅仅是显示以树头的全体下列情况)—先意识后代码作业里的数据结构题目真是十分经典又精准2-7变量的值传递与址传递看清题目 在力扣中 函数接口提供的变量不一定全部初始化 二叉树的前序遍历中就是针对于不知道应该提供多大空间给 像数组 堆区空间 可以直接申请大量空间而不是去计算需要多少空间申请多少空间在任何情况下的调试都是 意识-代码 而不是代码-意识二叉树的练习题采用递归的方法解决IO题目 I 输入 O 输出学习过程一定要诚实 脚踏实地2-9已知两序(如前序 后序)遍历求另一序的遍历1.(背景)求前序 后序 答案唯一 简单 求后序答案不唯一 难2.(划分区间)根据两序将能够划分的全部明确 明确的程度是亚级 (差一级看图写话)3.(画图)构建整体基本序列 构建左子树 右子树4.(检验)根据两种已经明确的序列其一画图 按图写话 对照另一序列。1-31在进行字符及字符串的操作时不要只想到%c与%s了还有字符操作函数strcpy strcat……在对字符串学习的部分时仅仅进行了字符串函数的模拟实现未进行实践练习 并且在后续实践练习之中没有想到使用。分母不为0//无论你要创建的什么堆大/小堆 必须执行堆有序的底层逻辑 否则创建了一个错误堆以后不要使用自动窗口了。听模块课 抓重点2-2等差数列求和公式(a1an)n/2读题要仔细2月2日重大错误反思错误位置向下调整算法逻辑不严密我的修改没有毛病 但是基于我创建的逻辑下 不是难以进行修改而是修改后仍然存在逻辑偏差难以准确达到目的导致花费大量时间 其中我秉持以改一下马上成功的思想进行调试 但是改完后依然存在偏差错误原因1逻辑粘连 边界模糊以后应该怎样进行避免前提只要我惯行以后应该怎么做遇到复杂问题别用画图来打草了 用纸缺少刷题练习2-3类比思想32位二进制中1的个数 通过类比思想10 进制中 1的个数 从而获得思路C网站的阅读练习学至C时试着跟写博客“w”读写 为了读和写建议⼀个新的⽂件 建⽴⼀个新的⽂件试着先预习对于绝对距离和的最小化问题中位数比平均数更优//因为中位数对极端值不敏感 如001002-4力扣函数作为接口实现递建立栈帧 归回收栈帧 所以归后如果再次调用相同函数那么栈帧与上一次建立的一致static 静态区非堆区 特别关注多次调用仍保留数据的情况 所以在解题时可以适当使用但在一些特殊情境下就要考虑一下了char arr[18] { “ABD##E#H##CF##G##” };2-5递归忌全类将所有情况细分忌重肯定要处理全部情况 如空树 所以你的代码要在处理特殊情况的情境下实现额外情况分作两部分一条件判断与处理二向下调用调试一深度思考矛盾点 纳入样例进行分析二按顺序进行排雷模板化一由于解决问题就只有那么几种类型所以可以想到以前的解决方式。二某些代码群是固定的不要去改变 变的是单条件型 比如leftresult maxDepth(root-left);rightresult maxDepth(root-right);return 1(leftresultrightresult ? leftresult:rightresult);递归的图是为了找到极点调试调用堆栈(作用仅仅是显示以树头的全体下列情况)—先意识后代码作业里的数据结构题目真是十分经典又精准2-7变量的值传递与址传递看清题目 在力扣中 函数接口提供的变量不一定全部初始化 二叉树的前序遍历中就是针对于不知道应该提供多大空间给 像数组 堆区空间 可以直接申请大量空间而不是去计算需要多少空间申请多少空间在任何情况下的调试都是 意识-代码 而不是代码-意识二叉树的练习题采用递归的方法解决IO题目 I 输入 O 输出学习过程一定要诚实 脚踏实地2-9已知两序(如前序 后序)遍历求另一序的遍历1.(背景)求前序 后序 答案唯一 简单 求后序答案不唯一 难2.(划分区间)根据两序将能够划分的全部明确 明确的程度是亚级 (差一级看图写话)3.(画图)构建整体基本序列 构建左子树 右子树4.(检验)根据两种已经明确的序列其一画图 按图写话 对照另一序列。2-20—2-21双指针法合并一个有序数组 分割为leftmidmid1right最好的方式是创建第二方数组接收 并且C代码很锉现在代码群理解而不是单行理解稳定性稳定相同的值在排序后的相对位置不变不稳定相同的值在排序后的相对位置改变1.三路划分(解决含大量 重复数据排序)小的放在左 相等的在中间 大的在右边left cur …… right cur遇小于key于left交换curleftcur为left1 cur遇大于key与right交换right-- right为n-1 cur遇到等于key那么cur 截止curright习惯养成特别区分left left2.内省排序 (IntroSort) (解决递归深度过深的问题) (工业常用)depth计算深度 默认当深度超过2*logN采用其他排序如堆排序因其对所有递归函数均适用而被广泛应用3.文件归并排序外排序数据量极大内存 放不下 要使用 磁盘空间 因为数组下标访问的磁盘属性极大地限制了访问速度等所以要采用内排序的方式(如归并排序 归并排序可进行内排序的核心是 读取与拷贝 极大地增加了灵活性)内排序[杭哥板书图示]()scanf printf 对象终端/控制台fscanf fprintf 对象文本文件excel文档单词积累

更多文章