C语言—程序环境和预处理

news2024/11/18 23:48:18

程序环境和预处理

  • 程序的翻译环境和执行环境
  • 编译、链接
    • 翻译环境
      • 编译
        • 预处理(预编译)
        • 编译
        • 汇编
      • 链接
    • 编译环境几个阶段的总结
  • 运行环境(执行环境)
  • 预处理详解
    • 预定义符号
    • #define
      • #define 定义标识符
      • #define 定义宏
      • #define 替换规则
      • #和##
      • 带副作用的宏参数
      • 宏和函数对比
    • #undef
    • 命令行定义
    • 条件编译
    • 文件包含
      • 头文件被包含的方式
      • 嵌套文件包含
      • 经典面试题
  • 预处理指令的相关补充

程序的翻译环境和执行环境

在ANSI(标准) C的任何一种实现中,存在两个不同的环境

  • 第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
  • 第二种是执行环境(运行环境),它用于实际执行代码。

在这里插入图片描述
test.c最后编译成test.exe这个过程依赖的是翻译环境(在当前环境底下用的是VS2019集成开发环境)。可执行程序如果运行这段代码运行产生效果这时所依赖的环境是运行环境最后就会产生结果。

总结:任何C的程序要运行起来一定要有翻译环境和执行环境

编译、链接

翻译环境

在这里插入图片描述

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

补充:

  • 编译是把每个源文件经过编译器处理会生成目标文件。
  • 编译依赖的是编译器,链接依赖的是链接器
  • cl.exe是VS底下的编译器,链接器在Windows底下是link.exe
  • VS2019称为集成开发环境,这样的集成开发环境是把小的组件集成在一起。

编译

编译分为预编译(预处理)、编译、汇编这三个步骤。

预处理(预编译)

实例一:

在这里插入图片描述
注:在预处理阶段完成了头文件的包含,eg:#include这样指令的处理。

实例二:

在这里插入图片描述

注:在预处理阶段完成了#define定义的符号和宏的替换。

实例三:

在这里插入图片描述
注:在预处理阶段完成了注释的删除。

总结:在预处理阶段完成的事情都是文本操作。eg:头文件的包含、#define定义的符号和宏的替换、注释删除等等这些操作。

补充:

  • 在预处理阶段注释被删除了,所以在代码中写再多的注释都不会给程序带来负担。注释写进去之后其实不会影响最终程序的编译。
  • #define定义的符号和宏在预处理阶段完成替换了,在编译和汇编的时候不能看到,在程序运行的时侯调试时#define定义的符号和宏是看不到的。

编译

在这里插入图片描述

在这里插入图片描述

注:

  • 编译阶段是把C语言代码转化成汇编代码
  • 编译阶段进行了语法分析、词法分析、语义分析、符号汇总这样相关的事情。

汇编

在这里插入图片描述

注:

  • 汇编阶段是把汇编代码转化成了二进制指令(机器指令)。
  • 在汇编阶段会生成符号表、把汇编指令转换成二进制指令。
  • test.o这个文件是elf(elf是文件的组织形式,是一种格式)格式的,其实是把test.o这个文件划分成一个一个的段,每个段存放的数据是不一样的,相同的.o文件里面段分的都是一样的,同一种段里面放的数据是不一样的。可以用readelf这个工具来看懂test.o文件。

实例一:

在这里插入图片描述

注:在编译阶段会把.c文件里面这种符号(全局的符号(函数名、全局变量之类的))全部汇总起来—符号汇总。

实例二:

在这里插入图片描述

首先对这两个文件进行预处理,再对这两个文件进行编译,在编译阶段它会把test.c文件里面的符号进行汇总,最后放到test.s文件里。把add.c文件里面的符号进行汇总,最后放到add.s文件里。他们各自把各自的符号(全局的符号)进行汇总,符号汇总完成之后,最后进行汇编,在汇编阶段进行了生成符号表,要把刚刚汇总的符号形成符号表(符号表里有符号和符号对应的地址)。

注:局部的变量是在程序运行的时候进去才创建的。

链接

链接阶段是把多个目标文件和链接库进行链接的,最后生成可执行程序。

实例一:
在这里插入图片描述

