笔记五:C语言编译链接

news2025/3/11 18:56:55

Faye:孤独让我们与我们所爱的人相处的每个瞬间都无比珍贵,让我们的回忆价值千金。它还驱使你去寻找那些你在我身边找不到的东西。

                                                                                                               ---------《寻找天堂》

目录

一、编译和链接的介绍

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

1.1.1 翻译环境

1.1.2  运行环境

1.2 预处理

1.2.1 预定义符号

 1.2.2 #define

      #define的语法:

        #define 替换规则:

         #和##

带副作用的宏参数         

​编辑        宏和函数对比

        #undef

1.3 编译 

 1.3.1 词法分析

 1.3.2 语法分析

1.3.3 语义分析 

1.4 汇编

 1.5 链接


一、编译和链接的介绍

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

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

        第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。

        第2种是执行环境,它用于实际执行代码。

注:ANSI C是由美国国家标准协会(ANSI)及国际标准化组织(ISO)推出的关于C语言的标准。ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型) 并支持多国字符集 (包括备受争议的三字符序列)。 ANSI C 标准同时规定了 C 运行期库例程的标准。

1.1.1 翻译环境

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

         翻译的几个环节,通过下面的图进行初步的了解:

1.1.2  运行环境

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

1.2 预处理

        在预处理阶段,源⽂件和头⽂件会被处理成为 .i 为后缀的⽂件。在 gcc 环境下想观察⼀下,对 test.c ⽂件预处理后的.i⽂件,命令(以下所有的命令在Linux下的指令)如下:

gcc -E test.c -o test.i

         在Linux下执行这条指令后,生成.i文件,查看里面内容大多都是宏

         预处理阶段主要处理那些源⽂件中#开始的预编译指令。
        ⽐如:#include,#define,处理的规则如下:

  • 将所有的 #define 删除,并展开所有的宏定义。
  • 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
  • 处理#include 预编译指令,将包含的头⽂件的内容插⼊到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头⽂件也可能包含其他⽂件。
  • 删除所有的注释
  • 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等。
  • 或保留所有的#pragma的编译器指令,编译器后续会使⽤。

经过预处理后的 .i ⽂件中不再包含宏定义,因为宏已经被展开。并且包含的头⽂件都被插⼊到 .i⽂件中。所以当我们⽆法知道宏定义或者头⽂件是否包含正确的时候,可以查看预处理后的 .i ⽂件来确认。

1.2.1 预定义符号

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

        这些预定义的符号都是语言内置的。下面使用部分宏:

#include<stdio.h>

int main() {
    //__FILE__进行编译的源文件   __LINE__文件当前的行号
	printf(" file:%s \n line:%d\n", __FILE__, __LINE__); 

    //__DATE__ 文件被编译的日期  __TIME__ 文件被编译的时间
	printf(" date:%s \n time:%lld\n", __DATE__, __TIME__);
    
	return 0;
}

        运行结果如下:

 1.2.2 #define

        #define是一种定义标识符,用来定义宏,下面是#define的功能介绍

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

         举个梨子:

#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定义标识符的时候,建议不要加上 ;   这样容易导致问题。比如下面的场景:

#include<stdio.h>
#define MAX 1000;
int main() {
	int condition = 0, max = 0;
	if (condition) 
		max = MAX;	
	else
		max = 0;
	return 0;
}

         在vs下进行编译,这里会出现语法错误。

      #define的语法:
语法:         #define name stuff

        下面是宏的申明方式:

        #define name( parament- list ) stuff    其中的 parament- list 是一个由逗号隔开的符号表,它们可能出现在 stuff  
         宏的命名约定:
把宏名全部大写
函数名不要全部大写
注意:
  • 参数列表的左括号必须与name紧邻。
  • 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分

         举个小梨子,定义一个数的平方的宏:

#define MUL(x) x*x    //参数列表的左括号必须与name紧邻

        这个宏接收一个参数 x ,如果在上述声明之后,MUL(5)。置于程序中,预处理器就会用下面这个表达式替换上面的表达式:5*5

#include<stdio.h>

#define MUL(x) x*x   //参数列表的左括号必须与name紧邻。

int main() {
	printf("%d\n", MUL(5));  // ==> printf("%d\n", 5*5)
	return 0;
}

         那么如果输入的参数是一个表达式呢?MUL宏输出的结果是否还是符合预期呢?此时将MUL(5)替换为 MUL(2+3),它的预期结果应该也是25,运行一下看看

        结果是11,为什么呢?这时候把(2+3)参数带入MUL宏中看看 ===>  2+3* 2+3 ====> 2+6+3,所以输出的结果变为了11。这样就比较清晰了,由替换产生的表达式并没有按照预想的次序进行求值。这里涉及到了运算符优先级的问题。在宏定义上加上两个括号,这个问题便轻松的解决了:

#define MUL(x) (x)*(x)

         举另外一个小梨子,定义一个数跟自己加和的宏:

#include<stdio.h>

#define SADD(x) (x)+(x)   //参数列表的左括号必须与name紧邻。

int main() {

	printf("%d\n", 10*SADD(2)+ SADD(2));  //预期结果 10*4+4=44
	return 0;
}

        欸,这又是怎么回事?参数我也加上了小括号,不应该呀。依旧是上面的分析方法,将宏在表达式中进行展开,10*SADD(2)+ SADD(2) =====>  10 * (2) + (2)+ (2) + (2)====> 20+6=26。乘法运算先于宏定义的加法,所以出现了26。这个问题的解决办法是在宏定义表达式两边加上一对括号就可以了:

#define SADD(x) ((x)+(x)) 

         这样运行结果便符合预期啦

         通过上面两个小梨子,得出以下的经验:

        用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用。
        #define 替换规则:
在程序中扩展 #define 定义符号和宏时,需要涉及几个步骤。
  • 1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。
  • 2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  • 3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
  • 1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
  • 2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
         #和##

         字符串是有自动连接的特点的,将两个或者多个字符串紧挨着它们会自动连接,形成一个组合后的字符串,通过下面的小梨子看看:

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

         如果把这些写到宏里是不是实现同样的效果呢?

         使用 # 把一个宏参数变成对应的字符串还可以添加部分参数,进行打印:

#include<stdio.h>


//使用 # ,把一个宏参数变成对应的字符串
//FORMAT 数据输出格式,VALUE 数据
#define PRINT(FORMAT,VALUE) printf("the value  of " #VALUE  " is "FORMAT"\n", VALUE);

int main() {

	int i = 10;
	PRINT("% d" , i + 5)
	return 0;
}

        代码中的 #VALUE 会预处理器处理为: "VALUE"

         ## 的作用

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

         通过一个小梨子看看:

#include<stdio.h>
#define ADD_TO_SUM(num, value) s##num += value;

int main() {
	int s1 = 0, s2 = 0, s3 = 0;
	ADD_TO_SUM(1, 10) // 作用是:给s1增加10.
	ADD_TO_SUM(2, 20)	//给s2增加20.
	ADD_TO_SUM(3, 30)	//给s3增加30.
	printf("s1: %d s2: %d s3: %d", s1, s2, s3);
	return 0;
}

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

         借用上面MUL的宏,运行下列代码:

#define MUL(x) (x)* (x)

int main() {
	int i = 2;
	printf("MUL: %d i: %d\n", MUL(i++),i);
	return 0;
}

         发现宏替换后MUL(i++) ====> (i++)* (i++) 。此后i被加加两次,产生了副作用

        宏和函数对比
        宏通常被应用于执行简单的运算。 那为什么不用函数来完成这个任务? 原因有二:
  • 1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹
  • 2. 更为重要的是函数的参数必须声明为特定的类型。 所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以 用于>来比较的类型。
宏是类型无关的
宏的缺点 当然和函数相比宏也有劣势的地方:
  • 1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
  • 2. 宏是没法调试的。
  • 3. 宏由于类型无关,也就不够严谨。
  • 4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
  • 宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。

        下面表格是将宏与函数进行对比:

 

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

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

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

1.3 编译 

        编译过程就是将预处理后的文件进行⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码文件。编译过程的命令如下:

gcc -S test.i -o test.s

          在Linux下执行这条指令后,生成.s文件,查看.s文件里面内容是相应的汇编代码

      对下面的代码进行编译的时候,流程会是怎么样的呢?

  num=(z+6)*(9/3)

 1.3.1 词法分析

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

上⾯程序进行词法分析后得到了13个记号

记号类型
num标识符
=赋值
(左圆括号
z标识符
+加号
6数字
)右圆括号
*乘号
(左圆括号
9数字
+加号
3数字
)右圆括号

 1.3.2 语法分析

         接下来语法分析器,将对扫描产⽣的记号进行语法分析,从⽽产⽣语法树。这些语法树是以表达式为节点的树

1.3.3 语义分析 

         由语义分析器来完成语义分析,即对表达式的语法层⾯分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。

1.4 汇编

         汇编器是将汇编代码转转变成机器可执行的指令,每⼀个汇编语句几乎都对应⼀条机器指令。就是根据汇编指令和机器指令的对照表⼀⼀的进行翻译,也不做指令优化。汇编的命令如下:

gcc -c test.s -o test.o

 在Linux下执行这条指令后,生成.o文件,查看里面内容为机器语言

 1.5 链接

        链接是一个复杂的过程,链接的时候需要把一堆文件链接在⼀起才生成可执行程序。链接过程的命令如下:

gcc test.o -o test

