【C语言】-程序编译的环境和预处理详解-让你轻松理解程序是怎么运行的!!
作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
程序的编译
- 前言
- 一、 程序的翻译环境和执行环境
- 二、 详解翻译环境
- 2.1编译环境
- 2.1.1预编译
- 2.1.2编译
- 2.1.3汇编
- 2.2执行环境
- 三、预处理详解
- 3.1 预定义符号
- 3.2 #define
- 3.2.1 #define 定义标识符
- 3.2.2 #define 定义宏
- 3.2.3 #define 替换规则
- 3.2.4 #和##
- 3.2.5 带副作用的宏参数
- 3.2.6 宏和函数对比
- 3.3 #undef
- 3.4 条件编译
- 3.5 文件包含
- 3.5.1 头文件被包含的方式:
- 3.5.2 嵌套文件包含
- 四、总结
前言
亲爱的友友们,好久不见,今天我又来更新好的文章了,今天我们讲程序的编译环境,相信大家已经对敲代码已经不在感到胆怯了,但是我们所写的代码是怎么变成我们想要的结果的,今天我就带大家来解决这个问题,这方面的问题读者知道能把我今天所讲的给弄明白就好了,不用过分深究
程序的翻译环境
程序的执行环境
详解:C语言程序的编译+链接
一、 程序的翻译环境和执行环境
在ANSI(标准) C的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令(二进制指令)。
第2种是执行环境,它用于实际执行代码
这就是我们平时写的代码,我们需要通过编译加链接的方式才能将源文件变成可执行程序
而我们经常使用的vs2019是一个集成开发环境,包括了翻译环境和执行环境,所以我们运行之后可以直接看到我们想要的结果,所以今天我为了给读者们更好的讲解这个,我们使用vscode这个工具,我用Windows配置了一个gcc编译器,这是通过敲命令行的方式来执行代码的,看一看到每一步的过程
那我们就接着看下去吧
二、 详解翻译环境
2.1编译环境
1.组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
2.每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
3.链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中
我们看到上面那一幅图,可以知道我们通过vs2019中编译器就可以实现从源文件到目标文件的过程,我们就需要使用gcc编译器来一步步看怎么从源文件到目标文件的
我们把编译环境分为三个过程:
1.预编译(预处理)
2.编译
3.汇编
2.1.1预编译
#include <stdio.h>
#define count 10//定义一个宏
int main()
{int i = 0;for(i=0; i<count; i++)//一个循环{printf("%d ", i);}return 0;
}
我们来看一段这个代码,我们来看预编译后是怎么样的,使用:gcc -E test.c -o test.i(把.c文件预编译成.i文件,这个test.i文件名不是固定的,使用者自己可以修改)
test.i
我们发现代码变成了八百多行,我们写的代码是在最下面,那么上面八百多行代码是什么呢?实际是头文件的包含,我们也发现我们之前的注释也不见了,定义的宏也变成了具体的值所以我们得出结论:
1.头文件的包含
2.删除注释
3.#define的替换等
2.1.2编译
我们使用的命令行是:gcc -S test.c(这里不需要写-o,因为默认生成.s的文件)
test.s
变成汇编代码,对于没有学习汇编语言的人来说是看不懂的,但学过,仔细分析一下就可以知道想要表达的内容
在编译阶段变成我们不熟悉的汇编代码
还有语法分析,词法分析,语义分析,符号汇总(比如main,一些函数的地址,一些全局变量等)
符号汇总是为了后面形成符号表的
2.1.3汇编
我们使用的命令行是: gcc -c test.c(这里不需要写-o,在gcc编译器中目标文件默认为.o文件)
test.o
我们发现这个已经是.o文件,变成二进制文件,所以目前在vscode上已经打不开了,我们用文本打开看看
都是我们看不懂的东西,把汇编代码变成二进制指令,形成符号表,这里我们不能很直观的看到形成后的样子,我通过画图让大家简单了解一下
这里我们就可以看到对应关系,如果大家看不懂没有关系,我们只需要理解就可以了
我们可以通过在线的vim机自己去看看怎么回事:
VIM学习资料
简明VIM练级攻略:
link
给程序员的VIM速查卡
link
这张图包括的链接:
2.2执行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。
三、预处理详解
3.1 预定义符号
_FILE_ //进行编译的源文件
_LINE_ //文件当前的行号
_DATE_ //文件被编译的日期
_TIME_ //文件被编译的时间
_STDC_ //如果编译器遵循ANSI C,其值为1,否则未定义
这些预定义符号都是语言内置的。
举个例子:
3.2 #define
3.2.1 #define 定义标识符
语法: #define name stuff
举个例子:
#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是在预处理阶段就完成的,我们在vscode给大家演示
#include <stdio.h>
#define count 10//定义一个宏
int main()
{int a[count]={0};return 0;
}
2.我们可以看到直接把数值带入,我们在来看一个例子:
int main()
{int n = 0;switch(n){ case 1:break;case 2:break;}return 0;
}
我们知道,写switch语句要主要写break;但是有的时候我们会忘记,所以我们使用#define
#define CASE break;caseint main()
{ int n = 0;switch (n){case 1:CASE 2:CASE 3:CASE 4:}return 0;
}
我们要注意第一个case要自己写
提问:在define定义标识符的时候,要不要在最后加上 ; ?
#define MAX 1000;
#define MAX 1000
建议不要加上 ; ,这样容易导致问题。
比如下面的场景:
if(condition)max = MAX;
elsemax = 0;
我们来看:
这里会出现语法错误。
3.2.2 #define 定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
下面是宏的申明方式:
#define name( parament-list ) stuff,其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中.
注意:
参数列表的左括号必须与name紧邻。
如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
如:
1.
#define SQUARE( x ) x * x
这个宏接收一个参数 x .
如果在上述声明之后,你把SQUARE( 5 );置于程序中,预处理器就会用下面这个表达式替换上面的表达式:5 * 5
我们来看:
警告:
这个宏存在一个问题:
观察下面的代码段:
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
按照我们想要的结果应该是36,但答案却是11,原因就是宏定义是先带入在计算的,为了避免这个错误:我们应该加上括号:
2.
#define SQUARE( x ) (x) * (x)
这样预处理之后就产生了预期的效果:
int a=(5 + 1) * (5 + 1) );
3.这里还有一个宏定义:
#define DOUBLE(x) (x) + (x)
定义中我们使用了括号,想避免之前的问题,但是这个宏可能会出现新的错误。
int a = 5;
printf("%d\n" ,10 * DOUBLE(a));
我们想要的结果应该是100,但结果却是55,所以我们应该这样修改;
#define DOUBLE( x) ( ( x ) + ( x ) )
//这个问题,的解决办法是在宏定义表达式两边加上一对括号就可以了。
提示:
所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
3.2.3 #define 替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
- 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
3.2.4 #和##
#的作用
如何把参数插入到字符串中?
首先我们看看这样的代码:
int main()
{int a = 10;printf("the value of a is %d\n", a);int b = 20;printf("the value of b is %d\n", b);
我们发现这么写好麻烦,而且布置到下次求的是什么参数的价值,我们对printf里面的变量就不能写的这么死,我们应该把参数插入字符串里面来解决这个问题:
我们来看一段代码:
char* p = "hello ""bit\n";
printf("hello"," bit\n");
printf("%s", p);
我们发现字符串是有自动连接的特点的。
我们利用这一个特点来试试:
#define PRINT(FORMAT, VALUE) printf("the value is " FORMAT "\n", VALUE)
...
PRINT("%d", 10);
我们看到结果和我们想要的还是有点差距的:
这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中,只有“%d”是字符串,才放到原来的字符串里面的,如果我们也想把a,b也放进去我们应该怎么做呢??
另外一个技巧是:
使用 # ,把一个宏参数变成对应的字符串。
比如:
#define PRINT(FORMAT, VALUE)\printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", a);
这样就达到我们想要i的结果,通过#把不是字符串的宏参数加到了字符串中了
##的作用
##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。
#define ADD_TO_SUM(num, value) \sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.
把sum和5连在一起变成了sum5
这样的连接必须产生一个合法的标识符。否则其结果就是未定义的
3.2.5 带副作用的宏参数
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
x+1;//不带副作用
x++;//带有副作用#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
这里我们得知道预处理器处理之后的结果是什么:
z = ( (x++) > (y++) ? (x++) : (y++));
所以输出的结果是:
x=6 y=10 z=9
3.2.6 宏和函数对比
宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。
#define MAX(a, b) ((a)>(b)?(a):(b))
那为什么不用函数来完成这个任务?
两个原因:
1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
我们通过反汇编可以看到宏比我们函数所使用的时间要短,并且对于宏你可以传任意参数类型进去,而函数只能传定义的参数
当然和宏相比函数也有劣势的地方:
- 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序
的长度。 - 宏是没法调试的。
- 宏由于类型无关,也就不够严谨。
- 宏可能会带来运算符优先级的问题,导致程容易出现错。
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
#define MALLOC(num, type)\(type *)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);//类型作为参数
//预处理器替换之后:
(int *)malloc(10 * sizeof(int));
宏和函数的一个对比
命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。
那我们平时的一个习惯是:
1.把宏名全部大写
2.函数名不要全部大写
3.3 #undef
这条指令用于移除一个宏定义。
#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
我们看这样是可以的
这样就不行了,因为我们已经取消的对宏的定义
3.4 条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
#include <stdio.h>
#define __DEBUG__
int main()
{int i = 0;int arr[10] = {0};for(i=0; i<10; i++){arr[i] = i;#ifdef __DEBUG__printf("%d\n", arr[i]);//为了观察数组是否赋值成功。 #endif //__DEBUG__}return 0;
}
常见的条件编译指令:
1.
#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif
这里我就不在给大家演示了,我们现在用的还不多·,在我们看到源码的时候就会发现用的特别多
3.5 文件包含
我们已经知道, #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方一样。
这种替换的方式很简单:
预处理器先删除这条指令,并用包含文件的内容替换。
这样一个源文件被包含10次,那就实际被编译10次。
3.5.1 头文件被包含的方式:
本地文件包含
#include "filename"
查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。
如果找不到就提示编译错误。
库文件包含
#include <filename.h>
查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
这样是不是可以说,对于库文件也可以使用 “” 的形式包含?
答案是肯定的,可以。
但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。
Linux环境的标准头文件的路径:
/usr/include
VS环境的标准头文件的路径:
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
3.5.2 嵌套文件包含
comm.h和comm.c是公共模块。
test1.h和test1.c使用了公共模块。
test2.h和test2.c使用了公共模块。
test.h和test.c使用了test1模块和test2模块。
这样最终程序中就会出现两份comm.h的内容。这样就造成了文件内容的重复。
如何解决这个问题?
答案:条件编译。
每个头文件的开头写:
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
或
#pragma once
就可以避免头文件的重复引入。
因为以后在上班的时候,项目是由许多人一起开发出来的,难免会出现头文件重复引用,所以我们要使用田间编译防止头文件被重复引用
四、总结
相信大家看到这里对程序是怎么运行的应该已经有了初步的了解,说到这里对于C语言知识的分享可能就要告一段落了,接下来我会将继续分享数据结构相关的知识了,到时候欢迎大佬们来支持,那我们今天就先到这吧
相关文章:

【C语言】-程序编译的环境和预处理详解-让你轻松理解程序是怎么运行的!!
作者:小树苗渴望变成参天大树 作者宣言:认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧! 程序的编译前言一、 程序的翻译环境和执行环境二、 详解翻译环境2.1编译环境2.1.1预编…...

MapBox动态气泡图渲染教程
先来看效果: 视频效果: 屏幕录制2023-02-22 15.34.57 首先我们来介绍一下思路。对于mapbox和openlayers这样的框架来讲,气泡图中的气泡本质上就是一个div,就是将一个dom元素追加到canvas上的固定位置而已。 在mapbox中有marker的概念,官网也有示例: Attach a popup to …...

在 Ubuntu18.04 上编译安装 GMP
(2021.08.04)最近为了安装 IBM 的开源项目 HElib C,需要在服务器上先安装GMP和NTL,NTL需要依赖GMP,所以先来安装一下GMP,记录一下在服务器上安装成功的过程:) 直接安装libgmp二进制文…...

到底什么样的条件才能被浙大MBA录取?攻略集合
新一年管理类联考已悄然启动,很多考生把目标也都放在了浙江大学MBA项目上,那么浙江大学MBA项目好考吗?报考流程是怎样的?杭州达立易考教育在这里给大家汇总整理了浙大MBA项目相关资讯,分享给想要报考浙大MBA的同学&…...

Impacket工具使用
Impacket工具说明 Impacker是用户处理网络协议的Python类集合,用于对SAB1-3或IPv4/IPv6 上的TCP/UPD/ICMP/IGMP/ARP/IPv4/IPv6/SMB/MSRPC/NTLM/Kerberos/WMI/LDAP 等进行低级的编程访问,数据包可以从头开始构建,也可以从原始数据包中解析, 面向对象API使用处理协议的深层结构变…...

华为OD机试真题Python实现【RSA 加密算法】真题+解题思路+代码(20222023)
RSA 加密算法 题目 RSA 加密算法在网络安全世界中无处不在 它利用了极大整数因数分解的困难度,数据越大安全系数越高 给定了一个32位正整数,请对其进行因数分解 找出哪两个素数的乘积 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Python)真题目录汇总 ## 输…...

