int 和 float 的计算机实现是什么?
float Q_rsqrt( float x ) {
union { float f; uint32_t i; } y;
float x2 = x * 0.5F;
y.f = x;
y.i = 0x5f3759df - ( y.i >> 1 );
y.f = y.f * ( 1.5F - ( x2 * y.f * y.f ) );
// y.f = y.f * ( 1.5F - ( x2 * y.f * y.f ) );
return y.f;
}
类型转换
int i = 1;
char* pc = (char*)(&i);
if(pc[0] == '\x1') {
puts("This system is little-endian");
} else {
puts("This system is big-endian");
}
大括号与作用域
int main() {
int i = 0;
for ( ; i < ({
int r = rand();
printf("r == %d\n", r);
(r + i)%10;
}) ; i++ ); // gcc
printf("i == %d\n", i);
return 0;
}
指针与地址 (现代 C++ 中, 应该使用 using)
T *p; p = &o; p = (T *) malloc(size);
T0 *p(T1, T2); p = ( T0 (*)(T1, T2) ) pf;
const T *p; T * const p;
int n; int * const np = &n; int *const *npp = &np;
// 指向指向 非const 的 int 的 const 指针的 非 const 指针
int (*(*foo)(double))[3] = NULL;
assert(foo == NULL);
foo = ( int (*(*)(double))[3] ) fp;
int bar = (*(*foo)(1.2))[1];
void (*signal(int sig, void (*func)(int)))(int);
// a better way
typedef void (*sighandler_t)(int);
sighandler_t signal(int, sighandler_t);
#include
int main()
{
int (*fp)(void) = &main;
// int (*fp)(void) = main;
// int (*fp)(void) = *main;
int (**fpp)(void) = &fp;
printf("fp == %p\n", fp);
printf("&fp == %p\n", &fp);
printf("&fpp == %p\n", &fpp);
return 0;
}
// fp == 0x401122 // main .text
// &fp == 0x7ffddaf5d8b8 // fp stack
// &fpp == 0x7ffddaf5d8b0 // fpp stack
// 函数调用也是运算符, 联系到函数调用的汇编实现, 一切就说的通了
// 在编译器眼中, &func == func
// file1.c
char s[64] = "Hello World!\n";
// file2.c
extern char *s;
int main() {
printf("%p\n", s); // 防止 s 被优化掉
// printf("%c\n", s[0]; // Segmentation fault
return 0;
}
// extern 既非强符号,也非弱符号。
// 无法匹配相同类型强符号之后就归入.bss节。
// gcc file1.c file2.c
// xxd a.out 会发现 "Hello World!\n" 位于 .rodata 节
动态分配内存
volatile 变量是什么意思? 原子变量是什么意思?
echo hello world的执行过程?
- 文件系统如何通过 inode 管理? 目录项 (路径) 和 inode 的关系?
- shell 的文件操作
pwd,cd(本质上是 shell 的函数, 而不是一个可执行的二进制文件, 至少在 Linux 上如此)lscp,mv,rm,ln,ln -smkdir,rmdir(empty only)touchfind-type(d,f)-name-mtime-size -4k -size +4G-exec rm {} \;chmod,chown,ls -l- 其他磁盘驱动有关的命令
df与du(estimate) 的区别?cat(head,tail,tee) 与dd(DataDef) 的区别? (dd可以调用 seek)- 重定向
>,<(>>,1>,2>,&>)
<<指定长文档的结束符<<<指定长字符串- 以上两种用法等同于stdin重定向为输入的长文档/长字符串
nc(NetCat) 与curl(pupjq) 的区别? (curl面向 HTTP)echo与printf的区别?echo str,echo "str",echo 'str'- 桌面环境 (KDE plasma) 中程序的运行实际上也是 一个特殊的 shell 的子进程, 利用
pstree命令可以观察到 plasmashell 的子进程有 code, netease, chrome.
echo, hello, world.echo $PATH) 获得这个可执行目标文件的绝对路径 (实际上是 which echo 之一).echo $? 结果是 0.)# include
__attribute__((constructor)) void hello() {
printf("hello\n");
}
// See also: atexit(3)
__attribute__((destructor)) void goodbye() {
printf("goodbye\n");
}
int main() {
return 0;
}
环境变量
- 函数 (或者仅仅是一条命令) 会在当前的 shell 环境中执行, 函数可以对环境变量进行更改.
- 脚本会在单独的进程中执行 (脚本作为参数传给 SheBang 指定的解释器程序), 脚本需要使用
export将环境变量导出, 并将值传递给环境变量.
# leaf @ DAVE in ~/Roland/book/Cpp_Missing [16:14:15]
$ sudo find -L /sys/class/backlight -maxdepth 2 -name 'brightness'
/sys/class/backlight/intel_backlight/brightness
find: File system loop detected; ‘/sys/class/backlight/intel_backlight/subsystem’ is part of the same file system loop as ‘/sys/class/backlight’.
# leaf @ DAVE in ~/Roland/book/Cpp_Missing [16:14:27] C:1
$ sudo echo 50000 > /sys/class/backlight/intel_backlight/brightness
zsh: permission denied: /sys/class/backlight/intel_backlight/brightness
# leaf @ DAVE in ~/Roland/book/Cpp_Missing [16:14:35] C:1
$ echo 50000 | sudo tee /sys/class/backlight/intel_backlight/brightness
50000
/sys/ln? (ls -i)sudo 的位置, 时机?
grep(GlobalREgexPrint) 与awk(family names of its authors) 的区别?
grep 查找正则 pattern
grep -C Contextgrep -v Invertawk 是一个语言, Linux 上运行的是其实现 gawk.@include "filename"
@load "filename"
@namespace "name"
pattern { action statements }
function name(parameter list) { statements }
管道
|与重定向的区别?
echocatxargs的区别?
find . -type f | xargs -d "\n" ls -lt | head
# 不含目录项(文件夹) 按时间列出 仅列出前几项
echo ... … -> stdoutcat filepath filepath 的内容 -> stdoutxargs command ... 执行 command stdin …
su,sudo,useradd,usrdel
jobs与ps的区别?
man
public (protected) private
this 指针,
T &成员函数返回(*this)
istream &read(instream &is, T &obj);ostream &read(onstream &os, T &obj);
构造函数, 析构函数, 拷贝与赋值
友元
foo=bar # foo <- 'bar'
foo = bar # 向 foo 传入 ['=', 'bar']
echo "$foo" # 特殊符号, 字段的替换
# "$Foo" 为空串
echo '$foo' # 字符串
#!/bin/bash
# 通过 source 加载函数以及环境变量
savepwd(){
echo "$(pwd)" > $HOME/savepwd.cache
echo "save pwd $(pwd)"
}
cdoldpwd(){
# 注意小括号和引号的关系
cd "$(cat "$HOME/savepwd.cache")"
}
mcd () {
mkdir -p "$1"
cd "$1"
}
$0 - 脚本名$1 到 $9 - 脚本的参数。 $1 是第一个参数,依此类推。$@ - 所有参数$# - 参数个数$? - 前一个命令的返回值$$ - 当前脚本的进程识别码!! - 完整的上一条命令,包括参数。常见应用:当你因为权限不足执行命令失败时,可以使用 sudo !! 再尝试一次。$_ - 上一条命令的最后一个参数。echo "Starting program at $(date)"
# date会被替换成日期和时间
echo "Running $0 with $# arguments with pid $$"
for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
# 我们将标准输出流和标准错误流丢弃
# 如果模式没有找到, 则grep退出状态 != 0
if [[ $? -ne 0 ]]; then
# [ ] 是 test 的语法糖 (同义词)
# [[ ]] 是 bash 的关键字
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done
convert image.{png,jpg}
# convert image.png image.jpg
cp /path/{foo,bar,jia}.sh /newpath
# cp /path/foo.sh /path/bar.sh /path/jia.sh /newpath
rm foo?
rm foo*
# 也可以结合通配使用
mv *{.py,.sh} folder
mkdir foo bar
# 创建foo/a, foo/b, foo/c, bar/a, bar/b, bar/c
touch {foo,bar}/{a..c}
touch foo/x bar/y
diff <(ls foo) <(ls bar)
# 为什么不是 diff $(ls foo) $(ls bar)
false || echo "not short circuiting"
true && echo "not short circuiting"
echo "this will" ; echo "always run" ;
shellchecktldr (TooLongDon’tRead)locate fd 作为 find 的替代品rg ag ack 作为 grep 的替代品fzf 改进 historybroot nnn ranger 改进文件管理#!/usr/bin/env bash
n=$(( RANDOM % 100 ))
# 双小括号是 let 语句的语法糖, 而且是 POSIX 标准.
# 意义是可以使用类似 C 的语法
if [[ n -eq 0 ]]; then
echo "Finished!"
>&2 echo "End with n==$n"
exit 1
fi
# And 符号在右侧提示文件描述符 >&1 >&2
# 左侧的文件描述符直接书写 1> 2>
# And 符号在左侧是语法糖, &> 展开成 1> 2>
echo "Running."
count=0
while true
do
./random_try.sh &> /dev/null
if [[ $? -ne 0 ]]; then
echo "in total $count times"
exit 0
fi
((count++))
done
count=0
for ((count=1;;count++))
do
./random_try.sh &> /dev/null
if [[ $? -ne 0 ]]; then
echo "in total $count times"
exit 0
fi
done
count=0
until [[ "$?" -ne 0 ]];
do
count=$((count+1))
./random_try.sh &> /dev/null
done
echo "in total $count times"
正则表达式
. 除换行符之外的"任意单个字符"* 匹配前面字符零次或多次+ 匹配前面字符一次或多次[abc] 匹配 a, b 和 c 中的任意一个(RX1|RX2) 任何能够匹配RX1 或 RX2的结果^ 行首$ 行尾其他
? 非贪婪 (本意是匹配恰好一个字符) sed 不支持 perl 支持perl -pe PATTERN
[^ ]+ 匹配任意非空且不包含空格的序列cd, ls, tree, find.
cd -
find . -name FileName
tldr
ctrl+left ctrl+right home end 快速编辑命令 跳转
### workspace
mkdir ~/mine/
mkdir ~/mine/scripts/
cd ~/mine/scripts/
vim filename.sh
### activate
chmod +x filename.sh
mkdir ~/bin/
ln -s $(readlink -f filename.sh) ~/bin/filename
updcmd=$(printf "\n\nexport PATH=\$PATH:~/bin/\n")
echo $updcmd >> ~/.bashrc
source ~/.bashrc
TODO tmux oh_my_zsh vim.netrw(:Rexplore)
buffer
normal
visual
insert
计数来结合“名词”和“动词”,这会执行指定操作若干次。
修饰语改变“名词”的意义。修饰语有 i,表示“内部”,和 a, 表示“周围”。
. 重复上一个动作 q{字母} 录制 q 结束录制 @{字母} 重复 录制时可以递归调用该宏 q{字母}q 清除宏
/ ? 查找 n N 跳转到结果 * # 快速
:s 替换 例如 %s/\[.*\](\(.*\))/\1/g 将有命名的 Markdown 链接替换成简单 URLs & 同 :s 重复上一个替换
:sp :vsp 分割 Ctrl+w 先导键
:r 读入
:e 新建 buffer
bn bp 切换 buffer
:tabnew 新建 tab
gt gT 切换 tab
Q 进入 Ex 模式
H M L 页面
J 合并行
跳转上一位置 alt+left alt+right / ctrl+i ctrl+o
跳转段落 / ctrl+u ctrl+d { }
括号跳转 ctrl+shift+pipe / percent
快速插入 ctrl+enter ctrl+shift+enter / o O
上下跳转 up down / gk gj
光标位置不变 页面上下移动 ctrl+up ctrl+down / ctrl+e ctrl+y
字符串运算:
gerpawksed (弱)tr (替换)diff (比较不同)# 在远端首先过滤,再发送过滤好的流
ssh SEVER_NAME 'journalctl | grep sshd | grep "Disconnected from"' > ssh.log
less ssh.log
# 用 sed 处理构成的表格的流
cat ssh.log
| grep sshd
| grep "Disconnected from"
| sed -E 's/.*Disconnected from (invalid |authenticating )?user (.*) [^ ]+ port [0-9]+( \[preauth\])?$/\2/'
| sort | unique -c # count
| sort -nk1,1 # number, [1,1+1) 列
| tail -n10
| awk '{print $2}' | paste -sd, # 合并行, 并选择分隔符
# sort -r 或者 head 实现倒序
# uniq 只能过滤相邻的行, 所以必须先排序
# 正则表达式中 \n awk 中 $n 的含义类似 shell
awk '$1 == 0 && $2 ~ /^[^ ]+$/ {print $0} | wc -l'
或者使用 << / <<< 向 awk 输入以下语句:
BEGIN { count = 1 }
$1 == 0 && $2 ~ /^[^ ]+$/ { count += 1 }
END { print count }
数学运算:
bcR --slave -e python3 -c irb …gnuplot 绘制图标
missing semester 之后的内容包括
echo hello world 例子的深入) 命令行环境与开发密切相关. 我们暂且忽略, 首先补全 C++ 面向对象的基础知识.
) 与
n
a
m
e
.h
name\verb|.h|
name.h 的区别: 是否位于 std 命名空间.std::string::size_type 类似 size_t .char a[] 引入了函数 begin end.# include
int main (int argc, char *argv[], char *envp[]) {
for ( auto it = std::begin(argv[0]);
it != std::end(argv[0]);
it++ ) { }
}
auto, decltype()const int *pci, int *const cpi, constexpr int *cpifor (auto &c : str) { }
throw, try, catch// 常见异常
# include
# include
# include
# include
# include
using uint_list = std::vector<std::string::size_type>;
int main() {
// 构造函数传入参数
uint_list x{4}; // [rand, rand, rand, rand]
// 与 allocator 有关
uint_list y(4,0); // [0, 0, 0, 0]
// 构造函数传入 initializer_list
uint_list z{4,0}; // [4, 0]
return 0;
}
/** T 是类型
* para* 是一个实参
*/
// 常量初始化, 引用初始化
// 聚合初始化 (数组, POD)
T array[3] = {para0, para1}; /* [T(para0), T(para1), T()] */
T *parray[3] = {new T(para0), new T(para1) /*, nullptr*/};
static T obj; T(); T obj{}; T obj = {}; // 零初始化
T obj; new T; new T(); // 默认初始化
T obj(); T obj{}; T(); T{}; new T{}; new T(); // 值初始化
T obj(paras, ...); // 直接初始化
/* 非class */ T obj{paras}; // 直接初始化
static_cast<T>(another); // 直接初始化
// 闭包内通过捕获的参数初始化
// T::T : Tplus() { ... } T::T : Tplus{} { ... }
T{paras, ...}; T obj{paras, ...}; new T{paras, ...}; // 直接列表初始化
T obj = {paras, ...}; obj = {paras, ...}; // 复制列表初始化
f({paras, ...}); /* return {paras, ...}; */ // 复制列表初始化
int x0; // 如果在栈上 warning: 'x' is used uninitialized [-Wuninitialized]
static int x_static; // 如果在 .bss 节(全局变量),被操作系统初始化为零。
int x1{}; // int 执行零初始化
int x2(); // 这是函数声明, 没有函数定义无法链接
int x3 = {}; // 这也是“零初始化”的语法
int *px0 = new int; // int 执行零初始化
int *px1 = new int(); // 这也是“零初始化”的语法
int *px2 = new int{}; // 这也是“零初始化”的语法
class cComplex {public: double re; double im; Complex()=default;};
struct sComplex {double re; double im;};
cComplex cc0(); sComplex sc0(); // warning: empty parentheses were disambiguated as a function declaration [-Wvexing-parse]
// g++ 认为是函数声明
sComplex sc1{}; // 执行零初始化
sComplex sc2 = {}; // 这也是“零初始化”的语法
cComplex *pcc0 = new cComplex(); // 执行默认构造函数,初始化为0
sComplex *psc0 = new sComplex(); // 执行零初始化
sComplex *psc1 = new sComplex{}; // 这也是“零初始化”的语法
/*** ** * ** ***/
struct A { int x; int y; int z; };
// A a{.y = 2, .x = 1}; // 错误:指派符的顺序不匹配声明顺序
A b{.x = 1, .z = 2}; // OK:b.y 被初始化为 0 Warning: missing initializer for member 'main()::A::y' [-Wmissing-field-initializers]
struct B {
string x;
unsigned int y = -1U;
int z = -1;
};
B{.z=0} // 以 {} 初始化 x,这样会调用默认构造函数
// 然后以 = -1U 初始化 y
// 然后以 = 0 初始化 z
struct S {
int x;
struct T {
int i;
int j;
int k[3];
} y;
};
// 等价
S s0 = { 1, { 2, 3, {4, 5, 6} } };
S s1 = {1,2,3,4,5,6};
S s2{ 1, { 2, 3, {4, 5, 6} } };
S s3{1,2,3,4,5,6};
// 类似指派符
enum Foo { A, B, C=10, D, E=1, F, G=F+C};
// A=0, B=1, C=10, D=11, E=1, F=2, G=12
/*** ** * ** ***/
// ===== C中合法,C++中不合法!=====
数组
error: ISO C++ does not allow C99 designated initializers [-Wpedantic]
int array0[5] = {[4]=5,[0]=1,2,3,4}; // 保有 1,2,3,4,5
int array1[MAX] = { // 开始初始化 a[0] = 1, a[1] = 3, ...
1, 3, 5, 7, 9, [MAX-5] = 8, 6, 4, 2, 0
};
// 对于 MAX=6 ,数组保有 1,8,6,4,2,0
// 对于 MAX=13 ,数组保有 1,3,5,7,9,0,0,0,8,6,4,2,0(“稀疏数组”)
乱序
warning: missing initializer for member 'main()::A::x' [-Wmissing-field-initializers]
warning: missing initializer for member 'main()::A::y' [-Wmissing-field-initializers]
struct A { int x; int y; int z; };
A a0{.z = 3, .y = 2, .x = 1};
混合
error: either all initializer clauses should be designated or none of them should be
A a1{.x=1, 2, 3};
嵌套
error: expected primary-expression before '.' token
struct B {A a;}
B b0{.a.x=1};
/*** ** * ** ***/
// 调用 T::T()
T obj; new T; new T(); // 默认初始化
T obj(); T obj{}; T(); T{}; new T{}; new T(); // 值初始化
// 调用相应参数的 T::T(para0, ...)
T obj(para0, ...); new T(para0, ...);
// 调用 T::T(std::initializer_list)
T obj{item0, ...}; new T{item0, ...}; // 称作:直接列表初始化
T obj = {item0, ...}; // 称作:复制列表初始化
// allocator
#include
template <class T> class MyAllocator{
public:
//下面是类型别名,实现中的可选项
using value_type = T;
using pointer = T *;
using const_pointer = const T *;
using void_pointer = void *;
using const_void_pointer = const void *;
using size_type = size_t;
using difference = std::ptrdiff_t;
//重新绑定函数
template <class U>
struct rebind{
using other = MyAllocator<U>;
};
MyAllocator() = default;
~MyAllocator() = default;
//分配内存
pointer allocate(size_type numObjects){
allocCount += numObjects;
std::cout << "MyAllocator::allocate 分配内存对象个数: " << numObjects << std::endl;
return static_cast<pointer>(operator new(sizeof(T) * numObjects));
}
//分配内存
pointer allocate(size_type numObjects, const_void_pointer hint){
return allocate(numObjects);
}
//释放内存
void deallocate(pointer p, size_type numObjects){
std::cout << "MyAllocator::deallocate 释放内存对象个数: " << numObjects << std::endl;
allocCount = allocCount - numObjects;
operator delete(p);
}
//分配器支持最大的内存数
size_type max_size() const
{
return std::numeric_limits<size_type>::max();
}
// //构造对象
// template
// void construct(U *p, Args &&... args)
// {
// new (p) U(std::forward(args)...);
// }
// //销毁对象
// template
// void destroy(U *p)
// {
// p->~U();
// }
//返回每次分配/删除的内存数
size_type get_allocation_count() const {
return allocCount;
}
private:
//统计当前内存的使用量
size_type allocCount;
};
int main(){
std::vector<int, MyAllocator<int>> v(0);
for (size_t i = 0; i < 32; i++){
v.push_back(i);
std::cout << "[总计分配]" << v.get_allocator().get_allocation_count() << std::endl;
}
return 0;
}
MyAllocator::allocate 分配内存对象个数: 1
[总计分配]1
MyAllocator::allocate 分配内存对象个数: 2
MyAllocator::deallocate 释放内存对象个数: 1
[总计分配]2
MyAllocator::allocate 分配内存对象个数: 4
MyAllocator::deallocate 释放内存对象个数: 2
[总计分配]4
[总计分配]4
MyAllocator::allocate 分配内存对象个数: 8
MyAllocator::deallocate 释放内存对象个数: 4
[总计分配]8
[总计分配]8
[总计分配]8
[总计分配]8
MyAllocator::allocate 分配内存对象个数: 16
MyAllocator::deallocate 释放内存对象个数: 8
[总计分配]16
[总计分配]16
[总计分配]16
[总计分配]16
[总计分配]16
[总计分配]16
[总计分配]16
[总计分配]16
MyAllocator::allocate 分配内存对象个数: 32
MyAllocator::deallocate 释放内存对象个数: 16
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
[总计分配]32
MyAllocator::deallocate 释放内存对象个数: 32