C学习历程的总汇

C学习历程的总汇

前言:在学习C时信息闭塞 没有接触到还有"博客"这么一个广阔的复习、学习平台 也就没有提交相关博文 但是电子笔记还是有很多的包括 每天的学习笔记 基础数据结构像顺序表 单向链表 双向链表 队列 均进行了模拟实现 小型游戏扫雷 小型通讯录项目 小型游戏贪吃蛇 以及 常用函数的模拟实现像 str系列函数 strcpy strcat strcmp strstr

下面是我的每日学习记录笔记

12-13---12-17

12-13

int a=0:栈自动分配空间,直接用;

链表节点:指针只是地址,必须手动在堆上申请内存,才能让指针指向合法空间,进而存储数据。 //作为手动管理的空间//

开平方根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的参数要求 → 更规范、更安全。

阶段性调试------测试代码(别太自信)

使用例子模拟的过于狭隘,没有抓到题眼,导致无法兼顾多种情况

#include<string.h>

char arr[13][13];

memset(arr, (unsigned char)'*', sizeof(char) * 13 * 13);

及时复习真乃神技也

图片:memset的使用

文档:多个额外函数的使用规则

12-14

将一天内学到的,记录在记事本里。包括图片内容,反正就是达到提醒第二天记得复习的目的

bool #if 0 使用文档

听讲作业可以补充新的想法与代码运用

接下来几天试着使用while循环体来解决问题-----用几天后思考与for的区别

听作业可以补漏自己答案不全却显示正确之处

图片:死循环

打草决定十分正确,有价值 但不能局限于此----更应该改进尽量使得打草少些就能将题目分析完全 (仅打草关键部分,其余部分可以自己想到如何编写 ----多思考----)。

12-15

#define的使用:(无分号)

#define LLL int

LLL 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("pa+1 = %p\n",pa+1); //地址加4

int 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-19

Size_t的使用:

  • size_t file_size = 1024 * 1024; // 1MB

// strlen 返回 size_t

  • size_t len = strlen(str);

    // memcpy 的第三个参数是 size_t

  • size_t copy_size = (sizeof(buffer) < len ? sizeof(buffer) : len);

  • memcpy(buffer, str, copy_size);