App.vue中读取不到路由的信息
问题: 首先定义了一个路由,并且在路由元里面存储了一个变量,在App.vue里面访问这个变量的时候却显示undefined!在路由对应的组件中却能访问到! 定义的路由元信息: 为啥访问不到…,懵逼的我在App.vue里…...

Lambda表达式详解
文章目录1、Lambda表达式简介2、如何使用Lambda表达式3、在哪里使用Lambda表达式3.1 函数式接口3.2函数描述符4、四大核心函数式接口4.1 Predicate4.2 Consumer4.3 Function4.4 Supplier5、方法引用5.1 方法引用的使用情况6、构造器引用7、数组引用8、复合Lambda表达式的有用方…...

网关的通用设计框架
概念 网关,很多地方将网关比如成门, 没什么问题, 但是需要区分网关与网桥的区别。 网桥:工作在数据链路层,在不同或相同类型的LAN之间存储并转发数据帧,必要时进行链路层上的协议转换。可连接两个或多个网络…...

API 接口应该如何设计?如何保证安全?如何签名?如何防重?
说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安全呢(防窃取)?除了https的协议之外,能不能加上通用的一套算法以及规范来保证传输的安全性呢&am…...

LeetCode-131. 分割回文串
目录题目思路回溯题目来源 131. 分割回文串 题目思路 切割问题类似组合问题。 例如对于字符串abcdef: 组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个…。切割问题:切割一个a之后&…...

