【C语言】预处理编译链接调试技巧详解

news2024/11/19 3:18:56

主页:醋溜马桶圈-CSDN博客

专栏:C语言_醋溜马桶圈的博客-CSDN博客

gitee:mnxcc (mnxcc) - Gitee.com

目录

1.预处理

1.1 预定义符号

1.2 #define

1.2.1 #define 定义标识符

1.2.2 #define 定义宏

1.2.3 #define 替换规则

1.2.4 #和##

1.2.4.1 # 的作用

1.2.4.2 ## 的作用 

1.2.5 带有副作用的宏参数

1.2.6 宏和函数的对比 

对比

1.2.7 内联函数 

1.2.8 命名约定 

1.3 #undef

1.4 命令行定义 

1.5 条件编译

1.5.1 常见的条件编译指令

1.6 头文件包含

1.6.1 头文件被包含的方式

1.6.2 嵌套文件的包含

2.程序环境 编译和链接

2.1 翻译环境和执行环境

2.2 编译和链接

2.3 翻译

2.3.1 翻译的几个阶段

2.3.1.1 预编译

2.3.1.2 编译

词法分析

符号汇总

2.3.1.3 汇编

生成符号表

2.4 链接

2.4.1 合并段表

 2.4.2 合并符号表和重定位

2.5 运行

3.实用调试技巧 

3.1 什么是bug?

3.2 调试是什么?有多重要?

3.3 调试的基本步骤

3.4 debug和release的介绍

3.5 windows环境调试介绍

3.5.1 调试环境的准备

3.5.2 学会快捷键

3.5.3 调试的时候查看程序当前信息 

3.5.3.1 查看临时变量的值

3.5.3.2 查看内存信息

3.5.3.3 查看调用堆栈

3.5.3.4 查看汇编信息

3.5.3.5 查看寄存器信息

3.6 如何写出好(易于调试)的代码

3.6.1 常见的coding技巧

3.6.1.1 assert

3.6.1.2 const

常量指针

指针常量

区分常量指针和指针常量 

3.7 编程常见的错误

3.7.1 编译型错误

3.7.2 链接型错误

3.7.3 运行时错误

3.8 编程思维 


1.预处理

1.1 预定义符号

__FILE__          //进行编译的源文件

__LINE__         //文件当前的行号

__DATE__       //文件被编译的日期

__TIME__        //文件被编译的时间

__STDC__       //如果编译器遵循ANSI C,其值为1,否则未定义

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

举个例子:

1.2 #define

#define是一种预处理指令,他有两种用法:

  1. #define 定义常量(标识符)
  2. #define 定义宏 

1.2.1 #define 定义标识符

语法:

        #define  name  stuff

举个例子:

#define 是完全替换,比如

所以在定义的时候,为了强调他是一个整体,需要自己带上括号:

注意:由于是完全替换,在define定义标识符的时候,不要在最后加   ;   否则替换的时候会将  也替换过去,会导致语法错误

1.2.2 #define 定义宏

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

下面是宏的声明方式:

#define name( parament-list ) stuff

其中的parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中

注意:

参数列表的左括号必须与name紧邻

如果两者之间有任何空白存在,参数列表就会被释解释为stuff的一部分

如:

#define定义宏也是完全替换,比如:

为了防止出现失误,我们在声明的时候需要加上括号:

我们在写宏的时候,如果逻辑需要,我们可以加上足够多的括号来使宏变得完整

1.2.3 #define 替换规则

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

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

注意:

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

1.2.4 #和##

1.2.4.1 # 的作用

如何把参数插入到字符串中?、

我们发现字符串是有自动连接的特点的

假设有这样的代码:

我们如何用宏来实现printf的功能呢,这里我们使用#

他的替换是周怎么完成的呢 

这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中

使用#,把一个宏参数变成对应的字符串 

比如:代码中的#N会被预处理器处理为:“N”

所以“#N”即被处理为““N””

1.2.4.2 ## 的作用 

