【ONE·C || 程序编译简述】

news2025/7/29 23:48:57

总言

  C语言:程序编译相关。
  

文章目录

  • 总言
  • 1、程序的翻译环境和运行环境
    • 1.1、简述
    • 1.2、翻译环境:程序编译与链接
      • 1.2.1、简介:程序如何从.c文件形成.exe可执行程序
      • 1.2.2、过程说明
    • 1.3、运行环境
  • 2、预处理详解
    • 2.1、预定义符号
    • 2.2、#define
      • 2.2.1、#define定义标识符
      • 2.2.2、#define定义宏
      • 2.2.3、#define替换规则介绍
      • 2.2.4、#和##
        • 2.2.4.1、#
        • 2.2.4.2、##
      • 2.2.5、 带副作用的宏参数
    • 2.3、#undef
    • 2.4、命令行定义
    • 2.5、条件编译
      • 2.5.1、格式一
      • 2.5.2、格式二:多个分支的条件编译
      • 2.5.3、判断是否被定义
      • 2.5.4、嵌套指令
    • 2.6、文件包含
      • 2.6.1、头文件包含的方式
        • 2.6.1.1、本地文件包含
        • 2.6.1.2、库文件包含
      • 2.6.2、嵌套包含
        • 2.6.2.1、相关现象
        • 2.6.2.2、解决方案一
        • 2.6.2.3、解决方案二

  
  

1、程序的翻译环境和运行环境

1.1、简述

  在ANSI C的任何一种实现中,存在两个不同的环境。
  1、翻译环境:源代码被转换为可执行的机器指令。
  2、执行环境:用于实际执行代码。
  二者关系如下:
在这里插入图片描述

  
  

1.2、翻译环境:程序编译与链接

1.2.1、简介:程序如何从.c文件形成.exe可执行程序

在这里插入图片描述

  1、组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
  2、每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
  3、链接器同时也会引入标准C函数库中任何被该程序所用到的函数,也可以搜索到个人程序库,将其需要的函数链接到程序中。
  
  

1.2.2、过程说明

  该部分内容后续可完善。
  
  
  

1.3、运行环境

  1. 程序要运行,则必须载入内存中。在有操作系统的环境中,这个步骤一般由操作系统完成。在独立的环境中,程序载入一般由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序载入内存后就可开始执行,其首要调用main函数。(这就是之前说的main函数是程序的入口,一个工程项目中有且仅有一个main函数。)
  3、开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址,同时,程序也可以使用静态(static)内存。存储于静态内存中的变量,在程序的整个执行过程一直保留他们的值
  
  
  

2、预处理详解

2.1、预定义符号

  这些预定义符号都是语言内置的。

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

  
  演示例子如下:

#include <Windows.h>
int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("name:%s file:%s line:%d date:%s time:%s i=%d\n\n", __func__, __FILE__, __LINE__, __DATE__, __TIME__, i);
		Sleep(1000);
	}
	return 0;
}

在这里插入图片描述
  如果有需要,结合文件处理所学,可以将其输出为文本文件:

#include <Windows.h>
int main()
{
	int i = 0;
	FILE* pf = fopen("log.txt", "a");
	if (pf == NULL)
	{
		return 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("name:%s file:%s line:%d date:%s time:%s i=%d\n\n", __func__, __FILE__, __LINE__, __DATE__, __TIME__, i);
		Sleep(1000);
	}

	fclose(pf);
	pf = NULL;

	return 0;
}

  
  
  

2.2、#define

  #define有两个作用,一是定义标识符,而是定义宏;
  

2.2.1、#define定义标识符

  1)、引入
  我们使用宏定义标识符,根据1中所学,可知预处理阶段会进行宏替换,现在我们来验证它:

#define NUM 100+200
#define STR "Death in the Young"

int main()
{
	int num = NUM;
	char* str = STR;

	return 0;
}

  如下图所示,左边为我们写的代码,右边则是预处理后的内容:
在这里插入图片描述
  关于如何在VS中生成右侧.i文件,操作步骤如下:
在这里插入图片描述

  
  
  2)、假如宏定义标识符中加入了;,结果会怎样?

