【C语言】程序环境预处理 -- 详解

news2024/11/15 4:45:40

一、程序的翻译环境和执行环境

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

  1. 翻译环境,在这个环境中源代码被转换为可执行的机器指令。
  2. 执行环境,它用于实际执行代码。

1、翻译环境

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

编译本身也分为几个阶段:

⚪sum.c
int g_val = 2023;
void print(const char *str)
{
    printf("%s\n", str);
}

⚪test.c

#include <stdio.h>
int main()
{
    extern void print(char *str);
    extern int g_val;
    printf("%d\n", g_val);
    print("hello world\n");

    return 0;
}

解析图(VS2019):


2、执行环境

程序执行的过程:

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

int main()
{
    int a = 10;
    int b = 20;
    int ret = Add(a, b);
 
    return 0;
}


二、预处理详解

1、预处理符号

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

// __STDC__在VS2019下测试,为未定义。说明其并不遵守ANSI C标准
  • 这些预定义符号都是语言内置的。
#include <stdio.h>

int main()
{
    printf("%s\n", __FILE__);     // 返回使用行代码所在的源文件名,包括路径
    printf("%d\n", __LINE__);     // 返回文件当前的行号
    printf("%s\n", __DATE__);     // 返回程序被编译的日期
    printf("%s\n", __TIME__);     // 返回程序被编译的时间
    printf("%s\n", __FUNCTION__); // 返回所在函数的函数名

    return 0;
}


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

如果一个程序特别复杂,这时再去调试可能就会无从下手。所以需要代码在运行的过程中记录一些日志信息,通过日志信息分析程序哪块地方出了问题,再进行排查就相对容易很多。

#include <stdio.h>
 
int main()
{
    int i = 0;
    FILE* pf = fopen("test.txt", "a+"); // 追加的形式,每运行一次就追加
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
    for (i = 0; i < 5; i++)
    {
        printf("* 错误日志 ");
        printf("%d *\n", i+1);
        printf("发生时间:%s  %s\n", __DATE__, __TIME__);
        printf("具体位置:%s,函数名为%s,第%d行。\n", __FILE__, __FUNCTION__, __LINE__);
        printf("\n");
    }
    fclose(pf); 
    pf = NULL;
 
    return 0;
}


2、#define

(1)#define 定义标识符

// 语法:
#define name stuff
#include <stdio.h>

#define day 100

int main()
{
    int t = day;
    printf("%d\n", t);

    return 0;
}

 

在预处理阶段就会day 替换为 100。预处理结束后 int t = day 这里就没有 day 了,会变为 int t = 100。 

// 预处理前
int t = day;

// 预处理后
int t = 100;
#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__ )

int main()
{
    register int num = 0;
    reg int num = 0; // 这里reg就等于register


    do_forever // 预处理后替换为 for(;;); 
        ; // 循环体循环的是一条空语句
 
    do_forever; // 那么可以这么写,这个分号就是循环体,循环的是一个空语句


    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:
        // 最后一个case没有break
    }
 
    return 0;
}

#define 定义标识符时,为什么末尾没有加上分号?
#define day 100; //error
#define day 100

后果: 

#include <stdio.h>
 
#define day 100;
int main()
{
    int t = day; // int t = 100;;
 
    // int t = 100;
    // ;
 
    return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
 
#include <stdio.h>
 
#define day 100;
 
int main()
{
    int a, b;
    if (a > 10)
        b = day; // b = 100;;
    else // else不知道如何匹配了
        b = -day; // b = -100;;
 
    return 0;
}

结论:在 #define 定义标识符时,虽然语法支持,但是尽量不要在末尾加分号!


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

如果 SQUARE(3),预处理就会用 3 来替代 X*X 的内容,替换后为 3*3。

SQUARE (3+1) 的结果是什么?
#include <stdio.h>
 
#define SQUARE(X) X*X
 
int main()
{
    printf("%d\n", SQUARE(3+1));
 
    return 0;
}

 

这里将 3+1 替换成 X,那么 X 就是 3+1, 3+1*3+1, 根据优先级算得结果为 7。要看作为一个整体,完全替换。宏的参数是完成替换的,他不会提前完成计算,而是替换进去后再计算。替换是在预处理阶段时替换,表达式真正计算出结果是在运行时计算。 

// 以下为正确代码:
#include <stdio.h>
 
// 整体再括一个括号,更加严谨
#define SQUARE(X) ((X)*(X))
 
int main()
{
    printf("%d\n", SQUARE(3+1));
 
    return 0;
}

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

// correct
#define DOUBLE(X) ((X)+(X))
 
int main()
{
    printf("%d\n", 10 * DOUBLE(3+1));
 
    return 0;
}

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


3、#define 替换规则

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

注意

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

#define M 100

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


4、# 和 ##

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

#X 替换成参数所对应的字符串。

改进:可以打印其他类型的数字。

#include <stdio.h>
#define PRINT(X, FORMAT) printf("变量"#X"的值是 "FORMAT"\n", X);
 
int main()
{
    int a = 10;
    PRINT(a, "%d");
 
    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()
{
    int VS2019 = 100;
    printf("%d\n", CAT(VS, 2019)); // printf("%d\n", VS2019);
 
    return 0;
}

注意:## 也可以将多个符号合成一个符号,比如 X##Y##Z


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(a, b) ( (a) > (b) ? (a) : (b) ) 

int main()
{
    int x = 5;
    int y = 8;
    int z = MAX(x++, y++);
    //z = ( (x++) > (y++) ? (x++) : (y++));
    printf("x=%d y=%d z=%d\n", x, y, z);

    return 0;
}

结论:写宏的时候尽量避免使用这种带副作用的参数。

6、宏和函数对比

宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。
#define MAX(a, b) ((a)>(b)?(a):(b))
// 宏
#include <stdio.h>
 
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
 
int main()
{
    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()
{
    int a = 10;
    int b = 20;
    int m = Max(a, b);
    printf("%d\n", m);
 
    return 0;
}
为什么不用函数来完成这个任务?
  1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹
  2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的

和宏相比函数也有劣势的地方:

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

  • 宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
#include <stdio.h>
#include <stdlib.h>
 
#define MALLOC(num, type) (type*)malloc(num*sizeof(type))
 
int main()
{
    // 原本的写法:malloc(10*sizeof(int));
    // 修改后:malloc(10, int);
 
    int* p = MALLOC(10, int); // (int*)malloc(10*sizeof(int))
    
    return 0;    
}

宏和函数的对比:

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


7、命名约定

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

8、#undef

  • 这条指令用于移除一个宏定义。
#undef NAME 
// 如果现存的一个名字需要被重新定义,那么它的旧名字首先要被移除。
#define M 100

int main()
{
	int a = M;
#undef M
	printf("%d\n", M); //err:未定义标识符

	return 0;
}

9、命令行定义

  • 在编译的时候通过命令行的方式对其进行相关的定义,叫做命令行编译。
许多  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 -D ARRAY_SIZE=10 programe.c

10、条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
  • 调试性的代码,删除可惜,保留又碍事,所以我们可以选择性的编译。
#include <stdio.h>
 
#define __DEBUG__ // 就像一个开关一样
 
int main()
{
    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;
}

 

#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
*/

int main1()
{
#if 1 
	printf("hehe\n");
#endif
	return 0;
}


/*
2.
#if 常量表达式
	//...
#elif 常量表达式
	//...
#else
	//...
#endif
*/

int main2()
{
#if 1==1
	printf("hehe\n");
#elif 1==2
	printf("haha\n");
#else 
	printf("heihei\n");
#endif
	return 0;
}


/*
3.
3.1
#define name1
int main()
{
#ifdef name1 //如果name1被定义,下面的语句则参与编译
	//...
#endif
	return 0;
}

3.2
#define name2
int main()
{
#if defined(name2)//如果name2被定义,下面的语句则参与编译
	//...
#endif
	return 0;
}
*/


#define TEST
int main3()
{
#ifdef TEST
	prinntf("test\n");
#endif
	return 0;
}


/*
4.
4.1
int main()
{
#ifndef name1 //如果name1未定义,下面的语句则参与编译
	//...
#endif
	return 0;
}

4.2
int main()
{
#if !defined(name2)//如果name2未定义,下面的语句则参与编译
	//...
#endif
	return 0;
}
*/


int main4()
{
#ifndef HEHE
	printf("hehe\n");
#endif
	return 0;
}


/*
5.嵌套指令
#if define(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
*/

11、文件包含

  • #include 指令可以使另外一个文件被编译,就像它实际出现于 #include 指令的地方一样。
这种替换的方式很简单:
预处理器先删除这条指令,并用包含文件的内容替换。 这样一个源文件被包含 10 次,那就实际被编译10 次。
头文件被包含的方式:
  • 本地文件包含
#include "filename"

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

" " 的查找策略:

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。 如果找不到就提示编译错误。
linux 环境的标准头文件的路径:
/usr/include
VS 环境的标准头文件的路径:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include

库文件包含:

#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 // 让头文件即使被包含多次,也只包含一份
头文件中的 ifnde / define / endif 是干什么用的?

防止头文件被重复多次包含。

#include <filename.h> 和 #include "filename.h" 有什么区别?

尖括号(< >)是包含库里面的头文件的,双引号(" ")是包含自定义头文件的。它们在查找策略上不同,尖括号直接去库目录下查找。而井号双引号是现去自定义的代码路径下查找,如果找不到头文件,则在库函数的头文件目录下查找。

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

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

相关文章

如何让qt tableView每个item中个别字用不同颜色显示?

如何让qt tableView每个item中个别字用不同颜色显示&#xff1f; 从上面图片可以看到&#xff0c;Item为红色&#xff0c;数字5为黑色。 要实现在一个控件实现不同颜色&#xff0c;目前想到的只有QTextEdit 。有两种方法&#xff0c;第一种是代理&#xff0c;第二种是通过setI…

python+mysql+前后端分离国内职位数据分析(源码+文档+指导)

系统阐述的是使用国内python职位数据分析系统的设计与实现&#xff0c;对于Python、B/S结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了 Flask框架和MySql数据库技术搭建系统的整体…

Linux(实操篇二)

Linux实操篇 Linux(实操篇二)1. 常用基本命令1.3 时间日期类1.3.1 date显示当前时间1.3.2 显示非当前时间1.3.3 date设置系统时间1.3.4 cal查看日历 1.4 用户管理命令1.4.1 useradd添加新用户1.4.2 passwd设置用户密码1.4.3 id查看用户是否存在1.4.4 cat /etc/passwd 查看创建了…

【python】python智能停车场数据分析(代码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

TypeError: ‘set‘ object is not subscriptable

问题出现的背景&#xff1a;写了一个python脚本&#xff0c;在脚本里用到了 pyexcel_xlsx 这个包&#xff0c;这个包可以读取excel文件。在本地运行可以运行成功&#xff0c;在Linux服务器上面运行报这个错。两边python都是用到3.8版本的&#xff0c;pyexcel_xlsx 版本也相同…

2023-8-26 字符串哈希

题目链接&#xff1a;字符串哈希 #include <iostream>using namespace std;typedef unsigned long long ULL;const int N 100010, P 131;char str[N]; ULL h[N], p[N];ULL get(int l, int r) {return h[r] - h[l - 1] * p[r - l 1]; }int main() {int n, m;cin >…

3000-6000元优质全单吉他推荐,雅马哈LL16、卡马A1、VEAZEN费森S88和伊斯特曼E1D深度评测对比,哪一款会是你心目中的首选呢?

对于初学新手和进阶的朋友来说&#xff0c;可以入手一把性价比很不错的吉他当然是最理想的&#xff0c;对音质和手感有更高要求的&#xff0c;后期想要演出需求的&#xff0c;不妨考虑全单吉他。下面就给大家推荐四款市面上3000-6000元比较热门值得推荐的全单吉他系列品牌&…

分类预测 | MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测

分类预测 | MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测 目录 分类预测 | MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测…

从源码到原理剖析activity核心知识点

如何在onResume方法中获取到View的宽高&#xff1f; 有两种方式&#xff1a;post和addOnGlobalLayoutListener override fun onResume() {super.onResume()Log.e("onresume",tabBottom.width.toString()"--"tabBottom.height.toString())//view.post之所以…

Docker之私有仓库 RegistryHabor

目录 一、Docker私有仓库&#xff08;Registry&#xff09; 1.1 Registry的介绍 二、搭建本地私有仓库 2.1首先下载 registry 镜像 2.2在 daemon.json 文件中添加私有镜像仓库地址 2.3运行 registry 容器 2.4Docker容器的重启策略 2.5为镜像打标签 2.6上传到私有仓库 2…

SpringBoot+MyBatisPlus+MySql+vue2+elementUi的案例、java访问数据库服务、java提供接口服务

文章目录 前言后端关键代码前端关键代码完整代码 前言 1、项目不使用前后端分离。 2、在创建SpringBoot的时候要注意各个插件间的版本问题。 3、后端技术SpringBootMyBatisPlusMySql。 4、前端技术vue2elementUi。 后端关键代码 简单介绍 1、数据库名称ssm_db 2、表名称tbl_bo…

「MySQL-03」用户管理与给用户授权

目录 一、用户管理 1. 用户信息 2. 创建用户 3. 删除用户 4. 修改用户密码 二、给用户授权 0.MySQL数据库提供的权限列表 1. 给用户授权 2. 回收权限 一、用户管理 1. 用户信息 1.0 数据库mysql和user表 安装好 MySQL后&#xff0c;里面会有一个默认的数据库mysql里面有一个u…

保姆级 Keras 实现 Faster R-CNN 十

保姆级 Keras 实现 Faster R-CNN 十 一. 建议区域矩形二. 定义 ProposalLyaer1. __init__函数2. build 函数3. call 函数3.1 生成 anchor_box3.2 找出 anchor 处最大分数, 最大分数对应的 anchor_box 和修正参数3. 3 修正 anchor_box3.4 完成 call 函数 4. compute_output_shap…

高精度地图定位在高速公路自动驾驶系统中的应用

近年来随着汽车保有量不断增加&#xff0c;随之而来的是: ( 1) 严重的交通拥堵&#xff0c;通行效率低下&#xff0c;用在通行上的时间不断增加; ( 2) 交通事故频发&#xff0c;交通事故导致的伤亡人数和费用不断增加&#xff0c;而且绝大多数事故是由人为因素导致的; ( 3) 大气…

视频批量剪辑利器!轻松在固定的位置上添加str字幕,

在如今的数字时代&#xff0c;视频内容的制作和分享变得越来越普遍。如果你是一个视频创作者&#xff0c;或者经常需要编辑和分享视频内容&#xff0c;那么我们为你带来了一款视频批量剪辑工具&#xff0c;让你轻松在固定位置添加字幕&#xff0c;打造专业级剪辑效果&#xff0…

Markdown中的LaTeX公式详解

引言 LaTeX是一种用于排版科学和数学文档的排版系统&#xff0c;它能够以高质量的方式生成复杂的数学公式。在CSDN&#xff08;Cnblogs和CSDN&#xff09;这样的博客平台中&#xff0c;也支持使用LaTeX语法插入数学公式。本文将详细介绍在CSDN中使用LaTeX公式的方法和常用语法&…

开源代码扫描工具 Socket新增对 Go 生态系统的支持

导读继日前宣布完成 2000 万美元的 A 轮融资后&#xff0c;开源代码扫描工具 Socket 紧接着宣布新增了对 Go 语言的支持&#xff1b;此前其仅支持 JavaScript 和 Python 语言。 “在过去的几个月中&#xff0c;我们观察到针对 Golang 的供应链攻击有所增加。意识到这种迫在眉睫…

聚类分析 | MATLAB实现基于FCM模糊C均值聚类结果可视化

聚类分析 | MATLAB实现基于FCM模糊C均值聚类结果可视化 目录 聚类分析 | MATLAB实现基于FCM模糊C均值聚类结果可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 FCM模糊C均值聚类&#xff0c;聚类结果可视化&#xff0c;MATLAB程序。 FCM&#xff08;Fuzzy C-Means&a…

【KafkaStream】简单使用

Kafka Stream是什么 Kafka Streams是一套客户端类库&#xff0c;它可以对存储在Kafka内的数据进行流式处理和分析。 1. 什么是流处理 流处理平台&#xff08;Streaming Systems&#xff09;是处理无限数据集&#xff08;Unbounded Dataset&#xff09;的数据处理引擎&#x…

C++的静态栈以及有点鸡肋的array数组

目录 1.静态栈 1.举例展示 2.注意事项 2.array 1.静态栈 1.举例展示 1.我们想到栈&#xff0c;就会想到是一个数组来维护它的&#xff0c;并且一般由于不知道存储的多少内容&#xff0c;所以一般都是用动态数组不断的在堆上开辟新的空间。 但是C支持了一个新的语法就是静…