【C++】string类的基本使用
层楼终究误少年,自由早晚乱余生。你我山前没相见,山后别相逢… 文章目录一、编码(ascll、unicode字符集、常用的utf-8编码规则、GBK)1.详谈各种编码规则2.汉字在不同的编码规则中所占字节数二、string类的基本使用1.string类的本质…...

【第一章 - 绪论】- 数据结构(近八千字详解)
目录 一、 数据结构的研究内容 二、基本概念和术语 2.1 - 数据、数据元素、数据项和数据对象 2.2 - 数据结构 2.2.1 - 逻辑结构 2.2.2 - 存储结构 2.3 - 数据类型和抽象数据类型 三、抽象数据类型的表现与实现 四、算法和算法分析 4.1 - 算法的定义及特性 4.2 - 评价…...

QIfw制作软件安装程序
前言 Qt Installer Framework是Qt默认包的发布框架。它很方便,使用静态编译Qt制作而成。从Qt的下载地址中下载Qt Installer Framework,地址是:http://download.qt.io/official_releases/qt-installer-framework/ 。支持我们自定义一些我们需要的东西包括页面、交互等。 框…...

【C++】C++入门(上)
前言: C是在C语言的基础上不断添加东西形成的一门语言,在C语言的基础上引入了面向对象的思想。因此C既是面向对象的语言,也是面向过程的语言。因为C是以C语言为基础的,所以基本上C兼容所有的C语言。目前最常用的版本是C98和C11这两…...