#define NUM 100+200;
#define STR "Death in the Young";

  以下为我们的验证结果:
在这里插入图片描述
  可以看到,会多形成一个语句。在上述情况下,这样子的空语句好像不会造成什么影响,但在有些场景下使用则会带来麻烦,以下为相关举例:

#define NUM 100+200;
#define STR "Death in the Young";
int main()
{
	int num = 0;
	if (1)
		num = NUM;
	else
		num = -1;


	return 0;
}

  如图所示为宏替换后的结果,可看到if语句后,由于多了一条空语句,后续的else则没有对应匹配结果。(PS:此处并非是else就近匹配原则失效。)
在这里插入图片描述
  
  
  
  3)、其它例子演示

  现在我们可以来简单概括一下#define定义标识符的相关语法:

语法:
 #define name stuff

  需要注意的是name部分是严格按照sutff部分定义,如果stuff部分是一个表达式,则相应的name部分也会变为表达式。

  其它情况演示:

#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__ )  

  
  
  

2.2.2、#define定义宏

  1)、引入
  如何用#define定义一个宏函数?

#define Add(x,y) x+y;

int main()
{
	int result = Add(1, 2);
	printf("%d\n", result);
	return 0;
}

  可以看到上述对应宏的地方被替换。
在这里插入图片描述
  
  询问:上述写法是否具有什么缺陷?
  我们在初识C中简单介绍了使用宏需要注意括号问题,因此上述代码可以改进如下:

#define Add(x,y) ((x)+(y));

  1、对每个参数都要添加括号;
  2、对宏整体也要添加括号;
  
  
  2)、宏中参数要添加括号的原因说明

  举例一:我们的本意是要计算平方,可当参数不带括号时,若输入的是表达式,宏只会原封不动替换,以至得到一个错误逻辑:

#define SQUARE(x) x*x

int main()
{
	int a = 9;
	int r = SQUARE(a+1);
	printf("%d\n", r);

	return 0;
}
预期:10*10=100,实际结果:9+1*9+1=19

在这里插入图片描述

  
  
  3)、宏中整体要添加括号的原因说明
  举例二:宏中函数本意是计算倍数值,但因为宏整体不带括号时,而输入的又是表达式,宏原封不动替换后得到错误结果:

#define DOUBLE(x) (x)+(x)
int main()
{
	int ret = 3*DOUBLE(100);
	printf("%d\n", ret);

	return 0;
}
预期:3*(100+100)=600,实际:3*100+100=400

在这里插入图片描述

  
  
  上述两个例子正确写法如下:

#define SQUARE(x) ((x)*(x))
#define DOUBLE(x) ((x)+(x))

  一个结论: 用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
  
  
  

2.2.3、#define替换规则介绍

  在程序中扩展#define定义符号和宏时,需要涉及几个步骤:

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

#define DOUBLE(x) (x)+(x)
#define Digit 200
int main()
{
	int ret = 3*DOUBLE(Digit);
	printf("%d\n", ret);

	return 0;
}
int ret = 3*DOUBLE(200);//如此类套用下,预处理时先解决标识符
int ret = 3*(200)+(200);//再解决宏

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

#define Digit 200
int main()
{
	printf("Digit 的取值是 %d\n", Digit);
	return 0;
}

  "Digit 的取值是 %d\n"这是字符串,故其中的Digit是不会被替换的。
在这里插入图片描述
  
  
  

2.2.4、#和##

2.2.4.1、#

  1)、铺垫知识:字符串是有自动连接的特性
  当两个连续字符串中间什么也没有时(此处空格可省略不加):

int main()
{
	char* p = "hello" " world\n";
	printf("hello" " world\n");
	printf("%s\n",p);
	return 0;
}

在这里插入图片描述

  
  
  2)、问题引入

int main()
{
	int a = 10, b = 20, c = 30;
	printf("the value of a is %d\n", a);
	printf("the value of b is %d\n", b);
	printf("the value of c is %d\n", c);

	return 0;
}