##可以把位于他两边的符号合成一个符号

他允许宏定义从分离的文本片段创建标识符

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

1.2.5 带有副作用的宏参数

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

x+1;//不带副作用

x++;//带副作用

MAX宏可以证明具有副作用的参数所引起的问题 

这段代码输出的结果是什么?

这里我们得知道预处理之后的结果是什么: 

这段代码是证明执行的呢?

1.2.6 宏和函数的对比 

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

比如在两个数中找出较大的一个

#define MAX(a, b) ((a)>(b)?(a):(b))

那为什么不用函数来完成这个任务?

原因有二:

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

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

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

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

对比

建议:

如果逻辑比较简单,可以使用宏来实现

如果计算逻辑比较负责,那么就使用函数实现 

1.2.7 内联函数 

C99之后,C++引入了内联函数的概念  inline关键字

内联函数具有函数和宏的双重优点:

  1. 内联函数是函数
  2. 内联函数又像宏一样,在调用的地方展开

1.2.8 命名约定 

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

那我们平时的一个习惯是:

  • 把宏名全部大写               //MAX
  • 函数名不要全部大写        //Max

1.3 #undef

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

1.4 命令行定义 

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

例如:当我们根据同一个源文件要编译出一个程序的不同版本的时候,这个特性有点用处

(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大写,我们需要一个数组能够大写。)

1.5 条件编译

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

条件编译就是:满足条件就编译,不满足条件就不编译

比如说:

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

1.5.1 常见的条件编译指令

1.
#if   常量表达式
        //...
#endif
//常量表达式由预处理器求值
如:
#define __DEBUG__ 1
#if __DEBUG__
        //..
#endif

表达式为真则编译,为假则不编译

2.多个分支的条件编译
#if 常量表达式
        //...
#elif 常量表达式
        //...
#else
        //...
#endif

只会选择以一个#if或者#elif执行 

3.判断是否被定义
#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

判断(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 与 #endif 是配套使用的,同时出现,同时消失

1.6 头文件包含

我们已经知道,#include 指令可以使另外一个文件被编译,就像它实际出现于 #include 指令的地方一样

这种替换的方式很简单:

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

1.6.1 头文件被包含的方式

头文件的包含一般有两种方式:

1.包含本地文件(自己的.h文件)
#include "xxx.h"(用双引号)

2.包含标准库中的文件
#include <xxx.h> (用尖括号)

查找策略:

先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件

#include包括""和<>这两种情况

""是在用户工作目录下寻找(用户的工作目录是通过编译器指定的)
<>是找系统标准库函数,通过系统环境变量指定系统库目录

如果找不到就提示编译错误

1.6.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

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

2.程序环境 编译和链接

2.1 翻译环境和执行环境

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

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

2.2 编译和链接

2.3 翻译

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

2.3.1 翻译的几个阶段

2.3.1.1 预编译

预编译的过程:

  1. 注释的替换(删除) 注释被替换成一个空格
  2. 头文件的包含  #include < >
  3. #define 符号的替换

所有的预处理指令都是在预编译阶段处理的 (文本操作)

2.3.1.2 编译
词法分析

假如有下面一段代码

array[index] = (index+4)*(2+6) 

将源代码程序输入扫描器,扫描器的任务就是简单的进行词法分析,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)

上面程序进行词法分析后得到了16个记号:

生成一棵语法树

符号汇总

一个工程中可以包含多个.c文件,如何在一个.c文件中调用另一个.c文件中的函数呢

这里我们了解一个概念叫做符号汇总

假设有这样的代码

进行符号汇总

注意:符号汇总只能汇总全局变量

2.3.1.3 汇编

把汇编代码翻译成了二进制的指令,生成了.o文件(目标文件)

生成符号表

假设给汇总的符号给上地址,生成一个符号表

2.4 链接

2.4.1 合并段表

 2.4.2 合并符号表和重定位

