C语言详解(预编译)

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C语言

🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。


目录

  • 前言
    • 1、预定义符号
    • 2、#define定义常量和标识符
    • 3、#define定义宏
    • 4、带有副作用的宏参数
    • 5、宏替换的规则
    • 6、宏和函数的对比
    • 7、# 和
      • 7.1 #运算符
      • 7.2 ##运算符
    • 8、命名的约定
    • 9、#undef
    • 10、命令行定义
    • 11、条件编译
    • 12、头文件的包含
      • 12.1 头文件被包含的方式
        • 12.1.1 本地文件包含
        • 12.1.2 库文件包含
      • 12.2 嵌套文件的包含
  • 总结

前言

本篇文章将详细介绍编译过程中预编译的具体细节
在C语言的学习中部分人可能会忽视这一部分的学习,因为像VS这样相对强大的集成开发环境,我们在写好代码后只需要开始执行即可,所以部分人认为这一部分不值得我们花费时间去学习
其实不然,学习C语言预编译过程可以帮助我们更深入地了解C语言的编译过程和语法特性,提高代码编写的效率和质量,以及拓展编程技能


1、预定义符号

C语言设置了一些预定义符号,可以直接使用,预定义符号也是在预编译阶段处理的

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

例如:

在这里插入图片描述


2、#define定义常量和标识符

#define定义的常量和标识符在预编译阶段完成替换

基本语法:

#define name stuff

特别的,为了区分普通常量这个name我们一般用大写形式
比如:

#define MAX 10000
#define REG register

#define后面的代码理论上讲只能写一行,但是如果后面的代码过长,我们可以使用'\'来实现换行,相当于转义转义字符'\'转义了转义字符'\n'

#define DEBUG_PRINT printf("file:%s\tline:%d\t\
							date:%s\ttime:%s\n,\
							__FILE__,__LINE__,\
							__DATE__,__TIME__)

值得注意的是,行末最好不要加;,在某些场景下是没什么问题,但是在大多数情况下是有语法错误的,所以我们要养成良好的编程习惯,行末不加;


3、#define定义宏

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

#define name(parament_list) stuff

其中parament-list(参数列表)是一个由逗号隔开的符号表,它们可能出现在stuff

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

举例:输入一个数,输出它的平方数

#include <stdio.h>
#define SQUARE(x) x*x

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = SQUARE(n);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

上面的代码看似没有什么问题,但当我们想计算n+1的平方数时,就会出现问题:

#include <stdio.h>
#define SQUARE(x) x*x

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = SQUARE(n + 1);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

这是为什么呢?

原因就是带参数的宏在替换的时候括号内的表达式是不做任何计算的

也就是说,上面替换后的形式是:5 + 1 * 5 + 1,为了解决这个问题,我们可以在定义宏的时候给x加上括号:

#define SQUARE(x) (x)*(x)

这样替换后的结果就变成了:(5 + 1)*(5 + 1),但是这样给单独的参数加括号的形式在某些场景下还是存在问题,比如:

#include <stdio.h>
#define SQUARE(x) (x)+(x)

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 5 * SQUARE(n + 1);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

那为了解决这个问题,我们可以(x)+(x)整体加上括号:((x) + (x))

#include <stdio.h>
#define SQUARE(x) ((x)+(x))

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = 5 * SQUARE(n + 1);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

所以,在写宏的时候一定不要吝啬括号


4、带有副作用的宏参数

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

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

上面两个表达式的值是相同的,但是第一个表达式x的本身没有发生改变,而第二个表达式x本身发现了改变,这就是副作用

例如:使用宏实现求两个数的较大值

#include <stdio.h>
#define MAX(x, y) ((x)>(y)?(x):(y))