注:

  • 链接阶段完成的是合并段表和符号表的合并和符号表的重定位。

  • 生成的.o文件是elf格式的,这种elf格式的文件是由几个段组成的,如果有多个这样的目标文件,最终要把这些目标文件链接生成一个可执行程序(可执行程序的格式也是elf格式的)。把多个文件进行链接时就要把相同段的数据合并在一起——合并段表。

  • 链接期间这样的相同的符号表来自多个.o文件的这种符号表也要合并在一起,符号表合并的时候会把有效的符号的地址进行保留——符号表的合并和重定位

  • 当符号表合并之后,这时链接好的可执行程序调用函数时通过地址就可以找到函数。

实例二:

在这里插入图片描述
注:

  • 符号表里提供的符号就是最后在链接的时候通过符号和它的地址去找对应的函数,链接期间通过地址去其他源文件中把想要找的函数找到(其他的.o文件里面要把想要找的这些函数找到),如果这些函数在链接期间都找不到这个函数就找不到了。

  • .o (二进制文件)、.out(可执行程序)文件都是按照elf格式存放的,这种格式就是把一个文件划分成一个一个的段。

编译环境几个阶段的总结

在这里插入图片描述

Linux环境下生成相关文件的命令:

  1. 预处理 选项 gcc -E test.c -o test.i
    预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
  2. 编译 选项 gcc -S test.c
    编译完成之后就停下来,结果保存在test.s中。
  3. 汇编 gcc -c test.c
    汇编完成之后就停下来,结果保存在test.o中。

VIM的相关命令:Linux环境基础开发工具使用

运行环境(执行环境)

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。

  2. 程序的执行便开始。接着便调用main函数。

  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。

  4. 终止程序。正常终止main函数;也有可能是意外终止。

注:

  • 程序开始执行的时候,每一次函数调用都会为自己开辟一块空间,这块空间就是运行时堆栈(函数栈帧)
  • 运行时堆栈、堆栈指的都是栈。堆指的就是堆。

预处理详解

预定义符号

预定义符号在预处理阶段被处理的已经定义好的这种符号,这些符号是可以直接被使用的。

预定义符号是C语言已经内置好的。

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

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

实例一:

在这里插入图片描述

实例二:

int main()
{

	int i = 0;
	FILE* pf = fopen("log.txt", "a+");
	if (pf == NULL)
	{
		perror("fopen\n");
		return 1;
	}
	for (i = 0; i < 10; i++)
	{
		fprintf(pf, "%s %d %s %s %d\n", __FILE__, __LINE__, __DATE__, __TIME__, i);
	}
	fclose(pf);
	pf = NULL;

	
	return 0;
}

实例三:

在这里插入图片描述

注:__STDC__这个预定义符号在VS2019编译器中不支持,VS2019编译器也不支持ANSI C这个标准。Linux环境下GCC编译器是支持ANSI C的。

#define

#define 定义标识符

语法:#define name stuff

实例一:

#define M 1000
#define reg register
#define do_forever for(;;)  //for(;;)循环的基本结构


int main()
{
	reg int num = 0;
	do_forever;
	int m = M;
	//在预处理阶段就会把M替换成1000,预处理完之后这个地方就没有M是1000。
	printf("%d\n", m);

	return 0;
}

实例二:

#define CASE break;case
int main()
{
	int n = 1;

	switch (n)
	{
	case 1:
	CASE 2 :
	CASE 3 :
		;
	}

	return 0;
}

实例三:

#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定义符号的时候内容的后面尽量不要加;,除非必须要加。

#define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。

下面是宏的申明方式:

#define name( parament-list ) stuff

//name:名字
//parament-list:参数列表
//stuff:内容
//其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。

注:参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

实例一:

//版本一
//#define SQUARE( x ) x * x //这个宏接收一个参数 x

//版本二
#define SQUARE(x) (x) * (x)

int main()
{
	//版本一

	printf("%d\n", SQUARE(5));  //25
	//把上面的代码置于程序中,预处理器就会用下面这个表达式替换上面的表达式
	//printf("%d\n", 5*5);

	//注意这个宏存在一个问题
	int a = 5;
	printf("%d\n", SQUARE(a + 1)); //11
	//替换文本时,参数x被替换成a + 1,所以这条语句实际上变成了printf ("%d\n",a + 1 * a + 1 );
	//由替换产生的表达式并没有按照预想的次序进行求值,这时就需要在宏定义上加上两个括号,这个问题便轻松的解决了

	//版本二
	//这样预处理之后就产生了预期的效果 printf ("%d\n",(a + 1) * (a + 1) );
	printf("%d\n", SQUARE(a + 1)); //36
	return 0;
}