2.5 运行

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成
  2. 程序的执行便开始。接着便调用main函数
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程中一直保留他们的值
  4. 终止程序。正常终止main函数;也有可能是意外终止

3.实用调试技巧 

3.1 什么是bug?

作为程序员,每天被bug支配着,当然应该了解下对手了。

bug原意本来为昆虫的意思,1947年9月9日,葛丽丝·霍普(Grace Hopper)发现了第一个电脑上的bug。当在Mark II计算机上工作时,整个团队都搞不清楚为什么电脑不能正常运作了。经过大家的深度挖掘,发现原来是一只飞蛾意外飞入了一台电脑内部而引起的故障(如图所示)。这个团队把错误解除了,并在日记本中记录下了这一事件。也因此,人们逐渐开始用“bug”来称呼计算机中的隐错。

在这里插入图片描述

3.2 调试是什么?有多重要?

所有发生的事情都一定有迹可循,如果问心无愧,就不需要掩盖也就没有迹象了;如果问心有愧,就必然需要掩盖,那就一定会有迹象,迹象越多就越容易顺藤而上,这就是推理的途径

顺着这条途径顺流而下就是犯罪,逆流而上就是真相

一名优秀的程序员是一名出色的侦探

每一次尝试都是尝试破案的过程

调试(英语:Debugging / Debug),又称除错,是发现和减少计算机程序或电子仪器设备中程序

错误的一个过程。

3.3 调试的基本步骤

  • 发现程序错误的存在
  • 隔离、消除等方式对错误进行定位
  • 确定错误产生的原因
  • 提出纠正错误的解决办法
  • 对程序错误予以改正,重新测试

发现程序错误:程序员自己、测试人员、用户 

3.4 debug和release的介绍

  • Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序
  • Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用

测试人员站在用户的角度,测试的是发布版本 

代码:

#include <stdio.h>
int main()
{
 char *p = "hello world";
 printf("%s\n", p);
 return 0;
}

上述代码在Debug环境的结果展示

上述代码在Release环境的结果展示

Debug和Release反汇编展示对比

所以我们说调试就是在Debug版本的环境中,找代码中潜伏的问题的一个过程

那编译器进行了哪些优化呢? 请看如下代码:

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {0};
    for(i=0; i<=12; i++)
   {
        arr[i] = 0;
        printf("hehe\n");
   }
    return 0;
}

如果是 debug 模式去编译,程序的结果是死循环。

如果是 release 模式去编译,程序没有死循环。

那他们之间有什么区别呢?

就是因为优化导致的

3.5 windows环境调试介绍

3.5.1 调试环境的准备

在环境中选择 debug 选项,才能使代码正常调试

3.5.2 学会快捷键

最常使用的几个快捷键:

F5

  • 启动调试,经常用来直接跳到下一个断点处

F9

  • 创建断点和取消断点
  • 断点的重要作用,可以在程序的任意位置设置断点
  • 这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去

F10

  • 逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句

F11

  • 逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部

CTRL + F5

  • 开始执行不调试,如果你想让程序直接运行起来而不调试就可以直接使用 

在笔记本电脑上可以配合使用FN键 

3.5.3 调试的时候查看程序当前信息 

3.5.3.1 查看临时变量的值

在调试开始之后,用于观察变量的值

3.5.3.2 查看内存信息

在调试开始之后,用于观察内存信息

3.5.3.3 查看调用堆栈

通过调用堆栈,可以清晰的反映函数的调用关系以及当前调用所处的位置

3.5.3.4 查看汇编信息

3.5.3.5 查看寄存器信息

可以查看当前运行环境的寄存器的使用信息

多多动手,尝试调试,才能进步

  •  一定要熟练掌握调试技巧
  • 初学者可能80%的时间在写代码,20%的时间在调试。但是一个程序员可能20%的时间在写程序,但是80%的时间在调试。
  • 我们所讲的都是一些简单的调试。
  • 以后可能会出现很复杂调试场景:多线程程序的调试等
  • 多多使用快捷键,提升效率 

