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

从Linux内核中学习高级C语言宏技巧

Linux内核可谓是集C语言大成者,从中我们可以学到非常多的技巧,本文来学习一下宏技巧,文章有点长,但耐心看完后C语言level直接飙升。

本文出自:大叔的嵌入式小站,一个简单的嵌入式/单片机学习、交流小站

从Linux内核中学习高级C语言宏技巧

1.用do{}while(0)把宏包起来

#define init_hashtable_nodes(p, b)  do {      \  int _i;              \  hash_init((p)->htable##b);        \  ...略去          \} while (0)

Linux中常见如上定义宏的形式,我们都知道do{}while(0)只执行一次,那么这个有什么意义呢?

我们写一个更简单的宏,来看看

#define fun(x) fun1(x);fun2(x);

则在这样的语句中:

if(a)  fun(a);

被展开为

if(a)  fun1(x);fun2(x);;

fun2(x)将不会执行!有同学会想,加个花括号

#define fun(x) {fun1(x);fun2(x);}

则在这样的语句中

if (a)  fun(a);else  fun3(a);

被展开为

if (a)  {fun1(x);fun2(x);};else  fun3(a);

注意}后还有个;这将会出现语法错误

但是假如我们写成

#define fun(x) do{fun1(x);fun2(x);}while(0)

则完美避免上述问题!

2.获取数组元素个数

写一个获取数组中元素个数的宏怎么写?显然用sizeof

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(*arr))

可以用,但这样是存在问题的,先看个例子

#include<stdio.h>int a[3] = {1,3,5};int fun(int c[]){  printf("fun1 a= %d\n",sizeof(c));}int main(void){  printf("a= %d\n",sizeof(a));  fun(a);  return 0;}

输出:

a = 12;b = 8;//32位电脑为4

为什么?因为数组名和指针不是完全一样的,函数参数中的数组名在函数内部会降为指针!sizeof(a),在函数中实际上变成了sizeof(int *)。

上面的宏存在的问题也就清楚了,这是一个非常重大,且容易忽略的bug!

让我们看看,内核中怎么写:

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

(arr)[0]是0长数组,不占用内存,GNU C支持0长数组,在某些编译器下可能会出错。(不过不是因为这个来避开上面的问题)

