- 博客(46)
- 收藏
- 关注
原创 虚拟地址空间,一个进程中的内存分段,全网最详细
堆区是隐式链表,由block组成,每个块由三部分组成,状态位0、1表示使用和未使用,大小位,表示这个块空间多大,剩下的是存放数据可使用的块,malloc的原理,寻找原理:有最佳适配,就是选定需要开辟的内存与前后已用块之间夹着的空闲块相差最小的块,提高空间利用率;调用的函数开头的指令:把上一个函数函数的栈基地址压栈,新的函数的栈基寄存器ebp赋值为旧的栈顶,最后新的栈顶减去一个数,这个数在编译时已经确定了即为该函数临时变量的总大小,扩大到足够的栈帧空间。栈区是每个线程独享,是函数的运行地。
2024-07-29 20:46:02
317
1
原创 用通俗易懂的话语解释自旋锁和悲观锁区别
自旋锁就是你直接到饭店点餐并等待,时刻看着有没有上菜,而悲观锁是持悲观态度,以为点菜时间会很久所以直接点外卖,然后自己在一边打游戏,直到外卖员打电话告诉你(唤醒阻塞线程)为止。乐观锁里面用CAS指令(比较并交换),如果符合预期结果就把新值写入,儿悲观锁每次操作前不管三七二十一都上锁。
2024-03-10 21:03:45
545
原创 c++函数重载调用匹配问题
函数重载调用:①先精准匹配,没有就提升转换,比如int 整形提升为long,再没有标准转换(截断或不同类型的)。②只要f1函数有一个参数比f2优先级高那么它就成为最优匹配,但是其他位置的参数不能低于f2函数对应位置的参数,否则又会把f2列入最优选择,有两个最优就无法判定调用哪个了。③最后一步检查二义性,是否有冲突,比如默认参数的函数有一个值不写和本来就是只有一个形参就有矛盾编译器不知道选哪个所以报错。④返回类型是不作为重载条件,但可以不同,默认参数函数既能精准匹配类型又能强制转换。
2024-03-04 10:38:03
666
原创 一篇保证弄懂人工智能原理
人思考过程的原理:神经元有树突和轴突,树突是输入有很多分支,轴突是输出,它们都是传递电信号但是有阈值,只有达到阈值才会激活,产生电信号传递给下一个神经元,否则截断。人作决定的过程:综合多个输入判断处结果,如有人叫我出门,此类信息激活不了我出去的冲动,因为可能是不太熟的人,后来又得知是妈妈教我出去的,所以还是挺重要,但如果是要帮忙的,那就没兴趣了依然激活不了,此时补充一个信息是出去吃大餐,不是要干活的,那么这三个端口加起来足以激发产生决定了,在数学中是卷积。即使是干活,那也很乐意,这个端口就比较重要。
2024-03-02 15:43:46
583
原创 外挂开发单机游戏内存挂,一段话弄懂什么是特征码
该变量一开始不好定位地址,第一次使用改善数值缩小范围找到后跳转到该地址复制附近地址的数值过去,第二次执行同样的操作,对比两次数值都一样的变量就留下,以后以这堆数据作联合搜索加快筛选,虽然真正要修改的变量有很多其他数值一样的变量来干扰,但因为附近数值的限定结果变得几乎唯一。
2024-02-29 00:19:38
899
原创 C语言获取时间与日期的函数大全
一、最简单获取秒数的,windows和linux都支持用time()函数,获取从1970年到现在过了多少秒,time_t其实是long int 类型。二、linux下的,使用gettimeofday()接口获取微秒级别的,传结构体的地址进去,tv_sec是秒,tv_usec是秒余下的微秒值。/* 为日光节约时间的修正状态,如果为非0代表启用日光节约时间修正 *///从每年1月1日开始的天数[0-365],0代表1月1日。/* 为1970-01-01至今的秒数*///星期[0-6],0代表星期天。
2024-02-22 15:33:04
872
原创 java为什么要有异常?
一般try-catch针对于编译时异常运行时异常对于普通的情况如数组越界、除数为0,一般不进行处理,如果有异常运行的时候虚拟机会直接抛出然后中断,本质代码还是有错误需要及时修改,就算处理也只是加上一些显示的信息,比如客户端由于网络拥堵连接不上导致错误,如果不处理就会直接抛出乱码并终止,处理了就以友好的方式显示出来还可以退出,继续尝试。
2024-02-22 14:29:28
372
原创 虚拟地址空间与堆区
因为堆区一开始不知道要使用多少内存,需要动态开辟,堆区一开始向系统申请一块大内存,然后零售给程序,free只是归还给虚拟空间,并未真正释放,如果有malloc函数扫描到之前的释放的块那可直接使用,而不用向系统频繁申请空间。等到调用malloc函数时查找块从头到尾遍历完了实在没有空闲块就会移动堆区有效指针break以实现扩容,是内核调用,花费时间。虚拟地址空间:为了减少内存移动,提高利用率,隔离进程保护安全,加入了虚拟的地址,把内存分割成很多页映射存储需要经过转换才能访问实际物理内存。
2024-02-22 09:05:45
547
原创 简单理解多线程原子指令与CAS
而mov eax,ecx就是,区别是对内存操作一次以上就不是,操作0或1次就是,通过atomic原子变量,对该变量读或修改时,很大可能生成原子指令(伪原子),本质是加上指令前缀lock,对内存总线锁住。如果针对比较后等于原先预定值,才能对其修改,比如原先饼干是有5包的,但读取出来是4包证明被人偷了,这种情况下则不吃那么快去找人算账,即自身不修改为3,退出执行。如果只用atomic则多条指令单个是原子的,但几个加起来就不是原子的。mutex互斥锁在判断锁变量是否被其他线程占用时也用到了原子锁。
2024-02-21 21:49:30
537
原创 函数是什么?全网最通俗易懂
函数别称功能、方法,是代码集合,可重复调用,可看成一个榨汁机,而参数就是放进去的水果,函数里面的代码就是榨汁机的功能,就是无论把什么果放进去都是一样的流程,即绞碎。也可以把函数里面的内容(临时变量)当成草稿纸上的计算,而要写在试卷的答案是函数返回值。调用者无需关心底层事情,比如一个蛋糕师,他可能懂得奶油是怎么来的,但是为了方便直接买来(调用函数),然后再和水果、面包等拼装成蛋糕。是因为假设有这个数字,该做的什么运算,有占位作用,如同练功夫,做这个动作时没人,但人来了(实参)就会生效。
2024-02-17 17:12:09
562
原创 C语言猜数字小游戏智能版
不仅有对错,还有评分系统,根据回答的次数给予合适的反馈。此代码适合linux和安卓系统,如果windows想用得删除\033[xxm之类的ANSI打印转义字符,换成windows对应的清屏、切换颜色函数。
2024-02-16 17:00:19
900
原创 scanf,gets,fgets三个函数区别
scanf除了%c其他不会吸收空格、换行符,以换行当成输入的终止即缓冲区解除阻塞状态,前提是前面得有内容;以空格当成读取数据的终止。gets函数会读取空白字符,会抛开缓冲区的换行但不会读入目标数组,并且即使没有输入内容,一遇到换行也会停止输入。fgets吸收换行。参数上,gets和scanf都是stdin输入流,只能从键盘中读取数据输入到程序的内存上,而fgets不仅能从键盘字符串,还能读取文件,fets有三个参数,有读入的字符个数限制。
2024-02-15 21:32:52
388
原创 用c语言做一个心算小游戏
有加减和乘法3种运算,由于除法涉及到浮点数存储有误差,所以比较难实现,改程序还有判定分数机制,根据难度给定合适的分数,随机抽取运算题目和符号。下面的代码适合Linux和安卓上的编译器,因为用了ANSI转义字符\033[3xm,其中x是0到7的数字表示颜色,如果电脑也想用那么只需删除这个即可。
2024-02-15 15:08:58
589
原创 i++和++i的区别是什么?
mov是赋值指令,eax是寄存器由于两个内存中的变量无法直接赋值需要先存到寄存器上。简单理解为i++是先使用后自增,++i是先自增再使用,但比较笼统,i++不一定先调用后自增,比如if(i++>6) break;此处i的值先保存到编译器自动插入的临时变量上,然后i自增,最后用临时变量的值跟6比较(调用)如果++i就不会产生临时变量。
2024-02-15 11:23:25
330
原创 C语言获取时间函数大全
一、最简单获取秒数的,windows和linux逗支持用time()函数,获取从1970年到现在过了多少秒,time_t其实是long int 类型。二、linux下的,使用gettimeofday()接口获取微秒级别的,tv.sec是秒,tv.usec是秒余下的微秒值。四、使用clock()函数,windows下是毫秒,linux下是微秒,这个是程序执行到函数调用经过的时钟滴答次数。//从每年1月1日开始的天数[0-365],0代表1月1日。//星期[0-6],0代表星期天。int tm_sec;
2024-02-15 10:59:51
1093
原创 C语言一个程序exe的内存分布,了解底层是如何工作的
如果用到了新的页面一个页面4k,就要分配物理页。而代码区是进程共享的,指使着如何运算,他们之间的逻辑关系,运用哪个数据进行怎样的操作,比如y=x*x+3;每个核心都有独自的缓存和寄存器,通过缓存协议cpu时间轮片保证物理上同一时间段只能一个线程通过数据总线访问内存,但如果是栈区的数据可以读入缓存就能真正意义并发读取来运算,写数据的话看谁抢到权限才能写入,加了读写锁就可以并发读、依次写,写的优先级比读高,意思是cpu无论正在读或写,后面排队的线程如果前面是读,那排在后面的写操作可以插队。
2024-02-13 20:53:05
400
原创 C语言结构体 全网最简单易懂
首元素的地址就是结构体地址,每个元素必须对齐到元素自身大小与编译器默认对齐数两者最小值的整数倍(vs为8个字节,linux没有默认对齐数),对齐就是元素起始地址存放在指定偏移量处,而整个结构体的占用对齐到所有元素对齐数中最大对齐数的整数倍(对齐位置是结构体尾部)。如果结构体里面有结构体,小的结构体里面也要按照以前那样对齐,作为整体要对齐到自身最大对齐数的整数倍。最后大的结构体则对齐到所有元素中(包括子结构体里面的元素)最大对齐数的整数倍。如果是个结构体指针就用箭头操作符ptrs->num=10;
2024-02-13 20:26:53
342
原创 使用反汇编强力破解单机游戏外挂
反汇编找基地址过程:因为要修改的变量地址老是会发生变化,这样每次进游戏都要重新找,很费时间,所以干脆找出固定的基地址,通过固定算式表出变量地址。如果后面找的代码和前面一样,回到原点,证明是个循环指针,函数连锁调用的原因,这时放弃这条路,后退几步找别的路。eax+5560是类内阳光地址,eax是类的首地址,而保存eax数值的00FE82E8就是sun类的指针变量p,查找看看谁访问了p。要修改的变量一般是类中有内部类,一层层嵌套,所以才有那么多偏移,而最终数据是在堆区动态开辟的,所以才每次运行地址都不同。
2024-02-13 13:31:48
650
原创 初识结构体
首元素的起始地址就是结构体的地址,每个元素必须对齐到元素自身大小与编译器默认对齐数两者最小值的整数倍(vs为8个字节,linux没有默认对齐数),对齐就是元素起始地址存放在指定偏移量处,而整个结构体对其到所有元素对齐数中最大的对齐数整数倍。结构体里面有结构体,小的结构体里面也要按照以前那样对齐,作为整体要对齐到自身最大对齐数的整数倍。最后大的结构体则对齐到所有元素中(包括子结构体里面的元素)最大对齐数的整数倍。构体是一种构造数据类型,可以存储不同类型的变量,声明类型:struct (结构体类型名)S{
2024-02-13 12:24:00
396
原创 一文搞懂C语言数组的地址和首元素地址区别,以及易混淆成多重指针。
取地址数组名和数组首元素的地址虽然数值都相同,区别是编译器记录的信息不同,取地址数组名取出的是整个数组的地址,比数组名高一维度,可以赋值给该数组大小的数组指针。数组作为函数形参时会退化一级,即:一维整形数组退化成整形指针,二维数组变成一维数组指针,所以假如void sort(int arr[5])这样说明数组大小也没用,可以换成int arr[]或int*arr代替,但是当参数是int a[5][4]这时候4不可省略,5可以省略,也可以换成int (*a)[4]数组指针这种写法,不可用int**a。
2024-02-11 13:31:48
971
原创 C语言数组和指针的区别
取地址数组名和数组首元素的地址虽然数值都相同,区别是编译器记录的信息不同,取地址数组名取出的是整个数组的地址,比数组名高一维度,可以赋值给该数组大小的数组指针。那么这个p的地址(&p)就跟数组首元素地址不同了,因为数组名跟多重指针不同,多重指针每个层级的地址都不同,是一个指针变量保存着另外更低级的指针的地址,而那个指针存放的地址解引用又对应着另外的指针。但是多维数组的数组名他不是一级保存着下一级的逻辑关系,仅仅是表示索引后他是属于多少个元素的数组指针,他在多少维,而不是解引用跳转到该地址去找指针。
2024-02-10 14:47:13
456
原创 C语言if语句底层原理,从汇编深入理解
if语句原理:cmp指令先用两个数相减,改变标志寄存器,如sf符号位,cf进位位,ov溢出位,zf零标志位,然后下面条件转移指令,cpu微电路中的程序计数器(ip)连接着多路分配器,把条件转移指令逻辑判断后的状态是0或1作为偏移值,如if(value!0值处保存的是下一句指令的地址,1值保存着条件指令对应要跳转的代码地址。i条件判断语句是程序中必不可少的语句,没有它程序就只会按照原先设定的顺序执行,而不会灵活变通,在cpu底层可没有"如果"这个概念,只有数值的体现。
2024-02-09 16:16:44
636
原创 static关键字
static关键字的作用在c语言里面,在函数里可以定义static变量,代表这个生命周期是直到程序结束,和全局变量一样,但作用域不是全局,只暴露在函数里。定义的static函数:不会参与合并,只对本.c文件使用,即使两个.c源代码文件有相同名字的函数,那也是两个不同的函数。在全局里定义的static变量,则代表仅限于这个.c代码使用变量,如果别的文件有同名变量也不会合并上,不会冲突,
2024-02-09 09:18:14
313
原创 单链表实现任意位置删除和插入
指定位置前面插入(STL不提供该接口,因为每次要循环找前面节点费时间,且要分情况判断pos是不是头节点如果是就头插,因为prev的下一个最多只能是第二个永远找不到pos)使用prev变量保存前面目标节点的前面一个位置,每次循环prev指向下一个。
2024-02-09 08:30:02
364
原创 signed和unsigned有什么区别?
虽然a是无符号数但它的值减45的结果依然可以看作-43的补码,也可看作有符号数42亿多,关键看左边接收结果的变量是什么类型,以后读取时即作条件判断或cout打印(具有类型重载的函数)时就用什么类型下的方式。①逻辑判断大小:if语句会为有无符号数翻译的条件转移指令不同,如jg和ja,jg在判断大小时多考虑了符号位,实现了人类理解上的逻辑判定。②运算结果:如果一个有符号与另一个无符号变量运算,就都当成无符号,结果只能大于或等于0,配合上面逻辑判断举例子:unsigned int a=0;
2024-02-09 08:23:05
586
原创 C++构造函数的顺序以及参数列表
对象成员默认调无参构造,如果不用参数列表只能为公共权限的对象成员的属性赋值,或是在大的类的构造函数里面显式调用有参构造,但在调用自身构造前已经构造了对象成员一次,要是在花括号里面再构造属于重复了。而参数列表上的代码属于构造函数花括号前的,可以先执行传参的行为,给对象成员传参就等于调用它的构造函数了。严格上来说(从汇编看)是先调用自身的构造函数,大的那个类的每个构造函数里面都包含对象成员的构造函数,只不过把对象成员的构造先放在前面,而大的类的构造即花括号里的代码放后面。
2024-02-08 20:20:26
492
原创 编程自学建议,过来人的经验
在按照顺序学习的时候,有时想着尝试跨入另一门,比如说还没学完java就想写安卓程序、没学汇编就想做外挂,没学过的只能自己猜测,这样无论看多少篇文档都很难明白,建议要么干脆从头耐心学起,要么就不纠结这个了。不推荐零零散散查阅资料式的学习,因为不知道学到哪,形成不了系统的知识体系,容易忘记或重复学习,有时间还是从头学习。记忆方法:间隔重复记忆法,就是分时间段学习,比一次性彻底学懂并记住了一个知识点再学另外的更高效,等到还记得30%的时候再回顾,先不着急看,而是在脑海中不断回想,这时再看正确的。
2024-02-06 16:16:20
530
原创 scanf函数的简单原理
处理过程是从第一个字符开始,拿当前的ascii值减去48(如果后面还有位)再乘以10再加上 下个数字的ascii码值减48,然后把结果乘以10再加上下个数字的ascii码值,反复乘十再累加就处理好一个十进制数的字符串了。432这3个字符'4','3','2'分别对应ascii码值的52,51,50。从首个字符开始,拿它的ascii码值52减48等于4,再乘以10,就是40,然后40+(51-48)=43,然后43×10+(50-48)=432,最后把432这个值存进变量a里面去。另外,可私聊免费教C语言。
2024-01-05 13:53:26
363
1
原创 简单易懂C语言语法,初识C语言的结构
①其中#后面是预处理指令,代表将stdio.h的内容拷贝过来,要想使用输入输出语句就得引用这个库,全称标准输入输出库,好比如你上学要写字可能只需用到黑笔但也把笔袋带过去了。⑧:分号是语句与语句的间隔,每个语句后面都有,语句是c语言的最小操作单元,相当于分子,但翻译成汇编语言会有更精细的操作,相当于原子。④int a=0创建了一个名字为a的整形变量,其中int是类型,类型相当于建筑图纸,而a为变量实体,后面的=0是对a写入0;⑤printf:打印函数,把双引号里面的内容输出到屏幕,""是字符串。
2024-01-05 13:41:45
444
1
原创 从汇编理解C语言函数调用时内存的变化
函数调用时在栈中的内存:先用push指令把参数压栈,然后调用call指令,call指令包含两个操作:把下一条语句的地址压栈,接着跳转到函数起始地址处。调用的函数开头的指令:把上一个函数函数的栈基地址压栈,新的函数的ebp栈基指针指向旧的栈顶,最后新的栈顶减去一个数,这个数在编译时确定的栈帧大小,用于存放临时变量。
2024-01-04 09:13:57
397
1
原创 C语言贪吃蛇代码
printf(" \033[31m1.红色\033[32m 2.绿色\033[33m 3.黄色 \033[34m 4.青色\n");printf(WHITE"时间:%d秒 长度:%d格 最高记录%d\n",Time,length,big_c);printf("\t \033[35m5.紫色\033[36m 6.蓝色\033[37m 7.白色\n");printf("\033[34m方向按钮为2468,5为暂停\n");printf(PURPLE"[输入对应的序号]\n");
2024-01-04 09:04:01
554
1
原创 if语句和switch语句对比
if语句和switch对比:在当条件是很多个整数时可以考虑用switch,switch只有在分支多且各个数字间隔小,比较紧凑时才会生成地址跳转表,是case数字与地址点对点的直接跳转,case后面的数字相当于地址表的索引值,地址表就是以数组的形式存储各个情况的代码起始地址在全局区。如果分支多但是数字间隔大,那么会用二叉树,时间复杂度是log2(n),也比if语句一个接着一个比较快多了。
2024-01-04 07:55:14
347
1
原创 if语句和三目运算符对比
if语句和三目运算符对比:三目运算符比if语句多两句或四句汇编代码用于存储两个表达式的值到临时变量上,但是有时候用三目表达式写比较方便,比如if(x>y)用三目max=x>y?
2024-01-03 21:33:11
497
1
原创 入门大概了解编程
先讲条件分支语句,包括if语句和swtich,if()括号里填写条件,如果条件满足即为真,if后面的语句才会执行,否则跳过忽略。i起到一个记录循环次数的作用,将整个循环比作一年,i就如同日历,每过一天都要撕掉一页(i--),不然时间过去了日期没过,永远也到不了过年(终止循环)。而循环嵌套就相当于一年有50周,一周有7天,一年就是外面的大循环,一周是小循环,他们是包含、被包含的关系。函数可看成一个榨汁机,而参数就是放进去的水果,函数里面的代码就是榨汁机的功能,就是无论把什么果放进去都是一样的流程,即绞碎。
2024-01-03 21:18:50
331
1
原创 c语言有符号数和无符号数本质区别
cpu存储和运算都是以补码形式进行的,有符号数和无符号数在内存中都是存储二进制数值,只是运算完的结果编译器会根据两个数是什么类型的来决定是有符号数还是无符号数,如果一个有符号另一个是无符号,就统一当成无符号,结果只能大于或等于0,比如①逻辑运算:unsigned int c a=0;这样if语句会执行,因为减法就是等于加上它的负数的补码,补码就是111111111111111111111111111110,然而在无符号中就当成4亿多来看待,所以大于0,因为if语句针对有无符号数翻译的条件转移指令不同。
2024-01-03 13:42:28
557
1
原创 冒泡排序和选择排序的区别
冒泡排序和选择排序区别:冒泡排序是一个接着下一个比较,如果不是理想的顺序那么会不断挪动,目的是找出最大或最小数在右边。选择排序就好比第一个数字站在擂台上,大吼一声:“还有谁比我小?剩余数字来挨个打擂,如果出现比第一个数字小的数,则新的擂主产生,以下标的方式保存,但数据本体并未交换。每轮打擂结束都会找出剩下未排序的一个最小的数,保存在数组arr[i]处,将其交换置未排序的首位。经过 n-1 轮打擂,所有的数字就按照从小到大排序完成了。选择排序比冒泡排序交换数值的次数更少,只有在一轮结束后才交换。
2024-01-03 13:31:30
386
1
原创 C语言数组名和多重指针的区别
取地址数组名和数组首元素的地址虽然数值都相同,区别是编译器记录的信息不同,取地址数组名取出的是整个数组的地址,可以由数组指针保存,而它自增1就会跨过整个一维数组的大小,首元素地址自增只是迈向1个元素的长度。这是因为数组名是指针常量,作数值的记录,最终地址由各维下标乘以对应级别的字节大小然后累加,再加上数组名地址。那么这个数组指针p的地址就跟数组地址不同了,因为数组名跟多重指针不同,多重指针每个层级的地址都不同,是一个指针变量保存着另外更低级的指针的地址,而那个指针存放的地址解引用又对应着另外的指针。
2024-01-03 12:45:38
331
1
cpu指令的相关问题
2023-08-09
TA创建的收藏夹 TA关注的收藏夹
TA关注的人