在做菜单时使用转移表 (附有tp解析

除法避免除以0

perror仅仅打印错误原因 不会中止!!!

常在顺序表/结构体中用到的技法:
1.用于大小变动的非定向存在所以以#define 来规则大小如:

#define NAME_MAX 20;

#define GENDER_MAX 10;

2.perror公式化打印错误场景如:
复制代码
// 场景1:fopen打开不存在的文件

FILE \*fp = fopen("text.txt", "r");

if (fp == NULL) {

    perror("fopen failed"); // 自定义提示+错误信息

    ***return 1;***

}

3.释放节点需要指针移动:

要十分注意内存泄漏(指针移动而导致指针先前指向的内存无指针指向 )应该先释放后移动。原因:原先指向的内存数据依然保留但是无法找到!!!

const int* p 和 int const * p ------ 两者等价

12-20

m = left + ( (right - left) >>1 ) !!!!:使用">>"必须加上括号!!!!

再次复习转移表 图片

O(1):指空间复杂度不额外开辟空间

链表访问的关键在于:基于分支与循环语句----指针移动与边界处理

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-23

char str[10] = { 0 };

虽然说char的类型但是可以初始化---结果为str字符数组内部存储 10个' \0 '.

及时反思自己要做的与自己目标的影响

复习了一轮后应当试着 打草 与 写代码时都应该思考---适当减少打草时的付出

strncpy(tmp, arr1, a);

tmp[a] = '\0';

注意:strcpy不会在字符后自动添加'\0'因此特定情况下需要手动添加

复制代码
strcmp中除了abc  abf之间的比较如果  abc  ab那么仍然第一个字符串更大

strcpy(tmp,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

试着两天复习一次

Add

Sub

Mul

Div

图片:计算器的实现

反思:

做什么任务就按照什么心态 什么顺序 去完成 。不是为了完成某个既定目的去猛冲 目的 只是完成任务前提前制定的粗略行动方向。等到自己真正熟悉了再去规划下一个目标

int* parr[11] = { NULL }; //将指针数组中的11个元素全部初始化为空指针


12-25

%lp:用于格式化输出 / 输入指针(以十六进制显示地址),是 64 位系统下对 %p 的扩展;(是部分编译器对%p的扩展如:Gcc

scanf(" %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(elmen1,elmen2)>e)

{

复制代码
//循环实现址交换

char temp;

for(size\_t k = 0;k <sg\_size;k++)

	{

	temp=elmen1\[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*))

  1. 正确写法:int (*cmp_all)(...) 才是"返回int的函数指针"

void my_qsort(..., int (*cmp_all)(const void*, const void*))

my_qsort(arr01, (size_t)3, ...) → 3本身是整型,无需强制转size_t(VS2022 会自动隐式转换)。

图片

字符串后自动加'\0'的情况 :

1.用双引号" "包裹的字符串常量初始化字符数组时,编译器会自动在末尾追加'\0',即使数组长度大于 字符串长度,剩余位置也会初始化为'\0'

2.字符串常量本身在内存中会被编译器自动追加'\0',赋值给char*时指针指向的是**包含'\0'**的内存区域:

3.大部分字符串库函数(<string.h>)会保证结果字符串末尾'\0'(前提是目标数组空间足够):像 strcpy strcat

4.全局 // 静态字符数组若未初始化,编译器会自动将所有元素初始化为'\0':

复制代码
   // 全局数组:自动初始化为全'\\0'

	      char str5\[10];

	      static char str6\[5]; // 静态数组:同样全'\\0'

5.即使是字符串常量初始化,但数组长度严格等于字符数,编译器不会添加'\0'(空间不足):

总结:只有 "双引号字符串常量初始化" 且空间足够时,编译器才自动加'\0',其余场景需手动处理。

sizeof ( arr + 0 ) 首元素地址

sizeof(arr[0]) 首元素 === *(arr + 0)

char arr[] = "abcdef";

strlen( &arr ); 从首元素开始找0x00000000 明显是6

char *p ="abcdef";

strlen(&p); 二级指针

函数模拟要求 绝对的精准

模拟函数实现时不要落下const限制:

1.使用const体现了一种声明 : 无法更改只读。

2.加了const后,函数可以同时接收 只读数据(比如常量字符串)和 可写数据(比如普通数组),兼容性更好。

模拟函数 实现的核心目标是 "展示函数的核心逻辑"(教学场景),而非打造工业级健壮性的生产级代码

arr02[5] = '\0'; //将 数组下标为5 的元素赋值' \0 ';

12-27

数组指针的 "使命" 就是存储数组的内存起始位置,是操作整个数组的指针工具。


函数模拟实现在C中最重要 无论对于哪个方面!!


  1. strstr(返回值为char*,而非int)
    strstr的核心功能是查找子串在字符串中首次出现的起始地址,内存地址必须用指针(char*)表示

char* s1, s2; //仅仅s1类型为char* s2类型为char #define PCHAR char效果与其类似 但是 typedef char PCHAR 使用的话则s1 s2均会为char* 类型。

明天: 模拟实现函数重新学习(使用deepseek辅助)

函参检验

强转时考虑类型范围

形参传入void* des:

指针赋值 无效 :(char*)des = (char*)des+1;

这是最核心的错误,本质是对 "强制类型转换后的临时指针" 赋值,无法真正移动原指针。

正确:

((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 * 4+16^1 * 1. 所以得到0x1000 0014

若一个整形值+1那么就是加1

long整形没有固定值但是在VS2022中为4个字节

算数 指针 两种计算题型的第一步永远是将变量类型分析明白

int (*p)[4] ; //p是数组指针 每个元素是4个整形元素

1-1

在判断参数类型时使用代换法---防止被变量名字干扰 如:

void print_arr(int* arr, int row, int col); 其中arr是单个指针类型为int * 没有说明为数组。

12-30---1-6

void print_arr(int* arr, int row, int col); //arr 是int * 类型 是单个指针

scanf(" %d",arr[ i ]); //落下了&

逻辑不充分

重新遍历寻找 i = -1;不是i = 0;

找不到自然直接打印 目的是为了找到需要删除的数字 后 打印

#define Add A+B //表示Add代替A +B (无括号

对齐数恒指类型 union中的short[ 7 ]是申请了14个字节的空间

监视的特殊情况:

在后续如果不使用则会将变量优化掉导致监视不到

为避免这种情况发生导致误判 可以在后面加一个调用的语句来不让其优化

1-4---1-11

1-4

图片:联合体的使用

memset(puc,0,4) :

void *memset(void *ptr, int value, size_t num);

ptr:要填充的内存起始地址(此处是 puc);

value:填充的字节值(此处是 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;j<10000 ; j*=10)

{

sum += (i/j) * (i%j) ;

}

enum

malloc(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

检查点:xxx

1-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

置为空NULL

int 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-9

char a1[ ] = "zhang";

int s = 18;

fprintf(pf, "%s %d", a1, s);

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 系统中,很多文件没有后缀名,但仍然是有效文件。

我试着总结一下文本操作:

fgets

rewind( FILE* stream) ;

fgetc

fclose

先fclose

后置为NULL

背:

预处理只会处理 #开头的语句,编译阶段只校验语法,链接时才会去找实体。

·每个步骤的具体操作方式:

预处理:相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有头文件(都已经被展开了)、宏定义(都已经替换了),没有条件编译指令(该屏蔽的都屏蔽掉了),没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。

编译:将预处理完的文件逐一进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。编译是针对单个文件编译的,只校验本文件的语法是否有问题,不负责寻找实体。

链接:通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在此过程中会发现被调用的函数未被定义。需要注意的是,链接阶段只会链接调用了的函数/全局变量,如果存在一个不存在实体的声明(函数声明、全局变量的外部声明),但没有被调用,依然是可以正常编译执行的。

1-10

作业笔记

<stdlib.h>

rand函数生成0~32767之间的整数

rand((unsigned int)time())

函数的形参在 栈 中保存

整"型"

1-14顺序表学习

习惯养成:

无论什么时候使用

1.#define时都要在其参数上加入"(())"

2.避免使用具有副作用的宏参数 如:(a++)

3.宏的定义名称全部大写

4.以后不单独写char 而是signed char / unsigned char

5.结构体内存对齐

6.3==i 防止误用赋值符号" = "

顺序表为什么使用两个源文件一个头文件而不是两个头文件一个源文件?:

头文件(.h/.hpp):核心作用是声明(告诉编译器 "有这个函数 / 变量 / 类,它的样子是什么"),不负责实现,是 "接口说明";

源文件(.c/.cpp):核心作用是定义 / 实现(告诉编译器 "这个函数具体怎么执行"),是 "接口实现"。

初始化传值不行 怎样传地址

答:&ps

free需要包含<stdlib.h>

void* Fun( )//可以返回任何指针但是会隐式转换为void*

问号表达式不直接赋值需要接收者

申请空间的监视中误将第一次的申请看成第二次的申请以为仍然丢失了空间导致a内的数据变为随机值

long long int 打印使用%l ld

函数体void SLFind(Con* s, char name[10]) 我却在char name[10]处传入了int类型的1

strcmp返回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-16

5.结构体内存对齐

解题的代码优化方法的进阶测试才是真正的解题

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;
SLTNode
ptail = *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-26

if语句后误加分号

初始化后加循环 循环条件没有排除初始化时的情况

realloc的扩容分作 原地扩容 与 异地扩容

return pst->top == 0 ; //简便写法:如果正确返回1 反之 返回0

1-27

1.预处理 //进行适当的删除后 进行纯代码替换

2.编译 将代码转化为CPU能识别的指令

3.链接 解决函数与变量引用的过程

4.执行 操作系统加载可执行文件到内存,CPU 执行机器指令

一定要注意好free和访问的先后顺序

习惯养成:

将单独情况纳入群体不要单独处理 因为可能出现难以调和的解决方法

字符串s[ 3 ] :

while(*s)

{

s++;//就可以实现下标的移动

}

line114 既然使用了Pop那么就应该考虑栈空情况但依然访问line114的情况。所以有if判断空情况

1-28

QNode* 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-29

switch多语句加{ }default后也要加break default与case的相对位置无关。

感觉我目前的复习如课件 方式太低效了没有重点不如去抓重点去复习从每天开始在复习日常后需要找到自己的痛点复习

函数栈帧Excel文档

1-30

试着先学习新知识再复习旧知识

链表实现队列的出队操作对头尾指针的影响:

出队操作,一定会修改头指针,如果出队之后,队列为空,需要修改尾指针。

1-31

在进行字符及字符串的操作时不要只想到%c与%s了还有字符操作函数:

strcpy strcat......

在对字符串学习的部分时仅仅进行了字符串函数的模拟实现未进行实践练习 并且在后续实践练习之中没有想到使用。

分母不为0

//无论你要创建的什么堆(大/小堆) 必须执行堆有序的底层逻辑 否则创建了一个错误堆

以后不要使用"自动窗口"了。

听模块课 抓重点

2-2

等差数列求和公式:(a1+an)n/2

读题要仔细

2月2日重大错误反思:

错误位置:向下调整算法逻辑不严密

我的修改没有毛病 但是基于我创建的逻辑下 不是难以进行修改而是修改后仍然存在逻辑偏差难以准确达到目的导致花费大量时间 其中我秉持以:改一下马上成功的思想进行调试 但是改完后依然存在偏差

错误原因1:逻辑粘连 边界模糊

以后应该怎样进行避免?

前提:只要我惯行

以后应该怎么做:

遇到复杂问题别用画图来打草了 用纸!

缺少刷题练习

2-3

类比思想

32位二进制中1的个数? 通过类比思想:10 进制中 1的个数? 从而获得思路

C++网站的阅读练习

学至C++时试着跟写博客

"w+"(读写) 为了读和写,建议⼀个新的⽂件 建⽴⼀个新的⽂件

试着先预习

对于绝对距离和的最小化问题,中位数比平均数更优!

//因为中位数对极端值不敏感 如:0+0+100

2-4

力扣函数作为接口实现

递建立栈帧 归回收栈帧 所以归后如果再次调用相同函数那么栈帧与上一次建立的一致

static 静态区非堆区 特别关注多次调用仍保留数据的情况 所以在解题时可以适当使用但在一些特殊情境下就要考虑一下了

char arr[18] = { "ABD##E#H##CF##G##" };

2-5

递归:

忌全类(将所有情况细分

忌重

肯定要处理全部情况 如:空树 所以你的代码要在处理特殊情况的情境下实现额外情况

分作两部分:

一:条件判断与处理

二:向下调用

调试:

一:深度思考矛盾点 纳入样例进行分析

二:按顺序进行排雷

模板化

一:由于解决问题就只有那么几种类型所以可以想到以前的解决方式。

二:某些代码群是固定的不要去改变 变的是单条件型 比如:

leftresult = maxDepth(root->left);

rightresult = maxDepth(root->right);

return 1+(leftresult>rightresult ? leftresult:rightresult);

递归的图是为了找到极点

调试:

调用堆栈(作用仅仅是显示以树头的全体下列情况)---先意识后代码

作业里的数据结构题目真是十分经典又精准

2-7

变量的值传递与址传递

看清题目 在力扣中 函数接口提供的变量不一定全部初始化 二叉树的前序遍历中就是

针对于不知道应该提供多大空间给 像数组 堆区空间 可以直接申请大量空间而不是去计算需要多少空间申请多少空间


在任何情况下的调试都是 意识->代码 而不是代码->意识


二叉树的练习题采用递归的方法解决

IO题目 I 输入 O 输出

学习过程一定要诚实 脚踏实地

2-9

已知两序(如:前序 后序)遍历求另一序的遍历:

1.(背景)求前序 后序 答案唯一 简单 求后序答案不唯一 难

2.(划分区间)根据两序将能够划分的全部明确 明确的程度是亚级 (差一级看图写话)

3.(画图)构建整体基本序列 构建左子树 右子树

4.(检验)根据两种已经明确的序列其一画图 按图写话 对照另一序列。

1-31

在进行字符及字符串的操作时不要只想到%c与%s了还有字符操作函数:

strcpy strcat......

在对字符串学习的部分时仅仅进行了字符串函数的模拟实现未进行实践练习 并且在后续实践练习之中没有想到使用。

分母不为0

//无论你要创建的什么堆(大/小堆) 必须执行堆有序的底层逻辑 否则创建了一个错误堆

以后不要使用"自动窗口"了。

听模块课 抓重点

2-2

等差数列求和公式:(a1+an)n/2

读题要仔细

2月2日重大错误反思:

错误位置:向下调整算法逻辑不严密

我的修改没有毛病 但是基于我创建的逻辑下 不是难以进行修改而是修改后仍然存在逻辑偏差难以准确达到目的导致花费大量时间 其中我秉持以:改一下马上成功的思想进行调试 但是改完后依然存在偏差

错误原因1:逻辑粘连 边界模糊

以后应该怎样进行避免?

前提:只要我惯行

以后应该怎么做:

遇到复杂问题别用画图来打草了 用纸!

缺少刷题练习

2-3

类比思想

32位二进制中1的个数? 通过类比思想:10 进制中 1的个数? 从而获得思路

C++网站的阅读练习

学至C++时试着跟写博客

"w+"(读写) 为了读和写,建议⼀个新的⽂件 建⽴⼀个新的⽂件

试着先预习

对于绝对距离和的最小化问题,中位数比平均数更优!

//因为中位数对极端值不敏感 如:0+0+100

2-4

力扣函数作为接口实现

递建立栈帧 归回收栈帧 所以归后如果再次调用相同函数那么栈帧与上一次建立的一致

static 静态区非堆区 特别关注多次调用仍保留数据的情况 所以在解题时可以适当使用但在一些特殊情境下就要考虑一下了

char arr[18] = { "ABD##E#H##CF##G##" };

2-5

递归:

忌全类(将所有情况细分

忌重

肯定要处理全部情况 如:空树 所以你的代码要在处理特殊情况的情境下实现额外情况

分作两部分:

一:条件判断与处理

二:向下调用

调试:

一:深度思考矛盾点 纳入样例进行分析

二:按顺序进行排雷

模板化

一:由于解决问题就只有那么几种类型所以可以想到以前的解决方式。

二:某些代码群是固定的不要去改变 变的是单条件型 比如:

leftresult = maxDepth(root->left);

rightresult = maxDepth(root->right);

return 1+(leftresult>rightresult ? leftresult:rightresult);

递归的图是为了找到极点

调试:

调用堆栈(作用仅仅是显示以树头的全体下列情况)---先意识后代码

作业里的数据结构题目真是十分经典又精准

2-7

变量的值传递与址传递

看清题目 在力扣中 函数接口提供的变量不一定全部初始化 二叉树的前序遍历中就是

针对于不知道应该提供多大空间给 像数组 堆区空间 可以直接申请大量空间而不是去计算需要多少空间申请多少空间


在任何情况下的调试都是 意识->代码 而不是代码->意识


二叉树的练习题采用递归的方法解决

IO题目 I 输入 O 输出

学习过程一定要诚实 脚踏实地

2-9

已知两序(如:前序 后序)遍历求另一序的遍历:

1.(背景)求前序 后序 答案唯一 简单 求后序答案不唯一 难

2.(划分区间)根据两序将能够划分的全部明确 明确的程度是亚级 (差一级看图写话)

3.(画图)构建整体基本序列 构建左子树 右子树

4.(检验)根据两种已经明确的序列其一画图 按图写话 对照另一序列。

2-20---2-21

双指针法合并一个有序数组 分割为left mid mid+1 right最好的方式是创建第二方数组接收 并且C代码很锉

现在 代码群 理解而不是单行理解

稳定性:

稳定:相同的值在排序后的相对位置不变

不稳定:相同的值在排序后的相对位置改变

1.三路划分:(解决含 大量 重复 数据排序)

小的放在左 相等的在中间 大的在右边

left cur ...... right cur遇小于key于left交换cur++left++

复制代码
  cur为left+1       cur遇大于key与right交换right--

  right为n-1         cur遇到等于key那么cur++  截止:cur>right

习惯养成:

特别区分++left left++

2.内省排序 (IntroSort) (解决递归深度过深的问题) (工业常用)

depth计算深度 默认当深度超过2*logN采用其他排序如堆排序

复制代码
         因其对所有递归函数均适用而被广泛应用

3.文件归并排序

外排序:数据量极大 内存 放不下 要使用 磁盘空间 因为数组下标访问的磁盘属性极大地限制了访问速度等所以要采用内排序的方式(如:归并排序 归并排序可进行内排序的核心是 读取与拷贝 极大地增加了灵活性)

内排序:[杭哥板书图示]()


scanf printf 对象终端/控制台

fscanf fprintf 对象文本文件

excel"文档单词积累"

相关推荐
艾莉丝努力练剑2 小时前
【Linux线程】Linux系统多线程(五):<线程同步与互斥>线程互斥
linux·运维·服务器·c语言·c++·学习·ubuntu
南無忘码至尊2 小时前
Unity学习90天 - 第4天 - 认识物理系统基础并实现物体碰撞反弹
学习·unity·游戏引擎
亚空间仓鼠2 小时前
Python学习日志(二):基础语法
windows·python·学习
南無忘码至尊2 小时前
Unity学习90天 - 第4天 - 学习预制体 Prefab + 实例化并实现按鼠标生成子弹
学习·unity·游戏引擎
AnalogElectronic2 小时前
PHP学习02,PHP + jQuery + HTML + MySQL+nginx 做一个多用户云笔记项目
学习·php·jquery
我不是懒洋洋2 小时前
【经典题目】链表OJ(轮转数组、返回倒数第k个节点、链表的回文结构)
c语言·开发语言·数据结构·算法·链表·visual studio
SteveSenna2 小时前
aubo i5+pika realsense+ACT训练完整流程
人工智能·学习·算法·机器人
鱼鳞_2 小时前
Java学习笔记_Day30(File)
笔记·学习
励志的小陈2 小时前
数据结构--堆(C语言实现)
android·c语言·数据结构