sizeof(arr) / sizeof((arr)[0]很好理解数组大小除去元素类型大小即是元素个数,真正的精髓在于后面__must_be_array(arr)宏

#define __must_be_array(a)  BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

先看内部的__same_type,它也是个宏

# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))

__builtin_types_compatible_p 是gcc内联函数,在内核源码中找不到定义也无需包含头文件,在代码中也可以直接使用这个函数。(只要是用gcc编译器来编译即可使用,不用管这个,只需知道:

当 a 和 b 是同一种数据类型时,此函数返回 1。

当 a 和 b 是不同的数据类型时,此函数返回 0。

再看外部的(精髓来了

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))

上来就是个小技巧:!!(e)是将e转换为0或1,加个-号即将e转换为0或-1。

再用到了位域:

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。这时候可以用位域

struct struct_a{  char a:3;  char b:3;  char c;};

a占用3位,b占用3位,如上结构体只占用2字节,位域可以为无位域名,这时它只用来作填充或调整位置,不能使用,如:

struct struct_a{  char a:3;  char  :3;  char c;};

当位数为负数时编译无法通过!

当a为数组时,__same_type((a), &(a)[0]),&(a)[0]是个指针,两者类型不同,返回0,即e为0,-!!(e)为0,sizeof(struct { int:0; })为0,编译通过且不影响最终值。

当a为指针时,__same_type((a), &(a)[0]),两者类型相同,返回1,即e为1,-!!(e)为-1,无法编译。

3.求两个数中最大值的宏MAX

思考这个问题,你会怎么写

3.1一般的同学:

#define MAX(a,b) a > b ? a : b

存在问题,例子如下:

#include<stdio.h>#define MAX(x,y) x > y ? x: yint main(void){  int i = 14;  int j = 3;  printf ("i&0b101 = %d\n",i&0b101);  printf ("j&0b101 = %d\n",j&0b101);  printf("max=%d\n",MAX(i&0b101,j&0b101));  return 0;}

输出:

i&0b101 = 4j&0b101 = 1max=1

明显不对,因为>运算符优先级大于&,所以会先进行比较再进行按位与。

3.2稍好的同学:

#define MAX(a,b) (a) > (b) ? (a) : (b)

存在问题,例子如下:

#define MAX(x,y) (x) > (y) ? (x) : (y)int main(void){  printf("max=%d",3 + MAX(1,2));  return 0;}

输出:

max = 1

同样是优先级问题+优先级大于>。

附优先级表:同一优先级的运算符,运算次序由结合方向所决定。

优先级

运算符

名称或含义

使用形式

结合方向

1

[]

数组元素下标

数组名[常量表达式]

左到右

()

圆括号、函数参数表

(表达式)/函数名(形参表)

.

成员选择(对象)

对象.成员名

->

成员选择(指针)

对象指针->成员名

2

-

负号运算符

-表达式

右到左

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

sizeof

长度运算符

sizeof(表达式)

3

/

表达式 / 表达式

左到右

*

表达式 * 表达式

%

余数(取模)

整型表达式 % 整型表达式

4

+

表达式 + 表达式

左到右

-

表达式 - 表达式

5

<< 

左移

变量 << 表达式

左到右

>> 

右移

变量 >> 表达式

6

大于

表达式 > 表达式

左到右

>=

大于等于

表达式 >= 表达式

小于

表达式 < 表达式

<=

小于等于

表达式 <= 表达式

7

==

等于

表达式 == 表达式

左到右

!=

不等于

表达式 != 表达式

8

&

按位与

表达式 & 表达式

左到右

9

^

按位异或

表达式 ^ 表达式

左到右

10

|

按位或

表达式 | 表达式

左到右

11

&&

逻辑与

表达式 && 表达式

左到右

12

||

逻辑或

表达式 || 表达式

左到右

13

?:

条件运算符

表达式1? 表达式2: 表达式3

右到左

14

=

赋值运算符

变量 = 表达式

右到左

/=

除后赋值

变量 /= 表达式

*=

乘后赋值

变量 *= 表达式

%=

取模后赋值

变量 %= 表达式

+=

加后赋值

变量 += 表达式

-=

减后赋值

变量 -= 表达式

<<=

左移后赋值

变量 <<= 表达式

>>=

右移后赋值

变量 >>= 表达式

&=

按位与后赋值

变量 &= 表达式

^=

按位异或后赋值

变量 ^= 表达式

|=

按位或后赋值

变量 |= 表达式

15

逗号运算符

表达式, 表达式, …

左到右

3.3良好的同学

#define MAX(a,b) ((a) > (b) ? (a) : (b))

避免了前两个出现的问题,但同样还有问题存在:

#include<stdio.h>#define MAX(x,y) ((x) > (y) ? (x): (y))int main(void){  int i = 2;  int j = 3;  printf("max=%d\n",MAX(i++,j++));  printf("i=%d\n",i);  printf("j=%d\n",j);  return 0;}

期望结果:

max=3,i=3,j=4

实际结果

max=4,i=3,j=5

尽管用括号避免了优先级问题,但这个例子中的j++实际上运行了两次。

3.4Linux内核中的写法

#define MAX(x, y) ({        \  typeof(x) _max1 = (x);      \  typeof(y) _max2 = (y);      \  (void) (&_max1 == &_max2);    \  _max1 > _max2 ? _max1 : _max2; })

下面进行详解。

3.4.1.GNU C中的语句表达式

表达式就是由一系列操作符和操作数构成的式子。 例如三面三个表达式

a+bi=a*2a++

表达式加上一个分号就构成了语句,例如,下面三条语句:

a+b;i=a*2;a++;

A compound statement enclosed in parentheses may appear as an expression in GNU C.

——《Using the GNU Compiler Collection》6.1 Statements and Declarations in Expressions

GNU C允许在表达式中有复合语句,称为语句表达式:

({表达式1;表达式2;表达式3;...})

语句表达式内部可以有局部变量,语句表达式的值为内部最后一个表达式的值。

例子:

int main(){  int y;  y = ({ int a =3; int b = 4;a+b;});  printf("y = %d\n",y);  return 0;}

输出:y = 7。

这个扩展使得宏构造更加安全可靠,我们可以写出这样的程序:

#define max(x, y) ({        \  int _max1 = (x);      \  int _max2 = (y);      \  _max1 > _max2 ? _max1 : _max2; })int main(void){  int i = 2;  int j = 3;  printf("max=%d\n",max(i++,j++));  printf("i=%d\n",i);  printf("j=%d\n",j);  return 0;}

但这个宏还有个缺点,只能比较int型变量,改进一下:

#define max(type,x, y) ({        \  type _max1 = (x);      \  type _max2 = (y);      \  _max1 > _max2 ? _max1 : _max2; })

但这需要传入type,还不够好。

3.4.2 typeof关键字

GNU C 扩展了一个关键字 typeof,用来获取一个变量或表达式的类型。

例子:

int a;typeof(a) b = 1;typeof(int *) a;int f();typeof(f()) i;

于是就有了

#define max(x, y) ({        \  typeof(x) _max1 = (x);      \  typeof(y) _max2 = (y);      \  _max1 > _max2 ? _max1 : _max2; })

3.4.3真正的精髓

对比一下,内核的写法:

#define max(x, y) ({        \  typeof(x) _max1 = (x);      \  typeof(y) _max2 = (y);      \  (void) (&_max1 == &_max2);    \  _max1 > _max2 ? _max1 : _max2; })

发现比我们的还多了一句

(void) (&_max1 == &_max2);

这才是真正的精髓,对于不同类型的指针比较,编译器会给一个警告:

warning:comparison of distinct pointer types lacks a cast

提示两种数据类型不同。

至于加void是因为当两个值比较,比较的结果没有用到,有些编译器可能会给出一个警告,加(void)后,就可以消除这个警告。

4.通过成员获取结构体地址的宏container_of

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({      \  const typeof(((type *)0)->member) *__mptr = (ptr);  \  (type *)((char *)__mptr - offsetof(type, member));  \})

4.1作用

我们传给某个函数的参数是某个结构体的成员,但是在函数中要用到此结构体的其它成员变量,这时就需要使用这个宏:container_of(ptr, type, member)

ptr为已知结构体成员的指针,type为结构体名字,member为已知成员名字,例子:

struct struct_a{  int a;  int b;};int fun1 (int *pa){  struct struct_a *ps_a;  ps_a = container_of(pa,struct struct_a,a);  ps_a->b = 8;}int main(void){  float f = 10;  struct struct_a s_a ={2,3};  fun1(&s_a.a);  printf("s_a.b = %d\n",s_a.b);  return 0;}

输出:s_a.b=8。

本例子中通过struct_a结构体中的a成员地址获取到了结构体地址,进而对结构体中的另一成员b进行了赋值。

4.2详解

首先来看:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这个是获取在结构体TYPE中,MEMBER成员的偏移位置。

定义一个结构体变量时,编译器会按照结构体中各个成员的顺序,在内存中分配一片连续的空间来存储。例子:

#include<stdio.h>struct struct_a{  int a;  int b;  int c;};int main(void){  struct struct_a s_a ={2,3,6};  printf("s_a   addr = %p\n",&s_a);  printf("s_a.a addr = %p\n",&s_a.a);  printf("s_a.b addr = %p\n",&s_a.b);  printf("s_a.c addr = %p\n",&s_a.c);  return 0;}

输出

s_a   addr = 0x7fff2357896cs_a.a addr = 0x7fff2357896cs_a.b addr = 0x7fff23578970s_a.c addr = 0x7fff23578974

结构体的地址也就是第一个成员的地址,每一个成员的地址可以看作是对首地址的偏移,上面例子中,a就是首地址偏移0,b就是首地址偏移4字节,c就是首地址偏移8字节。

我们知道C语言中指针的内容其实就是地址,我们也可以把某个地址强制转换为某种类型的指针,(TYPE *)0)即将地址0,通过强制类型转换,转换为一个指向结构体类型为 TYPE的常量指针。