int main()
{
	int a = 10;
	int b = 20;
	int ret = MAX(a, b);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

上面代码中宏参数在宏定义中出现了两次,我们使用MAX(a, b);时没什么问题,但当我们使用MAX(a++, b++);时问题就会出现:

#include <stdio.h>
#define MAX(x, y) ((x)>(y)?(x):(y))

int main()
{
	int a = 10;
	int b = 20;
	int ret = MAX(a++, b++);
	printf("%d\n", ret);
	printf("a = %d, b = %d\n", a, b);
	return 0;
}

在这里插入图片描述

可以发现a和b的值会发生改变,就是表达式求值的时候出现了永久性效果。

与函数对比:

#include <stdio.h>

int MAX(int x, int y)
{
	printf("a = %d, b = %d\n", x, y);
	return (x > y ? x : y);
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = MAX(a++, b++);
	printf("%d\n", ret);
	return 0;
}

请添加图片描述

从上面的代码中可以看出来,带参数的宏替换和函数传参是非常相似的,但是它们的传参是有本质区别的。
带参数的宏替换是直接将参数做整体替换,替换过后的表达式是:((a++)>(b++)?(a++):(b++));而函数参过后的表达式是:(a > b ? a : b)


5、宏替换的规则

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

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

例如:

#include <stdio.h>
#define M 10
#define N M + 2
#define MAX(x, y) ((x)>(y)?(x):(y))

int main()
{
	int ret = MAX(M, N);
	
	return 0;
}

MAX(M, N)首先被替换成:((10)>(M + 2)?(10):(M + 2))
然后((10)>(M + 2)?(10):(M + 2))再被替换成:((10)>(10 + 2)?(10):(10 + 2))

注意:

  • 宏参数和#define定义中可以出现其他#define定义的符号,但宏不能实现递归

比如:#define N M + 2这个是可以的,但#define N N + 2是不行的。

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

比如:

#include <stdio.h>
#define M 10
#define N M + 2
#define MAX(x, y) ((x)>(y)?(x):(y))

int main()
{
	printf("MAX(M, N)");
	return 0;
}

请添加图片描述

可以看到宏MAX(M, N)并没有展开。


6、宏和函数的对比

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

比如在两个数中找较大数,用宏实现更有优势:

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

那为什么不用函数呢?原因有二:

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

但是和函数相比宏还是有劣势的:

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

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

#include <stdio.h>
#define MALLOC(n, type) (type*)malloc(n * sizeof(type))

int main()
{
	//int* p = (int*)malloc(10 * sizeof(int));
	int* p = MALLOC(10, int);
	//int *p = (int*)malloc(10 * sizeof(int));

	return 0;
}

宏和函数的对比:

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

7、# 和

7.1 #运算符

#运算符将宏的一个参数转换为字符串字面量,它仅允许出现在带参数的宏的替换列表中
#运算符所执行的操作可以理解为“字符串化
比如:当我们有一个变量int a = 10;的时候,我们想打印出:the value of a is 10.
下面是常规写法:

#include <stdio.h>

int main()
{
	int a = 10;
	printf("the value of a is %d\n", a);
	return 0;
}

如果我们想把打印的这条代码通过宏替换来实现,该怎么做呢?

#include <stdio.h>
#define PRINT(format, n) printf("the value of n is "format"\n", n)

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

如果写成上面这种代码很明显并没有解决问题,因为如果我们将n写成%d时并不能打印出a,而只能打印出a的值,那为了能打印出a本身的字面量,我们就可以使用#操作符
如下:

#include <stdio.h>
#define PRINT(format, n) printf("the value of "#n" is "format"\n", n)

int main()
{
	int a = 10;
	PRINT("%d", a);
	//printf("the value of "a" is ""%d""\n", a);
	double b = 3.14;
	PRINT("%lf", b);
	//printf("the value of "b" is ""%lf""\n", b);
	return 0;
}

请添加图片描述

所以我们说:#运算符所执行的操作可以理解为“字符串化”,上面的代码中是将a和b字符串化了。

当n = a的时候,#n 就相当于“a”


7.2 ##运算符

##可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。##被称为记号粘合
这样的连接必须产生一个合法的标识符,否则其结果就是未定义的。

比如现在有这么一个问题:当我们写一个函数来求两个数的较大值的时候,不同的类型我们就需要写不同的函数,这样写太繁琐了,我们可以使用宏来简化这件事:

#include <stdio.h>
#define GENERIC(type) \
type type##_max(type x, type y)\
{\
	return ((x) > (y) ? (x) : (y));\
}

GENERIC(int)
//int int_max(int x, int y)
//{
//	return ((x) > (y) ? (x) : (y));
//}

GENERIC(double)
//double double_max(double x, double y)
//{
//	return ((x) > (y) ? (x) : (y));
//}

int main()
{
	printf("%d\n", int_max(10, 20));
	printf("%lf\n", double_max(3.14, 6.28));
	return 0;
}

请添加图片描述

上面的代码中我们利用宏替换来实现创建不同类型的函数,type##_max中的##操作符将type_max连接成了一个新的标识符


8、命名的约定

一般来讲函数和宏的使用语法很相似,所以语言本身没法帮我们区分二者,那我们平时的习惯是:

  • 把宏名全部大写
  • 函数名不要全部大写或不大写

9、#undef

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

#include <stdio.h>
#define M 10

int main()
{
	printf("%d\n", M);
#undef M
	printf("%d\n", M);
	return 0;
}

请添加图片描述

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


10、命令行定义

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

编译指令:

//linux  环境演示
gcc -D ARRAY_SIZE=10 programe.c

11、条件编译

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

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

如:
#define _DEBUG_ 1
int main()
{
#if _DEBUG_
	printf("a");
#endif
	return 0;
}
2.多个分支的条件编译
#if   常量表达式
//...
#elif   常量表达式
//...
#else
//...
#endif

如:
#define M 1
int main()
{
#if M == 1
	printf("a");
#elif M == 2
	printf("b"):
#else
	printf("C");
#endif
	return 0;
}
3.判断是否被定义
//如果定义了
#if defined(symbol)
#ifdef symbol

如:
#define M 2
int main()
{
#ifdef M
	printf("a");
#endif
	return 0;
}

//如果没定义
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#ifdef 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
	

条件编译通常用于跨平台性代码的编译


12、头文件的包含

12.1 头文件被包含的方式

12.1.1 本地文件包含

一般指自己创建的头文件

#include "filename.h"

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


12.1.2 库文件包含

一般指标准库中头文件的包含

#include <filename.h>

查找策略:
直接去标准路径下去查找,如果找不到就提示编译错误。
那这样是不是就说明,对库文件也可以使用" "的形式包含呢?
答案是可以的。但是这样查找的效率比较低,也不容易区分是库文件还是本地文件


12.2 嵌套文件的包含

我们已经知道,#include指令可以使另外一个文件被编译,就像它实际出现于#include指令的地方一样。
这种替换的方式很简单:预编译器先删除这条指令,并用包含文件的内容替换
一个头文件被包含几次,就会被实际编译几次,如果重复包含,对编译的压力就比较大

#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"

int main()
{
	return 0;
}

如果像上面这样写,test.h文件的内容就会被拷贝5份
如果test.h文件比较大,这样预处理后代码量会剧增,如果工程比较大,有公共使用的文件,被大家都能用,又不做任何的处理,那么后果会不堪设想。
为了解决头文件被重复引入的问题,就要用到条件编译
我们在每个头文件的开头这样写:

#ifndef __FILENAME_H__
#define __FILENAME_H__

//...

#endif

或者

#pragma once

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


总结

  • 增强对C语言编译过程的整体理解:预编译是C语言编译过程的第一阶段,在预编译阶段可以对源代码进行预处理,如宏定义、头文件包含等。通过学习预编译过程,可以更全面地理解C语言代码的编译过程。
  • 优化代码结构:预编译指令能够简化代码结构、提高代码的重用性和可维护性。学习预编译过程可以帮助程序员更好地利用预编译指令优化代码结构,提高代码的质量。
  • 理解条件编译和跨平台编译:条件编译是预编译指令中的重要功能,可以根据不同条件编译不同的代码。通过学习预编译过程,可以了解如何使用条件编译来实现跨平台编译,提高代码的可移植性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【code-server】Code-Server 安装部署

Code-Server 安装部署 1.环境准备 可以参考 https://coder.com/docs/code-server/install code-server的安装流程进行安装&#xff0c;主机环境是 Centos7 建议使用 docker 方式进行安装&#xff0c;可能会出现如下报错&#xff0c;需要升级 GNC 的版本&#xff0c;由于影响交…

RabbitMQ配置与交换机学习

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

《吸血鬼猎人D》观后感

前言 在B站无意中发现了一部动漫电影《吸血鬼猎人D》&#xff0c;看着封面还不错&#xff0c;就试着点开了视频&#xff0c;看了一会儿&#xff0c;发现画面很精美&#xff0c;人物造型高大威猛&#xff0c;肌肉线条清晰可见。如果我没记错的话&#xff0c;这种风格在日本动漫中…

C语言复习总结(含代码例程)

数据类型 基本类型&#xff1a;字符型、整型、浮点型、双精度浮点型 构造类型&#xff1a;数组、指针、结构体、共用体和枚举 字符型&#xff1a; 类型表示&#xff1a; (signed) char char -- 有符号 char 型 unsigned char -- 无符号 char 型 类型大小&#xff…

平衡树之B树

平衡树 ├── AVL 树 ├── 红黑树 ├── 2-3 树 ├── B 树 ├── Splay 树 └── Treap没研究明白&#xff0c;算啦。不写了

Opus从入门到精通(一)简介

Opus从入门到精通(一):简介 Opus是什么? Opus编解码器是专门设计用于互联网的交互式语音和音频传输。它是由IETF的编解码器工作组设计的&#xff0c;合并了Skype的SILK和Xiph. Org的CELT技术。 Opus编解码器的设计目的是处理广泛的交互式音频应用程序,包括IP语音,视频,游戏…

列出docker常用的命令

一、基础命令 docker run 创建并启动一个容器 docker ps 列出当前运行的容器 docker ps -a 列出所有容器&#xff0c;包括未运行的 docker stop 停止一个运行中的容器 docker start 启动一个已停止的容器 docker restart 重启容器 docker rm 删除一个或多个容器 docker pull 从…

揭秘Netflix背后的魔法:如何用三层架构打造个性化推荐帝国

推荐系统就像一家餐厅的菜单推荐 想象一下&#xff0c;你走进一家餐厅&#xff0c;面对琳琅满目的菜单&#xff0c;不知道点什么好。这时候&#xff0c;服务员给你推荐了几道菜&#xff0c;这些推荐是基于你以往的口味偏好和其他顾客的选择。Netflix的推荐系统也是类似的&…

浅谈golang字符编码

1、 Golang 字符编码 Golang 的代码是由 Unicode 字符组成的&#xff0c;并由 Unicode 编码规范中的 UTF-8 编码格式进行编码并存储。 Unicode 是编码字符集&#xff0c;囊括了当今世界使用的全部语言和符号的字符。有三种编码形式&#xff1a;UTF-8&#xff0c;UTF-16&#…

【PL理论】(21) 函数式语言:支持匿名函数 fun x → E | 设计递归函数 | 支持递归函数:let rec ...

&#x1f4ad; 写在前面&#xff1a;本章我们将讲解支持匿名函数&#xff0c;先回顾一下 F# 语言表示函数的方法&#xff0c;然后引出它。随后我们讲解一下如何设计递归函数&#xff0c;最后让我们的 F- 语言支持递归函数。 目录 0x00 回顾&#xff1a;F# 语言 0x01 支持匿名…

iOS--oc对象,类,和元类本质

iOS--oc对象&#xff0c;类&#xff0c;和元类本质 前言实例对象的具体结构自定义类对象的结构继承关系 类信息的存放对isa、superclass总结 前言 最近在学习runtime的过程中&#xff0c;发现其中消息发送-动态方法解析-消息转发中涉及到了大量的类与对象的底层知识&#xff0…

[NCTF 2018]flask真香

打开题目后没有提示框&#xff0c;尝试扫描后也没有什么结果&#xff0c;猜想是ssti。所以尝试寻找ssti的注入点并判断模版。 模版判断方式&#xff1a; 在url地址中输入{7*7} 后发现不能识别执行。 尝试{{7*7}} ,执行成功&#xff0c;继续往下走注入{{7*7}}&#xff0c;如果执…

ubuntu certbot 生成https ssl证书

一、安装certbot应用 sudo apt update sudo apt install certbot python3-certbot-nginx二、生成证书 # 泛域名&#xff1a; certbot certonly -d *.你的主域名 --manual --preferred-challenges dns# 主域名&#xff1a; certbot certonly -d 你的主/子域名 --manual --pref…

java实战——图书管理项目

文章目录 项目所需要的技术栈项目演示项目准备工作环境准备数据库数据准备 前后端交互分析&#xff08;前端代码我们使用现成&#xff09;图书列表界面的创建查看前端发送的请求根据前端接收的返回值来编写model层根据请求编写controller层根据controller编写Service根据Servic…

代码随想录算法训练营第五十七 | 739. 每日温度、 496.下一个更大元素 I、503.下一个更大元素II

## 739. 每日温度 这里是引用 https://programmercarl.com/0739.%E6%AF%8F%E6%97%A5%E6%B8%A9%E5%BA%A6.html 第一次接触单调栈&#xff0c;看完视频讲解之后思路很清晰&#xff0c;对单调栈能够解决的问题类型有大致了解 class Solution { public:vector<int> dailyTemp…

SARscape5.6.2干涉叠加处理效率提升

SARscape5.6.2于2022年5月正式发布&#xff0c;包含若干更新和优化。干涉叠加处理模块在处理速度方面持续提升&#xff0c;表现在&#xff1a;PS干涉工作流处理、第一次反演和第二次反演优化&#xff0c;速度提升&#xff1b;SBAS处理干涉图生成和干涉图优化速度提升&#xff0…

C# WPF入门学习主线篇(二十)—— 资源和样式

C# WPF入门学习主线篇&#xff08;二十&#xff09;—— 资源和样式 欢迎来到C# WPF入门学习系列的第二十篇。在前面的章节中&#xff0c;我们探讨了布局管理及各种控件的使用。本篇博客将重点介绍WPF中的资源&#xff08;Resource&#xff09;和样式&#xff08;Style&#xf…

vue3+vite+ts 使用webrtc-streamer播放海康rtsp监控视频

了解webrtc-streamer webrtc-streamer 是一个使用简单机制通过 WebRTC 流式传输视频捕获设备和 RTSP 源的项目&#xff0c;它内置了一个小型的 HTTP server 来对 WebRTC需要的相关接口提供支持。相对于ffmpegflv.js的方案&#xff0c;延迟降低到了0.4秒左右&#xff0c;画面的…

数据预处理 #数据挖掘 #python

数据分析中的预处理步骤是数据分析流程中的重要环节&#xff0c;它的目的是清洗、转换和整理原始数据&#xff0c;以便后续的分析能够准确、有效。预处理通常包括以下几个关键步骤&#xff1a; 数据收集&#xff1a;确定数据来源&#xff0c;可能是数据库、文件、API或网络抓取…

linux中: IDEA 由于JVM 设置内存过小,导致打开项目闪退问题

1. 找到idea安装目录 由于无法打开idea&#xff0c;只能找到idea安装目录 在linux(debian/ubuntu)中idea的插件默认安装位置和配置文件在哪里? 默认路径&#xff1a; /home/当前用户名/.config/JetBrains/IntelliJIdea2020.具体版本号/options2. 找到jvm配置文件 IDEA安装…