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

news2025/1/16 14:55:59

目录

1. 程序的翻译环境

2. 程序的执行环境

3. C语言程序的翻译+链接

4. 预编译过程详解

4.1 预定义符号介绍

4.1.1 __FILE__ //进行编译的源文件

4.1.2 __LINE__//文件当前的行号

4.1.3 __DATE__//文件被编译的日期

4.1.4 __TIME__//文件被编译的时间

4.1.5 __STDC__//如果文件遵循ANSIC,其值就是1,否则未定义

4.2 预处理指令 #define

4.2.1 #define 定义标识符   

4.2.2 #define 定义宏

4.2.3 #define 替换规则

4.2.4 带有副作用的宏参数

4.3 宏和函数对比

4.4 预处理操作符#和##的介绍

4.4.1 # 的作用:在字符串中访问宏

4.4.2 ## 的作用:把位于它两边的符号合并成一个

4.5 命令行定义

4.6 预处理指令#include

4.7 预处理指令#undef

4.8 条件编译

4.8.1 常见的条件编译指令:


1. 程序的翻译环境

翻译环境是指把通常我们写的test.c源文件变成可编程程序test.exe的过程,我们称作翻译环境。

在翻译环境下源代码被转换为可执行的机器指令;也可以说是把C代码(我们写的代码)转换为二进制代码的过程。因为机器能读懂二进制代码,只能处理二进制下的代码,所以想要我们写的C程序执行,必须把C程序转换为机器可以读懂的语言,也就是二进制语言。

执行环境是指把可编程程序test.exe运行的过程,我们称作执行环境。

通常我们在工程中会写不知一个 .c 源文件,比如:main.c、contact.c、gpio.c等等,C语言的环境会首先把我们写的源文件通过编译器变成目标文件object,简称 .obj 目标文件,所有的目标文件+链接库通过链接器变成可执行的文件;

这也就是我们写下的C代码的运行过程。依托链接器和编译器的共同作用完成可执行文件的生成。

1. 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object)。

2. 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。

3. 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且他可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

将test.c编译成可执行文件test.exe文件的过程所经历的环境称作翻译环境;翻译环境又可以细分为编译+链接,而编译的过程依托于编译器,链接的过程依托于链接器;如果细分,编译的过程又可以分为预编译+编译+汇编

预编译也称为预处理阶段:在Linux系统下,gcc编译器执行预处理的过程为 gcc-E test.c,预处理的过程包括#include 头文件的包含、注释删除( // 后面的注释删除,gcc编译器下注释是通过空格来代替的)、#define。总的来说预编译过程就是执行文本操作的过程

编译过程:把预处理过程产生的test.i 文件通过Linux系统下的 gcc-S test.i 操作进行编译;生成 test.s 文件。test.s 文件就是把 test.i 文件通过编译生成的。总的来说就是把C语言代码编译成汇编代码,以便于汇编过程的使用。编译过程包括语法分析、词法分析、语义分析、符号汇总。

Linux环境下生成的 test.o 文件就是window环境下的 .obj(object目标文件),test.o 是Linux环境下 gcc-S test.s生成的;

汇编过程:就是把Linux环境的 test.s 文件转换成 test.o 文件,也就是目标文件;最终目标文件+链接库在链接器的作用下生成可执行的程序;把汇编指令转换成二进制指令。汇编过程包括形成符号表

符号表就是在test.c 文件到目标文件test.o 的过程中,会调用不同的函数,至少有main函数,当然也可能会有add.c、gpio.c、led.c等等,每个函数在该过程中都会最终形成一个表格,该表包含所有函数以及每个函数所对应的地址,最终整理形成符号表。

整个的过程可以说是:Linux环境下--->test.c--->test.i--->test.s--->test.o   ;    分别通过Linux环境下的gcc-E test.c--->gcc-S test.i--->gcc-S test.s过程所实现。

通过编译过程生成的Linux环境下的test.o文件(window环境下test.obj目标文件)会通过链接过程将目标程序转变为可执行程序 test.exe 文件。

链接过程:链接过程主要完成合并段表以及符号表的合并和符号表的重定位。

合并段表:合并段表的过程如下图,链接过程主要是吧test.o目标文件转换为test.exe可执行文件,其中涉及到通过链接器将不同的单一的文件捆绑在一起,形成可执行文件test.exe的过程;首先,我们需要知道,test.o 目标文件是有特定的文件格式的,不同的目标文件中对应的段如表简要表示;合并段表就是把不同的目标文件中相同的段表进行合并,最终,通过链接器形成可执行文件test.exe;因为最终只能生成一个可执行程序;