&((TYPE *)0)->MEMBER自然就是MEMBER成员对首地址的偏移量了。

而(size_t)是内核定义的数据类型,在32位机上就是unsigned int,64位就是unsiged long int,就是强制转换为无符号整型数。

再来看:

#define container_of(ptr, type, member) ({      \  const typeof(((type *)0)->member) *__mptr = (ptr);  \  (type *)((char *)__mptr - offsetof(type, member));  \})

第一句(其实这句才是精华)

const typeof(((type *)0)->member) *__mptr = (ptr);  \

typeof在前面讲过了,获取类型,这句作用是利用赋值来确保你传入的ptr指针和member成员是同一类型,不然就会出现警告。

第二句

 (type *)((char *)__mptr - offsetof(type, member));  \

有了前面的讲解,应该就很容易理解了,成员的地址减去偏移不就是首地址吗,为什么要加个(char *)强制类型转换?

因为offsetof(type, member)的结果是偏移的字节数,而指针运算,(char *)-1是减去一个字节,(int *)-1就是减去四个字节了。

最外面的 (type *),即把这个值强制转换为结构体指针。

5.#与变参宏

5.1#和##

#运算符,可以把宏参数转换为字符串,例子

#include <stdio.h>#define PSQR(x) printf("The square of " #x " is %d.\n",((x)*(x)))int main(void){    int y = 5;     PSQR(y);    PSQR(2 + 4);     return 0;}
输出:
The square of y is 25.The square of 2 + 4 is 36.

