当前位置: 首页 > news >正文

【C语言进阶】了解计算机的程序环境和预处理过程 掌握计算机预处理操作

在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:C语言进阶
🎯长路漫漫浩浩,万事皆有期待

文章目录

  • 1.编译与链接
    • 1.1 程序的翻译环境与执行环境:
    • 1.2 翻译环境:
    • 1.3 翻译阶段:
      • ①.编译:
      • ②.链接:
    • 1.4 运行环境:
  • 2. 预处理详解:
    • 2.1 预定义符号:
    • 2.2 #define:
      • 2.2.1 #define 定义标识符:
      • 2.2.2 #define 定义宏:
      • 2.2.3 #define 替换规则:
      • 2.2.4 带副作用的宏参数:
  • 3. 宏与函数:
    • 3.1 宏与函数对比:
    • 3.2 宏与函数的命名约定:
  • 4. 预处理操作符:
    • 4.1 预处理操作符 " # ":
    • 4.2 预处理操作符 " ## ":
  • 5. 条件编译:
    • 5.1 简述条件编译指令:
    • 5.2 常见条件编译指令:
      • ①. 单分支条件编译指令:
      • ②. 多分枝条件编译指令:
      • ③. 判断是否被定义:
      • ④.嵌套指令:
  • 7.总结:

1.编译与链接

1.1 程序的翻译环境与执行环境:

在研究程序的编译与链接细节之前,我们首先要了解我们程序的翻译以及执行环境,我们要知道,在 ANSI C 的任何一种实现中,都存在着两种环境:

翻译环境:在该环境中,我们所写下的 .c 等源代码将被转化成可执行的机器指令
执行环境:在该环境下,将会真正执行经过转化后生成的 .exe 可执行文件

1.2 翻译环境:

在翻译环境中执行的操作,简单来说可以分为三个步骤:
一:组成一个程序的每个源文件通过编译过程分别转换成目标代码(.obj)
二:每个目标文件由链接器捆绑在一起,形成一个单一而完整的可执行程序
三:链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人 的程序库,将其需要的函数也链接到程序中。

链接库指的是链接我们的工程中所引用的库函数所依赖的函数库以及各种第三方函数库。

1.3 翻译阶段:

如果我们再细分下来,翻译阶段又可以分为两个阶段,即编译链接

①.编译:

编译阶段又可以细分为三个阶段:
1.预编译(预处理):头文件的包含、#define 定义符号的替换、注释的删除,均为文本操作。
2.编译:将 C 语言的代码翻译成汇编代码,其具体动作包含了语法分析、词法分析、语义分析、符号汇总等。
3. 汇编:将汇编指令翻译为二进制指令,具体动作包含了形成符号表等。

②.链接:

连接阶段负责各个文件的链接相关操作,其具体动作包括了:
1.合并段表
2.合并符号表
3. 重定位符号表

1.4 运行环境:

在这个环境下,我们的程序就真正进入了运行阶段。我们程序的执行过程可以简述为

下面四个步骤:
①. 程序载入内存中:在有操作系统的环境中该步骤通常由操作系统完成;而在独立环境中则必须由我们自己手动完成;独立环境中的程序也有可能通过执行可执行代码置入只读内存来完成。
②. 调用 main 函数:程序正式开始执行。
③. 顺序执行程序代码 :在这个阶段中,我们的程序会使用一个运行时堆栈来存储函数的局部变量和返回地址。同时程序也可以使用静态内存来存储变量,并且这些存储于静态变量中的变量在整个程序的执行过程中将始终保留它们的值
④. 终止程序:一般情况下会正常终止 main 函数,但我们的程序也有可能会意外终止

2. 预处理详解:

2.1 预定义符号:

在 C 语言中有一些内置的预处理符号:

 __FILE__:进行编译的源文件__LINE__:文件当前的行号__DATE__:文件被编译的日期__TIME__:文件被编译的时间__STDC__:如果编译器遵循ANSI C标准,其值为1,否则未定义

