玩转C语言——文件操作、预处理、编译、链接

前言:

        经过前面的学习,我们已经对C语言的语法学习完毕了,今天,我们这节内容是为了修炼内功,为以后的学习打下一个坚实基础。话不多说,开始我们今天的学习吧!

一、文件操作

        1.⼆进制⽂件和⽂本⽂件

        根据数据的组织形式,数据⽂件被称为⽂本⽂件或者⼆进制⽂件。

         数据在内存中以⼆进制的形式存储,如果不加转换的输出到外存的⽂件中,就是⼆进制⽂件。

        如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的⽂件就是⽂ 本⽂件。

         ⼀个数据在⽂件中是怎么存储的呢? 字符⼀律以ASCII形式存储,数值型数据既可以⽤ASCII形式存储,也可以使⽤⼆进制形式存储。 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占⽤5个字节(每个字符⼀个字节),⽽ ⼆进制形式输出,则在磁盘上只占4个字节。

        2.流和标准流

        想要理解文件操作就不得不提到流这个概念,那什么是流?当我们写程序时需要从键盘上进行输入输出,那我们想?这些数据怎么从外部获取又怎么输出到外部设备?这种工作全部由流来完成,在这里你可以简单理解为想象成流淌着字符的河。

        C程序针对⽂件、画⾯、键盘等的数据输⼊输出操作都是通过流操作的。

         ⼀般情况下,我们要想向流⾥写数据,或者从流中读取数据,都是要打开流,然后操作。

        这时候或许你就要说了,我平时写程序时也没有打开什么流的,那我这算二般情况吗?答案是显而易见的。那这是为什么呢?

        经过我不懈的努力以及各位读者的陪伴我得出了如下结论:

        那是因为C语⾔程序在启动的时候,默认打开了3个流:

        stdin - 标准输⼊流,在⼤多数的环境中从键盘输⼊,scanf函数就是从标准输⼊流中读取数据。

        stdout - 标准输出流,⼤多数的环境中输出⾄显⽰器界⾯,printf函数就是将信息输出到标准输出流中。

         stderr - 标准错误流,⼤多数环境中输出到显⽰器界⾯。

        这是默认打开了这三个流,我们使⽤scanf、printf等函数就可以直接进⾏输⼊输出操作的 。

        stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为⽂件指针。

         C语⾔中,就是通过 FILE* 的⽂件指针来维护流的各种操作的。 

        3.文件指针

        缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。 每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名 字,⽂件状态及⽂件当前的位置等)。

        这些信息是保存在⼀个结构体变量中的。该结构体类型是由系 统声明的,取名 FILE。以下是VS2022编译环境中stdio.h中头文件文件类型声明:

#ifndef _FILE_DEFINED#define _FILE_DEFINEDtypedef struct _iobuf{void* _Placeholder;} FILE;
#endif_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned _Ix);

        不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异。

        每当打开⼀个⽂件的时候,系统会根据⽂件的情况⾃动创建⼀个FILE结构的变量,并填充其中的信 息,使⽤者不必关⼼细节。

         ⼀般都是通过⼀个FILE的指针来维护这个FILE结构的变量,这样使⽤起来更加⽅便。

        通过⽂件指针变量能够间接找到与它关联的⽂件。

        4.⽂件的打开和关闭

        ⽂件在读写之前应该先打开⽂件,在使⽤结束之后应该关闭⽂件。和之前使用内存函数一样,要有始有终。

        在编写程序的时候,在打开⽂件的同时,都会返回⼀个FILE*的指针变量指向该⽂件,也相当于建⽴了 指针和⽂件的关系。

        我们规定使用fopen来打开文件,使用fclose来关闭文件。

//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );

        mode表⽰⽂件的打开模式,下⾯都是⽂件的打开模式:

⽂件使⽤⽅式含义如果指定⽂件不存在
“r”(只读)为了输⼊数据,打开⼀个已经存在的⽂本⽂件出错
“w”(只写)为了输出数据,打开⼀个⽂本⽂件建⽴⼀个新的⽂件
“a”(追加) 向⽂本⽂件尾添加数据建⽴⼀个新的⽂件
“rb”(只读)为了输⼊数据,打开⼀个⼆进制⽂件出错
“wb”(只写)为了输出数据,打开⼀个⼆进制⽂件建⽴⼀个新的⽂件
“ab”(追加)向⼀个⼆进制⽂件尾添加数据建⽴⼀个新的⽂件
“r+”(读写)为了读和写,打开⼀个⽂本⽂件出错
“w+”(读写)为了读和写,建立⼀个⽂本⽂件建⽴⼀个新的⽂件
“a+”(读写)打开⼀个⽂件,在⽂件尾进⾏读写建⽴⼀个新的⽂件
“rb+”(读写)为了读和写打开⼀个⼆进制⽂件出错
“wb+”(读 写)为了读和写新建⼀个⼆进制⽂件建⽴⼀个新的⽂件
“ab+”(读 写)打开⼀个⼆进制⽂件,在⽂件尾进⾏读和写建⽴⼀个新的⽂件

                那咱们写一个文件吧:

#include <stdio.h>
int main()
{FILE* pf;pf = fopen("myfile,txt","w");//注意此处都应该是双引号if (pf == NULL){strerror(pf);return 1;}fputs("fopen example", pf);fclose(pf);return 0;
}

        那大家这时可能要问了fputs是个啥,别急,先缓一会,欲速则不达。且听我下回分晓:

        5.⽂件的顺序读写 

        以下是函数名:

函数名功能适⽤于
fgetc字符输⼊函数所有输⼊流
fputc 字符输出函数所有输出流
fgets⽂本⾏输⼊函数所有输⼊流
fputs⽂本⾏输出函数所有输出流
fscanf格式化输⼊函数所有输⼊流
fprintf格式化输出函数所有输出流
fread⼆进制输⼊⽂件
fwrite⼆进制输出⽂件

        上⾯说的适⽤于所有输⼊流⼀般指适⽤于标准输⼊流和其他输⼊流(如⽂件输⼊流);所有输出流⼀ 般指适⽤于标准输出流和其他输出流(如⽂件输出流)。

        其具体使用大家可自行探索。

        了解了顺序读写再来了解了解随机读写吧!

        6.⽂件的随机读写

        一共有三个函数,分别为:fseek,ftell,rewind

        6.1 fseek函数

        根据⽂件指针的位置和偏移量来定位⽂件指针。

int fseek ( FILE * stream, long int offset, int origin );

        用法如下:

        代码实现: 

#include <stdio.h>
int main()
{FILE* pf;pf = fopen("myfile,txt","wb");if (pf == NULL){perror(pf);return 1;}fputs("this is an apple", pf);fseek(pf , 9 ,SEEK_SET);fputs(" sam",pf);fclose(pf);return 0;
}
         6.2 ftell函数

        返回⽂件指针相对于起始位置的偏移量。

long int ftell ( FILE * stream );

        代码实现: 

#include <stdio.h>int main()
{FILE* pFile;long size;pFile = fopen("myfile.txt", "rb");if (pFile == NULL){perror("Error opening file");}else{fseek(pFile, 0, SEEK_END);   size = ftell(pFile);fclose(pFile);printf("Size of myfile.txt: %ld bytes.\n", size);}return 0;
}
6.3 rewind函数 

        让⽂件指针的位置回到⽂件的起始位置。

void rewind ( FILE * stream );

        代码实现: 

#include <stdio.h>int main()
{int n;FILE* pFile;char buffer[27];pFile = fopen("myfile.txt", "w+");for (n = 'A'; n <= 'Z'; n++){fputc(n, pFile);}rewind(pFile);fread(buffer, 1, 26, pFile);fclose(pFile);buffer[26] = '\0';puts(buffer);return 0;
}

        7. ⽂件读取结束的判定

        当我们运用函数读取文件时,我们改如何确定文件是正常关闭还是非正常关闭,我们接下来会来介绍文件读取结束判定的函数吗?不,这仅仅是为了feof函数洗清冤屈而已。

        各位牢记:不能使用feof函数的返回值来判断文件读取是否结束

        feof 的作⽤是:当⽂件读取结束的时候,判断是读取结束的原因是否是:遇到⽂件尾结束。

 

         有感兴趣的可下去自行查询⽂件读取结束的判定的函数。

        8.⽂件缓冲区

        ANSIC 标准采⽤“缓冲⽂件系统” 处理的数据⽂件的,所谓缓冲⽂件系统是指系统⾃动地在内存中为 程序中每⼀个正在使⽤的⽂件开辟⼀块“⽂件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才⼀起送到磁盘上。如果从磁盘向计算机读⼊数据,则从磁盘⽂件中读取数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的⼤⼩根据C编译系统决定的。

        这⾥可以得出⼀个结论: 因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件。 如果不做,可能导致读写⽂件的问题。