##运算符,可以把两个参数组合成一个。例子:

#include <stdio.h>#define PRINT_XN(n) printf("x" #n " = %d\n", x ## n);int main(void){    int x1 = 2;    int x2 = 3;    PRINT_XN(1);        // becomes printf("x1 = %d\n", x1);    PRINT_XN(2);        // becomes printf("x2 = %d\n", x2);    return 0;}
该程序的输出如下:
x1 = 2x2 = 3

5.2变参宏

我们都知道printf接受可变参数,C99后宏定义也可以使用可变参数。C99 标准新增加的一个 __VA_ARGS__ 预定义标识符来表示变参列表,例子:

#define DEBUG(...) printf(__VA_ARGS__)int main(void){  DEBUG("Hello %s\n","World!");  return 0;}

但是这个在使用时,可能还有点问题比如这种写法:

#define DEBUG(fmt,...) printf(fmt,__VA_ARGS__)int main(void){  DEBUG("Hello World!");  return 0;}

展开后

printf("Hello World!",);

多了个逗号,编译无法通过,这时,只要在标识符 __VA_ARGS__ 前面加上宏连接符 ##,当变参列表非空时,## 的作用是连接 fmt,和变参列表宏正常使用;当变参列表为空时,## 会将固定参数 fmt 后面的逗号删除掉,这样宏也就可以正常使用了,即改成这样:

#define DEBUG(fmt,...) printf(fmt,##__VA_ARGS__)

除了这些,其实Linux内核中还有很多宏和函数写得非常精妙。Linux内核越看越有味道,看内核源码,很多时候都会不明所以,但看明白后又醍醐灌顶,又感慨人外有人!

 本文出自:大叔的嵌入式小站,一个简单的嵌入式/单片机学习、交流小站

从Linux内核中学习高级C语言宏技巧

相关文章:

从Linux内核中学习高级C语言宏技巧

Linux内核可谓是集C语言大成者&#xff0c;从中我们可以学到非常多的技巧&#xff0c;本文来学习一下宏技巧&#xff0c;文章有点长&#xff0c;但耐心看完后C语言level直接飙升。 本文出自&#xff1a;大叔的嵌入式小站&#xff0c;一个简单的嵌入式/单片机学习、交流小站 从…...

详解Python的装饰器

Python中的装饰器是你进入Python大门的一道坎&#xff0c;不管你跨不跨过去它都在那里。 为什么需要装饰器 我们假设你的程序实现了say_hello()和say_goodbye()两个函数。 def say_hello():print "hello!"def say_goodbye():print "hello!" # bug hereif…...

k8s-Pod域名学习总结

k8s-Pod域名学习总结 大纲 k8s内置DNS服务 配置Pod的域名服务 CornDNS配置 默认Pod的域名 自定义Pod的域名 实战需求 1 Pod有自己的域名 2 集群内部的Pod可以通过域名访问其他的Pod 基础准备&#xff1a; 1 k8s 集群版本1.17 k8s内置DNS服务 k8s1.17安装完成后自动创建…...

0405习题总结-不定积分