这些符号都是语言内置的,可以直接使用:

#include<stdio.h>
int main()
{printf("进行编译的源文件:\n");printf("%s\n", __FILE__);printf("文件当前的行号:\n");printf("%d\n", __LINE__);printf("文件编译的日期:\n");printf("%s\n", __DATE__);printf("文件编译的时间:\n");printf("%s\n", __TIME__);return 0;
}

直接进行编译运行即可查看结果:

2.2 #define:

#define 的用处非常多,就比如我们常用的定义标识符常量、定义宏等等,而在这个过程中,也有一些细节值得我们去注意。

2.2.1 #define 定义标识符:

我们常常会使用 #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 的最后,要不要加分号 “ ; ” ?
最好不要添加,这是因为有可能会导致语法错误:

#define MAX 1000;
#include<stdio.h>
int main()
{int a = 1;int max = 0;if (a){max = MAX;}printf("max = %d\n", max);return 0;
}

例如这个例子,在预编译(预处理)阶段,会进行 #define 定义标识符的替换,于是 if 语句中的代码将会被替换为:

将被替换为:max = (MAX;);
即:max = MAX;;

此时我们发现,经过符号替换后,出现了语法错误

2.2.2 #define 定义宏:

在#define 的机制中,包括了一个规定,这个规定允许把参数替换到文本中,这种实现通常称为定义宏(或简称为宏)
宏的申明方式为:

#define name( parament-list ) stuff

其中的 parament-list 是一个由逗号隔开的符号列表,且可能出现在 stuff 中。
注意:参数列表的左括号必须与 name 紧邻。如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

例如我们定义一个宏 sqr 用来计算平方:

#define sqr(x) x*x

这个宏将会在执行时接受一个参数 x,如果在上述声明之后,将下面的代码置于程序中:

sqr( 5 );

则预处理器就会将上面的表达式替换为:

5 * 5

警告:这个宏的书写仍是不严谨的,随时可能导致错误的产生。
例如下面这样情况:

#define sqr(x) x*x
#include<stdio.h>
int main()
{int a = 5;printf("%d\n", sqr(a + 1));return 0;
}

看起来好像并没有什么问题,但事实上,这段代码在经过预编译的符号替换后将会变成:

#define sqr(x) x*x 
#include<stdio.h>
int main()
{int a = 5;printf("%d\n", a + 1 * a + 1);return 0;
}

原本想要计算 6 * 6,却在预编译的符号替换后出现了错误,打印了 5 + 1 * 5 +1。

解决方法很简单,只需要对宏定义进行简单修改即可:

#define sqr(x) (x)*(x)
#include<stdio.h>
int main()
{int a = 5;printf("%d\n", sqr(a + 1));return 0;
}

通过小括号的使用,在预编译阶段,这段代码将被替换为:

 printf("%d\n",(a+1)*(a+1));

但是其实这样写还有可能出现下面这样的错误:

#define sqr(x) (x)+(x)
#include<stdio.h>
int main()
{int a = 5;printf("%d\n", 10 * sqr(a));return 0;
}

经过了与编译后又将会被替换为:

 printf("%d\n", 10 * (a)+(a));

由于操作符中 * 的优先级高于 +,计算结果又将出现差错。
解决方案是再使用一对小括号对宏定义进行简单修改即可:

#define sqr(x) ((x)+(x))
#include<stdio.h>
int main()
{int a = 5;printf("%d\n", 10 * sqr(a));return 0;
}

这时的代码在经过预编译后将会被替换为:

printf("%d\n", 10 * ((a)+(a)));

总结 在用于对数值表达式进行求值的宏定义中,都应该用这种方式加上括号以避免在使用宏时由于参数中操作符或邻近操作符之间不可预料的相互作用,从而导致在经过预编译阶段的符号替换后出现错误。

2.2.3 #define 替换规则:

#define 在进行符号替换时,遵循以下规则:

  1. 调用宏时首先对参数进行检查,检查是否包含任何由 #define 定义的符号。如果有,它们将首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由 #define 定义的符号。如果有,就重复上述处理过程。

注意
1.宏参数和 #define 定义中可以出现其它 #define 定义的符号。但是对于宏,绝对不能出现递归
2. 当预处理器搜索 #define 定义的符号的时候,字符串常量的内容并不会被搜索

2.2.4 带副作用的宏参数:

这里所说的副作用是指表达式求值的时候出现的永久性效果

x + 1;//不带副作用
x++;//带有副作用

而这样的代码会通过下面这样的方式对结果造成影响:

#define MAX(a,b) ((a)>(b)?(a):(b))
#include<stdio.h>
int main()
{int x = 5;int y = 8;int z = MAX(x++, y++);printf("x = %d y = %d z = %d\n", x, y, z);return 0;
}

这段代码的实际结果为:
在这里插入图片描述

由这个例子我们可以得知,当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么在使用这个宏的时候就有可能导致不可预测的后果。

3. 宏与函数:

3.1 宏与函数对比:

宏 #define 常用于定义一些全局的宏常量等。宏还常常被应用于执行简单的运算,例如比较两数的大小可以这样用:

#define MAX(a,b) ((a)>(b)?(a):(b))
#include<stdio.h>
int main()
{int a = 5;int b = 10;printf("MAX = %d\n", MAX(a, b));return 0;
}

为什么我们不使用函数来完成这个过程呢?

在这里使用宏而不用函数的主要原因有两个:
①. 相比于函数宏在程序规模与速度方面更胜一筹:由于函数在调用时牵扯到函数栈帧的创建与销毁等操作,所以用于调用函数以及从函数返回的耗时可能比实际执行这个小型计算工作的耗时更多。
②. 宏是类型无关的:在声明、定义和使用函数时,函数的参数必须是特定的类型,所以函数只能在类型合适的特定表达式上使用。反观宏,无论是整形、长整型还是浮点型,都可以进行比较。

宏与函数相比也有劣势

①. 宏只能处理简单运算:我们知道在预处理阶段宏将会进行符号替换,这就意味着每次在使用宏的时候,一份宏定义的代码将插入到程序中。这样一来,如果宏比较长,就将会大幅度增加程序代码的长度。
②. 宏是没有办法进行调试的。
③. 由于宏具有类型无关的特点,因此也不够严谨
④. 宏可能会带来运算符优先级的问题,导致程序容易出错

宏也可以做到函数做不到的事,比如:

#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
#include<stdio.h>
#include<string.h>
int main()
{MALLOC(10, int);//预处理器替换后:(int*)malloc(10 * sizeof(int));return 0;
}

3.2 宏与函数的命名约定:

我们使用的宏定义函数,与普通函数的使用语法很相似,导致语言本身无法帮我们区分二者。所以我们通常对二者的命名进行约定:
①. 宏的名称全部大写。
②. 函数名不要全部大写。

如此就可以帮助我们区分,提升代码的可读性

4. 预处理操作符:

int main()
{char* p = "Hellow ""world\n";//*p指向两个常量字符串printf("Hellow world\n");printf("%s", p);return 0;
}

当我们将其编译运行发现,打印结果均为“ Hellow world”:
在这里插入图片描述
于是可以得知,字符串具有自动连接的特点。那么我们是不是就可以写出下面这样的代码呢?

#define PRINT(FORMAT,VALUE) printf("The value of VALUE is "FORMAT"\n",VALUE);
//可以使用宏来定义函数
//字符串"The value of VALUE is "与"\n"将会自动连接
//构造函数等价于printf("the value of VALUE is %d\n",VALUE);
#include<stdio.h>
int main()
{int a = 10;PRINT("%d", a + 3);return 0;
}