实例二:

//版本一
//#define DOUBLE(x) (x) + (x)

//版本二
#define DOUBLE(x) ((x)+(x))

int main()
{
	//版本一
	int a = 5;
	printf("%d\n", 10 * DOUBLE(a)); //55

	//替换之后:
	//printf("%d\n", 10 * (5) + (5));
	//乘法运算先于宏定义的加法,所以出现了55
	//这个问题的解决办法是在宏定义表达式两边加上一对括号就可以了

	//版本二
	printf("%d\n", 10 * DOUBLE(a));//100
	return 0;
}

注:

  • 宏它的参数是完成替换的,它不是先计算然后再传参的是替换进去再计算的。传进去之后替换完的表达式(预处理完的表达式),在对它进行计算(在编译完之后执行的时候进行计算)
  • 用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。

#define 替换规则

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

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

注意:

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

#和##

#

#是可以把参数插入到字符串中。#参数名作用是将参数名的内容所对应参数名的字符串。

实例一:

#define PRINT(X, FORMAT) printf("the value of "#X" is "FORMAT"\n", X);

int main()
{
	printf("hello world\n");  //hello world
	printf("hello " "world\n");   //hello world

	int a = 10;
	PRINT(a, "%d");
	//printf("the value of ""a"is ""%d""\n", a);
	//the value of a is 10

	int b = 20;
	PRINT(b, "%d");
	//printf("the value of ""b"is ""%d""\n", b);
	//the value of b is 20

	int c = 30;
	PRINT(c, "%d");
	//printf("the value of ""c"is ""%d""\n", c);
	//the value of c is 30

	float f = 5.5f;
	PRINT(f, "%f");
	//printf("the value of ""f"" is ""%f""\n", f);
	//the value of f is 5.500000

	return 0;
}

注:

  • #只能在宏里使用。
  • 字符串是有自动连接的特点的。
  • #的作用是把一个宏参数变成对应的字符串。

##

##的作用是可以把位于它两边的符号合成一个符号。 它允许宏定义从分离的文本片段创建标识符。

实例一:

#define CAT(X,Y,Z) X##Y##Z

int main()
{
	int theaugustcsdn = 100;
	printf("%d\n", CAT(the, august, csdn));

	//printf("%d\n", theaugustcsdn);

	return 0;
}

注:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。

带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

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

实例一:

#define MAX(X,Y) ((X)>(Y)?(X):(Y))


int main()
{
	int a = 5;
	int b = 8;

	int m = MAX(a++, b++);
	printf("a=%d b=%d\n", a, b);  //a=6 b=10

	//int m = ((a++) > (b++) ? (a++) : (b++));

	printf("m = %d\n", m);//m=9

	return 0;
}

注:带副作用的宏参数如果传到宏体内去,这种副作用一直会延伸到宏的体内而不仅仅是参数。

宏和函数对比

宏通常被应用于执行简单的运算。

实例一(在两个数中找出较大的一个):

#define MAX(X,Y) ((X)>(Y)?(X):(Y))

int Max(int x, int y)
{
	return x > y ? x : y;
}

int main()
{
	int a = 5;
	int b = 8;
	int m = MAX(a, b);
	//如果是宏,他在预处理阶段就变了
	//int m = ((a)>(b)?(a):(b));

	m = Max(a, b);
	return 0;
}

原因是因为:

  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
  2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。

当然和宏相比函数也有劣势的地方:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  2. 宏是没法调试的(调试的是可执行程序,运行的时候调试)。
  3. 宏由于类型无关,也就不够严谨。
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。

实例二:

#define MALLOC(num, type) (type*)malloc(num*sizeof(type))

int main()
{
	int*p = MALLOC(10, int); //类型作为参数
	//预处理器替换之后:	
	//int*p = (int*)malloc(10 * sizeof(int));
	return 0;
}

宏和函数的一个对比:

属性#define定义宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数的调用和返回的额外开销,所以相对慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。(因为宏是把代码替换到宏体里面去的这样的话宏在周围表达式的上下文环境中很可能还有其他的操作符会影响到替换进来的表达式里面的操作符就会导致操作符优先级的问题)函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一次,结果更容易控制。
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。
调试宏是不方便调试的函数是可以逐语句调试的
递归宏是不能递归的函数是可以递归的

总结:如果运算的逻辑足够简单用宏更好一些,如果运算的逻辑相对复杂一些用函数更好一些。

补充:在C99之后引入了内联函数(inline),内联函数结合了宏的优点和函数的优点,内联函数就可以替换掉宏和函数这块的要求和好处。

命名约定

一般来讲函数的宏的使用语法很相似。所以语言本身没法区分二者。

程序员平时的一个习惯是:把宏名全部大写,函数名不要全部大写。

注:有时候把宏伪装成函数,这时就需要把它写成全小写

#undef

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

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

实例一:

#define M 100

int main()
{
	int a = M;
#undef M
	//printf("%d\n", M); //err 未定义标识符M

	return 0;
}

命令行定义

许多C的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。例如:当程序员根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,程序员需要一个很小的数组,但是另外一个机器内存大些,程序员需要一个数组能够大些。)

实例一:

在这里插入图片描述
注:在代码中的M值不是在代码中指定的,而是在编译的时候通过命令行的方式对M进行相关的定义——命令行定义(在命令行的地方对它进行定义)。

条件编译

在编译一个程序的时候程序员如果要将一条语句(一组语句)编译或者放弃是很方便的。因为有条件编译指令。

调试性的代码,删除可惜,保留又碍事,所以可以选择性的编译。

实例一:

在这里插入图片描述

常见的条件编译指令:

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 OPTION1
 		unix_version_option1();
 	#endif
 	#ifdef OPTION2
 		unix_version_option2();
 	#endif
#elif defined(OS_MSDOS)
 	#ifdef OPTION2
 		msdos_version_option2();
 	#endif
#endif

实例一:

#if 0  //以下代码不参与编译(相当于注释掉了)

#define PRINT 1

int main()
{
#if PRINT
	printf("hehe\n");  //hehe
#endif

	return 0;
}

#endif

在这里插入图片描述
在这里插入图片描述

实例二:

int main()
{
#if 1==1
	printf("hehe\n");  //hehe
#elif 2==2
	printf("haha\n");
#else 
	printf("heihei\n");
#endif

	return 0;
}

实例三:

#define TEST 0
#define HEHE 1

int main()
{
//如果TEST定义了,下面参与编译
//1
#ifdef TEST
	printf("test1\n");
#endif
//2
#if defined(TEST)
	printf("test2\n");
#endif
//代码1和2是一个意思

//如果HEHE不定义,下面参与编译
//3
#ifndef HEHE
	printf("hehe1\n");
#endif
//4
#if !defined(HEHE)
	printf("hehe2\n");
#endif
	return 0;
}
//代码3和4是一个意思

文件包含

#include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方一样。

这种替换的方式很简单:
预处理器先删除这条指令,并用包含文件的内容替换。
这样一个源文件被包含10次,那就实际被编译10次。

注:

  • #include是预处理指令

头文件被包含的方式

本地文件包含

#include "filename"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误。

Linux环境的标准头文件的路径:

/usr/include

VS环境的标准头文件的路径:

C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt

注意:按照自己的安装路径去找

库文件包含

#include <filename.h>

查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。这样可以说,对于库文件也可以使用 “” 的形式包含。但是这样做查找的效率就低些,建议库的用<>不要用"",当然这样也不容易区分是库文件还是本地文件了。

总结:

  • C语言库中提供的函数的头文件使用<>(库文件包含)、自定义的函数的头文件使用""(本地文件包含)。
  • <> 和 "“包含头文件的本质区别是(查找的策略的区别):”"包含头文件首先是在自己代码所在的目录下查找,其次如果第一种找不到,则在库函数的头文件目录下查找。<>包含头文件直接去库函数头文件所在的目录下查找。

嵌套文件包含

在这里插入图片描述
如果出现这样的场景: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

这样就可以避免头文件的重复引入。

在这里插入图片描述
在这里插入图片描述

