C语言宏定义详解

2023-05-16

宏定义引入

源程序在编译之前,会先进行预处理。

预处理并不是C语言编译器的组成部分,不能直接对它们进行编译。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对预处理之后的源程序进行编译处理,再经过链接得到可供执行的目标代码。

C 语言提供的预处理功能有三种,分别为宏定义、文件包含和条件编译。

宏定义在 C语言源程序中允许用一个标识符来表示一个字符串,称为“宏/宏体” ,被定义为“宏”的标识符称为“宏名”。

在预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去完全替换,这称为“宏替换”或“宏展开”。

宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。

在 C 语言中,宏分为有参数和无参数两种。无参宏的宏名后不带参数,其定义的一般形式为:

#define 标识符 字符串

#表示这是一条预处理命令(在C语言中凡是以#开头的均为预处理命令)

define:宏定义命令
标识符:所定义的宏名
字符串:可以是常数、表达式、格式串等

无参宏和有参宏示例:

// 不带参数的宏定义
#define MAX 10

/*带参宏定义*/
#define M(y) (((y)*(y))+(3*(y)))

/*宏调用*/
k=M(MAX);

注意宏不是语句,结尾不需要加“;”,否则会被替换进程序中,如:

#define N 10;               // 宏定义
int c[N];                   // 会被替换为: int c[10;]; 
//error:… main.c:133:11: Expected ']'

如果要写宏不止一行,则在结尾加反斜线符号使得多行能连接上,如:

#define HELLO "hello \
the world"

而且注意第二行要对齐,不要出现预期之外的空格,否则,如:

#define HELLO "hello the wo\
  rld"
printf("HELLO is %s\n", HELLO);
//输出结果为: HELLO is hello the wo  rld 

也就是行与行之间的空格也会被作为替换文本的一部分
而且由这个例子也可以看出:宏名如果出现在源程序中的“”内,则不会被当做宏来进行宏代换。

宏定义的优点:

方便程序的修改
使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时, 我们可以用较短的有意义的标识符来写程序,这样更方便一些。
相对于全局变量,两者的区别如下:
1、宏定义在编译前即会使用并替换,而全局变量要到运行时才可以。
2、宏定义的只是一段字符,在编译的时候被原封不动地替换到引用的位置。在运行中是没有宏定义的概念的。而变量在运行时要为其分配内存。
3、宏定义不可以被赋值,即其值一旦定义不可修改,而变量在运行过程中可以被修改。
4、宏定义只有在定义的所在文件中使用。 而全局变量可以在工程所有文件中使用,只要再使用前加一个声明就可以了。换句话说,宏定义不需要extern。

提高程序的运行效率

使用带参数的宏定义可完成函数调用的功能,又能减少系统开销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生函数调用时,需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。

宏定义的缺点:

1、由于是直接嵌入的,所以代码可能相对多一点;

2、嵌套定义过多可能会影响程序的可读性,而且很容易出错,不容易调试。

3、对带参的宏而言,由于是直接替换,并不会检查参数是否合法,存在安全隐患。

注意:如果宏体为空,就相当于用空字符来替换。但是,尽可能不要这么做,而且也没必要这么做。很多时候都会报错的。

什么时候用宏定义

宏函数,函数比较

从时间上来看

  • 宏只占编译时间,函数调用则占用运行时间(分配单元,保存现场,值传递,返回),每次执行都要载入,所以执行相对宏会较慢。
  • 使用宏次数多时,宏展开后源程序很长,因为每展开一次都使程序增长,但是执行起来比较快一点(这也不是绝对的,当有很多宏展开,目标文件很大,执行的时候运行时系统换页频繁,效率就会低下)。而函数调用不使源程序变长。

从安全上来看

  • 函数调用时,先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。
  • 函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。
  • 对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。
  • 宏的定义很容易产生二义性,如:定义#define S(a)  (a)*(a),代码S(a++),宏展开变成(a++)*(a++)这个大家都知道,在不同编译环境下会有不同结果。
  • 调用函数只可得到一个返回值,且有返回类型,而宏没有返回值和返回类型,但是用宏可以设法得到几个结果。
  • 函数体内有Bug,可以在函数体内打断点调试。如果宏体内有Bug,那么在执行的时候是不能对宏调试的,即不能深入到宏内部。
  • 附:C++中宏不能访问对象的私有成员,但是成员函数就可以。

宏函数的适用范围

  • 一般来说,用宏来代表简短的表达式比较合适。
  • 在考虑效率的时候,可以考虑使用宏,或者内联函数。
  • 还有一些任务根本无法用函数实现,但是用宏定义却很好实现。比如参数类型没法作为参数传递给函数,但是可以把参数类型传递给带参的宏。

什么是内联函数?
在C99中引入了内联函数(inline),联函数和宏的区别在于,宏是由预处理器对宏进行替代 ,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。

内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。

所以几乎可以这样认为:内联函数就是带了参数静态类型检查的宏。

宏使用注意事项

1、宏可以嵌套,但不参与运算:

#define M 5                 // 宏定义
#define MM M * M            // 宏的嵌套
printf("MM = %d\n", MM);    // MM 被替换为: MM = M * M, 然后又变成 MM = 5 * 5

宏代换的过程在上句已经结束,实际的 5 * 5 相乘过程则在编译阶段完成,而不是在预处理器工作阶段完成,所以宏不进行运算,它只是按照指令进行文字的替换操作。再强调下,宏进行简单的文本替换,无论替换文本中是常数、表达式或者字符串等,预处理程序都不做任何检查,如果出现错误,只能是被宏代换之后的程序在编译阶段发现。
 

2、宏定义必须写在函数之外,其作用域是 #define 开始,到源程序结束。如果要提前结束它的作用域则用 #undef命令,如:

#define M 5                 // 宏定义
printf("M = %d\n", M);      // 输出结果为: M = 5
#undef M              // 取消宏定义
printf("M = %d\n", M);      // error:… main.c:138:24: Use of undeclared identifier 'M'

一般情况下宏定义命令#define出现在函数的外部,宏名的有效范围是:从定义命令之后,到本文件结束。通常,宏定义命令放在文件开头处。但是是可以放在程序的函数内部的,只不过此时就违反了宏定义的初衷,而且只能在该函数内部使用。反正,就当做不能用在函数里就行,有的地方用也没错。

3、可以用宏定义表示数据类型,可以使代码简便(但不推荐):

#define STU struct Student      // 宏定义STU
struct Student{                 // 定义结构体Student
    char *name;
    int sNo;
};
STU stu = {"Jack", 20};         // 被替换为:struct Student stu = {"Jack", 20};
printf("name: %s, sNo: %d\n", stu.name, stu.sNo);

4、如果重复定义宏(应当避免),则不同的编译器采用不同的重定义策略。有的编译器认为这是错误的,有的则只是提示警告。Xcode中采用第二种方式。如:

#define M 5                 //宏定义
#define M 100               //重定义,warning:… main.c:26:9: 'M' macro redefined

5、建议给宏体和引用的每个参数加括号

不仅宏体是纯文本替换,宏参数也是纯文本替换。有以下一段简单的宏,实现乘法:

#define MULTIPLY(x, y) x * y

MULTIPLY(1, 2) 没问题,会正常展开成1 * 2 。有问题的是这种表达式MULTIPLY(1+2, 3) ,展开后成了==1+2 * 3 ,显然优先级错了。
对宏体和给引用的每个参数加括号,就能避免这问题。

#define MULTIPLY(x, y) ((x) * (y))

6、注意分号吞噬问题

有如下宏定义:

#define foo(x) bar(x); baz(x)

假设你这样调用:

if (!feral)
    foo(wolf);

这将被宏扩展为:

if (!feral)
    bar(wolf);
baz(wolf);

baz(wolf);,不在判断条件中,显而易见,这是错误。
如果用大括号将其包起来依然会有问题,例如

#define foo(x)  { bar(x); baz(x); }
if (!feral)
    foo(wolf);
else
    bin(wolf);

判断语言被扩展成:

if (!feral) {
    bar(wolf);
    baz(wolf);
};
else
    bin(wolf);

else将不会被执行,因为if语句后有个分号;表示判断语句就结束了,后面的else是无效代码。