5. Kimball维度建模常用术语及概念(一)
文章目录维度建模过程相关概念1. 收集业务需求与数据实现2. 协作维度建模研讨3. 四步骤维度设计过程4. 业务过程5. 粒度6. 描述环境的维度7. 用于度量的事实8. 维度模型事实表技术术语1. 事实表结构2. 可加、半可加、不可加事实3. 事实表中的空值4. 一致性事实5. 事务事实表6. …...

内核调试之Panic-Oops日志分析
这部分我们接着之前的思考,看看内核异常日志的分析。 1 Panic 调试 2 Oops调试 内核出现Panic或Oops错误,如何分析定位问题原因? 首先,保留现场,如下所示为一次非法虚拟地址访问错误。 EXT4-fs (sdc3): recovery c…...

论文解读 | [AAAI2020] 你所需要的是边界:走向任意形状的文本定位
目录 1、研究背景 2、研究的目的 3、方法论 3.1 Boundary Point Detection Network(BPDN) 3.2 Recognition Network 3.3 Loss Functions 4、实验及结果 论文连接:https://ojs.aaai.org/index.php/AAAI/article/view/6896 1、研究背景 最近,旨在…...

数据挖掘流程简单示例10min
数据挖掘流程简单示例10min 套路: 准备数据实现算法测试算法 任务1:亲和性分析 如果一个顾客买了商品X,那么他们可能愿意买商品Y衡量方法: 支持度support : 所有买X的人数 置信度confidence : 所有买X和Y的人数所有买X的人数…...