3.6 如何写出好(易于调试)的代码

  1. 代码运行正常
  2. bug很少
  3. 效率高
  4. 可读性高
  5. 可维护性高
  6. 注释清晰
  7. 文档齐全

3.6.1 常见的coding技巧

  1. 使用assert(断言)
  2. 尽量使用const
  3. 养成良好的编码风格
  4. 添加必要的注释
  5. 避免编码的陷阱
3.6.1.1 assert

assert函数是C语言标准库<assert.h>中的一个函数,函数原型为:

    void assert(int expression)

该函数输入参数只有一个int类型参数,返回值为void类型

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行

用法总结与注意事项

  1. 在函数开始处检验传入参数的合法性
  2. 每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败
  3. 不能使用改变环境的语句,因为assert只在Debug中生效,如果这么做,会使用程序在真正运行时遇到问题
  4. assert和后面的语句应空一行,以形成逻辑和视觉上的一致感
  5. 有的地方,assert不能代替条件过滤
3.6.1.2 const
常量指针

常量指针是指针指向的内容是常量,可以有以下两种定义方式

const int* n;

int const* n;

常量指针说的是不能通过这个指针改变变量的值,但是可以通过其他的引用来改变变量的值

int a=5;
const int* n=&a;
a=6;

常量指针指向的值不能改变,但是这并不意味着指针本身不能改变,常量指针可以指向其他的地址

int a=5;
int b=6;
const int* n=&a;
n=&b;
指针常量

指针常量是指指针本身是个常量,不能再指向其他的地址,写法如下

int*const n;

需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改

int a=5;
int*p=&a;
int* const n=&a;
*p=8;
区分常量指针和指针常量 

区分常量指针和指针常量的关键就在于星号(*)的位置,我们以星号为分界线

  • 如果const在星号的左边,则为常量指针 
  • 如果const在星号的右边,则为指针常量

如果我们将星号读作"指针",将const读作‘常量'的话,内容正好符合。

  • int const * n;是常量指针
  • int * const n;是指针常量

3.7 编程常见的错误

3.7.1 编译型错误

直接看错误提示信息(双击),解决问题,或者凭借经验就可以搞定

3.7.2 链接型错误

看错误提示信息,主要再代码中找到错误信息中的标识符,然后定位问题所在

一般是标识符名不存在或者拼写错误

3.7.3 运行时错误

借助调试,逐步定位问题

3.8 编程思维 

作为初学编程的各位小伙伴们,肯定已经或多或少地听说过编程思维这个词了,那么到底什么是编程思维呢

编程语言

表面含义,编程就是以各式的编程语言来编译代码,类似于英语,也是一门语言。那么作为语言,英语有诸多的语法,那么编程语言同样有一定的语法。毕竟写出来的代码是需要让机器看懂的。

编程思维

那么思维是什么呢,简单说就是做一件事情,脑海里要能够搭建起一个简单的框架,然后再填填补补。构建这个框架的思维就是编程思维,这要求咱们在编程前必须阅读并理解需求,不能只停留在代码的层面,要全局思考,结果会使得代码简洁又高效。

举例

For example,这是一段利用C语言写出最简单的逆序输出,显然,很通俗易懂,但是当输入n个内容,这段代码显然不适用了。

#include <stdio.h>
int main() {
    int a, b, c, d, e, f, g, h, i, j;
    scanf("%d %d %d %d %d %d %d %d %d %d",&a,&b,&c,&d,&e,&f,&g,&h,&i,&j);
    printf("%d %d %d %d %d %d %d %d %d %d", j, i, h, g, f, e, d, c, b, a);
}

那么就可以尝试使用下面这段码

#include<stdio.h>
int main()
{
    int arr[10] = {0};
    for(int i = 9;i>=0;i--)
    {
        scanf("%d",&arr[i]);
    }
    for(int i = 0;i<10;i++)
    {
        printf("%d ",arr[i]);
    }
    return 0;
}