通过do{…}while(0) 能够解决上述问题

#define foo(x)  do{ bar(x); baz(x); }while(0)
if (!feral)
    foo(wolf);
else
    bin(wolf);

被扩展成:

#define foo(x)  do{ bar(x); baz(x); }while(0)
if (!feral)
    do{ bar(x); baz(x); }while(0);
else
    bin(wolf);

使用do{…}while(0)构造后的宏定义不会受到大括号、分号等的影响,总是会按你期望的方式调用运行。这是一种形式上的解决方案,代码其实也只会执行一次,只是巧妙借用do…while的特性来将其包装成一整条语句。

7、对自身的递归引用

有如下宏定义:

#define foo (4 + foo)

按前面的理解,==(4 + foo)==会展开成==(4 + (4 + foo))==,然后一直展开下去,直至内存耗尽。但是,预处理器采取的策略是只展开一次。也就是说,foo只会展开成==4 + foo==,而展开之后foo的含义就要根据上下文来确定了。

对于以下的交叉引用,宏体也只会展开一次。

#define x (4 + y)
#define y (2 * x)

x展开成(4 + y) -> (4 + (2 * x)),y展开成(2 * x) -> (2 * (4 + y))。

注意,这是极不推荐的写法,程序可读性极差。

条件编译

假如现在要开发一个C语言程序,让它输出红色的文字,并且要求跨平台,在 Windows 和 Linux 下都能运行,怎么办呢?

这个程序的难点在于,不同平台下控制文字颜色的代码不一样,我们必须要能够识别出不同的平台

Windows 有专有的宏_WIN32,Linux 有专有的宏__linux__,以现有的知识,我们很容易就想到了 if else,请看下面的代码:

1

2

3

4

5

6

7

8

9

10

11

12

#include <stdio.h>

int main(){

    if(_WIN32){

        system("color 0c");

        printf("http://www.baidu.com\n");

    }else if(__linux__){

        printf("\033[22;31mhttp://www.baidu.com\n\033[22;30m");

    }else{

        printf("http://www.baidu.com\n");

    }

    return 0;

}

但这段代码是错误的,在 Windows 下提示 __linux__ 是未定义的标识符,在 Linux 下提示 _Win32 是未定义的标识符。对上面的代码进行改进:

1

2

3

4

5

6

7

8

9

10

11

12

#include <stdio.h>

int main(){

    #if _WIN32

        system("color 0c");

        printf("http://www.baidu.com\n");

    #elif __linux__

        printf("\033[22;31mhttp://www.baidu.com\n\033[22;30m");

    #else

        printf("http://www.baidu.com\n");

    #endif

    return 0;

}

#if、#elif、#else 和 #endif 都是预处理命令,整段代码的意思是:如果宏 _WIN32 的值为真,就保留第 4、5 行代码,删除第 7、9 行代码;如果宏 __linux__ 的值为真,就保留第 7 行代码;如果所有的宏都为假,就保留第 9 行代码。

这些操作都是在预处理阶段完成的,多余的代码以及所有的宏都不会参与编译,不仅保证了代码的正确性,还减小了编译后文件的体积

这种能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能

条件编译有多种形式,下面一一讲解。

#if 命令

#if 命令的完整格式为:

它的意思是:如常量“表达式1”的值为真(非0),就对“程序段1”进行编译,否则就计算“表达式2”,结果为真的话就对“程序段2”进行编译,为假的话就继续往下匹配,直到遇到值为真的表达式,或者遇到 #else。这一点和 if else 非常类似。

需要注意的是,#if 命令要求判断条件为“整型常量表达式”也就是说,表达式中不能包含变量,而且结果必须是整数;而 if 后面的表达式没有限制,只要符合语法就行。这是 #if 和 if 的一个重要区别。

#elif 和 #else 也可以省略,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

#include <stdio.h>

int main(){

    #if _WIN32

        printf("This is Windows!\n");

    #else

        printf("Unknown platform!\n");

    #endif

    

    #if __linux__

        printf("This is Linux!\n");

    #endif

    return 0;

}

#ifdef 命令