在这里插入图片描述
在这段代码中,我们使用了宏来构造一个新的函数,在这个我们自己构造出来的宏函数中,其中的两个短字符串将会自动连接成为一整个长字符串,并根据我们输入的参数进行打印。于是在这个过程中,就衍生出了两个实用的预处理操作符:# 与 ##。

4.1 预处理操作符 " # ":

预处理操作符" # "的作用:将宏参数转变成其对应的字符串
这是什么意思呢?例如下面这段代码中所使用的 #N :

#define _CRT_SECURE_NO_WARNINGS 1
#define PRINT(N) printf("The value of "#N" is %d\n",N);
//N将被视为字符串"N",且字符串 N 在打印时打印的是字符串 N 的内容#include<stdio.h>
int main()
{int a = 10;PRINT(a + 3);return 0;
}

在这段代码中,#N 将会被处理为字符串 N ,即" a + 3 ",于是整个宏定义函数就相当于:

printf("The value of ""a + 3"" is %d\n",VALUE);

即相当于字符串"The value of “、字符串"a + 3"与字符串” is %d\n"之间进行了自动连接,成为了一整个长字符串。
在这里插入图片描述

4.2 预处理操作符 " ## ":

预处理操作符" ## "的作用:将位于其两边的符号合成为一个符号。并且,它允许宏定义从分离的文本片段中创建标识符。

#define CAT(CLASS,NAME) CLASS##NAME
//宏定义函数的作用为将符号 CLASS 与符号 NAME 合并为符号 CCLASSNAME
#include<stdio.h>
int main()
{int class3xiaohong = 888;int back = CAT(class3, xiaohong);//使用宏定义函数 CAT ,将符号 class3 与符号 xiaohong 合并为 class3xiaohong//而符号 class3xiaohong 表示的是值为 888 的 int 类型变量,故用 int 类型变量 back 接收并打印或执行其他操作printf("%d\n", back);return 0;
}

在这段代码中,通过宏定义的预处理操作符" ## “将符号” class3 “与符号” xiaohong “进行了合并,合并成了符号” class3xiaohong “,接着我们看到,在这之前我们就已经定义并初始化了 int 类型变量 class3xiaohong,于是我们就可以使用 int 类型的变量对宏定义函数所合成的符号” class3xiaohong "所表示的值进行接收了。
最后进行打印,验证符号合成结果:

5. 条件编译:

5.1 简述条件编译指令:

通过使用条件编译指令,我们在编译一段代码时如果要将一条(或一组)语句编译或舍弃是很方便的。例如一些调试性的代码就可以通过使用条件编译指令来实现选择性的进行编译。

#include<stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){arr[i] = i + 1;printf("%02d ", arr[i]);
#if 0printf("\n");
#endif}return 0;
}

在这段代码中,包含在条件编译指令 #if 与 #endif 之间的换行打印是否执行,就取决于条件编译指令的参数,此时参数为 0 ,即为假,则不进行换行打印:在这里插入图片描述
那么我们再将参数改为1,即真:

