笔记五:C语言编译链接
Faye:孤独让我们与我们所爱的人相处的每个瞬间都无比珍贵,让我们的回忆价值千金。它还驱使你去寻找那些你在我身边找不到的东西。
---------《寻找天堂》
目录
一、编译和链接的介绍
1.1 程序的翻译环境和执行环境
1.1.1 翻译环境
1.1.2 运行环境
1.2 预处理
1.2.1 预定义符号
1.2.2 #define
#define的语法:
#define 替换规则:
#和##
带副作用的宏参数
编辑 宏和函数对比
#undef
1.3 编译
1.3.1 词法分析
1.3.2 语法分析
1.3.3 语义分析
1.4 汇编
1.5 链接
一、编译和链接的介绍
1.1 程序的翻译环境和执行环境
在ANSI C的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是执行环境,它用于实际执行代码。
注:ANSI C是由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准。ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型) 并支持多国字符集 (包括备受争议的三字符序列)。 ANSI C 标准同时规定了 C 运行期库例程的标准。
1.1.1 翻译环境
- 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
- 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
- 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中
翻译的几个环节,通过下面的图进行初步的了解:
1.1.2 运行环境
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
1.2 预处理
在预处理阶段,源⽂件和头⽂件会被处理成为 .i 为后缀的⽂件。在 gcc 环境下想观察⼀下,对 test.c ⽂件预处理后的.i⽂件,命令(以下所有的命令在Linux下的指令)如下:
gcc -E test.c -o test.i
在Linux下执行这条指令后,生成.i文件,查看里面内容大多都是宏
预处理阶段主要处理那些源⽂件中#开始的预编译指令。
⽐如:#include,#define,处理的规则如下:
- 将所有的 #define 删除,并展开所有的宏定义。
- 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
- 处理#include 预编译指令,将包含的头⽂件的内容插⼊到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头⽂件也可能包含其他⽂件。
- 删除所有的注释
- 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等。
- 或保留所有的#pragma的编译器指令,编译器后续会使⽤。
经过预处理后的 .i ⽂件中不再包含宏定义,因为宏已经被展开。并且包含的头⽂件都被插⼊到 .i⽂件中。所以当我们⽆法知道宏定义或者头⽂件是否包含正确的时候,可以查看预处理后的 .i ⽂件来确认。
1.2.1 预定义符号
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义的符号都是语言内置的。下面使用部分宏:
#include<stdio.h>int main() {//__FILE__进行编译的源文件 __LINE__文件当前的行号printf(" file:%s \n line:%d\n", __FILE__, __LINE__); //__DATE__ 文件被编译的日期 __TIME__ 文件被编译的时间printf(" date:%s \n time:%lld\n", __DATE__, __TIME__);return 0;
}
运行结果如下:
1.2.2 #define
#define是一种定义标识符,用来定义宏,下面是#define的功能介绍
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏( macro )或定义宏(define macro )。
举个梨子:
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:%s\ttime:%s\n" ,\__FILE__,__LINE__ , \__DATE__,__TIME__ )
在define定义标识符的时候,建议不要加上 ; 这样容易导致问题。比如下面的场景:
#include<stdio.h>
#define MAX 1000;
int main() {int condition = 0, max = 0;if (condition) max = MAX; elsemax = 0;return 0;
}
在vs下进行编译,这里会出现语法错误。
#define的语法:
语法: #define name stuff
下面是宏的申明方式:
把宏名全部大写函数名不要全部大写
- 参数列表的左括号必须与name紧邻。
- 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
举个小梨子,定义一个数的平方的宏:
#define MUL(x) x*x //参数列表的左括号必须与name紧邻
这个宏接收一个参数 x ,如果在上述声明之后,MUL(5)。置于程序中,预处理器就会用下面这个表达式替换上面的表达式:5*5
#include<stdio.h>#define MUL(x) x*x //参数列表的左括号必须与name紧邻。int main() {printf("%d\n", MUL(5)); // ==> printf("%d\n", 5*5)return 0;
}
那么如果输入的参数是一个表达式呢?MUL宏输出的结果是否还是符合预期呢?此时将MUL(5)替换为 MUL(2+3),它的预期结果应该也是25,运行一下看看
结果是11,为什么呢?这时候把(2+3)参数带入MUL宏中看看 ===> 2+3* 2+3 ====> 2+6+3,所以输出的结果变为了11。这样就比较清晰了,由替换产生的表达式并没有按照预想的次序进行求值。这里涉及到了运算符优先级的问题。在宏定义上加上两个括号,这个问题便轻松的解决了:
#define MUL(x) (x)*(x)
举另外一个小梨子,定义一个数跟自己加和的宏:
#include<stdio.h>#define SADD(x) (x)+(x) //参数列表的左括号必须与name紧邻。int main() {printf("%d\n", 10*SADD(2)+ SADD(2)); //预期结果 10*4+4=44return 0;
}
欸,这又是怎么回事?参数我也加上了小括号,不应该呀。依旧是上面的分析方法,将宏在表达式中进行展开,10*SADD(2)+ SADD(2) =====> 10 * (2) + (2)+ (2) + (2)====> 20+6=26。乘法运算先于宏定义的加法,所以出现了26。这个问题的解决办法是在宏定义表达式两边加上一对括号就可以了:
#define SADD(x) ((x)+(x))
这样运行结果便符合预期啦
通过上面两个小梨子,得出以下的经验:
用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用。
#define 替换规则:
- 1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
- 2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
- 1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
#和##
字符串是有自动连接的特点的,将两个或者多个字符串紧挨着它们会自动连接,形成一个组合后的字符串,通过下面的小梨子看看:
#include<stdio.h>
int main() {char* p = "hello ""world""!!!\n";printf("hello ""world""!!!\n");printf("%s", p);return 0;
}
如果把这些写到宏里是不是实现同样的效果呢?
使用 # ,把一个宏参数变成对应的字符串。还可以添加部分参数,进行打印:
#include<stdio.h>//使用 # ,把一个宏参数变成对应的字符串
//FORMAT 数据输出格式,VALUE 数据
#define PRINT(FORMAT,VALUE) printf("the value of " #VALUE " is "FORMAT"\n", VALUE);int main() {int i = 10;PRINT("% d" , i + 5)return 0;
}
代码中的 #VALUE 会预处理器处理为: "VALUE"
## 的作用
## 可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。
通过一个小梨子看看:
#include<stdio.h>
#define ADD_TO_SUM(num, value) s##num += value;int main() {int s1 = 0, s2 = 0, s3 = 0;ADD_TO_SUM(1, 10) // 作用是:给s1增加10.ADD_TO_SUM(2, 20) //给s2增加20.ADD_TO_SUM(3, 30) //给s3增加30.printf("s1: %d s2: %d s3: %d", s1, s2, s3);return 0;
}
带副作用的宏参数
x + 1 ; // 不带副作用 不会改变参数的数值x ++ ; // 带有副作用 参数的数值被永久修改
借用上面MUL的宏,运行下列代码:
#define MUL(x) (x)* (x)int main() {int i = 2;printf("MUL: %d i: %d\n", MUL(i++),i);return 0;
}
发现宏替换后MUL(i++) ====> (i++)* (i++) 。此后i被加加两次,产生了副作用
宏和函数对比
- 1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
- 2. 更为重要的是函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于>来比较的类型。
- 1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
- 2. 宏是没法调试的。
- 3. 宏由于类型无关,也就不够严谨。
- 4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
- 宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
下面表格是将宏与函数进行对比:
属性 | #define 定义宏 | 函数 |
代码长度 | 每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码 |
执行速度 | 更快 | 存在函数的调用和返回的额外开销,所以相对慢一些 |
操作符优 先级 | 宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会产生 不可预料的后果,所以建议宏在书写的时候多些括号。 | 函数参数只在函数调用的时候求 值一次,它的结果值传递给函 数。表达式的求值结果更容易预 测 |
带有 副作 用的 参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候求值一 次,结果更容易控制。 |
参数 类型 | 宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型 | 函数的参数是与类型有关的,如 果参数的类型不同,就需要不同 的函数,即使他们执行的任务是 相同的。 |
调试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递 归 | 宏是不能递归的 | 函数是可以递归的 |
#undef
这条指令用于移除一个宏定义。
#undef NAME// 如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
1.3 编译
编译过程就是将预处理后的文件进行⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码文件。编译过程的命令如下:
gcc -S test.i -o test.s
在Linux下执行这条指令后,生成.s文件,查看.s文件里面内容是相应的汇编代码
对下面的代码进行编译的时候,流程会是怎么样的呢?
num=(z+6)*(9/3)
1.3.1 词法分析
将源代码程序被输⼊扫描器,扫描器的任务就是简单的进⾏词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字⾯量、特殊字符等)。
上⾯程序进行词法分析后得到了13个记号
记号 | 类型 |
num | 标识符 |
= | 赋值 |
( | 左圆括号 |
z | 标识符 |
+ | 加号 |
6 | 数字 |
) | 右圆括号 |
* | 乘号 |
( | 左圆括号 |
9 | 数字 |
+ | 加号 |
3 | 数字 |
) | 右圆括号 |
1.3.2 语法分析
接下来语法分析器,将对扫描产⽣的记号进行语法分析,从⽽产⽣语法树。这些语法树是以表达式为节点的树
1.3.3 语义分析
由语义分析器来完成语义分析,即对表达式的语法层⾯分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。
1.4 汇编
汇编器是将汇编代码转转变成机器可执行的指令,每⼀个汇编语句几乎都对应⼀条机器指令。就是根据汇编指令和机器指令的对照表⼀⼀的进行翻译,也不做指令优化。汇编的命令如下:
gcc -c test.s -o test.o
在Linux下执行这条指令后,生成.o文件,查看里面内容为机器语言
1.5 链接
链接是一个复杂的过程,链接的时候需要把一堆文件链接在⼀起才生成可执行程序。链接过程的命令如下:
gcc test.o -o test
链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。链接解决的是一个项目中多文件、多模块之间互相调用的问题。比如: 在一个C的项目中有2个.c文件( test.c 和 add.c ),代码如下:
#include <stdio.h>
//test.c
//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;
int main()
{int a = 10;int b = 20;int sum = Add(a, b);printf("%d\n", sum);return 0;
}
int g_val = 2022;
int Add(int x, int y)
{return x+y;
}
我们已经知道,每个源⽂件都是单独经过编译器处理⽣成对应的⽬标⽂件。
- test.c 经过编译器处理⽣成 test.o
- add.c 经过编译器处理⽣成 add.o
我们在 test.c 的⽂件中使⽤了 add.c ⽂件中的 Add 函数和 g_val 变量。
我们在 test.c ⽂件中每⼀次使⽤ Add 函数和 g_val 的时候必须确切的知道 Add 和 g_val 的地址,但是由于每个⽂件是单独编译的,在编译器编译 test.c 的时候并不知道 Add 函数和 g_val 变量的地址,所以暂时把调⽤ Add 的指令的⽬标地址和 g_val 的地址搁置。等待最后链接的时候由链接器根据引⽤的符号 Add 在其他模块中查找 Add 函数的地址,然后将 test.c 中所有引⽤到Add 的指令重新修正,让他们的⽬标地址为真正的 Add 函数的地址,对于全局变量 g_val 也是类似的⽅法来修正地址。这个地址修正的过程也被叫做:重定位
相关文章:

笔记五:C语言编译链接
Faye:孤独让我们与我们所爱的人相处的每个瞬间都无比珍贵,让我们的回忆价值千金。它还驱使你去寻找那些你在我身边找不到的东西。 ---------《寻找天堂》 目录 一、编译和链接的介绍 1.1 程序的翻译环境和执行环境 1.1.1 翻译环境 1.1.2 运行环境 …...

GitCode 助力 vue3-element-admin:开启中后台管理前端开发新征程
源码仓库: https://gitcode.com/youlai/vue3-element-admin 后端仓库: https://gitcode.com/youlai/youlai-boot 开源助力,开启中后台快速开发之旅 vue3-element-admin 是一款精心打造的免费开源中后台管理前端模板,它紧密贴合…...

SyntaxError: Invalid regular expression flag “x“
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...

HiveServer2与Spark ThriftServer详细介绍对比
HiveServer2与Spark ThriftServer详细介绍对比 1、概述 1.1 HiveServer2 是 Apache Hive 提供的基于 Thrift 的服务进程,用于让远程客户端执行 Hive SQL 查询 ([一起了解一下HiveServer2 - zourui4271 - 博客园]。它是早期 HiveServer1 的改进版本,引入…...

ESP32S3N16R8驱动ST7701S屏幕(vscode+PlatfoemIO)
1.开发板配置 本人开发板使用ESP32S3-wroom1-n16r8最小系统板 由于基于vscode与PlatformIO框架开发,无espidf框架,因此无法直接烧录程序,配置开发板参数如下: 在platformio.ini文件中,配置使用esp32-s3-devkitc-1开发…...

软考初级程序员知识点汇总
以下是计算机技术与软件专业技术资格(水平)考试(简称“软考”)中 程序员(初级) 考试的核心知识点汇总,涵盖考试大纲的主要方向,帮助你系统复习: 一、计算机基础 计算机组…...

亲测解决笔记本触摸板使用不了Touchpad not working
这个问题可以通过FnFxx来解决,笔记本键盘上Fxx会有一个触摸板图标。如果不行应该玉藻设置中关了,打开即可。 解决办法 在蓝牙,触摸板里打开即可。 Turn it on in settings。...

13.数据结构(软考)
13.数据结构(软考) 13.1:线性表 13.1.1 顺序表 顺序存储方式:数组的内存是连续分配的并且是静态分配的,即在使用数组之前需要分配固定大小的空间。 时间复杂度: 读:O(1) 查询:1,(n1)/2&#x…...

开发环境搭建-完善登录功能
一.完善登录功能 我们修改密码为md5中的格式,那么就需要修改数据库中的密码和将从前端获取到的密码转化成md5格式,然后进行比对。比对成功则登录成功,失败则禁止登录。 二.md5格式 使用DigestUtils工具类进行md5加密,调用md4Dig…...

HAL库,配置adc基本流程
1. 初始化阶段---cubemx (1) GPIO初始化 函数:HAL_GPIO_Init() 作用:配置ADC引脚为模拟输入模式。 代码示例: // 使能GPIOA时钟 __HAL_RCC_GPIOA_CLK_ENABLE();// 配置PA1为模拟输入 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStr…...

DeepSeek爆火催生培训热潮,是机遇还是陷阱?
DeepSeek 掀起的学习风暴 最近,DeepSeek 以迅猛之势闯入大众视野,在国内引发了一场学习狂潮。它的出现,就像是在平静的湖面投入了一颗巨石,激起层层涟漪。 在各大社交平台上,与 DeepSeek 相关的话题讨论热度居高不下&…...

Apache Httpd 多后缀解析
目录 1.原因 2.环境 3.复现 4.防御 1.Apache Httpd 多后缀解析原因 Apache HTTP Server 在处理文件请求时,通常会根据文件的后缀来确定如何处理该文件。例如,.php文件会被交给 PHP 解释器处理,而.html文件则直接作为静态文件返回。 然而…...

备赛蓝桥杯之第十五届职业院校组省赛第五题:悠然画境
提示:本篇文章仅仅是作者自己目前在备赛蓝桥杯中,自己学习与刷题的学习笔记,写的不好,欢迎大家批评与建议 由于个别题目代码量与题目量偏大,请大家自己去蓝桥杯官网【连接高校和企业 - 蓝桥云课】去寻找原题࿰…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_modules
定义在 objs\ngx_modules.c #include <ngx_config.h> #include <ngx_core.h>extern ngx_module_t ngx_core_module; extern ngx_module_t ngx_errlog_module; extern ngx_module_t ngx_conf_module; extern ngx_module_t ngx_openssl_module; extern ngx_modul…...

css错峰布局/瀑布流样式(类似于快手样式)
当样式一侧比较高的时候会自动换行,尽量保持高度大概一致, 例: 一侧元素为5,另一侧元素为6 当为5的一侧过于高的时候,可能会变为4/7分部dom节点 如果不需要这样的话删除样式 flex-flow:column wrap; 设置父级dom样…...

【并发编程】聊聊定时任务ScheduledThreadPool的实现原理和源码解析
ScheduledThreadPoolExecutor 是在线程池的基础上 拓展的定时功能的线程池,主要有四种方式,具体可以看代码, 这里主要描述下 scheduleAtFixedRate : 除了第一次执行的时间,后面任务执行的时间 为 time MAX(任务执行时…...

【虚拟化】Docker Desktop 架构简介
在阅读前您需要了解 docker 架构:Docker architecture WSL 技术:什么是 WSL 2 1.Hyper-V backend 我们知道,Docker Desktop 最开始的架构的后端是采用的 Hyper-V。 Docker daemon (dockerd) 运行在一个 Linux distro (LinuxKit build) 中&…...

DeepSeek 医疗大模型微调实战讨论版(第一部分)
DeepSeek医疗大模型微调实战指南第一部分 DeepSeek 作为一款具有独特优势的大模型,在医疗领域展现出了巨大的应用潜力。它采用了先进的混合专家架构(MoE),能够根据输入数据的特性选择性激活部分专家,避免了不必要的计算,极大地提高了计算效率和模型精度 。这种架构使得 …...

c++实现最大公因数和最小公倍数
最大公因数和最小公倍数的介绍 读这篇文章,请你先对最大公因数以及最小公倍数进行了解: 最大公因数(英文名:gcd) 定义:最大公因数,也称最大公约数,指两个或多个整数共有约数&…...

知识库Dify和cherry无法解析影印pdf word解决方案
近期收到大量读者反馈:上传pdf/图文PDF到Dify、Cherry Studio等知识库时,普遍存在格式错乱、图片丢失、表格失效三大痛点。 在试用的几款知识库中除了ragflow具备图片解析的能力外,其他的都只能解析文本。 如果想要解析扫描件,…...

【记录一下学习】Embedding 与向量数据库
一、向量数据库 向量数据库(Vector Database),也叫矢量数据库,主要用来存储和处理向量数据。 在数学中,向量是有大小和方向的量,可以使用带箭头的线段表示,箭头指向即为向量的方向,…...

【第21节】C++设计模式(行为模式)-Chain of Responsibility(责任链)模式
一、问题背景 在 VC/MFC 开发中,消息处理机制是核心部分之一。VC 是基于消息和事件驱动的框架,消息的处理流程通常是通过链式传递的方式进行的。例如,一个 WM_COMMAND 消息的处理流程可能如下: (1)MDI 主窗…...

createrepo centos通过nginx搭建本地源
yum update 先安装一个nginx。 安装Nginx yum install gcc gcc-c pcre pcre-devel openssl openssl-devel libtool zlib zlib-devel -y cd /usr/local/src wget http://nginx.org/download/nginx-1.22.0.tar.gz tar -zxvf nginx-1.22.0.tar.gz cd nginx-1.22.0 ./configu…...

在 Docker 中搭建GBase 8s主备集群环境
本文介绍了如何在同一台机器上使用 Docker 容器搭建GBase 8s主备集群环境。 拉取镜像 拉取GBase 8s的最新镜像 docker pull liaosnet/gbase8s或者docker pull liaosnet/gbase8s:v8.8_3513x25_csdk_x64注:在tag为v8.8_3513x25_csdk_x64及之后的版本中,…...

【MySQL-数据类型】数据类型分类+数值类型+文本、二进制类型+String类型
一、数据类型分类 二、数值类型 1.bit类型 测试环境ubuntu 基本语法: bit[(M)]:位字段类型,M表示每个值的位数,范围从1~64;如果M被忽略,默认为1举例: create table testBit(id i…...

小谈java内存马
基础知识 (代码功底不好,就找ai优化了一下) Java内存马是一种利用Java虚拟机(JVM)动态特性(如类加载机制、反射技术等)在内存中注入恶意代码的攻击手段。它不需要在磁盘上写入文件,…...

简单的二元语言模型bigram实现
内容总结归纳自视频:【珍藏】从头开始用代码构建GPT - 大神Andrej Karpathy 的“神经网络从Zero到Hero 系列”之七_哔哩哔哩_bilibili 项目:https://github.com/karpathy/ng-video-lecture Bigram模型是基于当前Token预测下一个Token的模型。例如&#x…...

【清华大学】实用DeepSeek赋能家庭教育 56页PDF文档完整版
清华大学-56页:实用DeepSeek赋能家庭教育.pdf https://pan.baidu.com/s/1BUweVDeG2M8-t0QaIs3LHQ?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/8a9473493bb0 《实用DeepSeek赋能家庭教育》基于清华大学研究成果,系统阐述了DeepSeek人工智能技…...

黑洞如何阻止光子逃逸
虽然涉及广义相对论,但广义相对论说的是大质量物体对周围空间的影响,而不是说周围空间和空间中的光子之间的关系。也就是说,若讨论光子逃逸问题,则不必限定于大质量的前提,也就是说,若质量周围被扭曲的空间…...

1.4 单元测试与热部署
本次实战实现Spring Boot的单元测试与热部署功能。单元测试方面,通过JUnit和Mockito等工具,结合SpringBootTest注解,可以模拟真实环境对应用组件进行独立测试,验证逻辑正确性,提升代码质量。具体演示了HelloWorld01和H…...