【C/C++】详解程序环境和预处理(什么是程序环境?为什么要有程序环境?如何理解程序环境?)

news2024/11/18 19:30:22

目录

一、前言

二、 什么是程序环境?

三、 为什么要有程序环境?

四、如何理解程序环境?

🍎 ANSI C 标准 

🍐 翻译环境和执行环境

 五、详解翻译环境和执行环境

 🍇翻译环境(重点!!)

 💦编译环境(预处理---编译---汇编)

 💦链接环境(链接)

 🍉执行环境

 六、预处理详解

🍓 预定义符号

🍌#define 

 💦#define定义标识符

 💦#define定义宏

 💦#define替换规则

 🍊#和##

 💦#

💦##

 🍋#undef

🥝宏和函数对比

🍍文件包含 

 💦头文件被包含的方式

💦嵌套文件的包含 

 七、常考面试题

八、共勉 


一、前言

     本文主要是认识与学习C/C++中的程序环境和预处理因为这一部分是面试经常会考到的一个知识点,并且对于后续学习Linux系统编程有很大的帮助(主要是自己不清楚😊),所以接下来我将详细的剖析C/C++中的程序环境和预处理。

二、 什么是程序环境?

⭐ 程序环境:是指将源文件(code.c / code.cpp)转换为程序的过程

三、 为什么要有程序环境?

 如果没有程序环境编译器(gcc / g++)就无法识别我们所写的C/C++代码也无法将源代码转换成可执行程序

四、如何理解程序环境?

🍎 ANSI C 标准 

      ANSI C是由美国国家标准协会(ANSI)及国际化标准组织(ISO)推出的关于C语言的标准。ANSI C 主要标准化了现存的实现, 同时增加了一些来自 C++ 的内容 (主要是函数原型) 并支持多国字符集 (包括备受争议的三字符序列)。
ANSI C 几乎被所有广泛使用的编译器所支持,且多数C代码是在ANSI C基础上写的。


  : 在ANSI C(标准C) 的任何一种实现中,都存在程序环境,并且将程序环境又细分为两种不同的环境。


   :程序环境分为:翻译环境执行环境

🍐 翻译环境和执行环境

 💦翻译环境:在这个环境中源代码转换可执行程序机器指令

                     

     :在编译器中(以VS2019为例),我们写的C/C++代码都是文本的信息(各种字符串,数据,结构体等),站在人类的角度,我们能够理解,但是计算机不行,代码需要被翻译成计算机能够识别的指令: 二进制指令

 💦执行环境:它用于实际执行代码


    :原代码经过翻译环境后生成的二进制指令代码(可执行程序),再由执行环境执行生成最终需要的结果

 五、详解翻译环境和执行环境

 我们先来笼统地讲一下这两个环境的联动,在第二模块再进行细讲

  • 首先对于一个【test.c】的源文件来说,我们要将代码执行的结果输出到屏幕上,就需要有一个可执行程序【test.exe】
  • 将【test.c】文件转变为【test.exe】文件的这段过程叫做翻译环境翻译环境分为编译链接两部分,而对于编译来说,又可以进行细分为【预编译】、【编译】和【汇编】三个组成部分;当经过翻译环境之后,就会生成一个【test.exe的可执行文件
  • 此时再到执行环境,通过将程序读入内存,调用堆栈【stack】,存储函数的局部变量和返回地址,来计算出程序的运行结果,若是有打印语句就将结果打印在屏幕上💻

 🍇翻译环境(重点!!)

 接下去我们来详细说说翻译环境,也就是【编译】+【链接】的部分

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

   注意1:为什么需要多个源文件?

      对于目前没有接触过项目的程序员来说,你完全可以将所有的代码都塞在一个源文件中,这是因为一个人需要写的代码量非常的少。
      但你可以设想一下,现在你需要和一个团队进行配合来完成一个大项目,每个人都有不同的分工,但现在只有一个源文件,这会出现什么样的局面?
      只有一个源文件意味着什么?这意味着你的团队只能有一人写代码,而其他人只能干看着;如果有多个源文件,那每个人都能在不同的源文件上写代码来完成协同工作。

    注意2:这里的链接库是什么?

      由于每个源文件【.c】都会经过编译器处理生成目标文件多个目标文件又会经过链接器的处理以及链接库链接生成可执行程序

      但是一定有同学对这个链接库有所疑问,我们来看一段代码

  • 可以看到,对于这个我们写C语言时经常使用的printf(),它就被称为是库函数,包含在stdio.h这个头文件中。
  • 而对于库函数来说是存放在链接库里的。当程序里要使用来自外部的函数时,就是使用库函数,所以在链接时就应该把他们所依赖的链接库链接进来。