链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。链接解决的是一个项目中多文件、多模块之间互相调用的问题。比如: 在一个C的项目中有2个.c文件( test.c 和 add.c ),代码如下:

#include <stdio.h>
//test.c
//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;
int main()
{
    int a = 10;
    int b = 20;
    int sum = Add(a, b);
    printf("%d\n", sum);
    return 0;
}
int g_val = 2022;
int Add(int x, int y)
{
    return x+y;
}

         我们已经知道,每个源⽂件都是单独经过编译器处理⽣成对应的⽬标⽂件。

  • test.c 经过编译器处理⽣成 test.o
  • add.c 经过编译器处理⽣成 add.o

        我们在 test.c 的⽂件中使⽤了 add.c ⽂件中的 Add 函数和 g_val 变量。

        我们在 test.c ⽂件中每⼀次使⽤ Add 函数和 g_val 的时候必须确切的知道 Add 和 g_val 的地址,但是由于每个⽂件是单独编译的,在编译器编译 test.c 的时候并不知道 Add 函数和 g_val 变量的地址,所以暂时把调⽤ Add 的指令的⽬标地址和 g_val 的地址搁置。等待最后链接的时候由链接器根据引⽤的符号 Add 在其他模块中查找 Add 函数的地址,然后将 test.c 中所有引⽤到Add 的指令重新修正,让他们的⽬标地址为真正的 Add 函数的地址,对于全局变量 g_val 也是类似的⽅法来修正地址。这个地址修正的过程也被叫做:重定位

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

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

相关文章

【c语言概述、数据类型、运算符与表达式精选题】

c语言概述、数据类型、运算符与表达式精选题 一、易错题1.1&#x1f384; c程序的执行1.2&#x1f384; c程序的基本组成单元1.3&#x1f384; c程序的组成1.4&#x1f384; 5种基本类型数据类型长度1.5&#x1f384; C语言关键字1.6&#x1f384; 整型常量1.7&#x1f384; 合…

200个前卫街头氛围涂鸦艺术水墨颜料手绘笔迹飞溅PNG免扣迭加纹理素材 VANTABLACK TEXTURES

探索 Vantablack 200 纹理包&#xff1a;您获得前卫、高分辨率纹理的首选资源。非常适合旨在为其作品添加原始都市氛围的设计师。这些透明迭加层使用简单&#xff0c;但非常有效&#xff0c;只需单击几下&#xff0c;即可将您的设计从普通变为非凡。准备好用既酷又百搭的质地来…

深度学习模型Transformer核心组件—自注意力机制

第一章&#xff1a;人工智能之不同数据类型及其特点梳理 第二章&#xff1a;自然语言处理(NLP)&#xff1a;文本向量化从文字到数字的原理 第三章&#xff1a;循环神经网络RNN&#xff1a;理解 RNN的工作机制与应用场景(附代码) 第四章&#xff1a;循环神经网络RNN、LSTM以及GR…

nature genetics | SCENT:单细胞多模态数据揭示组织特异性增强子基因图谱,并可识别致病等位基因

–https://doi.org/10.1038/s41588-024-01682-1 Tissue-specific enhancer–gene maps from multimodal single-cell data identify causal disease alleles 研究团队和单位 Alkes L. Price–Broad Institute of MIT and Harvard Soumya Raychaudhuri–Harvard Medical S…

基于数据挖掘的疾病数据可视化分析与预测系统

【大数据】基于数据挖掘的疾病数据可视化分析与预测系统&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 &#x1f4cc; 技术核爆点&#xff1a;✔️ Python全栈开发Flask高能框架 ✔️ 爬虫技术…

《AI大模型专家之路》No.2:用三个模型洞察大模型NLP的基础能力

用三个模型洞察大模型NLP的基础能力 一、项目概述 在这个基于AI构建AI的思维探索项目中&#xff0c;我们实现了一个基于BERT的中文AI助手系统。该系统集成了文本分类、命名实体识别和知识库管理等功能&#xff0c;深入了解本项目可以让读者充分了解AI大模型训练和推理的基本原…

Spring Boot集成Minio笔记

一、首先配置MinIO 1、MinIO新建Bucket&#xff0c;访问控制台如图 创建访问密钥(就是账号和密码) 二、集成mino添加Minio客户端依赖 1.maven构建方式在pom.xml引入jar <dependency><groupId>io.minio</groupId><artifactId>minio</artifactI…

HCIA-路由重分布

一、路由重分布是指在同一个网络中&#xff0c;将一种路由协议所学习到的路由信息导入到另一种路由协议中的技术&#xff0c;实现通信。 二、实验 1、配置 AR1AR2AR3sy sy AR1 int g 0/0/1 ip add 192.168.1.254 24 int g 0/0/0 ip add 10.1.1.1 24 rip version 2 net 192.16…