KDJB1200六相继电保护测试仪
一、概述 KDJB1200继电保护测试仪是在参照电力部颁发的《微机型继电保护试验装置技术条件(讨论稿)》的基础上,广泛听取用户意见,总结目前国内同类产品优缺点,充分使用现代新的的微电子技术和器件实现的一种新型小型化微机继电保护测试仪。可…...

从WEB到PWA 开发-发布-安装
见意如题!本文主要来说说PWA开发!作为一个前端程序员,在没有任何Android/IOS的开发情况下,想想我们有多少种方法来开发一个原生移动应用程序!我们可以有非原生、混合开发,PWA等等手段。类似uniappÿ…...

FPGA纯vhdl实现MIPI CSI2 RX视频解码输出,OV13850采集,提供工程源码和技术支持
目录1、前言2、Xilinx官方主推的MIPI解码方案3、纯Vhdl方案解码MIPI4、vivado工程介绍5、上板调试验证6、福利:工程代码的获取1、前言 FPGA图像采集领域目前协议最复杂、技术难度最高的应该就是MIPI协议了,MIPI解码难度之高,令无数英雄竞折腰…...

《NFL橄榄球》:卡罗来纳黑豹·橄榄1号位
卡罗来纳黑豹(英语:Carolina Panthers)是一支位于北卡罗来纳州夏洛特的职业美式橄榄球球队。他们是国家美式橄榄球联合会的南区其中一支球队。他们与杰克逊维尔美洲虎在1995年加入NFL,成为扩充球队。 2018年球队市值为23亿美元&am…...

我说我为什么抽不到SSR,原来是这段代码在作祟...
本文是龚国玮所写,熊哥有所新增修改删减,原文见文末。 我说我为什么抽不到SSR,原来是加权随机算法在作祟 阅读本文需要做好心理准备,建议带着深究到底的决心和毅力进行学习! 灵魂拷问 为什么有 50% 的几率获得金币&a…...

MySQL MGR 集群新增节点
前言 服务器规划现状(CentOS7.x) IP地址主机名部署角色192.168.x.101mysql01mysql192.168.x.102mysql02mysql192.168.x.103mysql03mysql192.168.x.104proxysql01proxysql、keepalived192.168.x.105proxysql02proxysql、keepalived 新增服务器IP&#x…...

【单目标优化算法】蜣螂优化算法(Dung beetle optimizer,DBO)(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【C++】类和对象入门必知
面向过程和面向对象的初步认识类的引入类的定义类的访问限定符封装类的作用域类的实例化类对象模型this指针C语言和C实现Stack的对比面向过程和面向对象的初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解…...

day38 动态规划 | 509、斐波那契数 70、爬楼梯 746、使用最小花费爬楼梯
题目 509、斐波那契数 斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n - 1) F(n - 2),其…...

2023年备考软考必须知道的6件事
不知不觉,距离2023年上半年软考也只有不到100天的时间了,报名入口也将在3月13日正式开通,你是正在犹豫是否参加考试? 还是已经开始着手准备复习? 关于软考考试你还有哪些疑问? 2023年备考软考必须知道的6件事,建议收藏…...

GLOG如何控制输出的小数点位数
1 问题 在小白的蹩脚翻译演绎型博文《GLOG从入门到入门》中,有位热心读者提问说:在保存日志时,浮点型变量的小数位数如何设置? 首先感谢这位“嘻嘻哈哈的地球人”赏光阅读了小白这不太通顺的博客文章,并提出了一个很…...