显然,这段代码就有了一定的框架结构,这就是编程思维的外在体现。

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

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

相关文章

cleanmymac x试用期结束会扣费吗?

CleanMyMac X试用期结束后&#xff0c;如果您选择继续使用该应用程序&#xff0c;则需要购买许可证&#xff0c;否则可能会被扣费。为了避免不必要的扣费&#xff0c;建议您在试用期结束前做出决定&#xff0c;如果不打算继续使用&#xff0c;请及时删除该应用程序。 CleanMyMa…

前端html常用标签 笔记

一、基础 开始标签 结束标签 大部分是成对出现的标签&#xff0c;这个是空标签(/放在最后&#xff0c;/可以省掉) 缩进 向后Tab 、前向ShiftTab 红的就是元素属性 标签可以使内容有一些特殊的表现: 给body颜色后&#xff1a; h1 span(连起来) 标签&#xff1a; 标题h1 h2 h3 …

edga 0x800704cf错误码(同步失败)

edga 0x800704cf错误码(同步失败) 执行此操作需要 Internet。 你似乎没有连接到 Internet。请检查你的连接&#xff0c;然后再试一次。 0x800704cf 发送反馈 取消windows键R&#xff0c; 输入services.msc 进入本地服务管理&#xff0c; 重启 Microsoft Account Sign-in Assi…

力扣爆刷第105天之CodeTop100五连刷11-15

力扣爆刷第105天之CodeTop100五连刷11-15 文章目录 力扣爆刷第105天之CodeTop100五连刷11-15一、5. 最长回文子串二、33. 搜索旋转排序数组三、102. 二叉树的层序遍历四、200. 岛屿数量五、121. 买卖股票的最佳时机 一、5. 最长回文子串 题目链接&#xff1a;https://leetcode…

设计模式-装饰者模式在Java中使用实例-打印发票装饰抬头和脚注

场景 设计模式-装饰者模式在Java中的使用示例&#xff1a; 设计模式-装饰者模式在Java中的使用示例_java装饰者模式例子-CSDN博客 上面装饰器的调用示例如下 AbstarctComputer computer;//要买1台电脑computer new BaseComputer();//加一个内存条computer new MemoryDecor…

企业计算机服务器中了rmallox勒索病毒怎么办,rmallox勒索病毒解密流程步骤

在网络技术飞速发展的时代&#xff0c;越来越多的企业离不开网络办公&#xff0c;通过网络开展各项工作业务成为企业的常态&#xff0c;这也被国外众多黑客组织盯上的原因&#xff0c;近期&#xff0c;网络勒索病毒攻击的事件频发&#xff0c;越来越多的企业开始重视企业数据安…

Git命令:自用整理(仅供参考)

一、快速拉起分支开始工作 1、领取一个最新线上分支 git checkout -b 20231124_xxxx origin/main2、推送到远端 git push origin 20231124_xxxx 3、本地分支与远程分支建立关联 git branch --set-upstream-toorigin/20231124_xxxx 20231124_xxxx 4、检查分支关联是否成功…

嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记11:数字电位器MCP4017

系列文章目录 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记01&#xff1a;赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431&#xff08;HAL库开发&#xff09;——CT117E学习笔记02&#xff1a;开发环境安装 嵌入式|蓝桥杯STM32G431&#xff08;…

数据结构——排序之冒泡排序

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

如何借用 NTFS 交换数据流 实现隐藏文件?如何使用【文件包含】PHP伪协议?不同操作系统如何实现文件隐藏和木马伪装?