【项目】nnUnetv2复现

作者提出一种nnUNet(no-new-Net)框架,基于原始的UNet(很小的修改),不去采用哪些新的结构,如相残差连接、dense连接、注意力机制等花里胡哨的东西。相反的,把重心放在:预处理(resampling和normalization)、训练(loss,optimizer设置、数据增广)、推理(patch-based…

【大学生体质】智能 AI 旅游推荐平台(Vue+SpringBoot3)-完整部署教程

智能 AI 旅游推荐平台开源文档 项目前端地址 ☀️项目介绍 智能 AI 旅游推荐平台&#xff08;Intelligent AI Travel Recommendation Platform&#xff09;是一个利用 AI 模型和数据分析为用户提供个性化旅游路线推荐、景点评分、旅游攻略分享等功能的综合性系统。该系统融合…

TCP7680端口是什么服务

WAF上看到有好多tcp7680端口的访问信息 于是上网搜索了一下&#xff0c;确认TCP7680端口是Windows系统更新“传递优化”功能的服务端口&#xff0c;个人理解应该是Windows利用这个TCP7680端口&#xff0c;直接从内网已经具备更新包的主机上共享下载该升级包&#xff0c;无需从微…

恭喜!《哪吒2》明天将荣登世界影坛第六!目前仅差1.81亿元

全球总票房为为20.27亿美元&#xff01;3月8日将荣登世界影坛第六宝座&#xff01; 中国票房 内地票房 中国电影票房、灯塔、猫眼三大数据源加权平均得出《哪吒2》中国内地总票房为144.26亿元人民币。 港澳票房 目前港澳地区没有新的数据显示&#xff0c;按3月6日1905电影网…

e2studio开发RA4M2(15)----配置RTC时钟及显示时间

e2studio开发RA4M2.15--配置RTC时钟及显示时间 概述视频教学样品申请硬件准备参考程序源码下载新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置SWD调试口设置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user…

Flink深入浅出之04:时间、水印、TableSQL

深入理解Flink的waterMark的机制、Flink Table和SQL开发 3️⃣ 目标 掌握WaterMark的的原理掌握WaterMark的运用掌握Flink Table和SQL开发 4️⃣ 要点 &#x1f4d6; 1. Flink中的Time概念 对于流式数据处理&#xff0c;最大的特点是数据上具有时间的属性特征 Flink根据时…

MongoDB Compass 使用说明

MongoDB Compass 使用说明 安装工具栏按钮详细介绍Connect(连接)1. New Window&#xff08;新窗口&#xff09;2. Disconnect&#xff08;断开连接&#xff09;3. Import Saved Connections&#xff08;导入保存的连接&#xff09;4. Export Saved Connections&#xff08;导出…

Halcon 算子 一维码检测识别、项目案例

首先我们要明白码的识别思路 把窗口全部关闭读取新的图片图像预处理创建条码模型设置模型参数搜索模型获取条码结果显示条码结果 图像预处理和条码增强 对比度太低&#xff1a; scale_image&#xff08;或使用外部程序scale_image_range&#xff09;,增强图像的对比度图像模糊…

信号完整性基础:高速信号的扩频时钟SSC测试

扩频时钟 SSC 是 Spread Spectrum Clock 的英文缩写&#xff0c;目前很多数字电路芯片都支持 SSC 功能&#xff0c;如&#xff1a;PCIE、USB3.0、SATA 等等。那么扩频时钟是用来做什么的呢&#xff1f; SSC背景&#xff1a; 扩频时钟是出于解决电磁干扰&#xff08;EMI&#…

stm32移植LCD2002驱动

介绍 LCD2002支持20X2个字符串显示&#xff0c;引脚功能和读写时序跟LCD1602都很像 LCD类型&#xff1a;字符点阵 点 阵 数&#xff1a;202 外形尺寸&#xff1a;116.0mm37.0mm&#xff08;长宽&#xff09; 视域尺寸&#xff1a;83.0mm18.6mm 点 距 离&#xff1a;0.05mm…

OpenCV计算摄影学(18)平滑图像中的纹理区域同时保留边缘信息函数textureFlattening()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::textureFlattening 是 OpenCV 中用于图像处理的一个函数&#xff0c;旨在平滑图像中的纹理区域&#xff0c;同时保留边缘信息。该技术特别适…

“此电脑”中删除WPS云盘方法(百度网盘通用)

&#x1f4e3;此方法适用于卸载WPS云盘后&#xff0c;WPS云盘图标依然在此电脑中显示的问题。 原理&#xff1a;通过注册来进行删除 步骤&#xff1a; WIN键R,打开运行窗口&#xff0c;输入regedit命令&#xff0c;来打开【注册表编辑器】&#xff1b; 从左侧&#xff0c;依…