经典面试题

  1. 头文件中的 ifndef/define/endif是干什么用的?
    防止头文件被重复多次的包含
  2. #include <filename.h> 和 #include "filename.h"有什么区别?
    同样是包含头文件"“是用来包含自定义的头文件的,而<>是用来包含库里面的头文件的,它们在查找策略上有所区别:”"是先去自定义的这个文件路径代码路径底下去查找,如果找不到头文件再去库目录底下查找,而<>直接去库目录底下查找(标准的头文件库目录下查找)。

预处理指令的相关补充

在这里插入图片描述

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

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

相关文章

【MySQL】数据库中表的操作详解

【MySQL】数据库表的基本操作 一、表的创建二、查看表结构三、修改表结构3.1 添加表中字段3.2 修改表中字段3.3 删除表中字段3.4 修改表名3.5 修改列名 四、删除表 温馨提示&#xff1a;这里的表操作指的是表结构的操作&#xff0c;属于DDL数据定义语言 一、表的创建 CREATE …

hooks组件+例子+底层机制

1.React组件分类 函数组件 1.不具备"状态、ref、周期函数"等内容&#xff0c;第一次渲染完毕后&#xff0c;无法基于组件内部的操作来控制其更新&#xff0c;因此称之为静态组件!。但是具备属性及插槽&#xff0c;父组件可以控制其重新渲染 2.渲染流程简单&#xff…

Same Symbol | 哇咔咔!!!盘点一下表达矩阵中重复基因的处理方法!~

1写在前面 医院天天叫我们填问卷&#xff0c;我真是不能理解。&#x1fae0; 动不动就问我们对医院的福利满意吗&#xff0c;对自己的收入满意吗&#xff0c;觉不觉得工作负荷太重了&#xff1f;&#xff1f;&#xff1f;&#x1f642; 我们满不满意&#xff0c;觉不觉得累&…

大学物理(上)-期末知识点结合习题复习(2)——运动的描述考点总结、质点运动学-牛顿运动定律

目录 运动的描述 期末考点 质点运动学 牛顿运动定律知识点 题1(牛顿第二定律) 题目描述 题解 题2 (圆周运动) 题目描述 题解 运动的描述 期末考点 1.速度和加速度的推导 平均速度平均速度反映的只是在一段时间内位移的变化&#xff0c;如果需要精准的地知道质点在…

chatgpt赋能python:Python如何判断奇偶数?

Python如何判断奇偶数&#xff1f; 作为一门功能强大且容易上手的编程语言&#xff0c;Python具有许多有用的工具和功能。其中之一就是判断奇偶数。在本文中&#xff0c;我们将介绍Python中判断奇偶数的不同方法。 H1&#xff1a;Python中的基本判断方法 Python中最基本的判…

(八)CSharp-泛型协变和逆变(3)

一、协变和逆变 可变性分为三种&#xff1a; 协变、逆变和不变。 协变和逆变&#xff1a; 为泛型接口和泛型委托添加了一个处理类型转换问题的扩展。 问题&#xff1a; 当两个类对象是继承与派生的关系时&#xff0c;由于编译器通过泛型实例化时没法确认它们之间的关系&…

(数组) 1991. 找到数组的中间位置 ——【Leetcode每日一题】

❓1991. 找到数组的中间位置 难度&#xff1a;简单 给你一个下标从 0 开始的整数数组 nums &#xff0c;请你找到 最左边 的中间位置 middleIndex &#xff08;也就是所有可能中间位置下标最小的一个&#xff09;。 中间位置 middleIndex 是满足 nums[0] nums[1] ... num…

FTP协议详解

文章目录 1 FTP概述2 实验环境3 FTP详解3.1 文件传输过程3.2 报文格式3.3 数据连接3.4 主动模式3.5 被动模式3.6 匿名服务器 4 总结 1 FTP概述 FTP为File Transfer Protocol的缩写&#xff0c;即文件传输协议&#xff0c;是TCP/IP 协议族中的协议之一。FTP是一个用于在计算机网…

算法模板(3):搜索(3):图论提高

图论提高 最小生成树 &#xff08;1&#xff09;朴素版prim算法&#xff08; O ( n 2 ) O(n ^ 2) O(n2)&#xff09; 适用范围&#xff1a;稠密图易错&#xff1a;注意有向图还是无向图&#xff1b;注意有没有重边和负权边。从一个集合向外一个一个扩展&#xff0c;最开始只…

