当前位置: 首页 > news >正文

十五.程序环境和预处理

文章目录

  • 一.程序翻译环境和执行环境
    • 1.ANSI C 标准
    • 2.程序的翻译环境和执行环境
  • 二.程序编译和链接
    • 1.翻译环境
    • 2.编译本身的几个阶段
    • 3.运行环境
  • 三.预处理
    • 1.预定义符号
    • 2.#define
      • (1)#define定义标识符
      • (2)#define定义宏
      • (3)#define替换规则
    • 3.#和##
      • (1)#
      • (2)##
    • 4.#undef
    • 5.带"副作用"的宏参数
    • 6.宏和函数对比
    • 7.命名约定
  • 四.命令行编译
  • 五.条件编译
    • 1.条件编译常量表达式
    • 2.多分支的条件编译
    • 3.条件编译是否被定义
    • 4.条件编译的嵌套
  • 六.文件包含
    • 1.头文件被包含的方式
    • 2.嵌套文件的包含

一.程序翻译环境和执行环境

1.ANSI C 标准

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

2.程序的翻译环境和执行环境

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

  • 翻译环境:在该环境中,源代码被转换为可执行的机器指令。
  • 执行环境:用于实际执行代码。
    在这里插入图片描述

二.程序编译和链接

1.翻译环境

在这里插入图片描述

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

举个例子:test.c、add.c、minu.c
在这里插入图片描述

2.编译本身的几个阶段

举个例子:
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;
}

编译阶段为:
在这里插入图片描述
解析图如下:
在这里插入图片描述

3.运行环境

程序执行过程:

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

三.预处理

1.预定义符号

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;
}

运行结果:
在这里插入图片描述

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

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

2.#define

(1)#define定义标识符

#define NAME stuff

用法演示:

#include <stdio.h>#define TIMES 100int 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 registerint main(void) {register int num = 0;REG int num = 0; // 这里REG就等于registerreturn 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语句的时候自动字上breakint 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 TIMES 100;
#define TIMES 100

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

#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 定义标识符时,尽量不要在末尾加分号!(必须加的情况除外)

(2)#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*Xint main(void) {printf("%d\n", SQUARE(3)); // printf("%d\n", 3 * 3);return 0;
}

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

#include <stdio.h>#define SQUARE(X) X*Xint 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,可能会得到以下情况:

#include <stdio.h>#define DOUBLE(X) (X)+(X)int main(void) {printf("%d\n", 10 * DOUBLE(3+1));// printf("%d\n", 10 * (4) + (4)); // 我们本意是想得到80,但是结果为44,因为整体没带括号return 0;
}

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

(3)#define替换规则

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

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

注意事项:

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

3.#和##

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

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

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

(1)#

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

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

使用 # 解决上面的问题:

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

运行结果:
在这里插入图片描述
在这里插入图片描述

(2)##

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

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

用法演示:

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

运行结果:
在这里插入图片描述
##也可以将多个符号合成一个符号,比如 X##Y##Z

4.#undef

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

用于移除一个宏定义。

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

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

5.带"副作用"的宏参数

什么是副作用?
副作用就是表达式求值的时候出现的永久性效果,例如:

//不带有副作用
x + 1;
//带有副作用
x++;  int a = 1;
//不带有副作用
int b = a + 1; //b=2, a=1
//带有副作用
int b = ++a;  //b=2, a=2

当宏参数在宏的定义中出现超过一次的情况下,如果参数带有副作用,那么在使用这个宏的时候就可能出现危险,导致不可预料的后果。这种带有副作用的宏参数如果传到宏体内,这种副作用会一直延续到宏体内。

举个例子:

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

运行结果:
在这里插入图片描述
在这里插入图片描述
所以,写宏的时候尽量避免使用这种带副作用的参数。

6.宏和函数对比

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

#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 <stdio.h>
#include <stdlib.h>#define MALLOC(num, type) (type*)malloc(num*sizeof(type))int main(void) {// 原本的写法:malloc(10*sizeof(int));// 但我想这么写:malloc(10, int);int* p = MALLOC(10, int); // (int*)malloc(10*sizeof(int))...return 0;    
}

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

7.命名约定

命名约定,一般来讲函数的宏的使用语法很相似,所以语言本身没法帮我们区分二者。约定俗成的一个习惯是: 宏名全部大写,函数名不要全部大写。

四.命令行编译

什么是命令行编译?

在编译的时候通过命令行的方式对其进行相关的定义,叫做命令行编译。

许多C的编译器提供的一种能力,允许在命令行中定义符号。用于启动编译过程。当我们根据同一个源文件要编译出不同的一个程序的不同版本的时,可以用到这种特性,增加灵活性。

比如:假如某个程序中声明了一个某个长度的数组,假如机器甲内存有限,我们需要一个很小的数据,但是机器丙的内存较大,我们需要一个大点的数组。

#include <stdio.h>int main() {int arr[ARR_SIZE];int i = 0;for (i = 0; i < ARR_SIZE; i++) {arr[i] = i;}for (i = 0; i < ARR_SIZE; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

gcc 环境下测试:(VS 里面不太好演示)
gcc -D ARRAY_SIZE=10 programe.c

五.条件编译

在编译一个程序时,通过条件编译指令将一条语句(一组语句)编译或者放弃是很方便的。

调试用的代码删除了可惜,保留了又碍事。我们就可以使用条件编译来选择性地编译:

#include <stdio.h>#define __DEBUG__ // 就像一个开关一样int main(void)
{int arr[10] = {0};int i = 0;for (i = 0; i < 10; i++) {arr[i] = i;#ifdef __DEBUG__ // 因为__DEBUG__被定义了,所以为真printf("%d ", arr[i]); // 就打印数组    #endif // 包尾}return 0;
}

运行结果:1 2 3 4 5 6 7 8 9 10

如果不想用了,就把 #define DEBUG 注释掉:

#include <stdio.h>// #define __DEBUG__ // 关int main(void)
{int arr[10] = {0};int i = 0;for (i = 0; i < 10; i++) {arr[i] = i;#ifdef __DEBUG__ // 此时ifdef为假printf("%d ", arr[i]);      #endif}return 0;
}

1.条件编译常量表达式

#if 常量表达式……
#endif

如果常量表达式为真,参加编译。反之如果为假,则不参加编译。

用法演示:常量表达式为真

#include <stdio.h>int main(void) {
#if 1printf("Hello,World!\n");
#endifreturn 0;
}

2.多分支的条件编译

#if 常量表达式……
#else if 常量表达式……
#else……
#endif

多分支的条件编译,直到常量表达式为真时才执行。

用法演示:

#include <stdio.h>int main(void) {
#if 1 == 2 // 假printf("rose\n");
#elif 2 == 2 // 真printf("you jump\n");
#else printf("i jump\n")
#endifreturn 0;
}

运行结果:you jump

3.条件编译是否被定义

#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol

ifdef 和 if defined() ,ifndef 和 if !defined() 效果是一样的,用来判断是否被定义。

用法演示:

#include <stdio.h>#define TEST 0
// #define TEST2 // 不定义int main(void) {
/* 如果TEST定义了,下面参与编译 */
// 1
#ifdef TESTprintf("1\n");
#endif// 2
#if defined(TEST)printf("2\n");
#endif/* 如果TEST2不定义,下面参与编译 */
// 1
#ifndef TEST2printf("3\n");
#endif// 2
#if !defined(TEST2)printf("4\n");
#endifreturn 0;
}

运行结果:
在这里插入图片描述

4.条件编译的嵌套

和 if 语句一样,是可以嵌套的:

#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

六.文件包含

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

1.头文件被包含的方式

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

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

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

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

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

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

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

—————————————————————————————————
本篇到此结束,码文不易,还请多多支持!

相关文章:

十五.程序环境和预处理

文章目录一.程序翻译环境和执行环境1.ANSI C 标准2.程序的翻译环境和执行环境二.程序编译和链接1.翻译环境2.编译本身的几个阶段3.运行环境三.预处理1.预定义符号2.#define&#xff08;1&#xff09;#define定义标识符&#xff08;2&#xff09;#define定义宏&#xff08;3&…...

高并发系统设计之负载均衡

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录DNS负载均衡Nginx负载均衡负载均衡算法负载均衡配置超时配置被动健康检查与主动健康检查LVS/F5Nginx当我们的应用单实例不能支撑用户请求时&#xff0c;此时就需要扩容&#xff0c;从一台服务器扩容到…...

嵌入式Linux从入门到精通之第十四节:Linux IO控制技术

目录 设备控制概述 操作设备文件函数 监听文件描述符 示例 设备控制概述 对于硬件设备,Linux采用了与裸机完全不同的机制进行管理。 Linux下的所有硬件(IO、键盘、鼠标等)均是以文件的形式进行统一管理的,每个设备在/dev/目录下都有一个设备文件与之对应。操作相应的文件…...

/etc/fstab文件

文件/etc/fstab存放的是系统中的文件系统信息&#xff0c;当系统启动的时候&#xff0c;系统会自动地从这个文件读取信息&#xff0c;并且会自动将此文件中指定的文件系统挂载到指定的目录。当正确的设置了该文件&#xff0c;则可以通过mount /directoryname命令来加载一个文件…...

深度学习神经网络基础知识(一) 模型选择、欠拟合和过拟合

专栏&#xff1a;神经网络复现目录 深度学习神经网络基础知识(一) 本文讲述神经网络基础知识&#xff0c;具体细节讲述前向传播&#xff0c;反向传播和计算图&#xff0c;同时讲解神经网络优化方法&#xff1a;权重衰减&#xff0c;Dropout等方法&#xff0c;最后进行Kaggle实…...

同样做软件测试,为什么有人月入3k-5k,有人能拿到17-20k?

同样做软件测试&#xff0c;为什么有人月入3k-5k&#xff0c;有人能拿到17-20k&#xff1f; 虽然各大培训机构一直鼓吹软件测试行业薪资高&#xff0c;但是依旧有一些拿着3-5k薪资&#xff0c;甚至找不到软件测试工作的人。 先来看一些例子&#xff1a; 小A在一家培训机构学完…...

如何运行YOLOv5的代码,实现目标识别

YOLOv5和v8都由Ultralytics这家创业公司开发的https://github.com/ultralytics/yolov5环境配置git clone https://github.com/ultralytics/yolov5.git作者要求python3.6&#xff08;我用的3.8也能跑通&#xff09;torch1.7.0pip install -r requirements_my_version.txtrequire…...

【正点原子FPGA连载】第十四章SD卡读写TXT文本实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十四章SD卡读写…...

【人工智能AI :Open AI】我想写一本书,书名是《中国文学史》,帮我列一下目录,细化到三级目录,不少于2000字。

我想写一本书&#xff0c;书名是《中国文学史》&#xff0c;帮我列一下目录&#xff0c;细化到三级目录&#xff0c;不少于2000字。 中国文学史 第一章 经典文学 1.1 先秦文学 1.1.1 先秦诗歌 1.1.1.1 小雅 1.1.1.2 大雅 1.1.1.3 颂 1.1…...

「文档数据库之争」MongoDB和CouchDB的比较

MongoDB和CouchDB都是基于文档的NoSQL数据库类型。文档数据库又称mdocument store&#xff0c;通常用于存储半结构化数据的文档格式及其详细描述。它允许创建和更新程序&#xff0c;而不需要引用主模式。移动应用程序中的内容管理和数据处理是可以应用文档存储的两个字段。Mong…...

c++11 标准模板(STL)(std::unordered_set)(三)

定义于头文件 <unordered_set> template< class Key, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator<Key> > class unordered_set;(1)(C11 起)namespace pmr { templ…...

事件循环机制eventLoop?Js事件流?JavaScript如何实现异步编程?

单线程模式&#xff1a;由用户交互和修改dom的问题&#xff0c;只能决定js就是单线程任务异步模式诞生&#xff1a;同步模式遇到耗时操作页面便会阻塞&#xff0c;就像图片加载&#xff0c;接口获取&#xff0c;页面会一直等待&#xff1b;在执行主线程时&#xff0c;先执行同步…...

视频播放器倍速、清晰度切换、m3u8下载

视频上很容易就可以做到倍速播放&#xff0c;一般的视频格式都是每秒固定的帧数&#xff0c;按比例跳帧就可以了。音频上其实也可以用这种方式来直接删除一些周期&#xff0c;因为电脑里的音频也是数字化离散化地储存的。但是为了使声音不失真&#xff0c;应该都用了稍复杂一点…...

将Nginx 核心知识点扒了个底朝天(五)

什么叫 CDN 服务&#xff1f; CDN &#xff0c;即内容分发网络。 其目的是&#xff0c;通过在现有的 Internet中 增加一层新的网络架构&#xff0c;将网站的内容发布到最接近用户的网络边缘&#xff0c;使用户可就近取得所需的内容&#xff0c;提高用户访问网站的速度。 一般…...

【基础算法】差分

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…...

【LeetCode】剑指 Offer(5)

目录 写在前面&#xff1a; 题目&#xff1a; 题目的接口&#xff1a; 解题思路1&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 解题思路2&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a;…...

外包出来,朋友内推我去一家公司,问的实在是太...

外包出来&#xff0c;没想到算法死在另一家厂子&#xff0c;自从加入这家公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到8月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资直降30%&#xff0c;顿时有吃不起饭的赶脚。 好在有…...

刷题记录:牛客NC54585小魂和他的数列 [线段树卡常,真恶心]

传送门:牛客 题目描述: 一天&#xff0c;小魂正和一个数列玩得不亦乐乎。 小魂的数列一共有n个元素&#xff0c;第i个数为Ai。 他发现&#xff0c;这个数列的一些子序列中的元素是严格递增的。 他想知道&#xff0c;这个数列一共有多少个长度为K的子序列是严格递增的。 请你帮…...

2019蓝桥杯真题旋转 C语言/C++

题目描述 图片旋转是对图片最简单的处理方式之一&#xff0c;在本题中&#xff0c;你需要对图片顺时针旋转 90 度。 我们用一个 nm 的二维数组来表示一个图片&#xff0c;例如下面给出一个 34 的 图片的例子&#xff1a; 1 3 5 7 9 8 7 6 3 5 9 7 这个图片顺时针旋转 90 度…...

<JVM上篇:内存与垃圾回收篇>11 - 垃圾回收相关算法

对象存活判断 在堆里存放着几乎所有的 Java 对象实例&#xff0c;在 GC 执行垃圾回收之前&#xff0c;首先需要区分出内存中哪些是存活对象&#xff0c;哪些是已经死亡的对象。只有被标记为己经死亡的对象&#xff0c;GC 才会在执行垃圾回收时&#xff0c;释放掉其所占用的内存…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...