二、预处理

        1.预定义符号

        C语言设置了一些预定义符号,可以直接使用具体如下:

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

        可采取如下使用:

	printf("file: %s,line: %d,time : %s",__FILE__,__LINE__,__TIME__);

        2. #define定义

        大家C语言学习到这里相必对于define应该不会太过于陌生,但此处为了课程需要还是要介绍一下:#define可以用来定义常量具体方式如下:

#define MAX 10
#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 机制包括了⼀个规定,允许把参数替换到⽂本中,这种实现通常称为宏(macro)或定义宏 (define macro)。

        以下是一个简单的宏:

#define MUL(a,b) a*b

        这个宏是什么意思呢?很简单,我们拆分来看:#define是用来替换的对吧?即,用MUL(a,b)来替换 a*b,就是简单实现了一下两数相乘,你可以简单验证验证。

        相信经过大家的检验,已经发现了这个问题,若是没发现不妨使用以下代码一试:

#include<stdio.h>#define MUL(a,b) a*b 
int main()
{int a = 3;int b = 5;int ret = MUL(a+a, b+b);printf("%d", ret);return 0;
}

        按照大家的想法应该是先执行3+3=6,5+5=10,6*10=60,答案应该是60,但很不幸答案是23。

        这是为什么呢?大家要紧扣定义MUL只是简单经行宏替换,即:3+3*5+5 这个表达式,那该如何解决呢?很简单,加上括号即可,如下:

#define MUL(a,b) (a)*(b)

        这样的话就会按照我们的预想去执行。

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

        接下来,给大家介绍宏的副作用:

        3.宏的副作用

        大家观察以下代码并推断其输出结果:

#include<stdio.h>
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
int main()
{int x = 5;int y = 8;int z = MAX(x++, y++);printf("x=%d y=%d z=%d\n", x, y, z);return 0;
}

        大家想必都有自己的答案了,这里公布一下正确答案:

                x=6 y=10 z=9

        估计又没有符合大多数人的预期,这是为什么呢?咱们看一下替换后的代码就一目了然:

#define MAX(a, b) ( (x++) > (y++) ? (x++) : (y++) )

         这下你该明白了吧,当你觉得不明白时替换看一下便会一目了然。

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

         4.宏与函数对比

宏:

  1. 宏是一系列的VBA代码,用于自动化重复性任务或执行复杂的操作。
  2. 可以通过录制宏或手动编写VBA代码来创建宏。
  3. 宏可以执行各种操作,例如格式设置、数据处理、自定义功能等。
  4. 宏可以通过快捷键、按钮或特定事件触发执行。

函数:

  1. 函数是Excel内置的或自定义的公式,用于执行特定的计算或操作。
  2. 函数通常用于处理数据、进行数学运算、查找特定信息等。
  3. 函数可以嵌套组合,以实现更复杂的计算。
  4. 函数通常返回一个值,可以在单元格中直接使用。

        和函数相⽐宏的劣势:

        1. 每次使⽤宏的时候,⼀份宏定义的代码将插⼊到程序中。除⾮宏⽐较短,否则可能⼤幅度增加程序 的⻓度。

         2. 宏是没法调试的。

        3. 宏由于类型⽆关,也就不够严谨。

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

        5.条件编译 

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

        以下是条件编译的一些指令:

1.
#if 常量表达式//...
#endif//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif2.多个分⽀的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif3.判断是否被定义
#if defined(symbol)
#ifdef symbol#if !defined(symbol)
#ifndef symbol4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

        6.头⽂件的包含

       本地文件包含:

#include "filename"

        查找策略:先在源⽂件所在⽬录下查找,如果该头⽂件未找到,编译器就像查找库函数头⽂件⼀样在 标准位置查找头⽂件。

        如果找不到就提⽰编译错误。

        库⽂件包含:

#include<stdio.h>

        查找头⽂件直接去标准路径下去查找,如果找不到就提⽰编译错误。

        这样是不是可以说,对于库⽂件也可以使⽤ “ ” 的形式包含? 答案是肯定的,可以,但是这样做查找的效率就低些,当然这样也不容易区分是库⽂件还是本地⽂件 了。

        嵌套⽂件包含:

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

         这种替换的⽅式很简单:预处理器先删除这条指令,并⽤包含⽂件的内容替换。

        ⼀个头⽂件被包含10次,那就实际被编译10次,如果重复包含,对编译的压⼒就⽐较⼤。

        如何解决头⽂件被重复引⼊的问题?答案:条件编译。

        每个头⽂件的开头写:

#ifndef __TEST_H__
#define __TEST_H__
//头⽂件的内容
#endif //__TEST_H__

         或者:

#pragma once

        就可以避免头⽂件的重复引⼊。

三、编译和链接

        1.预处理(预编译)

        经过前面的学习我们已经对编译已经有了初步的了解,接下来咱们一起来学习吧!

        编译器在执行程序时会有以下流程:预处理、编译、汇编、链接。

        在预处理阶段,编译器会执行以下操作:

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

        2.编译         

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

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

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

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

        3.汇编 

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

        4.链接

        链接是⼀个复杂的过程,链接的时候需要把⼀堆⽂件链接在⼀起才⽣成可执⾏程序。

         链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。

        链接解决的是⼀个项⽬中多⽂件、多模块之间互相调⽤的问题。

        前⾯我们⾮常简洁的讲解了⼀个C的程序是如何编译和链接,到最终⽣成可执⾏程序的过程,其实很多 内部的细节⽆法展开讲解。⽐如:⽬标⽂件的格式elf,链接底层实现中的空间与地址分配,符号解析 和重定位等,如果你有兴趣,可以看《程序的⾃我修养》⼀书来详细了解。 

        5.运⾏环境 

        1.程序必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序 的载⼊必须由⼿⼯安排,也可能是通过可执⾏代码置⼊只读内存来完成。

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

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

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

完! 

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

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

相关文章

一文了解低功耗蓝牙BLE

低功耗蓝牙技术可以构建两种类型的设备:双模设备和单模设备。双模设备既支持经典蓝牙又支持低功耗蓝牙。单模设备只支持低功耗蓝牙。还有仅支持经典蓝牙的设备。 在链路层,设备被分为广播者、扫描者、从设备和主设备。广播者是传输数据包的设备,扫描者是接收广播者的数据包…

C语言 | Leetcode C语言题解之第10题正则表达式匹配

题目&#xff1a; 题解&#xff1a; bool isMatch(char* s, char* p) {int m strlen(s);int n strlen(p);// dp[i][j] 表示 s 的前 i 个字符和 p 的前 j 个字符是否匹配bool dp[m 1][n 1];memset(dp, false, sizeof(dp));dp[0][0] true; // 空字符串和空模式匹配// 处理 …

00-JAVA基础-javassist字节码操作

字节码操作 什么是字节码 Java字节码&#xff08;Java bytecode&#xff09;是Java虚拟机&#xff08;JVM&#xff09;执行的一种虚拟指令格式。它是由Java编译器生成的&#xff0c;基于栈的指令集&#xff0c;用于在Java虚拟机上执行。字节码文件包含了JVM能够识别的指令&am…

【JSON2WEB】 12基于Amis-admin的动态导航菜单树

【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSON2WEB前端框架搭建 【J…

Flume 拦截器概念及自定义拦截器的运用

文章目录 Flume 拦截器拦截器的作用拦截器运用1.创建项目2.实现拦截器接口3.编写事件处理逻辑4.拦截器构建5.打包与上传6.编写配置文件7.测试运行 Flume 拦截器 在 Flume 中&#xff0c;拦截器&#xff08;Interceptors&#xff09;是一种可以在事件传输过程中拦截、处理和修改…

FreeRtos入门-4 事件组与同步点

事件组 事件组 同步点 创建 xEventGroupCalc xEventGroupCreate();//1&#xff0c;创建事件组 xEventGroupSyc xEventGroupCreate() 设置 xEventGroupSetBits(xEventGroupCalc,(1<<0));//设置事件组bit0 位 xEventGroupSync(xEventGroupSyc,BUSYING,ALL,portMAX…

VB 通过COM接口解析PSD文件