这个命令通常是结合宏定义来使用。

#ifdef 命令的格式为:

它的意思是,如果当前的宏已被定义过,则对“程序段1”进行编译,否则对“程序段2”进行编译

也可以省略 #else

注意,因为一个宏要么定义了要么没定义,只有两种状态,所以最多只有一个分支。

举例:

VS/VC 有两种编译模式,Debug 和 Release。

  • 在学习过程中,我们通常使用 Debug 模式,这样便于程序的调试;
  • 而最终发布的程序,要使用 Release 模式,这样编译器会进行很多优化,提高程序运行效率,删除冗余信息

为了能够清楚地看到当前程序的编译模式,我们不妨在程序中增加提示,请看下面的代码:

1

2

3

4

5

6

7

8

9

10

11

#include <stdio.h>

#include <stdlib.h>

int main(){

    #ifdef _DEBUG

        printf("正在使用 Debug 模式编译程序...\n");

    #else

        printf("正在使用 Release 模式编译程序...\n");

    #endif

    system("pause");

    return 0;

}

当以 Debug 模式编译程序时,宏 _DEBUG 会被定义,预处器会保留第 5 行代码,删除第 7 行代码。反之会删除第 5 行,保留第 7 行。  

#ifndef 命令

#ifndef 命令的格式为:

与 #ifdef 相比,仅仅是将 #ifdef 改为了 #ifndef。它的意思是,如果当前的宏未被定义,则对“程序段1”进行编译,否则对“程序段2”进行编译,这与 #ifdef 的功能正好相反

区别

最后需要注意的是,

  • #if 后面跟的是“整型常量表达式”,
  • 而 #ifdef 和 #ifndef 后面跟的只能是一个宏名,不能是其他的

例如,下面的形式只能用于 #if:

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

#define NUM 10

int main(){

    #if NUM == 10 || NUM == 20

        printf("NUM: %d\n", NUM);

    #else

        printf("NUM Error\n");

    #endif

    return 0;

}

运行结果:NUM: 10

再如,两个宏都存在时编译代码A,否则编译代码B:

1

2

3

4

5

6

7

8

9

10

11

12

13

#include <stdio.h>

#define NUM1 10

#define NUM2 20

int main(){

    #if (defined NUM1 && defined NUM2)

        //代码A

        printf("NUM1: %d, NUM2: %d\n", NUM1, NUM2);

    #else

        //代码B

        printf("Error\n");

    #endif

    return 0;

}

运行结果:NUM1: 10, NUM2: 20

#ifdef 可以认为是 #if defined 的缩写。 


#ifndef和#if !defined的区别

//---------------------------------------------------------------------------------------------------------------------
//第一种方式:使用ifndef
#ifndef __HEADFILE_H__
#define __HEADFILE_H__

……头文件内容

#endif
//---------------------------------------------------------------------------------------------------------------------
//第二种方式:使用if !defined
#if !defined(__HEADFILE_H__)
#define __HEADFILE_H__

……头文件内容

#endif
//---------------------------------------------------------------------------------------------------------------------

两种方式的效果是一样的,以前常用的是第一种方式,但是在使用类似如下逻辑表达的时候,使用第二种方式将更加方便。

#if !defined(HEADFILE1) && !defined(HEADFILE2)

#ifdef或者#ifndef都只能后接一个宏定义,如果有多个宏定义的逻辑表达式,就可以使用#if加上defined或者!defined的形式。

补充

1、预定义宏:


__FUNTION__  获取当前函数名 
__LINE__ 获取当前代码行号 
__FILE__ 获取当前文件名 
__DATE__ 获取当前日期 
__TIME__ 获取当前时间
__STDC_VERSION__

2、宏参数创建字符串:”#运算符”

在宏体中,如果宏参数前加个#,那么在宏体扩展的时候,宏参数会被扩展成字符串的形式。如:

#include <stdio.h>

#define PSQR(x) printf("the square of "#x" is %d.\n",((x)*(x)))
#define PSQR2(x) printf("the square of %s is %d.\n",#x,((x)*(x)))

int main() {
    int R=5;

    PSQR(R);  //the square of R is 25.
    PSQR2(R); // the square of R is 25.

    return 0;
}

3、预处理器的粘合剂:”##运算符”
和#运算符一样,##运算符可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如

#define XNAME(n) x ## n
int x1=10;
XNAME(1)+=1;  //x1 11

4、可变宏:… 和_VA_ARGS

有些函数(如==prinft() #06906d==)可以接受可变数量的参数。

int __cdecl printf(const char * __restrict__ _Format,...);

实现思想就是在宏定义中参数列表的最后一个参数作为省略号(三个句号)。这样,预定义宏_VA_ARGS就可以被用在替换部分中,以表明省略号代表什么。

例如

#define PR(...) printf(__VA_ARGS_)
PR("Howdy");
PR("weight=%d,shipping=$%.2f.\n",wt,sp)

宏定义中的空格问题

宏定义分为三个部分,宏定义指令,宏名,宏体

这三个部分是通过空格来识别以分隔的,三者之间至少需要1个空格才能被区分为三个部分,第二个部分,即宏名部分的有效部分是从第一个非空格字符开始,到遇到第一个空格字符结束;同理,宏体部分也是如此,以第一个非空格字符开始,直到遇到第一个空格字符作为结束。

也就是说,不管三者之间隔了多少个空格,都会被忽略,只认第一个非空格字符作为起始。

#define                 A                             123

比如以上宏定义,宏名就是A,宏体就是123,里面没有任何空格。

正因为如此,所以任意部分之间都不能存在空格,一旦存在空格,就等于不止三个部分了,此时,宏定义就会报错,比如:

#define                 A                             12       3  

这是就会报错了。所以,三部分都是一个不能以空格作为分隔的整体。

对比下typedef中的空格问题:

typedef在定义形式上和宏定义类似,所以,想看看这里的空格是什么样的情况。

首先能确定的是,正常情况下,typedef的三部分和宏定义是一样的:

typedef                      int                    int32;

这三部分不以空格作为分隔的整体,并且,三部分之间至少要有一个空格,多的空格被忽略。

但是有时候,typedef不止三个部分,比如:

这种情况仍然可行,看语句中的颜色,可以猜想到,对于类型来说,是作为一个整体来看待的,不管中间有多少空格,都会给缩到一个空格,按照unsigned short int类型来作为一个整体。

debug宏

可以用宏定义来实现条件编译
程序有DEBUG版本和RELEASE版本,区别就是编译时有无定义DEBUG宏。

程序调试的debug宏
程序调试的常见方案:单步调试、裸机LED调试、打印信息、log文件
1、利用调试器进行单步调试(譬如IDE中,Jlink)适用于新手,最大的好处就是直观,能够帮助找到问题。缺点是限制性大、速度慢。

2、裸机使用LED、蜂鸣器等硬件调试,适合单片机裸机程序。
3、printf函数打印调试,比较常用,好处是具有普遍性,几乎在所有的情况下都能用。
4、log文件(日志文件)是系统运行过程中在特定时候会打印一些调试信息,日志文件记录下来这些调试信息以供后续查找追查问题。适合于系统级或者大型程序的调试。

打印信息不能太多也不能太少,调试信息太少会不够信息找到问题所在。调试信息太多会有大量的无用的信息淹没有用信息,导致有用信息无法看见,等于没有。

调试(DEBUG)版本和发行(RELEASE)版本的区别
DEBUG版本就是包含了调试信息输出的版本,在程序测试过程中会发布debug版本,这种版本的程序运行时会打印出来调试信息/log文件,这些信息可以辅助测试人员判断程序的问题所在。DEBUG版本的坏处是输出调试信息占用了系统资源,拖慢了系统运行速度。因此DEBUG版本的性能低于RELEASE版本。


RELEASE版本就是最终的发布版本,相较于DEBUG版本的功能代码是一样的,但是去掉了所有的调试信息。适合最终测试通过要发布的程序,因为去掉了调试信息所以程序的运行效率要更高。


DEBUG和RELASE版本其实是一套源代码。源代码中是有很多的打印调试信息的语句的,如何来控制生成DEBUG和RELEEASE版本?靠条件编译,靠一个宏。

DEBUG宏大概的原理是:

#ifdef DEBUG
#define dbg() printf()
#else
#define dbg()    
#endif

工作方式是:如果我们要输出DEBUG版本则在条件编译语句前加上#define DEBUG即可,这样程序中的调试语句dbg()就会被替换成printf从而输出;如果我们要输出RELEASE版本则去掉#define DEBUG,则dbg()就会被替换成空,则程序中所有的dbg()语句直接蒸发了,这样的程序编译时就会生成没有任何调试信息的代码。

分析几个DEBUG宏
1、应用程序中DEBUG宏

#ifdef DEBUG
#define DBG(...) fprintf(stderr, " DBG(%s, %s(), %d): ", __FILE__, __FUNCTION__, __LINE__); fprintf(stderr, __VA_ARGS__)
#else
#define DBG(...)
#endif

注:__FILE__等是C语言中的预定义宏,就是说这个东西是个宏定义,但是是C语言自己定义的。这些宏具有特殊的含义,譬如__FILE__表示当前正在编译的c文件的文件名。

2、内核中的DEBUG宏

#ifdef DEBUG_S3C_MEM
#define DEBUG(fmt, args...)     printk(fmt, ##args)
#else
#define DEBUG(fmt, args...)     do {} while (0)
#endif
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言宏定义详解 的相关文章

  • 4. 消息队列

    消息队列 队列又称消息队列 xff0c 常用于任务间通信的数据结构 xff0c 可以在任务与任务之间 xff0c 中断与任务之间传递消息 xff0c 实现任务接收来自其他任务或中断的不固定长度的消息 任务可从消息队列中读取消消息 xff0c
  • 网卡接口

    网络接口 网络接口 xff08 以太网 xff09 是硬件接口 xff0c LwIP是软件 xff0c 并且网卡也是由很多种的 LwIP使用一个数据结构 xff0c nitif来描述网卡 用户提供最底层的接口函数 xff0c LwIP则提供
  • 系统扇区

    系统结构 DBR xff0c 主引导记录 xff1b DPT xff0c 分区表 xff1b DBR xff0c 分区引导扇区 引导扇区 MBR Main Boot Record xff0c 主引导扇区 xff0c 硬盘主引导记录区 xff
  • 华为3COM交换机配置命令详解

    1 配置文件相关命令 Quidway display current configuration 显示当前生效的配置 Quidway display saved configuration xff1b 显示flash中配置文件 xff0c
  • 分区结构

    分区结构 DBR xff0c 引导记录区 xff0c 包括一个引导程序和一个BPB xff0c 即本分区参数记录表 系统可直接访问的第一个扇区 xff0c 大小为512个字节 xff08 特殊情况占用其他保留扇区 xff09 512字节中
  • 1. 列表和列表项

    列表和列表项 列表 列表是一个数据结构 xff0c 用来追踪任务 xff0c 列表中有一个指针指向列表项 列表是一个结构体 xff0c 内部携带一个指针 xff0c 指针指向列表项 xff0c 列表项形成双向链式结构挂载在列表下 一个列表下
  • C#简单串口调试助手【傻瓜教程】chanson_chapter01

    简易串口调试助手制作教程 C Winform chanson chapter01 说明 xff1a 本教程基于win10 x64 位操作系统 xff0c 软件环境为Microsoft Visual Studio Community 2015
  • 快速上手Ubuntu之安装篇——安装win7,Ubuntu16.04双系统

    Linux可以说是开发者的系统 xff0c 对于开发者来说 xff0c Linux发行版不仅为我们提供了无数强大的开发工具 xff0c 还能帮助我们从源码上学习操作系统是工作的 而且经常在命令行上工作的我们还能在别人面前耍下酷 Ubuntu
  • SpringBoot中事件的使用

    项目中很多场景可以使用事件来对系统进行解耦 xff0c 让系统更便于维护和扩展 SpringBoot项目默认是支持事件的 xff0c 我们只需要自定义事件 xff0c 然后发布事件 xff0c 之后监听事件处理业务逻辑即可 SpringBo
  • STM32F4串口通信(UART)详解

    UART串口通信 我们在生活中处处都会接触到或者是用到通信 QQ 微信 电话 这些都是最常见的人与人之间远程无线通信的方式 那么也有像红外遥控 蓝牙数据传输等器件或硬件之间的通信 这些通信方式都有一些共同点 无线 易受干扰 信号变差 等 而
  • 高度自定义的简繁转换 VS2022 C++ 工程

    首先秀一下简繁转换结果图 如下 include lt iostream gt include 34 KOpenCC KOpenCCExport h 34 include lt string gt ifdef WIN64 pragma com
  • 关于 make_unique 和 make_shared

    C 43 43 14 才加入make unique xff0c 据说当时忘记实现了 那么C 43 43 11 可以自己实现这个功能 xff1a template lt typename T typename Args gt std uniq
  • 音视频OSD——修改叠加信息的尺寸

    目录 分析 原理 错误想法 思考 代码 c h 效果 分析 在实际场景中 可能出现字模尺寸使用不合理的情况 此时就需要对OSD叠加的信息进行相应的缩放 思路如下 放大 位图中每个点 在YUV图像中画四个点 即放大两倍 缩小 位图中取奇数行或
  • ABAP 常用的系统变量說明

    系统内部有一个专门存放系统变量的结构SYST 其中最常用的系统变量有 SY SUBRC 系统执行某指令后 表示执行成功与否的变量 0 表示成功 SY UNAME 当前使用者登入SAP的USERNAME SY DATUM 当前系统日期 SY
  • 第四次产业革命源于人工智能(趋势…

    第四次产业革命源于人工智能 xff08 趋势解读 20 k字 xff09 秦陇纪10汇编 第四次产业革命源于人工智能 xff08 趋势解读 20 k字 xff09 xff0c 附 数据简化DataSimp 技术描述 学会及社区1k字 欢迎关
  • 无人机开源吊舱+云台+AI芯片级解决方案 (回复“无人机AI方案”下载PDF资料)

    无人机开源吊舱 43 云台 43 AI芯片级解决方案 xff08 回复 无人机AI方案 下载PDF资料 xff09 原创 xff1a 云汉智能 科学Sciences 今天 科学 Sciences 导读 xff1a 无人机开源吊舱 43 云台
  • Ubuntu16.04 远程桌面连接(VNC)

    1 查看我的Ubuntu系统版本 2 客户机win10下载vnc viewer安装 xff1a 下载地址 xff1a https download csdn net download qq 28284093 10387251 点击打开链接
  • Mark一下YOLO检测和跟踪

    主要在看这个 YOLO算法的原理与实现 https blog csdn net xiaohu2022 article details 79211732 E7 AE 97 E6 B3 95 E7 9A 84tf E5 AE 9E E7 8E
  • ROS中的launch文件

    launch文件怎么写 首先 xff0c ROS中的launch文件是自己创建的 xff08 我在ROS包源文件中并没有发现 xff09 接下来是launch文件的编写 xff0c 需要注意 1 xff1a 开头是 lt launch gt

随机推荐

  • Git Submodule管理项目子模块

    使用场景 当项目越来越庞大之后 xff0c 不可避免的要拆分成多个子模块 xff0c 我们希望各个子模块有独立的版本管理 xff0c 并且由专门的人去维护 xff0c 这时候我们就要用到git的submodule功能 常用命令 git cl
  • Spring Boot结合easyExcel实现自增序号

    有些业务功能要求能导出序号 xff0c 现在提供两种实现方式 通过mysql查询实现 xff1b 实现RowWriteHandler接口 xff0c 自定义拦截器 xff1b 一 通过mysql查询实现 通过自定义变量实现每行数据 43 1
  • Spring Boot 实体里的List集合参数验证

    Spring Boot 实体里的List集合参数验证 Spring Boot 通过Hibernate Validator 加验证大家都知道 不知道的话可以参考这篇文章SpringBoot里参数校验 参数验证 今天讲一下Spring Boot
  • 树形结构工具类,如:菜单、部门等

    1 树节点 span class token keyword package span span class token namespace com span class token punctuation span zjson span
  • EasyPoiUtil导出工具

    span class token keyword package span span class token namespace com span class token punctuation span zjson span class
  • sap上传excel文档字符限制处理

    abap中有多个函数处理上传的excel文档中的数据 xff0c 记录数据的信息 xff08 行 列 值 xff09 如 xff0c 函数KCD EXCEL OLE TO INT CONVERT xff1a data lt data in
  • ffmpeg部署和springboot使用

    视频存储部署 一 环境安装 1 1 yasm安装 在编译ffmpeg时 xff0c 执行 configure xff0c 报出了如下错误 xff1a nasm yasm not found or too old Use disable x8
  • 【docker】安装clickhouse

    一 联网安装clickhouse 1 为了方便安装 xff0c 将clickhouse的工作目录和数据目录都在同一个目录下 xff0c 在home下创建目录 mkdir clickhouse cd clickhouse 创建日志 配置文件
  • springboot整合mybatis-plus、clickhouse、mysql多数据源

    springboot的多数据源有多种方式 xff0c 本文按照指定不同dao mapper xml的方式来实现访问不同的数据源 这样的好处是不用注解去切换数据源 1 引入驱动 span class token generics span c
  • linux中的oracle启动和关闭

    一 启动数据库实例 span class token number 1 span 切换到oracle用户 su span class token operator span oracle span class token number 2
  • BigDecimal计算工具类

    方便以后大家使用 span class token keyword import span span class token namespace java span class token punctuation span math spa
  • 安装、使用mongodb踩过的坑

    轻松一下 没用分布式架构之前 xff0c 你只有一个问题 xff1a 并发性能不足 用了分布式架构 xff0c 多出了一堆问题 xff1a 数据如何同步 主键如何产生 如何熔断 分布式事务如何处理 使用mongodb踩过的坑 今天对安装 x
  • JS使用总结:简单标签赋值和取值:span等;实时触发事件

    JS使用总结 简单标签赋值和取值 xff1a span等 xff1b 实时触发事件 xff1b 了解一下简单标签赋值和取值实时触发事件代码 了解一下 作为后台开发 xff0c 今天使用了前端的技术 xff0c 做一个总结 xff1b 包括两
  • FreeRtos的笔记 (一)

    FreeRtos的笔记 一 一 前言 linux是分时系统 和 RTOS时实操作系统区别 RTOS时实操作系统 当外界事件或数据产生时 xff0c 能够接受并以足够快的速度予以处理 实时操作系统是保证在一定时间限制内完成特定功能的操作系统
  • UCOSII操作系统(一)--基础知识

    前言 比较主流的操作系统有UCOSII FREERTOS LINUX等 参考书籍 xff1a 嵌入式实时操作系统 COS II原理及应用 嵌入式实时操作系统uCOS II 邵贝贝 第二版 1 操作系统的作用 xff1a 操作系统是计算机硬件
  • ORB SLAM2 编译&运行

    1 依赖安装 xff1a 1 xff09 安装 GLEW xff1a sudo apt get install libglew dev 2 xff09 安装 libuvc xff1a git clone https github com k
  • SAP 一句话入门之SD

    SD是Sales and Distribution的简称 在SAP系统中 xff0c 销售与分销模块处在供应链下游 xff0c 关注从客户订单到向客户收款的全过程 SD模块中的Sales好理解 xff0c 而Distribution却容易被
  • realsense-ros安装

    一 realsense ros安装 1 Create a catkin workspace mkdir p realsense rosws src cd realsense rosws src catkin init workspace 2
  • thinkphp页面请求时间超过40S报404错误解决办法

    最近在写一个thinkphp项目的时候 xff0c 发现Ajax从后端请求数据时间比较长 xff0c 大概需要45秒左右 xff0c 但是一旦请求时间超过40s xff0c 页面就会超时404了 xff0c 一开始以为是ajax请求时间不能
  • C语言宏定义详解

    宏定义引入 源程序在编译之前 xff0c 会先进行预处理 预处理并不是C语言编译器的组成部分 xff0c 不能直接对它们进行编译 经过预处理后 xff0c 程序就不再包括预处理命令了 xff0c 最后再由编译程序对预处理之后的源程序进行编译