(文章复现)面向配电网韧性提升的移动储能预布局与动态调度策略(1)-灾前布局matlab代码

参考文献&#xff1a; [1]王月汉,刘文霞,姚齐,万海洋,何剑,熊雪君.面向配电网韧性提升的移动储能预布局与动态调度策略[J].电力系统自动化,2022,46(15):37-45. 1.基本原理 1. 1 目标函数 本文以最恶劣光伏出力场景下的移动储能配置成本与负荷削减成本最小为目标&#xff0c;建…

(数组) 724. 寻找数组的中心下标 ——【Leetcode每日一题】

❓724. 寻找数组的中心下标 难度&#xff1a;简单 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为…

2023-6-9

1.网络训练&#xff1a; 在训练前先要看看读取数据的时间&#xff08;常见的性能瓶颈&#xff09;2.import dis dis 是 Python 内置的一个模块&#xff0c;其全称为 “Disassembler for Python bytecode”&#xff0c;用于反汇编 Python 字节码。它可以将 Python 代码编译成字…

视频换天造物实践秒变科幻大片实践记录

视频换天造物实践秒变科幻大片实践记录&#xff0c;过程中遇到些坑&#xff0c;结果还是相当震撼 预装软件&#xff1a; matplotlib scikit-image scikit-learn scipy numpy torch torchvision opencv-python opencv-contrib-python 安装使用的时候可能碰上scikit-image 新版…

傅里叶级数简介

先看动图 将函数f(x) 用 sin(nx) cos(nx) 的形式表示出来的方式就是傅里叶级数 这里有几个使用条件 收敛性&#xff1a;符合迪力克雷收敛条件。简单理解为 f(x) 必须是一个丝滑的曲线。周期性&#xff1a; f(x) 必须是一个周期函数 还有一个基础条件&#xff0c;三角函数具…

element-plus布局排版问题总结(更新ing)

文章目录 el-container空隙修改app组件 el-container空隙 源码-更改了容器的显示&#xff0c;占满屏幕 <template><div class"common-layout"><el-container><el-header><el-row class"el-row1"><el-col :span"12&…

oppo r11 升级8.1系统 图文教程

Time: 2023年6月11日13:39:25 By:MemroyErHero 1 预留一定的空间,存放刷机包. 2 导入刷机包 r11.ozip 到手机上 3 手机文件管理器 打开 r11.ozip 文件 4 点击立即更新即可 5 重要的事情说三遍,刷机过程中 不能关机 不能断电 否则会变成砖头 重要的事情说三遍,刷机过程中 …

cmake 基本使用

目录 CMake都有什么? 使用cmake一般流程为&#xff1a; 1 生成构建系统 使用命令在build外编译代码: cmake基本语法 指定使用最低版本的cmake 指定项目名称 指定生成目标文件的名称 指定C版本 cmake配置文件使用 cmake配置文件生成头文件 版本号定义方法一: 版本号定…

软件测试正在面试银行的可以看下这些面试题

前言 最近呢有很多的小伙伴问我有没有什么软件测试的面试题&#xff0c;由于笔者之前一直在忙工作上的事情&#xff0c;没有时间整理面试题&#xff0c;刚好最近休息了一下&#xff0c;顺便整理了一些面试题&#xff0c;现在就把整理的面试题分享给大家&#xff0c;废话就不多说…

C 语言实现简单工厂模式

文章目录 1. 背景介绍2. 设计实现3. 运行测试4. 总结 1. 背景介绍 印象中&#xff0c;设计模式是由面向对象的语言(C、JAVA)才能完成的&#xff0c;而 C 语言是面向过程的语言&#xff0c;不能实现设计模式。但C 语言中有 函数指针、回调函数 等机制&#xff0c;使用这些机制便…

Java中线程的生命周期

Java中线程的生命周期 Java中线程的声明周期与os中线程的生命周期不太一样&#xff0c;java中线程有6个状态&#xff0c;见下&#xff1a; NEW: 初始状态&#xff0c;线程被创建出来但没有被调用 start() 。RUNNABLE: 运行状态&#xff0c;线程被调用了 start()等待运行的状态…