最近有PS测评的需求&#xff0c;故而想到了解析psd文件&#xff0c;目的就是为了获取文档信息和图层信息&#xff1b;获取PS的图像信息有很多方式&#xff0c;有过程性的&#xff0c;比如监听PS的各种操作事件&#xff1b;有结果性的&#xff0c;比如本文写的解析PSD文件。 0.…

使用pip安装geopandas(24.4更新)

geopandas是我们用Python进行地理分析常用的库&#xff0c;在数据处理、分析、制图等场景中有着极为广泛的应用&#xff0c;但是在安装过程中会出现各种问题。​geopandas的安装方式有很多&#xff0c;今天我们选取较为简单的pip来进行geopandas的安装。 ​首先&#xff0c;我…

内部类(InnerClass)

概述 什么是内部类 将一个类A定义在另一个类B里面&#xff0c;里面的那个类A就称为内部类&#xff08;InnerClass&#xff09;&#xff0c;类B则称为外部类&#xff08;OuterClass&#xff09;。 为什么要声明内部类呢 具体来说&#xff0c;当一个事物A的内部&#xff0c;还有…

Java web第一次作业

1.学会用记事本编写jsp文件&#xff0c;并放进tomcat的相关目录下&#xff0c;运行。 源代码&#xff1a; <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head> <title>我的第一个JSP页面</ti…

JavaSE——运算符

1. 概念 运算符是一种用于执行特定操作的符号或关键字。在编程中&#xff0c;运算符用于对变量、常量和表达式进行操作&#xff0c;以产生一个结果。 作为一门计算机语言&#xff0c; Java 也提供了一套丰富的运算符来操纵变量。 Java 中运算符可分为以下&#xff1a;算术运算…

电商系列之促销

> 插&#xff1a;AI时代&#xff0c;程序员或多或少要了解些人工智能&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家…

算法沉淀——动态规划篇(子数组系列问题(下))

算法沉淀——动态规划篇&#xff08;子数组系列问题&#xff08;下&#xff09;&#xff09; 前言一、等差数列划分二、最长湍流子数组三、单词拆分四、环绕字符串中唯一的子字符串 前言 几乎所有的动态规划问题大致可分为以下5个步骤&#xff0c;后续所有问题分析都将基于此 …

CSS之第一个CSS样式和CSS选择符

前端这些博客&#xff0c;我觉得都是固定的语法&#xff0c;故而不会以过多的文字进行描述&#xff0c;本系列博文均以实例和代码介绍的方式进行&#xff0c;主要按照代码进行。不会以过多的文字描述。 第一个CSS样式 <!DOCTYPE html> <html lang"en">…

【JavaEE初阶系列】——文件操作 IO 之 文件系统操作

目录 &#x1f4dd;认识文件 &#x1f6a9;树型结构组织 和 目录 &#x1f388;绝对路径和相对路径 &#x1f6a9;文件类型 &#x1f4dd;文件系统操作 &#x1f388;File 概述 &#x1f388;File类的使用 1. 绝对路径 vs 相对路径 2. 路径分隔符 3. 静态成员变量 4…

【C语言】翻译环境与运行环境

一、前言 在我们学习C语言的时候&#xff0c;第一个接触的程序就是&#xff1a;在屏幕上打印” hello word! “&#xff0c;可当时的我们却未去深入的理解与感悟&#xff0c;一个程序代码是如何运行的&#xff1b;而这一期的博客&#xff0c;则是带着我们&#xff0c;通过C代码…

mac电脑安装redis教程

1、下载地址 Download | RedisRedisYou can download the last Redis source files here. For additional options, see the Redis downloads section below.Stable (7.2)Redis 7.2 …https://redis.io/download/#redis-downloads 2、安装 2.1 解压下载后的压缩文件 2.2 进入…

Vulnhub:WESTWILD: 1.1

目录 信息收集 arp nmap nikto whatweb WEB web信息收集 dirmap enm4ulinux sumbclient get flag1 ssh登录 提权 横向移动 get root 信息收集 arp ┌──(root㉿ru)-[~/kali/vulnhub] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 0…

LeetCode-236. 二叉树的最近公共祖先【树 深度优先搜索 二叉树】

LeetCode-236. 二叉树的最近公共祖先【树 深度优先搜索 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;递归判断解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖…