nginx
1 | server { |
Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server
配置块,但是赋值操作在bar中实现,foo中的foo为空
1 | server { |
1 | map $args $foo { |
1 | # nginx有11个阶段 postread -> server_rewrite -> find_config -> rewrite -> post_rewrite -> preaccess -> access -> postaccess -> precontent -> content -> log |
lua的使用
1 | print("2" + 8) |
1 | -- 字符串连接 |
1 | -- 获取字符串长度 |
1 | local x = 10 -- 局部变量 |
1 | -- lua中的函数可以当作变量 |
1 | -- if ip is true return ip, if ip is nil return "unknow" |
lua
的八种数据类型
nil
:空类型boolean
number
:整数和浮点数都用number表示string
:可以有三种表示方式
1 | str1 = 'this is test1' |
function
:function表示lua中的函数,可以接受多个参数,也可以用...
表示可变数目的参数,function可以返回多个参数userdata
:用户自定义数据thread
– todotable
相当于数组,下标从1开始,里面的元素可以是各种类型的,函数也可以作为元素
1 | tb = { |
table还可以当作哈希表
1 | tb = { |
effective-C++
1 view c++ as a federation of languages
c
pass by value比pass by reference更高效,c中的引用实质上还是指针
object-oriented c++
有构造函数和析构函数存在,传值会调用拷贝构造函数和析构函数
template c++
pass by reference 更高效,在使用模版时,并不知道是什么类型,使用reference不用拷贝
pass by reference-to-const
STL
2 prefer const enum inline to define
1 | #define PI 3.14 |
const 比define更好
1 | class foo{ |
num是类中的专属常量,如果在类外取它的地址的话,需要在类外给出定义式
1 | const int foo::num; |
1 | #include <iostream> |
如果不在类外给出定义式,在类外访问num的地址会出现错误,原因是并没有给num分配内存
1 | class foo { |
也没分配地址,所以要在外面给出定义式,在类中仅仅只是声明式
1 | class foo { |
像这样,虽然能够输出num的值,但是并没有给num分配内存,仅仅只是替换成5
也可以这样写
1 | class foo{ |
使用inline
替换’#define’
3 use const whenever possible
git使用
csapp
1 | #include <stdio.h> |
预处理阶段:预处理器根据#开头的命令,读取头文件,并插入到程序中,得到.i文件
1 | # 1 "hello.c" |
编译阶段:编译器将hello.i翻译成文本文件hello.s
1 | .arch armv8-a |
汇编阶段:汇编器将hello.s翻译成机器语言的指令,将结果保存在.o文件中
链接阶段:hello.c文件中调用了printf()函数,printf函数存在于printf.o的程序中,链接器负责处理合并,得到可执行文件
算法
埃氏筛
1 | vector<bool> is_prime(n, true); |
排序算法
1 | void bubble_sort(std::vector<int>& nums) { |
回溯
1 |
|
二叉树的层序遍历
1 | struct TreeNode { |
高精度乘法
1 | #include <iostream> |
高精度加法
1 | #include <iostream> |
位运算
1 | // 不使用+-来计算两数之和 |
01背包
问题描述:给定 n 个物品,第 i 个物品的重量为 wgt[i−1]、价值为 val[i−1] ,和一个容量为 cap 的背包。每个物品只能选择一次,问在限定背包容量下能放入物品的最大价值。
定义状态:[i, c],物品编号i和背包容量c
对于当前物品,有两种方案
不放入背包:[i, c] = [i - 1, c]
放入背包:[i, c] = [i - 1, c - weight[i - 1]],价值增加val[i];
dp[i,c]=max(dp[i−1,c],dp[i−1,c−wgt[i−1]]+val[i−1]
滑动数组
定长滑动数组
给你字符串 s
和整数 k
。
请返回字符串 s
中长度为 k
的单个子字符串中可能包含的最大元音字母数。
例如s = "abciiidef", k = 3
从左到右遍历 s。
首先统计前 k−1=2 个字母的元音个数,这有 1 个。
s[2]=c 进入窗口,此时找到了第一个长为 k 的子串 abc,现在元音个数有 1 个,更新答案最大值。然后 s[0]=a 离开窗口,现在元音个数有 0 个。
s[3]=i 进入窗口,此时找到了第二个长为 k 的子串 bci,现在元音个数有 1 个,更新答案最大值。然后 s[1]=b 离开窗口,现在元音个数有 1 个。
s[4]=i 进入窗口,此时找到了第三个长为 k 的子串 cii,现在元音个数有 2 个,更新答案最大值。然后 s[2]=c 离开窗口,现在元音个数有 2 个。
s[5]=i 进入窗口,此时找到了第四个长为 k 的子串 iii,现在元音个数有 3 个,更新答案最大值。然后 s[3]=i 离开窗口,现在元音个数有 2 个。
s[6]=d 进入窗口,此时找到了第五个长为 k 的子串 iid,现在元音个数有 2 个,更新答案最大值。然后 s[4]=i 离开窗口,现在元音个数有 1 个。
s[7]=e 进入窗口,此时找到了第六个长为 k 的子串 ide,现在元音个数有 2 个,更新答案最大值。然后 s[5]=i 离开窗口,现在元音个数有 1 个。
s[8]=f 进入窗口,此时找到了第七个长为 k 的子串 def,现在元音个数有 1 个,更新答案最大值。遍历结束。
步骤:
入:下标为 i 的元素进入窗口,更新相关统计量。如果 i<k−1 则重复第一步。
更新:更新答案。一般是更新最大值/最小值。
出:下标为 i−k+1 的元素离开窗口,更新相关统计量。
代码:
1 | class Solution { |
不定长滑动数组
给定一个字符串 s
,请你找出其中不含有重复字符的 最长 子串 的长度。
1 | int lengthOfLongestSubstring(string s) { |
越长越合法
给你一个字符串 s
,它只包含三种字符 a, b 和 c 。
请你返回 a,b 和 c 都 至少 出现过一次的子字符串数目。
1 | int numberOfSubstrings(string s) { |
现在的已经满足了,更长的子串就满足,+left
越短越合法
给你一个整数数组 nums
和一个整数 k
,请你返回子数组内所有元素的乘积严格小于 k
的连续子数组的数目。
1 | int numSubarrayProductLessThanK(vector<int>& nums, int k) { |
长的合法,长的所有子串都合法,+ right - left + 1
恰好
例如,要计算有多少个元素和恰好等于 k 的子数组,可以把问题变成:
计算有多少个元素和 ≥k 的子数组。
计算有多少个元素和 >k,也就是 ≥k+1 的子数组。
答案就是元素和 ≥k 的子数组个数,减去元素和 ≥k+1 的子数组个数。这里把 > 转换成 ≥,从而可以把滑窗逻辑封装成一个函数 f,然后用 f(k) - f(k + 1) 计算,无需编写两份滑窗代码。
1 | int my_fun(vector<int>& nums, int goal) { |
二分
1 | mid = left + (right - left) / 2; //避免溢出 |
1 | // lower_bound,指向第一个 ≥ value 的元素的迭代器。 |
1 | auto it = lower_bound(nums, target); //二分,返回的是迭代器,值为*it |
1 | // upper_bound,指向第一个 > value 的元素的迭代器。 |
6.s081
Lab1
1 Sleep
首先判断sleep是否有参数,将argv[1]
转换为int类型,使用系统调用sleep实现
1 | 1 #include "kernel/types.h" |
2 pingpong
建立两个管道,fd1
, fd2
,通过read
和write
实现父进程和子进程之间的通信
1 | 1 #include "kernel/types.h" |
3 primes
在main函数中的父进程中输入2~35,在main函数的子进程中递归调用filter函数
在filter函数中,首先读的一定是质数,直接输出,通过这个prime判断其他数是否读入到下一个进程中
每个父进程都要写wait,否则会超时
1 | 1 #include "kernel/types.h" |
4 find
find
用法:find current_path target
fmtname
作用:根据当前路径返回最后一个/
后的内容
通过st.type
判断是文件还是目录
文件,与target进行比对,相同输出当前路径
目录,递归使用find,构造路径
1 | #include "kernel/types.h" |
5 xargs
xargs
的用法:echo a | xargs echo b,输入 b a,将前一个命令的输出作为后一个命令的参数
注意:下面代码中argv[0]是xargs
使用fork和exec使得新的进程得到执行
1 | #include "kernel/types.h" |
常见错误
报错信息
kex_exchange_identification: read: Connection reset by peer Connection reset by ::1 port 3316
使用vscode链接docker后在终端输入以下命令
1 | sudo service ssh restart |
wsl中出现AddressSanitizer:DEADLYSIGNAL
1 | echo 0 | sudo tee /proc/sys/kernel/randomize_va_space |
c++
const
const位于星号左侧,const用于修饰指针指向的变量;const位于星号右侧,const修饰指针本身
1 | int b = 500; |
1和2是等价的,都表示指向常量的指针,3表示指针本身不可变,但是指向的内容可变
1 | int a = 10; |
const int 必须用const int * p来指向
int* const p必须初始化
顶层const(top-level const)表示指针本身是一个常量,底层const(low-level const)表示指针所指的对象是一个常量,更一般的,顶层const可以表示任意的对象是常量
constexpr修饰指针,constexpr仅对指针有效
函数体外定义的变量存放在固定地址,函数体外的变量则不是,constexpr指针和引用只能用于函数体外的变量
constexpr
常量表达式(const expression)是指值不会改变并且在编译阶段就会得到计算结果的表达式
将变量声明为constexpr
类型,由编译器来验证变量的值是否为常量表达式
自定义类、IO库、string类型不能被定义为constexpr
1 | constexpr std::string str = "abc"; |
constexpr指针初值必须是nullptr或0
1 | int num = 20; |
static
被static修饰的变量只能在当前文件访问,函数同理
1 | // a.cpp 文件 |
修饰局部变量
1 | void foo() { |
extern
用于声明外部变量
File1.c
1 | #include <stdio.h> |
file2.c
1 | #include <stdio.h> |
函数同理,extern还可以链接c和c++,用extern声明一个函数是c语言,则该函数可以在c++文件中使用
c
1 | #include <stdio.h> |
c++
1 | #include <iostream> |
vector
1 | #include<vector> |
atoi stoi
1 | const char* str = "abc"; |
stoi提供了更安全的方式
tolower, toupper
1 | #include <cctype> |
unordered_map
哈希表
1 | // 声明 |
sort
1 | //基础用法 |
引用和指针
1 | int a = 10; |
b是a的引用,就是给a起个别名,对b进行操作实际上就是对a进行操作
1 | int* a = null; |
指针可以指向空,但是引用不能为空
1 | int a = 10; |
指针可以随意改变(除const修饰外),引用一旦绑定就不可以再改变
静态链接和动态链接
.cpp文件经过预处理成为.i文件,.i文件经过编译后成为.s文件,.s文件经过汇编后成为目标文件,即.o,静态链接将该.o文件和其他目标文件以及库文件链接起来,这个过程称为静态链接。
而动态链接将这个过程推迟到了运行时,由操作系统装载程序加载库
静态链接的代码装载速度快,但是文件体积大
动态链接的速度慢,但是文件体积小
c和c++的区别
- c只支持基本数据类型,还有结构体、枚举、联合;c++支持类和对象
- c++有封装的特性、有构造函数和析构函数、c++支持函数重载,可以定义同名但是参数列表不同的函数;c都做不到
- c++有异常处理机制;c没有
- c没有引用&
delete
释放new申请的空间,会调用析构函数
1 | #include <iostream> |
作用域解析操作符
访问全局变量
1 | #include <iostream> |
访问命名空间中的标识符
1 | #include <iostream> |
访问修饰符
public
:可以在任何函数中访问
protected
:只能在类中或者类的子类中访问
private
:只能在类中访问
strlen 和sizeof
1 | char ch[] = "Hello World"; |
对于char ch[]类型,是c风格的字符串,在末尾会自动加\0
sizeof会统计末尾的\0
strlen不统计
string 和char ch[]
string在堆上分配内存,sizeof获取的是string类的大小
char ch[]在栈上分配内存
lambda函数
1 | [capture list] (parameter list) -> return type { function body } |
capture list
表示获取列表用于表示lambda可以访问的外部变量
paramete list
表示lambda的参数
return type
表示返回类型
`function body 表示函数体
值捕获
1 | int x = 10; |
引用捕获
1 | int x = 10; |
类型别名
typedef
1 | typedef double wages; // wages是double的同义词 |
using
1 | using SI = Sales_item; |
explicit
防止隐式类型转换
1 | class MyClass { |
string_view
1 | #include <string_view> |
类型转换
static_cast
1 | #include <iostream> |
static_cast
用法,static_cast<要转换的类型>(变量)
dynamic_cast
- 上行转换,从子类转换成父类
- 下行转换,从父类转换成子类, 且要求父类中至少有一个虚函数
const_cast
- 移除
const
修饰符 - 添加
const
修饰符
reinterpret_cast
- 指针类型的转换
- 指针和整数之间的转换
- 非相关类型的转换
数组指针 todo,primer6.3
数组不能被拷贝,所以在函数中无法返回数组,但是可以返回数组的指针或引用
在写一个返回数组指针的函数时,可以用别名来声明
1 | typedef int arrT[10]; // 为int[10]起一个别名 |
如果不想使用别名来声明函数,需要了解下面三个的区别
1 | int *arr[5] = {1, 2, 3, 4, 5}; // 声明了一个大小为5的数组 |
deep copy and shallow copy
1 | class Person { |
对于基本数据类型,浅复制可以实现,但是当类中有引用或者指针时,就需要用到深复制
在写拷贝构造函数的时候,必须将参数写成引用,一是引用可以避免拷贝,二是c++语法要求,否则,在传参的时候就会调用拷贝构造函数,进入无限循环
而且最好写成const
类型,这样非const
类型也可以传入
rvalue
int a = 5
在这句代码中,a表示左值(lvalue
),5表示右值(rvalue
),左值表示在内存中可以找到它,右值在内存中找不到,
右值是一个立即数(immediate number
),
1 | int a = 20; // correct, a is a lvalue |
在写函数的参数时,最好将参数定义为const引用类型,首先可以保证函数内部不会对参数做出修改,而且引用不会进行拷贝操作;其次,const引用可以传入右值引用,而非const的左值引用只能传入左值。例如,当传入的参数为具体的数字的时候,此时参数为右值,如果函数参数定义为const引用,可以传入,否则会出错