#include<stdio.h>
int main()
{int arr[10] = { 0 };int i = 0;for (i = 0; i < 10; i++){arr[i] = i + 1;printf("%02d ", arr[i]);
#if 1printf("\n");
#endif}return 0;
}

这时条件编译指令参数为真,于是执行其中的代码进行换行打印
在这里插入图片描述

5.2 常见条件编译指令:

①. 单分支条件编译指令:

#if 常量表达式//执行操作
#endif//或:常量表达式由预编译器求值:
#define __DEBUG__ 1#if __DEBUG__//执行操作
#endif

②. 多分枝条件编译指令:

#if 常量表达式//执行操作
#elif 常量表达式//执行操作
#elif 常量表达式//执行操作
...#endif

③. 判断是否被定义:

#if defined(symbol)//如果 symbol 被定义过,则执行操作
#endif//或:
#if !defined(symbol)//如果 symbol 没有被定义过,则执行操作
#endif

④.嵌套指令:

#if defined(MAX)#if 1printf("%d\n", a > b ? a : b);#endif
#elif defined(MIN)#if 0printf("%d\n", a < b ? a : b);#endif
#endif

通过灵活的嵌套使用条件编译指令,就能实现某条或某组程序代码的选择性编译。

7.总结:

今天我们了解了计算机的程序环境和预处理过程的相关知识,学习了掌握计算机预处理操作以及对程序编译的环境、编译链接过程以及很重要的预处理指令 #define 进行了研究,并且对条件编译相关指令和宏与函数有了一定的了解和区分,希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

相关文章:

【C语言进阶】了解计算机的程序环境和预处理过程 掌握计算机预处理操作

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C语言进阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录1.编译与链接1.1 程…...

(三十六)大白话数据库幻读,本质到底是个什么问题?

上一讲我们给大家讲解了不可重复读这个问题&#xff0c;这个问题简单来说&#xff0c;就是一个事务多次查询一条数据&#xff0c;结果每次读到的值都不一样&#xff0c;这个过程中可能别的事务会修改这条数据的值&#xff0c;而且修改值之后事务都提交了&#xff0c;结果导致人…...

【算法经典题集】递归(持续更新~~~)

&#x1f63d;PREFACE&#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐ 评论&#x1f4dd;&#x1f4e2;系列专栏&#xff1a;算法经典题集&#x1f50a;本专栏涉及到的知识点或者题目是算法专栏的补充与应用&#x1f4aa;种一棵树最好是十年前其次是现在1.递归1.1 递归实现…...

多区域的OSPF实战配置

多区域的OSPF实战配置 需求 如图配置设备的接口IP地址如图规划OSPF网络的区域要求每个设备的 router-id 都是 x.x.x.x&#xff08;x是每个路由器的名字&#xff09;确保不同的PC之间可以互通 拓扑图 配置命令 PC1&#xff1a; 192.168.1.1 255.255.255.0 192.168.1.254PC2:…...

现在转行做程序员的多吗?

曾经有一名程序员说&#xff0c;他在编写程序时&#xff0c;就像一个发明家在做实验&#xff1b;当他把程序编好可以运行时&#xff0c;他就已经是个发明家了。 程序员作为众多转行人员首选的职业&#xff0c;也是被大众熟知了。但我们需要知道的不仅是它作为一个职业的定义&a…...

社招前端常见react面试题(必备)

解释 React 中 render() 的目的。 每个React组件强制要求必须有一个 render()。它返回一个 React 元素&#xff0c;是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素&#xff0c;则必须将它们组合在一个封闭标记内&#xff0c;例如 <form>、<group>、<div&g…...

力扣-变更性别

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;627. 变更性别二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他总结前言…...

【项目精选】医院管理住院系统的研究与实现(源码+论文+视频)

点击下载源码 本系统主要分为六大模块&#xff0c;分别是医生管理模块、病人管理模块、病床管理模块、收费管理模块、统计分析模块和系统功能模块 &#xff0c;医生、病人和医院的管理人员都可以通过此系统寻找出自己所需要的信息。 1.1 背景 医院管理住院系统是当今大部分现代…...

Lenovo Legion Y530-15ICH电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。硬件型号驱动情况主板Lenovo Legion Y530-15ICH处理器Intel Core™ i7-8750H (Coffee-Lake)已驱动内存16GB RAM DDR4 2667MHz已驱动硬盘2TB HP EX950 PCI-E Gen3 x4 NVMe SSD已驱动显卡Intel UHD Graphics 630Nvidia GTX 10…...

CICD 导航

目录内容链接产研服务产研服务参考文章&#xff1a;【产研】 服务部署配置及使用产研服务问题解决参考文章&#xff1a;【问题解决】产研.Net服务部署 配置 使用代码托管GitlabGitlab参考文章&#xff1a;Gitlab 安装 与 使用代码托管Gitlab问题解决参考文章&#xff1a;【问题…...

xgboost学习-原理

文章目录一、xgboost库与XGB的sklearn APIXGBoost的三大板块二、梯度提升树提升集成算法&#xff1a;重要参数n_estimators三、有放回随机抽样&#xff1a;重要参数subsample四、迭代决策树&#xff1a;重要参数eta总结一、xgboost库与XGB的sklearn API 现在&#xff0c;我们有…...

如何查看CUDA版本

Linux 查看CUDA版本&#xff1a; nvcc --version或 nvcc --VWindows 查看CUDA版本&#xff1a; nvcc --version或进入 CUDA 的安装目录查看&#xff1a; C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA使用PyTorch 查看 CUDA 版本 import torch print(torch.__ver…...

三、iperf3代码主要架构分析之main函数主要流程

iperf3是一个非常强大的工具&#xff0c;它是用C语言编写的。同时iperf3也是用C语言实现面向对象编程的典范&#xff0c;他以数据结构函数指针为基础&#xff0c;非常好的用C语言实现面向对象的编程的三大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态。相信通过阅读i…...

【概念辨析】大小端存储

一、情境 在进行内存调试窗口的查看时&#xff0c;是不是会有一种错觉&#xff0c;就是它存的数据与我们预期的都是颠倒的&#xff0c;比如&#xff1a; 这里的a就和我们预期的不是很相同。 二、大小端 大小端是计算机厂家根据自己的习惯制定的关于数据字节序的规则。 1.大端…...

并发编程-学习总结(下)

目录 1、Future 1.1、Callable和Runnable的不同 1.2、Future的主要功能 1.3、常用方法 1.4、Future使用注意事项 1.5、CompletableFuture(旅游平台问题) 1.5.1、需求 1.5.2、解决方案1&#xff1a;串行 1.5.3、解决方案2&#xff1a;线程池 1.5.4、解决方案3&#xf…...

arm汇编指令详细整理及实例详解

目录一、简介二、ARM 汇编指令说明2.1 32位数据操作指令2.2 32位存储器数据传送指令2.3 32位转移指令2.4 其它32位指令三、实例讲解3.1 MRS3.2 MSR3.3 PRIMASK3.4 FAULTMASK3.5 BX指令3.6 零寄存器 wzr、xzr3.7 立即寻址指令3.8 寄存器间接寻址指令3.9 寄存器移位寻址指令3.10 …...

高等数学笔记(下下)

无穷级数 定义 一般的&#xff0c;如果给定一个数列u1,u2,u3,...un,...,u_1, u_2, u_3, ... u_n, ... ,u1​,u2​,u3​,...un​,...,&#xff0c;那么由这个梳理构成的表达式u1u2u3...un...u_1u_2u_3...u_n...u1​u2​u3​...un​...叫做(常数项)无穷级数&#xff0c;简称(常…...

零基础如何入门网络安全(黑客)

我经常会看到这一类的问题&#xff1a; 学习XXX知识没效果&#xff1b;学习XXX技能没方向&#xff1b;学习XXX没办法入门&#xff1b; 给大家一个忠告&#xff0c;如果你完全没有基础的话&#xff0c;前期最好不要盲目去找资料学习&#xff0c;因为大部分人把资料收集好之后&a…...

【C++】map和set用法详解

文章目录1.关联式容器2.键值对3.树形结构的关联式容器3.1 set3.1.1 set的介绍3.1.2 set的模板参数列表3.1.3 set的使用3.2 mapmap的介绍map的模板参数列表map的使用关于map的元素访问总结3.3multimap1.关联式容器 我们接触过STL中的部分容器&#xff0c;比如&#xff1a;vecto…...

BLIP2-图像文本预训练

文章目录摘要解决问题算法模型结构通过frozen图像编码器学习视觉语言表征图像文本对比学习&#xff08;ITC&#xff09;基于图像文本生成&#xff08;ITG&#xff09;图文匹配&#xff08;ITM&#xff09;从大规模语言模型学习视觉到语言生成模型预训练预训练数据预训练图像编码…...

Faster-Rcnn修改转数据集文件

目录 学习python的一些基础知识 argparser assert关键字 让你秒懂Python 类特殊方法__getitem__ lxml.etree.fromstring的使用 统计一下json文件内的种类 正脸红外光 正脸-混合红外光 正脸-交叉偏振光 正脸-平行偏振光 正脸-紫外光 正脸-棕色光 调用mydataset可视化…...

带你沉浸式体验删库跑路

前言:学习的过程比较枯燥,后面会记录一些比较有意思的东西&#xff0c;比如程序员之间流传的删库跑路的梗,当然本次测试是在虚拟机上进行的并进行了快照保护,所以其实没太大问题。首先得要有一个虚拟机要有一个linux iso文件装在虚拟机上以上两点不是本文重点,如果有需要可以私…...

Linux学习(8.5)文件内容查阅

目录 文件内容查阅&#xff1a; 直接检视文件内容 cat (concatenate) tac (反向列示) nl (添加行号列印) 可翻页检视 more (一页一页翻动) less (一页一页翻动) 数据撷取 tail (取出后面几行) 非纯文字档&#xff1a; od 修改文件时间或建置新档&#xff1a; touc…...

【Docker】命令总结

目录 1.镜像命令 1.1拉取镜像 1.2查看镜像 1.3保存镜像 1.4导入镜像 2.容器命令 2.1创建并运行容器 2.2删除容器 2.3进入容器 2.4查看容器状态 2.5暂停容器 2.6恢复容器 2.7停止容器 2.8启动容器 2.8查看容器日志 3.数据卷命令 3.1创建数据卷 3.2查看所有数据…...

并发编程-学习总结(上)

目录 1、线程基础 1.1、线程实现方法 1.2、如何正确停止线程 1.3、Java线程的六种状态 1.4、wait/notify/notifyAll注意事项 1.4.1、为什么 wait 、notify、notifyAll必须在 synchronized 保护的同步代码中使用&#xff1f; 1.4.2、为什么 wait/notify/notifyAll 被定义…...

QT之OpenGL混合

QT之OpenGL混合1. 概述2. 实现2.1 丢弃片段2.1.1 Demo2.2 混合2.2.1 相关函数2.2.2 排序问题2.2.3 Demo1. 概述 OpenGL中&#xff0c;混合(Blending)通常是实现物体透明度(Transparency)的一种技术。 2. 实现 2.1 丢弃片段 在某些情况下&#xff0c;有些片段是只需要设置显…...

【1255. 得分最高的单词集合】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 你将会得到一份单词表 words&#xff0c;一个字母表 letters &#xff08;可能会有重复字母&#xff09;&#xff0c;以及每个字母对应的得分情况表 score。 请你帮忙计算玩家在单词拼写游戏中所能获…...

nginx模块介绍

新编译前&#xff0c;在对应的nginx原编译文件夹 如&#xff1a;nginx-1.23.0 下&#xff0c;要 make clean 清空以前编译的objs文件夹&#xff0c;实际上就是执行了rm objs文件夹。 很多要用到git&#xff0c;先yum install git -y echo-nginx-module 让nginx直接使用echo的…...

排错工具ping和trace(电子科技大学TCP/IP实验四)

一&#xff0e;实验目的 1、了解网络连通性测试的方法和工作原理 2、了解网络路径跟踪的方法和工作原理 3、掌握 MTU 的概念和 IP 分片操作 4、掌握 IP 分组生存时间&#xff08;TTL&#xff09;的含义和作用 5、掌握路由表的作用和路由查找算法 二&#xff0e;预备知识 …...

node.js中ws模块创建服务端和客户端

一、WebSocket出现的原因 1、Http协议发布REST API 的不足&#xff1a; 每次请求响应完成之后&#xff0c;服务器与客户端之间的连接就断开了&#xff0c;如果客户端想要继续获取服务器的消息&#xff0c;必须再次向服务器发起请 求。这显然无法适应对实时通信有高要求的场景…...