Skip to content

Commit 7c57db2

Browse files
author
yangjingjing
committed
init blog
1 parent 288f2df commit 7c57db2

24 files changed

Lines changed: 2419 additions & 29 deletions

_posts/2014-08-01-编译原理.md

Lines changed: 0 additions & 21 deletions
This file was deleted.

_posts/2014-08-01-编译原理Flex简介.md

Lines changed: 429 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
layout: post
3+
categories: [OS]
4+
description: none
5+
keywords: OS
6+
---
7+
# 编译原理入门工具
8+
9+
## lex, flex,yacc,bison等工具了解
10+
编译原理中,lex,flex,yacc,bison是四个非常常用的工具,下面我来深入地分别说明一下它们的使用场景、作用和原理。
11+
12+
1. Lex:Lex是一种用于创建词法分析器的工具,也称为扫描器生成器。它的主要作用是将输入的字符流转换为一系列的标记(tokens)。使用场景主要是在编译器的前端阶段,用来进行词法分析。它的工作原理是通过正则表达式来描述词法规则,然后根据这些规则生成相应的C语言代码。
13+
14+
2. Flex:Flex是Lex的另一种实现,同样是用来生成扫描器的工具。它与Lex的最大区别是,Flex生成的扫描器是可重入的,这使得它可以在多线程环境中使用。Flex的使用场景和作用与Lex基本相同,都是用来进行词法分析。其工作原理也是通过正则表达式来描述词法规则,然后生成相应的C语言代码。
15+
16+
3. Yacc:Yacc(Yet Another Compiler-Compiler)是一个用于生成语法分析器的工具。它的主要作用是根据输入的语法规则生成一个语法分析器,用来进行语法分析。使用场景主要是在编译器的前端阶段,用来进行语法分析。Yacc的工作原理是通过BNF(巴科斯-诺尔范式)来描述语法规则,然后根据这些规则生成相应的C语言代码。
17+
18+
4. Bison:Bison是Yacc的一种实现,也是用来生成语法分析器的工具。它与Yacc的主要区别在于,Bison生成的语法分析器是可重入的,这使得它可以在多线程环境中使用。同时,Bison还提供了更多的功能和更好的错误报告。Bison的使用场景和作用与Yacc相同,都是用来进行语法分析。其工作原理也是通过BNF来描述语法规则,然后生成相应的C语言代码。
19+
20+
总结来说,这四个工具都是用来帮助我们生成编译器的重要组成部分——扫描器和语法分析器。其中,Lex和Flex主要用来生成扫描器,而Yacc和Bison主要用来生成语法分析器。这些工具都通过将词法或语法规则转化为C语言代码,从而实现了从源代码到目标代码的转换。
21+
22+
拓展
23+
24+
Lex文件通常具有三个部分:定义部分,规则部分和C代码部分。
25+
26+
定义部分:在这部分,你可以定义一些宏,这些宏可以在后面的规则中使用。这部分的内容通常是一些正则表达式的定义,以及包含的C头文件等。
27+
28+
规则部分:在这部分,你需要为输入的字符或字符序列定义一些规则。每个规则由一个正则表达式和一段C代码组成。当输入的字符序列匹配到正则表达式时,就会执行对应的C代码。
29+
30+
C代码部分:在这部分,你可以写一些辅助函数,或者定义一些全局变量。
31+
32+
下面是一个简单的Lex文件的例子:
33+
```
34+
%{ #include <stdio.h>%}%%[0-9]+ { printf("A number: %s\n", yytext); }[a-zA-Z]+ { printf("A word: %s\n", yytext); }.|\n { }%%int main(void) { yylex(); return 0;}
35+
```
36+
该程序会将输入的文本分割成单词和数字,并输出它们。
37+
38+
要将Lex文件转换为词法分析器,你需要使用Lex工具。使用方法如下:
39+
40+
1. 编写一个Lex文件,例如上面的例子,保存为`lexer.l`
41+
42+
2. 在命令行中输入`lex lexer.l`,这会生成一个名为`lex.yy.c`的C文件。
43+
44+
3. 使用C编译器编译这个文件,例如`gcc lex.yy.c -o lexer`
45+
46+
4. 运行生成的程序,例如`./lexer`
47+
48+
49+
50+
51+
52+
53+
54+
55+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
layout: post
3+
categories: [Java]
4+
description: none
5+
keywords: Java
6+
---
7+
# Java比较compare
8+
9+
10+
##
11+
java异常:Comparison method violates its general contract解决
12+
13+
Comparison method violates its general contract,是因为sort排序中重写compare方法引发的异常,在sort排序中重写的方法一定要满足:可逆比较
14+
```
15+
Comparator<Integer> c = (o1, o2) -> { if (o1 > o2) { return 1; } else { return -1; } };
16+
```
17+
如上面的比较器就没有满足可逆性,当o1和o2相等时,o1和o2比较,返回-1,表示o1小于o2;但是当这两个元素交换位置时,o2比o1,结果返回还是-1,表示o2小于o1。这样就有两个元素互换比较,o1<o2并且o2<o1这两个结果相互矛盾,在某些情况下会出现异常。
18+
19+
因为JDK7以后,Arrays.sort方法换了排序方式,使用TimSort来进行排序,新的实现在自定义比较器违背比较规则的情况下有可能会抛出异常,原来的实现则是忽略了这个异常。所以为了保证不抛出异常,对比较器的比较规则要求比较严格:
20+
21+
- 自反性:x与y的比较结果和y与x的比价结果相反
22+
23+
- 传递性:如果x>y并且y>z,那么x>z
24+
25+
- 对称性:如果x=y,那么x与z的比较结果和y与z的比较结果相同
26+
27+
分析了一下代码,代码里确实写了一段排序,并且没有加等于号
28+
```
29+
30+
items = items.stream().sorted(
31+
Comparator.comparing(ContentVo::getProperties, (x, y) -> {
32+
if (Integer.valueOf(x.get("status").toString()) > Integer.valueOf(y.get("status").toString())) {
33+
return -1;
34+
} else {
35+
return 1;
36+
}
37+
}).thenComparing(ContentVo::getProperties, (x, y) -> {
38+
Date date1 = new Date();
39+
Date date2 = new Date();
40+
try {
41+
date1 = DateUtils.parseDate(x.get("createTime").toString());
42+
date2 = DateUtils.parseDate(y.get("createTime").toString());
43+
} catch (ParseException e) {
44+
e.printStackTrace();
45+
}
46+
if (date1.before(date2)) {
47+
return 1;
48+
} else {
49+
return -1;
50+
}
51+
})
52+
).collect(Collectors.toList());
53+
```
54+
于是修改了一下
55+
56+
解决方法
57+
58+
想要真正解决这个问题,并不是像很多网上帖子上所说加个等于条件就可以,这个是因为逆比较引发的问题,就应该从根源上解决这个问题:让compare方法在逆比较时不会出现矛盾
59+
60+
61+
62+
63+

_posts/2015-01-14-Stream分组.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,5 @@ posNoticesMap.put("SDSD","sdsad");
162162
163163
Map<String, String> newMap = posNoticesMap.entrySet().stream().collect(Collectors.toMap(entry -> StringUtils.uncapitalize(entry.getKey()), entry -> entry.getValue()));
164164
```
165+
166+
## stream去重
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
---
2+
layout: post
3+
categories: [C]
4+
description: none
5+
keywords: C
6+
---
7+
# Makefile快速入门
8+
9+
## Makefile介绍
10+
makefile可以简单的认为是一个工程文件的编译规则,描述了整个工程的自动编译和链接的规则。
11+
12+
## 基本组成
13+
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
14+
15+
### 显式规则
16+
显式规则说明了,如何生成一个或多的目标文件。这是由 Makefile 的写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
17+
18+
### 隐晦规则
19+
20+
由于我们的 make 命名有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 Makefile,这是由 make 命令所支持的。
21+
22+
### 变量的定义
23+
24+
在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
25+
26+
### 文件指示
27+
28+
其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像C语言中的 include 一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像C语言中的预编译 #if 一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
29+
30+
### 注释
31+
32+
Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,这个就像 C/C++ 中的“//”一样。如果你要在你的 Makefile 中使用“#”字符,可以用反斜框进行转义,如:“#”。
33+
34+
### Makefile三要素
35+
```
36+
语法:
37+
[目标]: [依赖]
38+
(tab制表符)[命令]
39+
```
40+
- 目标:规则的目标,可以是 Object File(一般称它为中间文件),也可以是可执行文件,还可以是一个标签;
41+
- 依赖:是我们的依赖文件,要生成 targets 需要的文件或者是另一个目标。可以是多个,也可以是没有;
42+
- 命令:make 需要执行的命令(任意的 shell 命令)。可以有多条命令,每一条命令占一行。
43+
44+
45+
### Makefile工作原理
46+
输入make命令:会执行Makefile的第一个目标,如果该目标有依赖,那么也会自动执行依赖的目标
47+
48+
49+
## 编译工程准备
50+
### 准备文件
51+
新建,项目文件夹hello,并创建文件main.cpp,factorial.cpp,printhello.cpp,functions.h。
52+
53+
hello目录的结构如下:
54+
```
55+
hello/
56+
├──main.cpp
57+
├──factorial.cpp
58+
├──printhello.cpp
59+
└──functions.h
60+
```
61+
main.cpp(调用printhello函数和factorial函数,并打印“This is main:”)
62+
```
63+
#define _FUNCTIONS_H_
64+
65+
#include <iostream>
66+
#include "functions.h" //包括了头文件,函数声明包含在头文件中
67+
using namespace std;
68+
69+
int main()
70+
{
71+
printhello();
72+
73+
cout << "This is main:" << endl;
74+
cout << "The factorial of 5 is:" << factorial(5) << endl;
75+
return 0;
76+
}
77+
```
78+
printhello.cpp(打印 "Hello World!" )
79+
```
80+
#include <iostream>
81+
#include "functions.h"
82+
using namespace std;
83+
84+
void printhello()
85+
{
86+
int i;
87+
cout << "Hello World!" << endl;
88+
}
89+
```
90+
actorial.cpp(做n的递归)
91+
```
92+
#include "functions.h"
93+
94+
int factorial(int n)
95+
{
96+
if (n==1)
97+
return 1;
98+
else
99+
return n * factorial(n-1);
100+
}
101+
```
102+
function.h(声明这两个函数)
103+
```
104+
#ifdef _FUNCTIONS_H_
105+
#define _FUNCTIONS_H_
106+
void printhello();
107+
int factorial(int n);
108+
#endif
109+
```
110+
111+
## 编译
112+
分别编译,再一起链接
113+
```
114+
g++ main.cpp -c
115+
g++ factorial.cpp -c
116+
g++ printhello.cpp -c
117+
g++ *.o -o main
118+
```
119+
直接将全部文件编译、链接、运行
120+
```
121+
g++ main.cpp factorial.cpp printhello.cpp -o main
122+
./main
123+
```
124+
这种方法适用于小型项目。对于大型项目来说,此法编译效率低,这时候make工具就派上用场了。
125+
126+
## 使用Makefile编译工程
127+
采用最基本的规则
128+
```
129+
hello: main.cpp printhello.cpp factorial.cpp
130+
g++ -o hello main.cpp printhello.cpp factorial.cpp
131+
```
132+
版本二
133+
使用变量CXX、TARGET 、OBJ,同时采用不同的依赖分别编译
134+
```
135+
CXX = g++
136+
TARGET = hello
137+
OBJ = main.o printhello.o factorial.o
138+
139+
$(TARGET): $(OBJ)
140+
$(CXX) -o $(TARGET) $(OBJ)
141+
142+
main.o: main.cpp
143+
$(CXX) -c main.cpp
144+
145+
printhello.o: printhello.cpp
146+
$(CXX) -c printhello.cpp
147+
148+
factorial.o: factorial.cpp
149+
$(CXX) -c factorial.cpp
150+
```
151+
版本三
152+
增加了通配符$@ $^ $^,并使用伪依赖.PHONY进行clean操作
153+
```
154+
CXX = g++
155+
TARGET = hello
156+
OBJ = main.o printhello.o factorial.o
157+
158+
CXXFLAGS = -c -Wall
159+
160+
$(TARGET): $(OBJ)
161+
$(CXX) -o $@ $^
162+
163+
%.o: %.cpp
164+
$(CXX) $(CXXFLAGS) $< -o $@
165+
166+
.PHONY: clean
167+
clean:
168+
rm -f *.o $(TARGET)
169+
```
170+
版本四
171+
使用了wildcard 和 patsubst
172+
```
173+
CXX = g++
174+
TARGET = hello
175+
SRC = $(wildcard *.cpp) #获取当前录下所有的.cpp文件
176+
OBJ = $(patsubst %.cpp, %.o, $(SRC)) #将$(SRC)中所有的.cpp替换成.o文件
177+
178+
CXXFLAGS = -c -Wall
179+
180+
$(TARGET): $(OBJ)
181+
$(CXX) -o $@ $^
182+
183+
%.o: %.cpp
184+
$(CXX) $(CXXFLAGS) $< -o $@
185+
186+
.PHONY: clean
187+
clean:
188+
rm -f *.o $(TARGET)
189+
```
190+
191+
## 常见的解析符
192+
193+
### 自动化变量
194+
$0 当前脚本的文件名。
195+
$n(n≥1) 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是 $1,第二个参数是 $2>。
196+
$# 传递给脚本或函数的参数个数。
197+
$* 传递给脚本或函数的所有参数。
198+
$@ 表示目标文件。
199+
$? 表示比目标更新的所有依赖,每个依赖之间以空格隔开
200+
$$ 当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。
201+
$^ 表示所有的依赖文件
202+
$< 表示第一个依赖文件
203+
204+
### 变量和赋值
205+
= 执行时再赋值
206+
:= 定义时就进行赋值
207+
?= 表示变量为空时再赋值
208+
+= 表示将值追加到变量的尾部
209+
always := 总是需要被编译的模块
210+
targets := 编译目标
211+
obj-y := 编译进内核的文件(夹)列表
212+
obj-m := 编译成外部可加载模块的列表
213+
lib-y := 和 lib-m := 编译成库文件
214+
subdir-y := 和 subdir-m := 表示需要递归进入的子目录
215+
216+
### 内置函数
217+
wildcard cfiles := $(wildcard *.c), 作用是匹配当前目录(不包含子目录)下所有.c文件,每个文件以空格隔开,然后赋值给cfiles变量
218+
219+
patsubst objs := $(patsubst %.c,%.o,$(wildcard *.c))作用是将当前目录(不包含子目录)下所有的.c文件替换成对应的.o文件,即将后缀为.c的文件替换为后缀为.o的文件,每个文件以空格隔开,然后赋值给objs变量。
220+
221+
abspath path := $(abspath main.c)作用是获取当前目录下main.c文件的绝对路径(含文件名,结果比如:/work/main.c),然后赋值给path变量。

_posts/2021-01-10-Linux内核开发.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
layout: post
3-
categories: Linux
3+
categories: [Linux]
44
description: none
55
keywords: Linux
66
---
@@ -10,11 +10,11 @@ keywords: Linux
1010
Linux操作系统是UNIX操作系统的一种克隆系统,是一种类UNIX操作系统,诞生于1991年10月5日(第一次正式向外公布的时间),起初的作者是Linus Torvalds。Linux操作系统的诞生、发展和成长过程依赖着5个重要支柱:UNIX操作系统、Minix操作系统、GNU计划、POSIX标准和Internet。
1111

1212
## 如何获取上游内核
13-
在了解Linux内核的各种衍生版本后,我们首先尝试一下获取上游内核(upstream kernel)。Linus树、linux-next树,以及绝大部分的开发树都可以从http://www.kernel.org/获取
13+
在了解Linux内核的各种衍生版本后,我们首先尝试一下获取上游内核(upstream kernel)。Linus树、linux-next树,以及绝大部分的开发树都可以从 http://www.kernel.org/ 获取。
1414

1515
Linux内核的开发都是在最新版上游内核的基础上进行的。其中最重要的就是作为所有树的根源的Linus树。下面介绍获取Linus树的两种方法。
1616
- 下载tar文件
17-
获取Linus树最简单的方法就是从kernel.org下载tar文件。2.6内核所有发布版本的tar文件都能够从http://www.kernel.org/pub/linux/kernel/v2.6/获取。
17+
获取Linus树最简单的方法就是从kernel.org下载tar文件。2.6内核所有发布版本的tar文件都能够从 http://www.kernel.org/pub/linux/kernel/v2.6/获取。
1818
- 使用Git
1919
```
2020
$git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6

0 commit comments

Comments
 (0)