举个例子:test.c、add.c、minu.c  分别为源文件

 💦编译环境(预处理---编译---汇编)

     :预处理

  在这个阶段,预处理器将对源代码进行预处理,处理以#开头的预处理指令

  主要的预处理指令包括宏定义、文件包含和条件编译等。(所有的预处理指令都是在预处理阶段处理的)预处理器会展开宏定义,并将#include指令所引用的头文件内容插入到源代码中。同时,条件编译指令会根据条件判断决定是否保留特定部分的代码。


主要作用:

  •  展开头文件   
  •  注释的删除
  •  宏定义的符号替换

      :编译
   编译器会将预处理后的代码转换成汇编代码(assembly code)。汇编代码是由CPU能够直接理解和执行的低级指令。
   在编译过程中,编译器会进行语法和语义分析(语法分析,词法分析,语义分析,符号汇总),检查代码是否符合C语言的规范并生成相应的汇编代码。

主要作用:

  • 语法分析
  •  词法分析
  •  语义分析
  •  符号汇总

       :汇编
    汇编器将汇编代码翻译成机器代码(二进制代码)
,并形成符号表。这些机器代码是计算机的底层指令,可以由计算机的CPU直接执行。
    汇编后的文件通常称为目标文件(Object File -- .obj),它包含了二进制代码以及一些有关符号(函数和变量)的信息,但还没有解析它们的具体位置。

主要作用:

  • 将汇编指令转换为二进制指令(需要特定的文本阅读器)
  •  形成符号表(没错,就这个功能)

 💦链接环境(链接)

      :链接
    链接器将目标文件与需要的库文件(静态链接库或动态链接库)进行合并,形成最终的可执行程序。(合并段表,符号表的合并以及重定位)
    链接器解析目标文件中的符号,将它们与其他目标文件或库文件中的定义进行匹配。如果找不到某个符号的定义,链接过程将失败,并给出相应的错误消息。


主要作用:

  •  把多个目标文件和链接库进行编译 
  •  合并段表
  •  符号表的合并和重定位

举个例子:

 sum.c 

int global_val = 2021;
void print(const char* string) {
    printf("%s\n", string);
}

② test.c 

#include <stdio.h>
 
int main(void) {
    extern void print(char* string);
    extern int global_val;
    printf("%d\n", global_val);
    printf("Hello,World!\n");
 
    return 0;
}

 翻译过程为:

 解析图如下:

 🍉执行环境

    接着我们来聊聊程序的执行环境,这一块的话因为内容过于复杂,有太多底层的细节,因此不在这里祥解

      :程序执行的过程🏃

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

 六、预处理详解

🍓 预定义符号

在C语言中,有一些预定义的符号,当我们需要查询当前文件的相关信息时,就可以使用这个预定义符号 

1.__FILE__			//进行变异的源文件
2.__LINE__			//文件当前的行号	
3.__DATE__			//文件被编译的日期
4.__TIME__			//文件被编译的时间
5.__STDC__			//如果编译器遵循ANSI C,其值为1,否则未定义
6.__FUNCTION__      //返回所在函数的函数名

在预处理阶段被处理的已经定义好的符号为预定义符号。这些符号是可以直接使用的,是在C语言中已经内置好的。

注意:值得注意的是,__ 为两个下划线!
用法演示:

#include <stdio.h>
 
int main(void) {
    printf("%s\n", __FILE__);     // 返回使用行代码所在的源文件名,包括路径
    printf("%d\n", __LINE__);     // 返回行号
    printf("%s\n", __DATE__);     // 返回程序被编译的日期
    printf("%s\n", __TIME__);     // 返回程序被编译的时间
    printf("%s\n", __FUNCTION__); // 返回所在函数的函数名
 
    return 0;
}

运行结果:

 那么这些预定义符号有什么用?

  • 如果一个工程特别复杂,这时去调试时可能会无从下手。所以需要代码在运行的过程中记录一些日志信息,通过日志信息分析程序哪里出了问题,再进行排查就如同瓮中捉鳖。

🍌#define 

 理解预处理,那#define肯定要理解,这个相信大家都用到过

 💦#define定义标识符

#define NAME stuff

用法演示:

#include <stdio.h>
 
#define TIMES 100
 