在这里插入图片描述
  如上,若此类语句具有很多条,是否有更高效的方法起到套用、批量的处理?
  可能我们首先能想到的是自定义函数:

void print1(int n)
{
	printf("the value of n is %d\n", n);
}

void print2(char c,int n)
{
	printf("the value of %c is %d\n", c,n);
}

int main()
{
	int a = 10, b = 20, c = 30;
	print1(a);
	print1(b);
	print2('c',c);
	return 0;
}

  如上述,若我们使用print1的写法, 并不能达到效果,若使用print2的效果,则需要多传递一个参数。
在这里插入图片描述
  还有么有什么比较方便的解决办法?
  
  
  3)、#的使用演示:
  关于# 的作用:把一个宏参数变成对应的字符串。
  
  演示一: 在我们使用宏时,若直接代入其效果和使用函数print1类似。

#define PRINT(N) printf("the value of N is %d\n",N)
int main()
{
	int a = 10, b = 20, c = 30;
	PRINT(a);
	PRINT(b);
	PRINT(c);

	return 0;
}

在这里插入图片描述

  
  演示二: 若加入了#,根据其作用,可得如下结果

#define PRINT(N) printf("the value of "#N" is %d\n",N)
int main()
{
	int a = 10, b = 20, c = 30;
	PRINT(a);
	PRINT(b);
	PRINT(c);

	return 0;
}

  对比.i文件,可看到#把一个宏参数变成对应的字符串的含义,即#N=="N",再加之之前1)中铺垫的字符串的连续性特点,可以达到完整输出一段字符的效果。且和print2相比,少使用了一个参数。

在这里插入图片描述
  
  
  演示三: 上述是对相同类型的使用方式,假如所给数据是不同类型,又该如何处理?

  写法一:

#define PRINT(N,format) printf("the value of "#N" is "format"\n",N)
int main()
{
	int a = 10, b = 20;
	double c = 22.2, d = 33.3;
	PRINT(a, "%d");
	PRINT(b, "%d");
	PRINT(c, "%lf");
	PRINT(d, "%lf");

	return 0;
}

在这里插入图片描述
  
  写法二:

#define PRINT(N,p) printf("the value of "#N" is %"#p"\n",N)
int main()
{
	int a = 10, b = 20;
	double c = 22.2, d = 33.3;
	PRINT(a,d);
	PRINT(b,d);
	PRINT(c,lf);
	PRINT(d,lf);

	return 0;
}

在这里插入图片描述
  
  
  

2.2.4.2、##

  1)、##的使用演示
  ##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。
  需要注意的是,这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

  代码演示如下:

#include<stdlib.h>
#include<time.h>

#define STU(name, id) name##id

int main()
{
	srand((unsigned int)time(NULL));

	int student1 = rand() % 40 + 60;
	int student2 = rand() % 40 + 60;
	int student3 = rand() % 40 + 60;
	int student4 = rand() % 40 + 60;
	int student5 = rand() % 40 + 60;


	printf("%d\n", STU(student, 1));
	printf("%d\n", STU(student, 2));
	printf("%d\n", STU(student, 3));
	printf("%d\n", STU(student, 4));
	printf("%d\n", STU(student, 5));


	return 0;
}

在这里插入图片描述

  
  
  

2.2.5、 带副作用的宏参数

  如下述代码,输出结果是什么?

#define MAX(x, y)  ((x)>(y)?(x):(y))

int main()
{
	int a = 5;
	int b = 8;
	int c = MAX(a++, b++);

	printf("%d\n", c);
	printf("%d\n", a);
	printf("%d\n", b);

	return 0;
}

  结果如图所示:
在这里插入图片描述
  原因如下:因为MAX在此处非函数而是宏,替换后效果如下,再加之后置自增的作用,故得到上述结果。

	int c = ((a++) > (b++) ? (a++) : (b++));

  
  
  

2.3、#undef

  #undef:这条指令用于移除一个宏定义。

#undef NAME
//如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。

  
  代码演示如下:

#define MAX(x, y)  ((x)>(y)?(x):(y))

int main()
{
	int a = 5;
	int b = 8;
	int c = MAX(a++, b++);

	printf("%d\n", c);
	printf("%d\n", a);
	printf("%d\n", b);

	//……
#undef MAX

	int d = MAX(a, b);
	printf("%d\n", d);

	return 0;
}

在这里插入图片描述
  
  
  

2.4、命令行定义

  许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。

//linux 环境演示:设INST为我们将设置的符号名
gcc -D INST=10 programe.c

  
  演示如下:
  打开vim写入这样一段C程序,可看到我们并没有定义数组大小。

[wj@VM-4-3-centos T0307]$ ls
test.c
[wj@VM-4-3-centos T0307]$ vim test.c
[wj@VM-4-3-centos T0307]$ cat -n test.c
     1	#include<stdio.h>
     2	
     3	int main()
     4	{
     5	    int arr[sz];
     6	    int i=0;
     7	    for (i=0;i<sz;++i)
     8	    {
     9	        arr[i]=i;
    10	    }
    11	    for(i=0;i<sz;++i)
    12	    {
    13	        printf("%d ",arr[i]);
    14	    }
    15	    return 0;
    16	}
[wj@VM-4-3-centos T0307]$

  现在我们来运行看看:

[wj@VM-4-3-centos T0307]$ ls
test.c

[wj@VM-4-3-centos T0307]$ gcc -o test.out test.c
test.c: In function ‘main’:
test.c:5:13: error: ‘sz’ undeclared (first use in this function)
     int arr[sz];
             ^
test.c:5:13: note: each undeclared identifier is reported only once for each function it appears in
[wj@VM-4-3-centos T0307]$ 

  可看到程序报错,报错原因是sz未定义。
  此时就可以使用我们的命令行定义来修正:需要注意这步操作也是在预处理阶段完成的。

[wj@VM-4-3-centos T0307]$ gcc -o test.out test.c -Dsz=10
[wj@VM-4-3-centos T0307]$ ls
test.c  test.out
[wj@VM-4-3-centos T0307]$ ./test.out
0 1 2 3 4 5 6 7 8 9 
[wj@VM-4-3-centos T0307]$ gcc -o test.out test.c -D sz=100
[wj@VM-4-3-centos T0307]$ ls
test.c  test.out
[wj@VM-4-3-centos T0307]$ ./test.out
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 

  
  
  

2.5、条件编译

  在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃时,可以使用条件编译指令。常见的条件编译指令如下 :
  

2.5.1、格式一

  语法格式如下:

#if 常量表达式
//...
#endif

  常量表达式为真,则条件编译内的语句执行。
  
  代码演示:

#define N 100+2

int main(void)
{
#if 1
		printf("get you the moon.\n");
#endif

#if 0
		printf("in the end.\n");
#endif

#if N
		printf("welcome to the internet\n");
#endif

	return 0;
}

在这里插入图片描述
  
  
  

2.5.2、格式二:多个分支的条件编译

  语法格式如下:

#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif

  
  代码演示:

#define N 222
int main()
{
#if 1
	printf("A Long Way.\n");
	printf("Whispering Still.\n");
#elif 0
	printf("Goodbye.\n");
	printf("Wake up.\n");
#elif N
	printf("Another World.\n");
#else
	printf("Over My Head.\n");
#endif
	return 0;
}

在这里插入图片描述
  
  
  

2.5.3、判断是否被定义

  语法格式如下:
  写法一:

#if defined (symbol) //如果定义了symbol,则执行语句
//...
#endif

#if !defined (symbol) //如果未定义symbol,则执行语句
//...
#endif

  写法二:第二种写法没有括号

int main()
{
#ifdef symbol //如果定义了symbol,则执行语句
//...
#endif

#ifndef symbol //如果未定义symbol,则执行语句
//...
#endif
	return 0;
}

  需要注意的是,这种判断只针对是否被定义,不在意被定义的值。
  
  以下为两种写法的代码演示:

#define N 0
int main()
{
#if defined (N)
	printf("normal no more.\n");
#endif

#if !defined (N)
	printf("the sound of silence.\n");
#endif
	return 0;
}

在这里插入图片描述

#define N 0
int main()
{
#ifdef N
	printf("normal no more.\n");
#endif

#ifndef N
	printf("the sound of silence.\n");
#endif
	return 0;
}

在这里插入图片描述
  
  
  

2.5.4、嵌套指令

  效果如下:

#if defined(OS_UNIX)
 #ifdef OPTION1
 unix_version_option1();
 #endif
 #ifdef OPTION2
 unix_version_option2();
 #endif
#elif defined(OS_MSDOS)
 #ifdef OPTION2
 msdos_version_option2();
 #endif
#endif

  此类定义在库中常见:
在这里插入图片描述

  
  
  

2.6、文件包含

2.6.1、头文件包含的方式

2.6.1.1、本地文件包含

  1)、基础说明
  相关格式如下:本地头文件,包含时需要使用引号

#include "filename"

  查找策略:①编译器先在源文件所在目录下查找。②如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。③如果找不到就提示编译错误。
  
  如下图,就是本地源文件所在路径,若找不到则去对应的库函数所在文件路径下寻找。

#include"test.h"
int mian()
{
	return 0;
}

在这里插入图片描述

  
  2)、Linux下库函数路径
  相关路径:

/usr/include

在这里插入图片描述

  
  

2.6.1.2、库文件包含

  1)、基础说明
  相关格式如下:对于库函数,其头文件可以直接去标准路径下去查找,如果找不到就提示编译错误。

#include <filename.h>

  问题:对于库函数头文件,是否可以使用“”的形式包含?
  回答:可以。但从效率角度讲,这样子效率相对角度。
  
  
  

2.6.2、嵌套包含

2.6.2.1、相关现象

  1)、现象演示
  #include 包含的头文件,会在我们的.源文件中展开。预处理器先删除这条指令,并用包含文件的内容替换。那么假设这样一个源文件中包含该头文件N次,那就实际也被编译了N次。
  如图所示:
在这里插入图片描述
  相关代码:

 2 int add(int x,int y)
  3 {
  4     return x+y;
  5 }
~

  1 #include<stdio.h>
  2 
  3 #include"test.h"                                                                                                                                                                        
  4 #include"test.h"
  5 #include"test.h"
  6 int main()
  7 {
  8     int a=10;
  9     int b=20;
 10     printf("%d\n",add(a,b));
 11     return 0;
 12 }
~
835 # 943 "/usr/include/stdio.h" 3 4
836 
837 # 2 "test.c" 2
838 
839 # 1 "test.h" 1
840 
841 int add(int x,int y)
842 {
843     return x+y;
844 }
845 # 4 "test.c" 2
846 # 1 "test.h" 1
847 
848 int add(int x,int y)
849 {
850     return x+y;
851 }
852 # 5 "test.c" 2
853 # 1 "test.h" 1                                                                                                                                                                          
854 
855 int add(int x,int y)
856 {
857     return x+y;
858 }
859 # 6 "test.c" 2
860 int main()
861 {
862     int a=10;
863     int b=20;
864     printf("%d\n",add(a,b));
865     return 0;
866 }

  
  
  

2.6.2.2、解决方案一

  2)、如何解决?
  方法一:

  相关格式如下:

#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif   
//这里的__TEST_H__是根据你的头文件来改变的,假如叫mylist.h,则为__MYLIST_H__

  
  实际运用如下:
在这里插入图片描述

  相关代码如下:

  1 #ifndef __TEST_H__
  2 #define __TEST_H__ 
  3 int add(int x,int y)
  4 {
  5     return x+y;
  6 }
  7 #endif
  8    
835 # 943 "/usr/include/stdio.h" 3 4
836 
837 # 2 "test.c" 2
838 
839 # 1 "test.h" 1
840 
841 
842 int add(int x,int y)
843 {
844     return x+y;
845 }
846 # 4 "test.c" 2
847 
848 
849 int main()
850 {
851     int a=10;
852     int b=20;
853     printf("%d\n",add(a,b));
854     return 0;
855 }     

  

2.6.2.3、解决方案二

  方法二:
  相关格式如下:

#pragma once

  
  实际运用如下:
在这里插入图片描述

  相关代码如下:

  9 #pragma once                                                                                                                                                                            
 10 int add(int x,int y)
 11 {
 12     return x+y;
 13 }

  
  
  
  
  
  
  
  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/394575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

参考文献怎么查找,去哪里查找?一篇文章讲明白这些问题

在我们撰写论文查找参考文献时&#xff0c;往往不知道从哪里入手&#xff0c;本文小编就针对下面这三个方面给大家详细讲解下&#xff1a; 一、查找参考文献方法 二、参考文献资料查找网站 三、参考文献格式规范 一、查找参考文献方法&#xff1a; 1、知网全球最大的中文数据…

DBeaver连接mysql、oracle数据库

1. DBeaver连接mysql 1&#xff09; 下载DBeaver https://dbeaver.io/download/&#xff0c;并安装 2) 新建数据库连接 3&#xff09;选择mysql驱动程序 4&#xff09;填写连接设置内容 5&#xff09;点击 “编辑驱动设置”&#xff0c;并填写相关信息 6&#xff09;选择本地…

九龙证券|朝着双向开放稳步前进――从沪深港通全面扩容看资本市场对外开放

2023年春天的资本商场&#xff0c;高水平双向敞开的步履益发铿锵。 伴随着沪深买卖所互联互通股票标的规划扩展规矩正式对外发布&#xff0c;3月13日&#xff0c;内地与香港资本商场行将迎来史上最大规划双向扩容——沪深股通标的股票合计将添加1034只&#xff0c;调整后沪股通…

Web前端学习:六 -- 练习小总结

1、背景颜色透明度写法&#xff1a; background&#xff1a;rgba(R&#xff0c;G&#xff0c;B&#xff0c;Alpha透明度) 透明度范围&#xff1a;0–1&#xff0c;1完全不透明&#xff0c;0完全透明 2、伪类 hovar&#xff1a; 当鼠标接触该元素是&#xff0c;显示另一种样…

安恒信息java实习面经

目录1.Java ME、EE、SE的区别&#xff0c;Java EE相对于SE多了哪些东西&#xff1f;2.jdk与jre的区别3.说一下java的一些命令&#xff0c;怎么运行一个jar包4.简单说一下java数据类型及使用场景5.Map跟Collection有几种实现&#xff1f;6.面向对象的特性7.重载和重写的区别8.重…

ElasticSearch 在Java中的各种实现

ES JavaAPI的相关体系&#xff1a; 词条查询 所谓词条查询&#xff0c;也就是ES不会对查询条件进行分词处理&#xff0c;只有当词条和查询字符串完全匹配时&#xff0c;才会被查询到。 等值查询-term 等值查询&#xff0c;即筛选出一个字段等于特定值的所有记录。 【SQL】 s…

工业物联网“杀手级”应用—预测性维护

一、预测性维护的必要性 随着新一轮科技革命和产业变革的兴起&#xff0c;工业物联网、大数据、人工智能等技术正与经济社会各领域加速渗透融合。由于市场竞争对精细化成本管控的要求&#xff0c;设备的重要性越来越凸显&#xff0c;设备的维护对策也必然从响应式维护&#xf…

[Java·算法·中等]LeetCode31. 下一个排列

每天一题&#xff0c;防止痴呆题目示例分析思路1题解1分析思路2题解2&#x1f449;️ 力扣原文 题目 整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。 例如&#xff0c;arr [1,2,3] &#xff0c;以下这些都可以视作 arr 的排列&#xff1a;[1,2,3]、[1,3,2]、…

LearnOpenGL-入门-9.摄像机

本人刚学OpenGL不久且自学&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正。 此篇有点难理解&#xff0c;但是学完会对FPS第一人称3D摄像机的实现有深刻的理解 我写的项目地址&#xff1a;https://github.com/liujianjie/LearnOpenGLProject LearnOpenGL中文官网&a…

一、Java类加载机制

文章目录什么是类的加载&#xff1f;类的生命周期加载连接[验证、准备、解析]初始化结束生命周期类加载器类加载方式双亲委派机制自定义类加载器总结--类加载什么是类的加载&#xff1f; 类的加载指的是将类的.class文件中的二进制数据读入到内存中&#xff0c;将其放在运行时…

Git的SSH密钥配置

Git的SSH密钥配置简记Githttps和ssh的区别基本需求SSH密钥类型ED25519 SSH 密钥RSA SSH 密钥查看您是否有现有的 SSH 密钥对设置流程设置user name和emailssh密钥配置检查是否存在ssh Key创建新的ssh key将ssh密钥添加到您的Git帐户验证您是否可以连接使用Git有一段时间了&…

代码随想录-51-110.平衡二叉树

目录前言题目1.求高度和深度的区别节点的高度节点的深度2. 本题思路分析&#xff1a;3. 算法实现4. pop函数的算法复杂度5. 算法坑点前言 在本科毕设结束后&#xff0c;我开始刷卡哥的“代码随想录”&#xff0c;每天一节。自己的总结笔记均会放在“算法刷题-代码随想录”该专…

OpenCV入门(五)快速学会OpenCV4文字绘制边界填充

OpenCV入门&#xff08;五&#xff09;快速学会OpenCV4文字绘制&边界填充 1.文字绘制 OpenCV中除了提供绘制各种图形的函数外&#xff0c;还提供了一个特殊的绘制函数&#xff0c;即在图像上绘制文字。 这个函数是putText()&#xff0c;它是命名空间cv2中的函数&#xff…

(二十三)操作系统-多生产者·多消费者问题

文章目录一、问题描述二、问题分析1. 关系分析2. 整理思路三、实现1. 代码2.如果不要互斥信号量3. 将盘子&#xff08;缓冲区&#xff09;容量设为2四、总结一、问题描述 桌子上有一只盘子&#xff0c;每次只能向其中放入一个水果。爸爸专向盘子中放苹果&#xff0c;妈妈专向盘…

用强化学习神包trl轻松实现GPT2可控文本生成

来源&#xff1a;投稿 作者&#xff1a;Sally can wait 编辑&#xff1a;学姐 模型github: lvwerra/trl: Train transformer language models with reinforcement learning. (github.com)https://github.com/lvwerra/trl 这个项目是复现 ”Fine-Tuning Language Models from H…

C++vector 简单实现

一。概述 vector是我们经常用的一个容器&#xff0c;其本质是一个线性数组。通过对动态内存的管理&#xff0c;增删改查数据&#xff0c;达到方便使用的目的。 作为一个线性表&#xff0c;控制元素个数&#xff0c;容量&#xff0c;开始位置的指针分别是&#xff1a; start …

Hive---拉链表

拉链表 文章目录拉链表定义用途案例全量流程增量流程合并过程第一步第二步第三步案例二&#xff08;含分区&#xff09;创建外部表orders增量分区表历史记录表定义 拉链表是一种数据模型&#xff0c;主要是针对数据仓库设计中表存储数据的方式而定义的&#xff0c;顾名思义&am…

从零开始学GeoServer源码十一(如何处理多个文件解析器Multipart Resolver引起的冲突问题)

目录前言1.现象2.排查问题3.找到问题4.解决问题5.总结前言 本文起源于我们遇到的一个问题&#xff0c;本来 GeoServer 使用的好好的&#xff0c;但是有天突然发现&#xff0c;无法在 GeoServer 中上传样式的 sld 文件了&#xff0c;报错 “No Multipart-config for Servlet” …

java.lang.IllegalArgumentException: itemView may not be null

报错截图&#xff1a;场景介绍&#xff1a;在使用recycleView 自动递增数据&#xff0c;且自动滚动到最新行&#xff1b; 当数据达到273条 时出现ANR&#xff1b;项目中 全部的列表适配器使用的三方库&#xff1a;BaseRecyclerViewAdapterHelper &#xff08;很早之前的项目&am…

《SQL基础》16. 锁

锁锁全局锁表级锁表锁元数据锁意向锁行级锁行锁间隙锁临键锁锁 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xff09;的争用以外&#xff0c;数据也是一种供许多用户共享的资源。如何保证数据并…