符号表的合并和符号表的重定位:在汇编过程中,我们已经形成了符号表;但是不同的目标文件是有不同的符号表的(函数可能相同,但函数对应的地址是不同的),将不同目标函数的符号表进行合并和重定位的过程我们称作符号表的合并和符号表的重定位;

具体的合并过程是将所有的函数罗列到一个表中,对应的函数地址不同时,以有效的地址作为函数的地址;有效的地址是说有些函数的地址只是起声明作用,这样的函数地址是无效的,真正有效的地址是函数执行相应功能时的地址;简单来说就是:头文字中的函数只是声明作用,其地址就无效,真是的地址在 .c文件中。

符号表的意义:当存在外部声明的函数时,主函数调用外部声明的函数是通过符号表调用的,更精确的来说是在符号表中查找声明函数的地址,如果声明函数的地址无效,程序就会报错,找不到外部声明函数;所以符号表对于放大的程序进行模块化、外部声明具有显著的意义;链接过程中程序的错误我们称作链接错误;

2. 程序的执行环境

执行环境是指把可编程程序test.exe运行的过程,我们称作执行环境。

执行环境用于实际的执行指令。执行环境就是在test.exe执行文件已经产生的前提下,程序是如何运行起来的过程。

1. 程序必须载入内存中。在有操作系统的环境下;一般这个过程由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存中完成的。简单的理解就是:比如说我们拿到一个开发板,老师让我们把程序弄到板子上,需要把我们写的程序烧录到开发板上,真正意义上烧录的过程就是程序写进内存的过程。

2. 程序执行由此开始;接着便调用main函数;写到内存上以后,程序就可以开始运行了。

3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值;意思就是说:当程序运行到main函数中以后,就会为我们main函数中的变量、静态变量、返回值开辟空间。

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

3. C语言程序的翻译+链接

将test.c编译成可执行文件test.exe文件的过程所经历的环境称作翻译环境;翻译环境又可以细分为编译+链接,而编译的过程依托于编译器,链接的过程依托于链接器;如果细分,编译的过程又可以分为预编译+编译+汇编

预编译也称为预处理阶段:在Linux系统下,gcc编译器执行预处理的过程为 gcc-E test.c,预处理的过程包括#include 头文件的包含、注释删除( // 后面的注释删除,gcc编译器下注释是通过空格来代替的)、#define。总的来说预编译过程就是执行文本操作的过程

编译过程:把预处理过程产生的test.i 文件通过Linux系统下的 gcc-S test.i 操作进行编译;生成 test.s 文件。test.s 文件就是把 test.i 文件通过编译生成的。总的来说就是把C语言代码编译成汇编代码,以便于汇编过程的使用。编译过程包括语法分析、词法分析、语义分析、符号汇总。

Linux环境下生成的 test.o 文件就是window环境下的 .obj(object目标文件),test.o 是Linux环境下 gcc-S test.s生成的;

汇编过程:就是把Linux环境的 test.s 文件转换成 test.o 文件,也就是目标文件;最终目标文件+链接库在链接器的作用下生成可执行的程序;把汇编指令转换成二进制指令。汇编过程包括形成符号表

符号表就是在test.c 文件到目标文件test.o 的过程中,会调用不同的函数,至少有main函数,当然也可能会有add.c、gpio.c、led.c等等,每个函数在该过程中都会最终形成一个表格,该表包含所有函数以及每个函数所对应的地址,最终整理形成符号表。

整个的过程可以说是:Linux环境下--->test.c--->test.i--->test.s--->test.o   ;    分别通过Linux环境下的gcc-E test.c--->gcc-S test.i--->gcc-S test.s过程所实现。

通过编译过程生成的Linux环境下的test.o文件(window环境下test.obj目标文件)会通过链接过程将目标程序转变为可执行程序 test.exe 文件。

链接过程:链接过程主要完成合并段表以及符号表的合并和符号表的重定位。

合并段表:合并段表的过程如下图,链接过程主要是吧test.o目标文件转换为test.exe可执行文件,其中涉及到通过链接器将不同的单一的文件捆绑在一起,形成可执行文件test.exe的过程;首先,我们需要知道,test.o 目标文件是有特定的文件格式的,不同的目标文件中对应的段如表简要表示;合并段表就是把不同的目标文件中相同的段表进行合并,最终,通过链接器形成可执行文件test.exe;因为最终只能生成一个可执行程序;

符号表的合并和符号表的重定位:在汇编过程中,我们已经形成了符号表;但是不同的目标文件是有不同的符号表的(函数可能相同,但函数对应的地址是不同的),将不同目标函数的符号表进行合并和重定位的过程我们称作符号表的合并和符号表的重定位;

具体的合并过程是将所有的函数罗列到一个表中,对应的函数地址不同时,以有效的地址作为函数的地址;有效的地址是说有些函数的地址只是起声明作用,这样的函数地址是无效的,真正有效的地址是函数执行相应功能时的地址;简单来说就是:头文字中的函数只是声明作用,其地址就无效,真是的地址在 .c文件中。

符号表的意义:当存在外部声明的函数时,主函数调用外部声明的函数是通过符号表调用的,更精确的来说是在符号表中查找声明函数的地址,如果声明函数的地址无效,程序就会报错,找不到外部声明函数;所以符号表对于放大的程序进行模块化、外部声明具有显著的意义;链接过程中程序的错误我们称作链接错误;

4. 预编译过程详解

我们已经学习了:test.c--->test.exe(翻译环境)包括翻译和链接;翻译又包括预编译、编译、汇编;这一节我们重点解析预编译过程

4.1 预定义符号介绍

预定义符号是本身就有的符号,通过 #define MAX 100 定义的符号是自己定义的,本身MAX是没有的,两者是不一样的。

__FILE__ //进行编译的源文件

__LINE__//文件当前的行号

__DATE__//文件被编译的日期

__TIME__//文件被编译的时间

__STDC__//如果文件遵循ANSIC,其值就是1,否则未定义

4.1.1 __FILE__ //进行编译的源文件

 __FILE__ :打印出的是main.c文件存在的绝对路径;

4.1.2 __LINE__//文件当前的行号

 __LINE__:打印出的是__LINE__这一行代码在编译器中对应的行数。

如果默认VS编译器左侧没有显示行数,可以通过以下流程设置行数;

vs2013(编译器)-->工具-->选项-->找到对应的编译环境(C/C++/C#)-->在常规一栏中勾选行数即可;具体流程因编译器而定。

4.1.3 __DATE__//文件被编译的日期

__DATE__:获得编译代码的日期

4.1.4 __TIME__//文件被编译的时间

__TIME__:获得编译代码的时间

4.1.5 __STDC__//如果文件遵循ANSIC,其值就是1,否则未定义

__STDC__:文件若遵循ANSIC,则返回1;

4.2 预处理指令 #define

#开头的指令都称为预处理指令;

4.2.1 #define 定义标识符   

语法:#define name stuff

 不止如此:

#define ret register
//#define ret "hehe"
int main()
{
    ret int a;//两个是等价的ret可以代替register
    register int a;
}

#define 定义的标识符最后一般不要加分号;加分号可能会出现语法错误;

4.2.2 #define 定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏。-----参数替换到文本中的意思是:宏定义中包含参数

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

 巨坑:注意:宏是替换的,而不是直接传参的,这跟数学上是有一定的差异的;

#define SQUART(x) x*x
int main()
{
	int ret = SQUART(5+1);
	printf("%d\n", ret);
	return 0;
}//11
//这个时候可能郁闷,为什么不是6*6=36;而是11
//注意,x是一个整体,整体代换以后会是5+1*5+1=11

//#define SQUART(x) (x)*(x)计算的是36

#define SQUART(x)  (x)*(x) 

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

4.2.3 #define 替换规则

1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果有的话,它们是首先被替换的;

2. 替换文本随后被插入到程序中原本文本的位置。对于宏,参数名被它们的值替换。

3. 最后,再次对结果文件进行扫描,看看他是否包含任何由#define定义的符号。如果是,就重复上述的检查;

注意:

1. 宏参数和#define定义中可以出现其他#define定义的常量。但是对于宏,不能出现递归。

int ret = SQUART(MAX+MAX);
//#define定义的宏SQUART中出现#define定义的另一个宏MAX;

 2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

printf("MAX=%d\n", ret);//字符串里的MAX不被替换的意思是指“MAX"字符串里的内容不被搜索

4.2.4 带有副作用的宏参数

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

a++是带有副作用的;牢牢铭记:宏的参数是替换进去的,而不是算好之后带进去的;(提前将整个式子带进去)

 为了彻底的避免宏中参数带来的副作用:尽量少的在宏中使用带有副作用的参数。

4.3 宏和函数对比

求a和b的最大值?比较宏和函数实现哪个更好?

在真实的程序运行过程中,调用函数不仅仅是一步就可以完成的;每当调用一个函数的同时,需要多步的准备工作来调用该函数。所以运行一个程序时,调用函数是比较费时的;而宏定义没有繁琐的步骤,相比而言是比较是比较省时的

不用函数的原因有二:

1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹

2. 更重要的一点是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之宏是与类型无关的

宏相比于函数的劣势有四:

1. 每当使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。

2. 宏没有办法进行调试。

3. 宏由于和类型无关。所以使用较多的宏也会使得程序的严谨性变低。

4. 宏可能会带来运算符优先级的问题,导致程序很容易出现错误。

宏有时候可以做函数永远做不到的事情:

1. 宏的参数可以出现类型,但是函数做不到。简单来说宏定义的参数可以单纯是函数的类型,但是使用外部函数时,参数绝不可以仅仅设置成一个函数类型;

#define SIZEOF(type)  sizeof(type)
int main()
{
	int ret = SIZEOF(int);
	//int ret=sizeof(int);
	printf("%d\n", ret);//printf("%d\n",sizeof(int));
	return 0;
}//4

4.4 预处理操作符#和##的介绍

首先介绍一个C语言中字符串的天然连接现象:纵使我把一个完整的字符串分开为多个字符串,C语言中依然很可以完整的打印出来

4.4.1 # 的作用:在字符串中访问宏

4.2.3 #define 替换规则中:注意2说:当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

通过 # 是可以访问字符串中的宏的;具体如下:

4.4.2 ## 的作用:把位于它两边的符号合并成一个

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

4.5 命令行定义

命令行定义是C编程器提供的一种能力,允许在命令行中定义符号。用于启动编程过程。在执行命令的过程中给它加上一个参数进行设置下面我们通过一个例子来具体的讲解?

4.6 预处理指令#include

#include指令可以使另一个文件被编译。

不只是在C语言的学习中,在单片机的学习过程中也是如此,#include有时候使用  <   >  引用的,但有时候是用双引号  "   "  引用的;这是如何区分的呢?

实际上,当我们引用文件时,有时候引用的是库文件,ag. stdio.h;有时候是本地文件,也就是我们自己写的文件,ag. LED.h、GPIO.h等

当我们引用的是库文件,此时就是#include <stdio.h>;如果是本地文件,就是#include "LED.h"

#include "LED.h"文件(双引号文件)在引用头文件时,程序首先访问的是工程所在的目录,如果在工程所在的目录下没有找到头文件,程序会紧接着访问编译器(VS)的安装路径,如果在安装路径下还是没有找到被引用的头文件,程序就会报错;Linux环境下标准头文件的路径:/ usr / include ;

#include <stdio.h>文件(尖括号)只会在标准路径下(安装路径)查找,如果找不到被引用的头文件,就会报错。因为双引号的查找范围比较大,所以相应的查找效率也会比较低,对于那些明知道在安装路径下的文件,可以直接用#include < >,加快程序的运行效率;

嵌套结构:

所谓嵌套是指,当程序工程非常庞大时,main函数中可能会引用多个头文件,而每个头文件之间又相互引用;当执行主程序时,某个头文件被多次引用,这就是嵌套。

解决这一问题的方法是:在头文件中进行定义。

#ifndef __LED_H__
#define __LED_H__

void contract(void);

#endif

这么定义的意思是#ifndef  如果 LED.h 没有被定义,则执行下述程序;第一次调用该头文件,显然LED.h没有被定义,则执行下述程序;当第二次调用该头文件时,#ifndef,LED.h 已经被定义了,则下述程序不会被执行,#endif 条件编译结束。这样头文件被重复多次包含的问题就被解决了。

还有一种解决头文件被重复多次包含在头文件中声明函数上方写这样一个程序:

#pragma once

4.7 预处理指令#undef

宏的命名约定:一般来讲函数的宏的使用语法很相似。  把宏名全部大写  函数名不要全部大写

#undef:指令用于移除一个宏定义;

通常我们使用#define来定义一个宏定义,当定义完一个宏定义以后,可以用#undef来移除宏定义。

4.8 条件编译

条件编译:在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们可以借助条件编译指令

有些调试的代码,我们删掉可惜,保留下来又比较碍事,因此我们通常会借助条件编译指令来解决这一问题。

4.8.1 常见的条件编译指令:

#if  常量表达式

#endif

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = 0;
		//#if条件编译指令,后面的常量表达式如果为真,则执行
		//如果为假,则不执行
#if 1//0
		printf("%d ", arr[i]);
#endif
	}
	return 0;
}

#if   //如果条件语句为真,则执行该语句

#elif   // 如果该条件语句为真,则执行该语句

#else  //如果前两个条件语句都不为真,则执行该语句

#endif  //结束条件编译语句

 ③ 判断是否被定义

#if defined(symbol)

#ifdef symbol  //和上一条的语句是同一个意思,如果symbol被定义,则执行该语句对应的程序

#if !defined(symbol)

#ifndef symbol //和上一条语句是同一个意思;和最上边的两个的意思恰恰相反;如果symbol没有被定义,则执行该语句对应的程序

 ④ 嵌套指令

#if defined(OS_UNIX)  //如果OS_UNIX被定义,则执行下述语句段,语句段中再根据OPTION1和OPTION2的真假进行判断执行哪个语句

        #ifdef OPTION1    //如果OPTION1为真,则执行 unix_version_option1();

                unix_version_option1();

        #endif

        #ifdef OPTION2  //如果OPTION2为真,则执行 unix_version_option2();

                unix_version_option2();

        #endif

#elif defined(OS_MSDOS)  //如果OS_UNIX为假,则跳过上述语句段;如果OS_MSDOS为真,则执行下述语句段

        #ifdef OPTION2    //如果OPTION2为真,则执行msdos_version_option2();

                msdos_version_option2();

        #endif

#endif

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

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

相关文章

基于python的学生信息管理系统

《学生信息管理系统》程序使用说明在IDLE中运行《学生信息管理系统》即可进入如图1所示的系统主界面。在该界面中可以选择要使用功能对应的菜单进行不同的操作。在选择功能菜单时&#xff0c;有两种方法&#xff0c;一种是输入1&#xff0c;另一种是按下键盘上的↑或↓方向键进…

【Javascript】高阶函数,JSON,forEach,map,filter,reduce函数

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录高阶函数箭头函数apply函数JSONfilter函数map函数总结reduce函数find/findIndex函数every/some函…

线缆行业单绞机控制算法(详细图解)

在了解单绞机之前需要大家对收放卷以及排线控制有一定的了解,不清楚的可以参看下面几篇博客,这里不再赘述,受水平和能力所限,文中难免出现错误和不足之处,诚恳的欢迎大家批评和指正。 收放卷行业开环闭环控制算法 PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博…

知识难懂到什么程度

爱因斯坦相对论&#xff0c;诺贝尔评委会都看不懂&#xff0c;到底说的是什么1905年&#xff0c;爱因斯坦发布狭义相对论&#xff1b;1916年&#xff0c;发布广义相对论&#xff1b;2年后&#xff0c;英国的汤姆逊教授通过实验验证了广义相对论的正确&#xff0c;同时也把爱因期…

容器适配器中stack queue priority_queue的介绍及模拟实现

文章目录容器适配器的概念deque的介绍及底层结构stack的介绍 stack的模拟实现 queue的介绍 queue的模拟实现 priority_queue的介绍 priority_queue的模拟实现 容器适配器的概念 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验…

清华大佬超全超详细讲解——C++STL看这份教程就够了

2022 年年度编程语言揭榜啦&#xff01;在上个月预想的 C、C、Python 三种候选语言中&#xff0c;C 脱颖而出&#xff0c;成为 TIOBE 2022 年度编程语言的最终获得者&#xff01;新的一波学习热潮要来了。TIOBE 编程语言社区排行榜是编程语言流行趋势的一个指标&#xff0c;每月…

数据分析思维(三)|测试/对比思维

测试/对比思维 1、概念 测试/对比思维可以说在数据分析的工作中随处可见。当我们通过各种手段得到一些结果数据后&#xff0c;如何评价结果的好坏呢&#xff1f;这个时候你可能会想到和标准结果进行比较、和之前的数据进行对照等等方法&#xff0c;这些方法归根结底就是一种测…

ubuntu 安装 Gitkraken 8.1.1 Pro 版本

GitKraken 是一个非常好用的 Git 图形界面客户端, 自 6.5.1 版本以后, GitKraken 对私有仓库不再免费开放使用 本文介绍一个 ubuntu 安装 GitKraken 8.1.1 Pro 版本的方法 环境准备 安装 yarn 测试过 node 18.12.1 版本, 没能升级到 Pro 版, 可能是因为 GitCracken 仓库太久…

springboot+sa-token-quick-login实现快速登录

当你的项目需要一个登录认证功能&#xff0c;这个登录界面可以不华丽、可以烂&#xff0c;但是一定要有&#xff0c;同时你又不想花费太多的时间浪费在登录页面上&#xff0c; 那么你便可以尝试一下Sa-Token-Quick-Login。 1、Sa-Token-Quick-Login Sa-Token-Quick-Login 可以…

5.4、TCP 流量控制(滑动窗口机制)

一般来说&#xff0c;我们总是希望数据传输得更快一些。 但如果发送方把数据发送得过快&#xff0c;接收方就可能来不及接收&#xff0c;这就会造成数据的丢失。 所谓流量控制(flow control)就是让发送方的发送速率不要太快&#xff0c;要让接收方来得及接收\color{red}让发送…

基于微信小程序的个人健康数据管理系统小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

第一章.机器学习的前期准备—jupyter 更换文件路径的方法,jupyter使用方法,训练/验证/测试数据集的概念,学习方式,常见应用

第一章.机器学习的前期准备 1.1 第一章.机器学习的前期准备 1.jupyter软件的安装 说明:可以使用Anaconda软件中的jupyter软件 1).jupyter 更换文件路径的方法&#xff1a; ①.查找电脑中是否存在 jupyter_notebook_config.py 文件&#xff0c;若不存在&#xff0c;通过命令提…

前端最基础面试题:说说JavaScript中如何判断数据类型?

1. 基本数据类型的判定&#xff1a;typeof [变量名] typeof 1 // number typeof string呀 // string typeof true // boolean typeof Symbol(abc) // symbol控制台验证&#xff1a; 2. 引用类型 object 的判断&#xff1a; ① constructor ② instanceof ③ Object.prototy…

Redux与前端表格施展“组合拳”,实现大屏展示应用的交互增强

Redux 是 JavaScript 状态容器&#xff0c;提供可预测化的状态管理。它可以用在 react、angular、vue 等项目中, 但与 react 配合使用更加方便一些。 Redux 原理图如下&#xff0c;可以看到store仓库是Redux的核心&#xff0c;通过维护一个store仓库管理 state。state 是只读的…

JS严格模式(use strict)

javascript语法不够严谨&#xff0c;例如我们在项目中不用关键字去定义了一个变量a&#xff0c;控制台一切正常。b123;console.log(b)但是&#xff0c;如果开启了严格模式呢&#xff1f;"use strict" b123; console.log(b)此时将会报错Uncaught ReferenceError: b is…

开关电源中功率电感均方根电流是如何推导的?来自《开关电源宝典》

3.2.8 功率电感的有效电流参考“1.7.3 功率电感”章节内容&#xff0c;我们知道&#xff0c;功率电感具有温升电流、RMS电流、饱和电流、额定电流等电流参数。在后续“第5章 降压电路的应用方法”应用实例中进行功率电感选型时&#xff0c;需要保证所选电感的额定电流参数大于实…

自定义类型:结构体,枚举,联合(1)

tips 1. 2. 结构基础知识复习 1. 结构是一些值的集合&#xff0c;这些值被称为成员变量&#xff0c;结构的每个成员可以是不同类型的变量。 2. 结构体类型&#xff0c;结构体成员&#xff0c;结构体变量&#xff0c;结构体指针的创建方式 3. 初始化结构体变量的时候&…

华为开源自研AI框架昇思MindSpore应用实践:FGSM网络对抗攻击

目录一、环境准备1.进入ModelArts官网2.使用CodeLab体验Notebook实例二、对抗样本定义三、攻击方法快速梯度符号攻击&#xff08;FGSM&#xff09;四、数据处理五、训练LeNet网络六、实现FGSM七、运行攻击近年来随着数据、计算能力、理论的不断发展演进&#xff0c;深度学习在图…

老板要求总部-分部异地组网,作为IT运维怎样才能避免踩坑?

最近在开年会&#xff0c;老板提出2023年要全面搭建30个分公司的广域网架构&#xff0c;总部和分公司网络实现统一管理。但是公司原有网络架构复杂&#xff0c;各分支又是不同运营商接入的现状&#xff0c;想要实现异地组网并不容易&#xff01;然而&#xff0c;老板还不断提出…

好用的基于vue的组织架构图组件

都是基于vue的组织架构图&#xff0c;有支持vue2.x和vue3.x,可自行选择使用 一、vue-okr-tree&#xff08;支持vue2&#xff09; 文档地址&#xff1a;vue2-okr-tree 这个文档里面只有使用方法&#xff0c;不像vue3-tree-org里面有详细的介绍和安装引入教程 1.安装与引入 n…