int main(void) {
    int t = TIMES;
    printf("%d\n", t);
 
    return 0;
}

运行结果:100
在预处理阶段会把 TIMES 替换为 100。预处理结束后 int t = TIMES 就没有TIMES 了,会变为 int t = 100。

// 预处理前
int t = TIMES;
// 预处理后
int t = 100;

当然了, #define 定义的符号可不仅仅只有数字,还可以用来做很多事,比如

1.#define REG register        //给关键字register,创建一个简短的名字
2.#define DEAD_LOOP for(;;)   //用更形象的符号来替换一种实现

 ① #define REG register,给关键字 register,创建一个简短的名字:

#define REG register
 
int main(void) {
    register int num = 0;
    REG int num = 0; // 这里REG就等于register
 
    return 0;
}

 ② #define DEAD_LOOP for(;;),用更形象的符号来替换一种实现:

#define DEAD_LOOP for(;;)
 
int main(void) {
    DEAD_LOOP // 预处理后替换为 for(;;); 
        ; // 循环体循环的是一条空语句
 
    DEAD_LOOP; // 那么可以这么写,这个分号就是循环体,循环的是一个空语句
 
    return 0;
}

 ③ #define CASE break;case ,在写case语句的时候自动字上break(很巧妙的偷懒):

#define CASE break;case     // 在写case语句的时候自动字上break
 
int main(void) {
    int n = 0;
    //switch (n) {
    //    case 1:
    //        break;
    //    case 2:
    //        break;
    //    case 3:
    //        break;
    //}
 
    switch (n) {
        case 1: // 第一个case不能替换
        CASE 2: // 相当于 break; case 2:
        CASE 3: // 相当于 break; case 3:
    }
 
    return 0;
}

 有个细节,再前面 #define 定义标识符时,为什么末尾没有加上分号呢?

 这是因为,分号也会被当作替换内容替换到文本当中,可能会导致出现错误:

#define _CRT_SECURE_NO_WARNINGS 1
 
#include <stdio.h>
 
#define TIMES 100;
 
int main(void) {
    int a, b;
    if (a > 10)
        b = TIMES; // b = 100;;
    else //else没有匹配对象
        b = -TIMES; // b = 100;;
 
    return 0;
}

 所以,在 #define 定义标识符时,尽量不要在末尾加分号!(必须加的情况除外)

 💦#define定义宏

#define NAME(parament-list) stuff

 #define 机制允许把参数替换到文本中,这种实现通常被称为宏(macro)或 定义宏(define macro),parament-list 是一个由逗号隔开的符号表,他们可能出现在 stuff 中。

注意:

  • 参数列表的左括号必须与 name 紧邻。
  • 如果两者之间由任何空白存在,参数列表就会将其解释为 stuff 的一部分。

用法演示:3*3=9 

#include <stdio.h>
 
#define SQUARE(X) X*X
 
int main(void) {
    printf("%d\n", SQUARE(3)); // printf("%d\n", 3 * 3);
 
    return 0;
}

那么,(3+1) 的结果是什么? 

#include <stdio.h>
 
#define SQUARE(X) X*X
 
int main(void) {
    printf("%d\n", SQUARE(3+1));
 
    return 0;
}

运行结果:7

这是因为替换是在预处理阶段时替换,表达式真正计算出结果是在运行时计算。所以先替换:3+1*3+1=7 

如果想获得 3+1 相乘(也就是得到 4×4 = 16) 的结果,我们需要给他们添加括号: 

#include <stdio.h>
 
// 整体再括一个括号,严谨
#define SQUARE(X) ((X)*(X))
 
int main(void) {
    printf("%d\n", SQUARE(3+1));
 
    return 0;
}

另外,整体再套一个括号!让代码更加严谨,防止产生不必要的错误。比如,,我希望得到 10* DOUBLE,可能会得到以下情况: 

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

 💦#define替换规则

 在程序中扩展 #define 定义符号或宏时,需要涉及的步骤如下:

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

注意事项:

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

 🍊#和##

我们知道,宏是把参数替换到文本中。那么如何把参数插入到字符串中呢?
比如这种情况,使用函数是根本做不到的: 

void print(int x) {
    printf("变量?的值是%d\n", ?) 函数根本做不到
}
 
int main(void) {
    int a = 10;
    // 打印内容:变量a的值是10
    print(a);
 
    int b = 20;
    // 打印内容:变量b的值是20
    print(b);
 
    int c = 30;
    // 打印内容:变量c的值是30
    print(c);
 
    return 0;
}

 这种情况,就可以用 来实现。

 💦#

#    //把一个宏参数变成对应的字符串

#把一个宏参数变成对应的字符串。

使用 # 解决上面的问题:

#include <stdio.h>
#define PRINT(X) printf("变量"#X"的值是%d\n", X);
// #X 就会变成 X内容所定义的字符串
 
int main(void) {
    // 打印内容:变量a的值是10
    int a = 10;
    PRINT(a); // printf("变量""a""的值是%d\n", a);
 
    // 打印内容:变量b的值是20
    int b = 20;
    PRINT(b); // printf("变量""b"的值是%d\n", b);
 
    // 打印内容:变量c的值是30
    int c = 30;
    PRINT(c); // printf("变量""c""的值是%d\n", c);
 
    return 0;
}

运行结果:


改进:让程序不仅仅支持打印整数,还可以打印其他类型的数(比如浮点数):

#include <stdio.h>
#define PRINT(X, FORMAT) printf("变量"#X"的值是 "FORMAT"\n", X);
 
int main(void) {
    // 打印内容:变量a的值是10
    int a = 10;
    PRINT(a, "%d");
 
    // 打印内容:变量f的值是5.5
    float f = 5.5f;
    PRINT(f, "%.1f"); //printf("变量""f""的值是 ""%.1f""\n", f);
 
    return 0;
}

运行结果:

💦##

##   //把位于它两边的符号合并成一个符号

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

用法演示: 

#include <stdio.h>
 
#define CAT(X,Y) X##Y
 
int main(void) {
    int vs2003 = 100;
 
    printf("%d\n", CAT(vs, 2003)); // printf("%d\n", vs2003);
 
    return 0;
}

运行结果:

100

 🍋#undef

#undef NAME	   //移除一个宏定义

用于移除一个宏定义。

用法演示:用完 M 之后移除该定义 

#include <stdio.h>
 
#define M 100
 
int main(void) {
    int a = M;
    printf("%d\n", M);
#undef M // 移除宏定义
 
    return 0;
}

🥝宏和函数对比

举个例子:在两数中找较大值

 用宏: 

#include <stdio.h>
 
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
 
int main(void) {
    int a = 10;
    int b = 20;
    int m = MAX(a, b); // int m = ((a)>(b) ? (a):(b))
    printf("%d\n", m);
    
    return 0;
}

  用函数:

#include <stdio.h>
 
int Max(int x, int y) {
    return x > y ? x : y;
}
 
int main(void) {
    int a = 10;
    int b = 20;
    int m = Max(a, b);
    printf("%d\n", m);
 
    return 0;
}

那么,宏和函数那种更好呢? 

 答案是宏

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

 当然,宏也有劣势的地方:

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

所以,如果一个运算的逻辑足够简单,建议使用宏。反之,如果一个运算的逻辑足够复杂,建议使用函数。
 

🍍文件包含 

我们已经知道,#include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方一样。替换方式为,预处理器先删除这条指令,并用包含文件的内容替换。这样一个源文件被包含10次,那就实际被编译10次。 

 💦头文件被包含的方式

#include "filename"
#include <filename.h>

 < > 和 " " 包含头文件的本质区别:查找的策略的区别:

  • " " 的查找策略:先在源文件所在的工程目录下查找。如果该头文件未找到,则在库函数的头文件目录下查找。(如果仍然找不到,就提示编译错误)
  • < > 的查找策略:直接去标准路径下去查找。(如果仍然找不到,就提示编译错误)

既然如此,那么对于库文件是否也可以使用 " " 包含?
答案是可以的。但是这样做查找的效率就低些,当然这样也不容易区分是库文件还是本地文件了。为了效率不建议这么做。

💦嵌套文件的包含 

头文件被重复包含的情况:

 

  • 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

还有一种非常简单的方法:

#pragma once // 让头文件即使被包含多次,也只编译一份

 七、常考面试题

 1. 由多个源文件组成的C程序,经过编辑、预处理、编译、链接等阶段会生成最终的可执行程序。下面哪个阶段可以发现被调用的函数未定义?( )

A.预处理
B.编译
C.链接
D.执行

【答案】:C

【解析】:

预处理只会处理#开头的语句,编译阶段只校验语法,链接时才会去找实体,所以是链接时出错的,故选C。这里附上每个步骤的具体操作方式: 

  1. 预处理:相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有头文件(都已经被展开了)、宏定义(都已经替换了),没有条件编译指令(该屏蔽的都屏蔽掉了),没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。
  2. 编译:将预处理完的文件逐一进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。编译是针对单个文件编译的,只校验本文件的语法是否有问题,不负责寻找实体。
  3. 链接:通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体在此过程中会发现被调用的函数未被定义。需要注意的是,链接阶段只会链接调用了的函数/全局变量,如果存在一个不存在实体的声明(函数声明、全局变量的外部声明),但没有被调用,依然是可以正常编译执行的

2. test.c文件中包括如下语句: 

#define INT_PTR int*
typedef int*int_ptr;
INT_PTR a,b;
int_ptr c,d;

 其中定义的四个变量,哪个变量不是指针类型?( )

A.a
B.b
C.c
D.d

【答案】:B

【解析】:

预处理的#define是查找替换,所以替换过后的语句是int* a, b;

 我们到Linux来看看,更加清晰

其中b只是一个int变量,如果要让b也是指针,必须写成int *a, *b; 而typedef没有这个问题,c、d都是指针 

八、共勉 

以下就是我对【C/C++】程序环境和预处理的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对Linux--gcc的理解,请持续关注我哦!!!!!   

 

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

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

相关文章

【K12】tk窗口+plt图像功能-学习物理中的串并联研究【附源码说明】

程序源码 import tkinter as tk import matplotlib.pyplot as plt# 初始化 matplotlib 的字体设置 plt.rcParams[font.family] SimHei# 计算串联电路的函数 def calculate_series():try:# 获取用户输入的电阻值并转换为浮点数r1 float(entry_r1.get())r2 float(entry_r2.ge…

Kotlin Multiplatform项目推荐 | 太空人分布图

Kotlin Multiplatform项目推荐 | 太空人分布图 项目简介 Kotlin Multiplatform项目是一种跨平台开发技术&#xff0c;它可以同时使用SwiftUI、Jetpack Compose、Compose for Wear OS、Compose for Desktop、Compose for Web、Kotlin/JS React等客户端框架&#xff0c;并且使…

【Linux】-cp模型

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

IDEA 安装阿里Java编码规范插件

1.File>Settings 2.安装之后重启 开发过程中如果有不符合规范的地方&#xff0c;会自动出现提示

单片机学习笔记---独立按键控制LED状态

上一节学习的是独立按键控制LED亮灭 这一节我们先来讲一下按键的抖动&#xff1a; 对于机械开关&#xff0c;当机械触点断开、闭合时&#xff0c;由于机械触点的弹性作用&#xff0c;一个开关在闭合时不会马上稳定地接通&#xff0c;在断开时也不会一下子断开&#xff0c;所以…

go slice 基本用法

slice&#xff08;切片&#xff09;是 go 里面非常常用的一种数据结构&#xff0c;它代表了一个变长的序列&#xff0c;序列中的每个元素都有相同的数据类型。 一个 slice 类型一般写作 []T&#xff0c;其中 T 代表 slice 中元素的类型&#xff1b;slice 的语法和数组很像&…

网络分层和网络原理之UDP和TCP

温故而知新 目录 网络分层 应用层 http协议 传输层 介绍 UDP协议 TCP协议 网络层 数据链路层 物理层 网络分层 一. 应用层 应用程序 现成的应用层协议有超文本协议http(不仅仅有文本&#xff09;. http协议 http://t.csdnimg.cn/e0e8khttp://t.csdnimg.cn/e0e8k 自定义应…

Linux 挂载读取、卸载 ntfs格式硬盘

windows常用的ntfs硬盘分区格式&#xff0c;在linux通常不能直接读取&#xff0c;不过挂载也是非常容易 一、挂载ntfs分区 1.安装 apt-get install ntfs-3g2.查看现在接上的硬盘 fdisk -l可以找到类似如下的&#xff0c;会显示microsoft basic data 3.创建挂载的目录 创…

Android P 背光机制流程分析

在android 9.0中&#xff0c;相比android 8.1而言&#xff0c;背光部分逻辑有较大的调整&#xff0c;这里就对android P背光机制进行完整的分析。 1.手动调节亮度 1.1.在SystemUI、Settings中手动调节 在界面(SystemUI)和Settings中拖动进度条调节亮度时&#xff0c;调节入口…

Topaz Video AI:无损放大,让你的视频更清晰!

在当今的数字时代&#xff0c;视频内容的重要性越来越受到人们的关注。无论是在社交媒体上分享生活片段&#xff0c;还是在商业领域中制作宣传视频&#xff0c;人们都希望能够展现出更高质量的视频内容。 然而&#xff0c;由于各种原因&#xff0c;我们经常会面临一个问题&…

MongoDB日期存储与查询、@Query、嵌套字段查询实战总结

缘由 MongoDB数据库如下&#xff1a; 如上截图&#xff0c;使用MongoDB客户端工具DataGrip&#xff0c;在filter过滤框输入{ profiles.alias: 逆天子, profiles.channel: }&#xff0c;即可实现昵称和渠道多个嵌套字段过滤查询。 现有业务需求&#xff1a;用Java代码来查询…

mysql 存储过程学习

存储过程介绍 1.1 SQL指令执行过程 从SQL执行的流程中我们分析存在的问题: 1.如果我们需要重复多次执行相同的SQL&#xff0c;SQL执行都需要通过连接传递到MySQL&#xff0c;并且需要经过编译和执行的步骤; 2.如果我们需要执行多个SQL指令&#xff0c;并且第二个SQL指令需要…

哪吒汽车与经纬恒润合作升级,中央域控+区域域控将于2024年落地

近日&#xff0c;在2024哪吒汽车价值链大会上&#xff0c;哪吒汽车与经纬恒润联合宣布合作升级&#xff0c;就中央域控制器和区域域控制器展开合作&#xff0c;合作成果将在山海平台新一代车型上发布。 哪吒汽车首席技术官戴大力、经纬恒润副总裁李伟 经纬恒润在智能驾驶领域拥…

数据结构(绪论+算法的基本概念)

文章目录 一、绪论1.1、数据结构的基本概念1.2、数据结构三要素1.2.1、逻辑结构1.2.2、数据的运算1.2.3、物理结构&#xff08;存储结构&#xff09;1.2.4、数据类型和抽象数据类型 二、算法的基本概念2.1、算法的特性2.2、“好”算法的特质2.2.1、算法时间复杂度2.2.2、算法空…

一款强大的矢量图形设计软件:Adobe Illustrator 2023 (AI2023)软件介绍

Adobe Illustrator 2023 (AI2023) 是一款强大的矢量图形设计软件&#xff0c;为设计师提供了无限创意和畅行无阻的设计体验。AI2023具备丰富的功能和工具&#xff0c;让用户可以轻松创建精美的矢量图形、插图、徽标和其他设计作品。 AI2023在界面和用户体验方面进行了全面升级…

【GTK】GtkApplicationWindow/GtkApplication

一、GtkApplicationWindow 在GTK&#xff08;GIMP Toolkit&#xff09;应用程序开发中&#xff0c;并不是每个GTK程序都必须有一个GtkApplicationWindow&#xff0c;但大多数基于GTK的图形界面应用程序都会使用GtkApplicationWindow&#xff0c;因为它是一个方便的类&#xff…

幻兽帕鲁搭建私服,一键更新方法

看着帕鲁这么火&#xff0c;估计更新会变为常态了&#xff0c;如果有自己搭建私服的话&#xff0c;跟着我下面的方法去进行更新吧&#xff01; 如果你还没有自己的私服&#xff0c;快去三五十搞一个吧&#xff0c;只需三五分钟&#xff0c;叫上你的小伙伴一起去搞起来吧 只需3分…

EXCEL VBA抓取网页JSON数据并解析

EXCEL VBA抓取网页JSON数据并解析 链接地址&#xff1a; https://api.api68.com/CQShiCai/getBaseCQShiCaiList.do?lotCode10036&date2024-01-26 Sub test() On Error Resume Next Sheet.Select Sheet1.Cells.ClearContents [a1:g1] Split("preDrawIssue|preDrawTi…

呼叫中心语音质检解决方案-苏州磐石云

呼叫中心语音质检解决方案是一种利用自动语音识别&#xff08;ASR&#xff09;和自然语言处理&#xff08;NLP&#xff09;技术来分析和评估呼叫录音的工具。它可以帮助呼叫中心监控和改进客户服务质量&#xff0c;提供以下功能和特点&#xff1a; 语音转文本&#xff1a;利用A…

dd 命令创建10G大小的文件

您可以使用Linux命令行工具dd来创建10GB大小的文件。以下是创建10GB文件的命令&#xff1a;dd if/dev/zero offile.img bs1G count10 在上述命令中&#xff0c;if参数指定输入文件为/dev/zero&#xff0c;这是一个特殊的文件&#xff0c;读取它会返回空字符&#xff08;即零&am…