如何借用 NTFS 交换数据流 实现隐藏文件?如何使用【文件包含】PHP伪协议?不同操作系统如何实现文件隐藏和木马伪装? NTFS交换数据流(Alternate Data Streams, ADS)是NTFS文件系统特有的一种功能,它允许在同一个文件名下存储多个数据流。除了默认的数据流(通常用于存储文…

牛客小白月赛89(A~C)

小白赛怎么这么难打&#xff0c;是什么小白&#xff0c;我的世界小白吗。 A. 伊甸之花 给你一个数组 a&#xff0c;问你是否找出一个 不等于 a 的数组 b&#xff0c;满足 其中数值都要在 [1,m] 的范围内 直接在 a 数组上修改&#xff0c;可以发现如果改了 a[1],a[2]&#xff…

XSS一-WEB攻防-XSS跨站反射型存储型DOM型标签闭合输入输出JS代码解析

演示案例&#xff1a; XSS跨站-输入输出-原理&分类&闭合XSS跨站-分类测试-反射&存储&DOM #XSS跨站-输入输出-原理&分类&闭合 漏洞原理&#xff1a;接受输入数据&#xff0c;输出显示数据后解析执行 基础类型&#xff1a;反射(非持续)&#xff0c;存储(…

【免费】面向多微网网络结构设计的大规模二进制矩阵优化算法

目录 1 主要内容 节点故障网络拓扑变化示意 约束条件 目标函数 2 部分代码 3 结果一览 4 下载链接 1 主要内容 当前电力系统中微电网逐步成为发展的主力军&#xff0c;微网中包括分布式电源和负荷&#xff0c;单一的微电网是和外部电源进行连接&#xff0c;即保证用电的…

横河Yokogawa AQ6150光波长计

181/2461/8938产品概述&#xff1a; 横河凭借着在光测试测量领域拥有的广泛客户经验&#xff0c;设计出了世界上可靠性高且广泛通用的光波长计。 AQ6150系 列具有特定的技术特点&#xff0c;有能力测量光子技术应用中使用的各种设备和系统&#xff0c;可以说是高效的光波长测量…

2024品牌私域运营:「去中心化」正在成为企业决胜关键

越来越多的品牌选择以DTC模式与消费者互动和销售。通过与消费者建立紧密联系&#xff0c;不仅可提供更具成本效益的规模扩张方式&#xff0c;还能控制品牌体验、获取宝贵的第一方数据并提升盈利能力。许多企业采取的DTC私域策略以交易为中心的方法往往导致了成本上升和运营复杂…

RHCE-3-远程登录服务

简介 概念 远程连接服务器通过文字或图形接口方式来远程登录系统&#xff0c;让你在远程终端前登录linux主机以取得可操作主机接口&#xff08;shell&#xff09;&#xff0c;而登录后的操作感觉就像是坐在系统前面一样 功能: 分享主机的运算能力 服务器类型&#xff1a;有限…

bert 适合 embedding 的模型

目录 背景 embedding 求最相似的 topk 结果查看 背景 想要求两个文本的相似度&#xff0c;就单纯相似度&#xff0c;不要语义相似度&#xff0c;直接使用 bert 先 embedding 然后找出相似的文本&#xff0c;效果都不太好&#xff0c;试过 bert-base-chinese&#xff0c;be…

图书馆培训英文

introduction move1 move2 move3 要么定性&#xff0c;要么定量 implications 指导意义 Corpus语料库 collocation 激烈的竞争 competiton ADJ 批改网www.pigai.org acdademic phrasebank 在这里插入图片描述 好文章不是写出来的&#xff0c;是改出来的。

Spring事务-两种开启事务管理的方式:基于注解的声明式事务管理、基于编程式的事务管理

Spring事务-两种开启事务管理的方式 1、前期准备2、基于注解的声明式事务管理3、基于编程式的事务管理4、声明式事务失效的情况 例子&#xff1a;假设有一个银行转账的业务&#xff0c;其中涉及到从一个账户转钱到另一个账户。在这个业务中&#xff0c;我们需要保证要么两个账户…

FastAPI+React全栈开发08 安装MongoDB

Chapter02 Setting Up the Document Store with MongoDB 08 Installing MongoDB and friends FastAPIReact全栈开发08 安装MongoDB The MongoDB ecosystem is composed of different pieces of software, and I remember that when I was starting to play with it, there w…