从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: y
int 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 = 4
j&0b101 = 1
max=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+b
i=a*2
a++
表达式加上一个分号就构成了语句,例如,下面三条语句:
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 = 0x7fff2357896c
s_a.a addr = 0x7fff2357896c
s_a.b addr = 0x7fff23578970
s_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 = 2
x2 = 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语言大成者,从中我们可以学到非常多的技巧,本文来学习一下宏技巧,文章有点长,但耐心看完后C语言level直接飙升。 本文出自:大叔的嵌入式小站,一个简单的嵌入式/单片机学习、交流小站 从…...
详解Python的装饰器
Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里。 为什么需要装饰器 我们假设你的程序实现了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 基础准备: 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的控件命名规则:蛇形命名方式 控件类型开头,以下是QT控件类型命名的参考范例 Buttons Buttons起始字符串对象名称举例Push Buttonbuttonbutton_loginTool Buttontool_button / buttonbutton_switchRadio Buttonradio_button / radioradio_boy…...
MATLAB与图像处理的那点小事儿~
目录 一、学习内容 二、matlab基本知识 三、线性点运算 四、非线性点运算,伽马矫正 五、直方图 1、直方图均衡化 (1)使用histep函数实现图像均衡化 (2)使用自行编写的均衡化函数实现图像均衡化 2、直方图规定…...
第十四届蓝桥杯模拟赛(第三期)Java组个人题解
第十四届蓝桥杯模拟赛(第三期)Java组个人题解 今天做了一下第三期的校内模拟赛,有些地方不确定,欢迎讨论和指正~ 文章目录第十四届蓝桥杯模拟赛(第三期)Java组个人题解填空题部分第一题【最小数】第二题【E…...
Go语言之条件判断循环语句(if-else、switch-case、for、goto、break、continue)
一、if-else条件判断语句 Go中的if-else条件判断语句跟C差不多。但是需要注意的是,Go中强制规定,关键字if和else之后的左边的花括号"{“必须和关键字在同一行,若使用了else if结构,则前段代码快的右花括号”}"必须和关…...
深入理解AQS
概念设计初衷:该类利用 状态队列 实现了一个同步器,更多的是提供一些模板方法(子类必须重写,不然会抛错)。 设计功能:独占、共享模式两个核心,state、Queue2.1 statesetState、compareAndSetSta…...
JVM学习笔记十:执行引擎
0. 前言 声明: 感谢尚硅谷宋红康老师的讲授。 感谢广大网友共享的笔记内容。 B站:https://www.bilibili.com/video/BV1PJ411n7xZ 本文的内容基本来源于宋老师的课件,其中有一些其他同学共享的内容,也有一些自己的理解内容。 1. …...
【2023-03-10】JS逆向之美团滑块
提示:文章仅供参考,禁止用于非法途径 前言 目标网站:aHR0cHM6Ly9wYXNzcG9ydC5tZWl0dWFuLmNvbS9hY2NvdW50L3VuaXRpdmVsb2dpbg 页面分析 接口流程 1.https://passport.meituan.com/account/unitivelogin主页接口:需获取下面的参数࿰…...
全志V853芯片放开快启方案打印及在快起方式下配置isp led的方法
全志V85x芯片 如何放开快启方案的打印? 1.主题 如何放开快启方案的打印 2.问题背景 产品:v851系列快启方案 软件:tina 其他:特有版本信息添加自由描述 (如固件版本,复现概率,特定环境&#x…...
大数据 | (一)Hadoop伪分布式安装
大数据原理与应用教材链接:大数据技术原理与应用电子课件-林子雨编著 Hadoop伪分布式安装借鉴文章:Hadoop伪分布式安装-比课本详细 大数据 | (二)SSH连接报错Permission denied:SSH连接报错Permission denied 哈喽&a…...
Django/Vue实现在线考试系统-06-开发环境搭建-Django安装
1.0 基本介绍 Django 是一个由 Python 编写的一个开放源代码的 Web 应用框架。 使用 Django,只要很少的代码,Python 的程序开发人员就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 Django 本身基于 MVC 模型,即 Model(模型)+ View(…...
KaiwuDB 时序引擎数据存储内存对齐技术解读
一、理论1、什么是内存对齐现代计算机中内存空间都是按照 byte 划分的,在计算机中访问一个变量需要访问它的内存地址,从理论上看,似乎对任何类型的变量的访问都可以从任何地址开始。但在实际情况中,通常在特定的内存地址才能访问特…...
IR 808 Alkyne,IR-808 alkyne,IR 808炔烃,近红外吲哚类花菁染料
【产品理化指标】:中文名:IR-808炔烃英文名:IR-808 alkyne,Alkyne 808-IR CAS号:N/AIR-808结构式:规格包装:10mg,25mg,50mg,接受各种复杂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.标准访问,请问先打印邮件还是短信?1.2.2.邮件⽅法暂停4秒钟,请问先打印邮件还是短信?分析1.2.3.新增⼀个普通⽅法hello(&…...
品牌营销 | 学习如何最大限度地发挥品牌营销的作用
您是否想过如何最大限度地发挥品牌营销的潜力?这是一项艰巨的挑战,通过了解品牌营销的基本组成部分,您可以成功地推广您的品牌。 (图源:Pixabay) 品牌营销的基本组成部分 你需要做什么来发展稳固的品牌&am…...
Linux驱动的同步阻塞和同步非阻塞
在字符设备驱动中,若要求应用与驱动同步,则在驱动程序中可以根据情况实现为阻塞或非阻塞一、同步阻塞这种操作会阻塞应用程序直到设备完成read/write操作或者返回一个错误码。在应用程序阻塞这段时间,程序所代表的进程并不消耗CPU的时间&…...
LearnOpenGL-光照-5.投光物
本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject 文章目录投光物平行光点光源聚光不平滑的例子平滑例子投光物 前面几节使用的光照都来自于空间中的一个点 即…...
【C语言】每日刷题 —— 牛客语法篇(1)
前言 大家好,今天带来一篇新的专栏c_牛客,不出意外的话每天更新十道题,难度也是从易到难,自己复习的同时也希望能帮助到大家,题目答案会根据我所学到的知识提供最优解。 🏡个人主页:悲伤的猪大…...
【深度学习】Subword Tokenization算法
在自然语言处理中,面临的首要问题是如何让模型认识我们的文本信息,词,是自然语言处理中基本单位,神经网络模型的训练和预测都需要借助词表来对句子进行表示。 1.构建词表的传统方法 在字词模型问世之前,做自然语言处理…...
五分钟了解支付、交易、清算、银行等专业名词的含义?
五分钟了解支付、交易、清算、银行等专业名词的含义?1. 支付类名词01 支付应用02 支付场景03 交易类型04 支付类型(按通道类型)05 支付类型(按业务双方类型)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领域模型命名规约:一、PO :(persistant object ),持久对象二、VO :(value object) ࿰…...
[2.2.4]进程管理——FCFS、SJF、HRRN调度算法
文章目录第二章 进程管理FCFS、SJF、HRRN调度算法(一)先来先服务(FCFS, First Come First Serve)(二)短作业优先(SJF, Shortest Job First)对FCFS和SJF两种算法的思考(三…...
【代码随想录Day55】动态规划
583 两个字符串的删除操作 https://leetcode.cn/problems/delete-operation-for-two-strings/72 编辑距离https://leetcode.cn/problems/edit-distance/...
Java开发 - 消息队列前瞻
前言 学完了Redis,那你一定不能错过消息队列,要说他俩之间的关联?关联是有的,但也不见得很大,只是他们都是大数据领域常用的一种工具,一种用来提高程序运行效率的工具。常见于高并发,大数据&am…...
MySQL连接IDEA详细教程
使用IDEA的时候,需要连接Database,连接时遇到了一些小问题,下面记录一下操作流程以及遇到的问题的解决方法。 目录 MySQL连接IDEA详细教程 MySQL连接IDEA详细教程 打开idea,点击右侧的 Database 或者 选择 View --> Tool Wind…...
线程(操作系统408)
基本概念 我们说引入进程的目的是更好的使用多道程序并发执行,提高资源的利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行的时候所付出的时间开销,提高操作系统的并发性能。 线程可以理解成"轻量级进程",…...
功耗降低99%,Panamorph超清VR光学架构解析
近期,投影仪变形镜头厂商Panamorph获得新型VR显示技术专利(US11493773B2),该专利方案采用了紧凑的结构,结合了Pancake透镜和光波导显示模组,宣称比传统VR方案的功耗、发热减少99%以上,可显著提高…...
【数据结构】带你深入理解栈
一. 栈的基本概念💫栈是一种特殊的线性表。其只允许在固定的一端进行插入和删除元素的操作,进行数据的插入和删除的一端称作栈顶,另外一端称作栈底。栈不支持随机访问,栈的数据元素遵循后进先出的原则,即LIFOÿ…...
认识CSS之如何提高写前端代码的效率
🌟所属专栏:前端只因变凤凰之路🐔作者简介:rchjr——五带信管菜只因一枚😮前言:该系列将持续更新前端的相关学习笔记,欢迎和我一样的小白订阅,一起学习共同进步~👉文章简…...
Vue中watch和computed
首先这里进行声明,这个讲的是vue2的内容,在vue3发生了什么变动与此无关 这里是官网: https://v2.cn.vuejs.org/v2/guide/installation.html computed > 计算属性 watch > 侦听器(也叫监视器) 其区别如下&…...
华为鲲鹏+银河麒麟v10 安装 docker-ce
设备:硬件:仅有ARM处理器,无GPU和NPU,操作系统麒麟银河V10,Kunpeng-920 #######参考原链接######### 华为鲲鹏银河麒麟v10 安装 docker-ce 踩坑 - akiyaの博客 在 arm64(aarch64) 架构服务器上基于国产化操作系统安…...
Lambda,Stream,响应式编程从入门到放弃
Lambda表达式 Java8新引入的语法糖 Lambda表达式*(关于lambda表达式是否属于语法糖存在很多争议,有人说他并不是语法糖,这里我们不纠结于字面表述)*。Lambda表达式是一种用于取代匿名类,把函数行为表述为函数式编程风…...
C语言枚举使用技巧
什么是C语言枚举 C语言枚举是一种用户自定义数据类型,它允许程序员定义一个变量,并将其限制为一组预定义的常量。这些常量被称为“枚举值”,并且可以通过名称进行引用。 在C语言中,枚举值是整数类型,它们的值默认从0…...
保姆级使用PyTorch训练与评估自己的EfficientNetV2网络教程
文章目录前言0. 环境搭建&快速开始1. 数据集制作1.1 标签文件制作1.2 数据集划分1.3 数据集信息文件制作2. 修改参数文件3. 训练4. 评估5. 其他教程前言 项目地址:https://github.com/Fafa-DL/Awesome-Backbones 操作教程:https://www.bilibili.co…...
【9】基础语法篇 - VL9 使用子模块实现三输入数的大小比较
VL9 使用子模块实现三输入数的大小比较 【报错】官方平台得背锅 官方平台是真的会搞事情,总是出一些平台上的莫名其妙的错误。 当然如果官方平台是故意考察我们的细心程度,那就当我没有说!! 在这个程序里,仿真时一直在报错 错误:无法在“test”中绑定wire/reg/memory“t…...
成功的项目管理策略:减少成本,提高质量
项目管理是一项具有挑战性的任务,项目团队需要合理的规划和策略,以确保项目的成功和达成预期。为了实现项目的成功,项目经理必须采用正确的策略,才能以最大限度地减少成本并提高项目质量。本文将探讨成功的项目管理策略࿰…...
centos 7下JDK8安装
下载安装包https://www.oracle.com/java/technologies/downloads/#java8-linux上传路径 /usr/local(替换为自己需要安装的路径)解压tar -zxvf jdk-8u131-linux-x64.tar.gz配置环境变量[rootlocalhost java]# vi /etc/profile添加如下配置在配置文件最后&…...
datatables.js中文项目使用案例
官方下载地址https://datatables.net/download/中文官网:http://datatables.club/资源引用<link href"~/datatables/datatables.min.css" rel"stylesheet" /> <script src"~/jquery.min.js" type"text/javascript"…...
Hadoop小结
Hadoop是什么Hadoop是一 个由Apache基金 会所开发的分布式系统基础架构。主要解决,海量数据的存储和海量数据的分析计算问题。广义上来说,Hadoop通 常是指一个更广泛的概念一Hadoop 生态圈。Hadoop优势Hadoop组成HDFS架构Hadoop Distributed File System,…...
经典卷积模型回顾14—vgg16实现图像分类(tensorflow)
VGG16是由牛津大学计算机视觉小组(Visual Geometry Group)开发的深度卷积神经网络模型。其结构由16层组成,其中13层是卷积层,3层是全连接层。 VGG16被广泛应用于各种计算机视觉任务,如图像分类、目标检测和人脸识别等。…...
#Vue2篇:keep-alive的属性和方法
定义 keep-alive 组件是 Vue.js 内置的一个高阶组件,用于缓存其子组件,以提高组件的性能和响应速度。 除了基本用法之外,它还提供了一些属性和方法,以便更好地控制缓存的组件。 属性 include属性用于指定哪些组件应该被缓存&a…...
webpack指南(项目篇)——webpack在项目中的运用
系列文章目录 webpack指南(基础篇)——手把手教你配置webpack webpack指南(优化篇)——webpack项目优化 文章目录系列文章目录前言一、配置拆分二、修改启动命令三、定义环境变量四、配置路径别名总结前言 前面我们对webpack的基…...
unicode字符集与utf-8编码的区别,unicode转中文工具、中文转unicode工具(汉字)
在cw上报的报警信息中,有一个name字段的值是\u4eba\u4f53 不知道是啥,查了一下,是unicode编码,用下面工具转换成汉字就是“人体” 参考文章:https://tool.chinaz.com/tools/unicode.aspx 那么我很好奇,uni…...
3D数学系列之——再谈特卡洛积分和重要性采样
目录一、前篇文章回顾二、积分的黎曼和形式三、积分的概率形式(蒙特卡洛积分)四、误差五、蒙特卡洛积分计算与收敛速度六、重要性采样七、重要性采样方法和过程八、重要性采样的优缺点一、前篇文章回顾 在前一篇文章3D数学系列之——从“蒙的挺准”到“蒙…...
Python错误 TypeError: ‘NoneType‘ object is not subscriptable解决方案汇总
目录前言一、引发错误来源二、解决方案2-1、解决方案一(检查变量)2-2、解决方案二(使用 [] 而不是 None)2-3、解决方案三(设置默认值)2-4、解决方案四(使用异常处理)2-5、解决方案五…...