文章目录1 不定积分的基本概念2 直接积分法-基本积分公式3 第一换元法-凑微分形式法4 第二类换元法5 分部积分求不定积分6 表格法积分7 有理函数求积分后记1 不定积分的基本概念 例1 f(x){x1,x≥012e−x12,x<0求∫f(x)dxf(x) \begin{cases} x1,\quad x\ge0\\ \frac{1}{2}e^…...

QT 常用控件类型命名参考

拟定的QT的控件命名规则&#xff1a;蛇形命名方式 控件类型开头&#xff0c;以下是QT控件类型命名的参考范例 Buttons Buttons起始字符串对象名称举例Push Buttonbuttonbutton_loginTool Buttontool_button / buttonbutton_switchRadio Buttonradio_button / radioradio_boy…...

MATLAB与图像处理的那点小事儿~

目录 一、学习内容 二、matlab基本知识 三、线性点运算 四、非线性点运算&#xff0c;伽马矫正 五、直方图 1、直方图均衡化 &#xff08;1&#xff09;使用histep函数实现图像均衡化 &#xff08;2&#xff09;使用自行编写的均衡化函数实现图像均衡化 2、直方图规定…...

第十四届蓝桥杯模拟赛(第三期)Java组个人题解

第十四届蓝桥杯模拟赛&#xff08;第三期&#xff09;Java组个人题解 今天做了一下第三期的校内模拟赛&#xff0c;有些地方不确定&#xff0c;欢迎讨论和指正~ 文章目录第十四届蓝桥杯模拟赛&#xff08;第三期&#xff09;Java组个人题解填空题部分第一题【最小数】第二题【E…...

Go语言之条件判断循环语句(if-else、switch-case、for、goto、break、continue)

一、if-else条件判断语句 Go中的if-else条件判断语句跟C差不多。但是需要注意的是&#xff0c;Go中强制规定&#xff0c;关键字if和else之后的左边的花括号"{“必须和关键字在同一行&#xff0c;若使用了else if结构&#xff0c;则前段代码快的右花括号”}"必须和关…...

深入理解AQS

概念设计初衷&#xff1a;该类利用 状态队列 实现了一个同步器&#xff0c;更多的是提供一些模板方法&#xff08;子类必须重写&#xff0c;不然会抛错&#xff09;。 设计功能&#xff1a;独占、共享模式两个核心&#xff0c;state、Queue2.1 statesetState、compareAndSetSta…...

JVM学习笔记十:执行引擎

0. 前言 声明&#xff1a; 感谢尚硅谷宋红康老师的讲授。 感谢广大网友共享的笔记内容。 B站&#xff1a;https://www.bilibili.com/video/BV1PJ411n7xZ 本文的内容基本来源于宋老师的课件&#xff0c;其中有一些其他同学共享的内容&#xff0c;也有一些自己的理解内容。 1. …...

【2023-03-10】JS逆向之美团滑块

提示&#xff1a;文章仅供参考&#xff0c;禁止用于非法途径 前言 目标网站:aHR0cHM6Ly9wYXNzcG9ydC5tZWl0dWFuLmNvbS9hY2NvdW50L3VuaXRpdmVsb2dpbg 页面分析 接口流程 1.https://passport.meituan.com/account/unitivelogin主页接口&#xff1a;需获取下面的参数&#xff0…...

全志V853芯片放开快启方案打印及在快起方式下配置isp led的方法

全志V85x芯片 如何放开快启方案的打印&#xff1f; 1.主题 如何放开快启方案的打印 2.问题背景 产品&#xff1a;v851系列快启方案 软件&#xff1a;tina 其他&#xff1a;特有版本信息添加自由描述 &#xff08;如固件版本&#xff0c;复现概率&#xff0c;特定环境&#x…...

大数据 | (一)Hadoop伪分布式安装

大数据原理与应用教材链接&#xff1a;大数据技术原理与应用电子课件-林子雨编著 Hadoop伪分布式安装借鉴文章&#xff1a;Hadoop伪分布式安装-比课本详细 大数据 | &#xff08;二&#xff09;SSH连接报错Permission denied&#xff1a;SSH连接报错Permission denied 哈喽&a…...

Django/Vue实现在线考试系统-06-开发环境搭建-Django安装

1.0 基本介绍 Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。 使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(…...

KaiwuDB 时序引擎数据存储内存对齐技术解读

一、理论1、什么是内存对齐现代计算机中内存空间都是按照 byte 划分的&#xff0c;在计算机中访问一个变量需要访问它的内存地址&#xff0c;从理论上看&#xff0c;似乎对任何类型的变量的访问都可以从任何地址开始。但在实际情况中&#xff0c;通常在特定的内存地址才能访问特…...

IR 808 Alkyne,IR-808 alkyne,IR 808炔烃,近红外吲哚类花菁染料

【产品理化指标】&#xff1a;中文名&#xff1a;IR-808炔烃英文名&#xff1a;IR-808 alkyne&#xff0c;Alkyne 808-IR CAS号&#xff1a;N/AIR-808结构式&#xff1a;规格包装&#xff1a;10mg&#xff0c;25mg&#xff0c;50mg&#xff0c;接受各种复杂PEGS定制服务&#x…...

elasticsearch

这里写目录标题1.初识ElasticSearch1.1 了解ES1.2 倒排索引1.2.1 正向索引1.2.2 倒排索引1.2.3 正向和倒排1.3 ES的一些概念1.3.1 文档和字段1.3.2 索引和映射1.3.3 mysql和elasticsearch1.4 安装ES、kibana1.初识ElasticSearch 1.1 了解ES elasticsearch是一款非常强大的开源…...

并发编程---java锁

java锁一 多线程锁synchronized案例分析1.1synchronized介绍1.2 synchronized案例分析1.2.1.标准访问&#xff0c;请问先打印邮件还是短信&#xff1f;1.2.2.邮件⽅法暂停4秒钟&#xff0c;请问先打印邮件还是短信&#xff1f;分析1.2.3.新增⼀个普通⽅法hello&#xff08;&…...

品牌营销 | 学习如何最大限度地发挥品牌营销的作用

您是否想过如何最大限度地发挥品牌营销的潜力&#xff1f;这是一项艰巨的挑战&#xff0c;通过了解品牌营销的基本组成部分&#xff0c;您可以成功地推广您的品牌。 &#xff08;图源&#xff1a;Pixabay&#xff09; 品牌营销的基本组成部分 你需要做什么来发展稳固的品牌&am…...

Linux驱动的同步阻塞和同步非阻塞

在字符设备驱动中&#xff0c;若要求应用与驱动同步&#xff0c;则在驱动程序中可以根据情况实现为阻塞或非阻塞一、同步阻塞这种操作会阻塞应用程序直到设备完成read/write操作或者返回一个错误码。在应用程序阻塞这段时间&#xff0c;程序所代表的进程并不消耗CPU的时间&…...

LearnOpenGL-光照-5.投光物

本人刚学OpenGL不久且自学&#xff0c;文中定有代码、术语等错误&#xff0c;欢迎指正 我写的项目地址&#xff1a;https://github.com/liujianjie/LearnOpenGLProject 文章目录投光物平行光点光源聚光不平滑的例子平滑例子投光物 前面几节使用的光照都来自于空间中的一个点 即…...

【C语言】每日刷题 —— 牛客语法篇(1)

前言 大家好&#xff0c;今天带来一篇新的专栏c_牛客&#xff0c;不出意外的话每天更新十道题&#xff0c;难度也是从易到难&#xff0c;自己复习的同时也希望能帮助到大家&#xff0c;题目答案会根据我所学到的知识提供最优解。 &#x1f3e1;个人主页&#xff1a;悲伤的猪大…...

【深度学习】Subword Tokenization算法

在自然语言处理中&#xff0c;面临的首要问题是如何让模型认识我们的文本信息&#xff0c;词&#xff0c;是自然语言处理中基本单位&#xff0c;神经网络模型的训练和预测都需要借助词表来对句子进行表示。 1.构建词表的传统方法 在字词模型问世之前&#xff0c;做自然语言处理…...

五分钟了解支付、交易、清算、银行等专业名词的含义?

五分钟了解支付、交易、清算、银行等专业名词的含义&#xff1f;1. 支付类名词01 支付应用02 支付场景03 交易类型04 支付类型&#xff08;按通道类型&#xff09;05 支付类型&#xff08;按业务双方类型&#xff09;06 支付方式07 支付产品08 收银台类型09 支付通道10 通道类型…...

4个工具,让 ChatGPT 如虎添翼!

LightGBM中文文档 机器学习统计学,476页 机器学习圣经PRML中文版...

初识PO、VO、DAO、BO、DTO、POJO时

PO、VO、DAO、BO、DTO、POJO 区别分层领域模型规约DO(Data Object)DTO(Data Transfer Object)BO(Business Object)AO(ApplicationObject)VO(View Object)Query领域模型命名规约&#xff1a;一、PO :(persistant object )&#xff0c;持久对象二、VO :(value object) &#xff0…...

[2.2.4]进程管理——FCFS、SJF、HRRN调度算法

文章目录第二章 进程管理FCFS、SJF、HRRN调度算法&#xff08;一&#xff09;先来先服务&#xff08;FCFS, First Come First Serve&#xff09;&#xff08;二&#xff09;短作业优先&#xff08;SJF, Shortest Job First&#xff09;对FCFS和SJF两种算法的思考&#xff08;三…...

【代码随想录Day55】动态规划

583 两个字符串的删除操作 https://leetcode.cn/problems/delete-operation-for-two-strings/72 编辑距离https://leetcode.cn/problems/edit-distance/...

Java开发 - 消息队列前瞻

前言 学完了Redis&#xff0c;那你一定不能错过消息队列&#xff0c;要说他俩之间的关联&#xff1f;关联是有的&#xff0c;但也不见得很大&#xff0c;只是他们都是大数据领域常用的一种工具&#xff0c;一种用来提高程序运行效率的工具。常见于高并发&#xff0c;大数据&am…...

MySQL连接IDEA详细教程

使用IDEA的时候&#xff0c;需要连接Database&#xff0c;连接时遇到了一些小问题&#xff0c;下面记录一下操作流程以及遇到的问题的解决方法。 目录 MySQL连接IDEA详细教程 MySQL连接IDEA详细教程 打开idea&#xff0c;点击右侧的 Database 或者 选择 View --> Tool Wind…...

wordpress 添加百度统计/搜索引擎优化排名

建筑模型设计制作实践室带锯、圆盘锯安全操作规程一、开机前1、检查锯齿是否变钝&#xff0c;固定锯片的螺丝是否坚固&#xff0c;锯条是否绷紧适中。2、接通电源&#xff0c;检查转动方向是否正确。3、根据锯切需要&#xff0c;调整锯条、档板等至合适位置&#xff0c;做好锯切…...

凡科怎么建站教程/公司推广网站

一.冒泡排序 代码示例&#xff1a; #将乱序列表中最大元素排列到列表末尾位置 def m_sort(alist):for j in range(0,len(alist)-1):if alist[j] > alist[j1]:temp alist[j]alist[j] alist[j1]alist[j1] tempreturn alist def m_sort_final(alist):for i in range(0,len(a…...

绵阳做手机网站建设/怎么让付费网站免费

原标题&#xff1a;CCI指标实战操作中使用技巧股到了较高地步&#xff0c;已经忘记了什么什么价、量、趋势&#xff0c;更不用说什么形态、划线了&#xff0c;一切都凝固在一眼而确定的反射中。反射&#xff01;一眼而已&#xff01;什么理论都是多余。炒股要先学好技术&#x…...

动态页网站/唐山seo推广公司

印度一位9岁的女孩儿Lavinashree日前通过了微软认证专家&#xff08;MCP&#xff09;考试&#xff0c;成为目前最年轻的微软认证专家。 <?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />MCP考试测试在系统工程、数据库管理和解决…...

网站建设服务商/排名查询

张某与杨某准备合作创作一部中篇小说&#xff0c;反映当今社会的情感问题。小说初稿完成后&#xff0c;取名《阴晴圆缺》。由于该两人对初稿均不满意&#xff0c;就商量待修改后再发表。之后&#xff0c;张某就买了新房子&#xff0c;一直忙于装修&#xff0c;抽不出时间过问讨…...

成都专业手机网站建设推广/移动网站推广如何优化

格雷码​在一组数的编码中&#xff0c;若任意两个相邻的代码只有一位二进制数不同&#xff0c;则称这种编码为格雷码&#xff08;Gray Code&#xff09;&#xff0c;另外由于最大数与最小数之间也仅一位数不同&#xff0c;即“首尾相连”&#xff0c;因此又称循环码或反射码。格…...