c语言

1.编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// hello.c
#include <stdio.h>

typedef struct test
{
int a;
int b;
} T;

int main(int argc, char const *argv[])
{
T t1;
printf("t1=%d, %d, %ld\n", t1.a, t1.b, &t1);

T t2 = t1;
printf("t2=%d, %d, %ld\n", t2.a, t2.b, &t2);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
# 头文件所在目录
find / -name "stdio.h" 2>/dev/null
/usr/include/stdio.h
/usr/include/c++/9/tr1/stdio.h
/usr/include/x86_64-linux-gnu/bits/stdio.h

# 编译hello.c
# 编译时,使用/usr/include/stdio.h替换#include<stdio.h>
# 链接时,自动链接
gcc hello.c -o hello

typedef还能这么写?

1
typedef const struct JNIInvokeInterface_ *JavaVM;

直接生成预编译的文件

1
gcc -I "/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers" -E hello.c -o hello.i

static

1
2
静态变量保存在数据区
用来限制作用域

c++

0.重要链接https://stibel.icu/

1.环境搭建

  1. 安装g++
1
sudo apt-get install build-essential

2.helloworld

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 0. #include 预处理指令
// 1. iostream头文件, 标准c的头文件stdio.h, 从c转化到c++的头文件cmath
// 2. std是命名空间
// 3. cout是对象
// 4. <<是运算符
// 5. "hello!"是运算符的右操作数
// 6. endl也是对象, 与“\n”的差别是,先换行然后flush

#include <iostream>

int main() {
std::cout << "hello!" << std::endl;
std::cout << "nani" "yyyy" << std::endl;
return 0;
}

3.随机数

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
srand((unsigned)time(NULL));
std::cout << rand() << std::endl;
std::cout << rand() << std::endl;
return 0;
}

4.整形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <climits>

int main() {
std::cout << CHAR_BIT << std::endl;
std::cout << CHAR_MIN << std::endl;
std::cout << CHAR_MAX << std::endl;
std::cout << SCHAR_MIN << std::endl;
std::cout << SCHAR_MAX << std::endl;
std::cout << UCHAR_MAX << std::endl;

std::cout << INT_MIN << std::endl;
std::cout << INT_MAX << std::endl;
std::cout << UINT_MAX << std::endl;

std::cout << LONG_MIN << std::endl;
std::cout << LONG_MAX << std::endl;
std::cout << ULONG_MAX << std::endl;

std::cout << LLONG_MIN << std::endl;
std::cout << LLONG_MAX << std::endl;
std::cout << ULLONG_MAX << std::endl;
return 0;
}

5.初始化器{}

1
2
3
4
5
6
7
8
9
#include <iostream>

int main() {
int val = {3};
int x{5};
std::cout << val << std::endl;
std::cout << x << std::endl;
return 0;
}

6.进制

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main() {
int val1 = 10;
int val2 = 0x10;
int val3 = 010;
std::cout << val1 << std::endl;
std::cout << std::hex; // 切换到16进制显示
std::cout << val2 << std::endl;
std::cout << std::oct; // 切换到8进制显示
std::cout << val3 << std::endl;
return 0;
}

7. 字符

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

int main() {
char ch;
std::cout << "please input a char:";
std::cin >> ch;
std::cout.put(ch);
std::cout.put('!');
std::cout.put('\');
std::cout << std::endl;
return 0;
}

8. 宽字符

1
2
3
4
5
6
7
8
#include <iostream>

int main() {
wchar_t ch;
std::wcin >> ch;
std::wcout << "what=" << ch << std::endl;
return 0;
}

9. bool

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main() {
bool flag = -12;
if (flag) {
std::cout << "flag is false" << std::endl;
} else {
std::cout << "flag is true" << std::endl;
}
std::cout << flag << std::endl;
std::cout << true << std::endl;
return 0;
}

10. const

1
2
3
4
5
6
7
8
9
#include <iostream>

int main() {
const int NANI = 100;
std::cout << NANI << std::endl;
//NANI = 3;
//std::cout << NANI << std::endl;
return 0;
}

11. 浮点数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

int main() {
float val = 10.0 / 3.0;
double val1 = 10.0 / 3.0;
const float sub = 1.0e6;
std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
std::cout << val << std::endl;
std::cout << val1 << std::endl;
std::cout << val * sub << std::endl;
std::cout << 10 * val * sub << std::endl;
std::cout << val1 * sub << std::endl;
std::cout << 10 * val1 * sub << std::endl;
return 0;
}

12. 类型转化

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main() {
int a = 1.3f + 1.8f;
std::cout << a << std::endl;

int b = int(1.3f) + int(1.8f);
std::cout << b << std::endl;

int c = static_cast<int> (2.3);
std::cout << c << std::endl;
return 0;
}

13. 自动类型 auto

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main() {
auto a = 1.2;
std::cout << a << std::endl;
auto b = 1.3f;
std::cout << b << std::endl;
auto c = 0;
std::cout << c << std::endl << sizeof(c) << std::endl;
return 0;
}

14. 数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int main() {
int arr[5];
int len = sizeof(arr) / sizeof(int);
for (int i=0; i<len; i++) {
std::cout << arr[i] << std::endl;
}
std::cout << "------" << std::endl;
std::cout << arr << std::endl;
std::cout << sizeof(arr[1]) << std::endl;
std::cout << sizeof(arr) << std::endl;
return 0;
}

15. 字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// cin 输入的字符串会自动在末尾追加'\0'
#include <iostream>
#include <cstring>

void print_arr(char arr[]) {
std::cout << "-----" << std::endl;
for (int i=0; i<5; i++) {
std::cout << int(arr[i]) << std::endl;
}
std::cout << "-----" << std::endl;
}

int main() {
char arr[5] = {0, 0, 0, 0, 5};
print_arr(arr);
std::cin >> arr;
print_arr(arr);
std::cout << strlen(arr) << std::endl;
std::cout << arr << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
// getline 获取指定长度的字符串
#include <iostream>

int main() {
const int MAX_LEN = 5;
char name[MAX_LEN];
std::cin.getline(name, MAX_LEN);
std::cout << name << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

int main() {
const int MAX_LEN = 5;

char name1[MAX_LEN];
std::cin.get(name1, MAX_LEN);
std::cout << name1 << std::endl;

// 用于读取'\n', getline会默认读取'\n'
std::cin.get();

char name2[MAX_LEN];
std::cin.get(name2, MAX_LEN);
std::cout << name2 << std::endl;
return 0;
}

16. string类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 字符串拼接,c和c++两种方式
#include <iostream>
#include <string>
#include <cstring>

int main() {
std::string s1;
std::cin >> s1;
std::string s2;
std::cin >> s2;
std::cout << "-->" + s1 + s2 << std::endl;

char s3[20];
strcpy(s3, "-->");
std::cin >> (s3 + 3);
char s4[10];
std::cin >> s4;
char* res = strcat(s3, s4);
std::cout << res << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <string>
#include <cstring>

int main() {
std::string s1;
std::cin >> s1;
std::cout << "-->" << s1.size() << std::endl;

char arr[10];
std::cin >> arr;
std::cout << "-->" << strlen(arr) << std::endl;
return 0;
}

17. 字符串字面值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
#include <cstring>

int main() {
wchar_t arr0[] = L"1234";
char16_t arr1[] = u"1234";
char32_t arr2[] = U"1234";
std::cout << sizeof(wchar_t) << std::endl;
std::cout << arr0 << "-->" << sizeof(arr0) << std::endl;
std::cout << arr1 << "-->" << sizeof(arr1) << std::endl;
std::cout << arr2 << "-->" << sizeof(arr2) << std::endl;

std::string s = "严";
std::cout << s << "-->" << s.size() << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
// +*( 会被当作分隔符
#include <iostream>
#include <string>
#include <cstring>

int main() {
std::string s = R"+*(ls\ll"hkks"("0\)0")+*";
std::cout << s << std::endl;
return 0;
}

18.结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>
#include <cstring>

struct Dog {
std::string name;
bool sex;
int age;
};

// 占用的bit数
struct Cat {
bool sex : 1;
int age : 32;
};

int main() {
Dog dog1 = { "gouzi", 1, 3 };
std::cout << dog1.name << std::endl;
dog1.name = "nani";
std::cout << dog1.name << std::endl;

struct Dog dog2 = { "what", 0, 3 };
std::cout << dog2.name << std::endl;

Dog dog3 = dog2;
std::cout << dog3.name << std::endl;

Dog dogs[3] = { dog1, dog2, dog3 };
for (int i=0; i<3; i++) {
std::cout << &(dogs[i].name) << "-->" << dogs[i].name << std::endl;
}

std::cout << sizeof(Cat) << " " << sizeof(Dog) << std::endl;
return 0;
}
}

19.共用体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>

struct Dog {
std::string name;
bool sex;
int age;
// 匿名共用体
union {
int ival;
long lval;
} id;
};

int main() {
Dog dog = {
"what",
1,
12,
4L
};
std::cout << dog.id.lval << std::endl;
std::cout << dog.id.ival << std::endl;
return 0;
}

20.枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

enum Color {
red,
green,
blue
};

int main() {
Color color = green;
std::cout << color << std::endl;

Color color1 = Color(2);
std::cout << color1 << std::endl;
return 0;
}

21.指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>

int main() {
int* p = new int;
int* pArr = new int[10] {
5, 6, 2, 4, 6
};
int arr[10] = {4, 3, 2, 1};

pArr++;
std::cout << pArr[2] << std::endl;
pArr--;

std::cout << arr[1] << std::endl;

delete[] pArr;
delete p;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 指向结构体的指针
#include <iostream>
#include <string>

struct Dog {
std::string name;
int sex;
};

int main() {
Dog* pDog = new Dog;
pDog->name = "gouzi";
pDog->sex = 1;
std::cout << pDog->name << std::endl;
std::cout << sizeof(*pDog) << std::endl;
delete pDog;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 复合类型指针
#include <iostream>
#include <string>

struct Dog {
std::string name;
int sex;
};

int main() {
const Dog** arr = new const Dog*[5];
Dog** parr = (Dog**) arr;
for (int i=0; i<5; i++) {
Dog* p = new Dog;
p->name = "dog-" + std::to_string(i);
*parr = p;
parr++;
}

std::cout << arr[1]->name << std::endl;

for (int i=0; i<5; i++) {
delete arr[i];
}
delete[] arr;

return 0;
}

22. vector & array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>
#include <vector>
#include <array>

struct Dog {
std::string name;
int sex;
};

int main() {
std::vector<int> list;
int x = 0;
while (x >= 0) {
std::cin >> x;
list.push_back(x);
std::cout << x << "-->" << list.size() << std::endl;
}

// array分配在栈上
std::array<int, 5> arr = { 3, 4, 5 };
arr[4] = 9;
for (int i=0; i<5; i++) {
std::cout << arr[i] << std::endl;
}
return 0;
}

23. cin对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>

struct Dog {
std::string name;
int sex;
};

int main() {
std::string str;
char ch;
// 这几种判断方式都可以, 判断cin对象,还可以检查磁盘错误
//while (!std::cin.eof()) {
//while (std::cin) {
while (std::cin.get(ch)) {
//std::cin.get();
str.append(1, ch);
}
std::cout << "-->" << str << std::endl;
return 0;
}

24. cctype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 统计总字符数和空格数
#include <iostream>
#include <string>
#include <cctype>

int main() {
int other = 0;
int space_count = 0;
int punct_count = 0;
int alpha_count = 0;
int enter_count = 0;
int num_count = 0;

char ch;
while (std::cin.get(ch)) {
if (isspace(ch)) space_count++;
if (ispunct(ch)) punct_count++;
if (isdigit(ch)) num_count++;
if (isalpha(ch)) alpha_count++;
if (ch == '\n') enter_count++;
other++;
}
std::cout << "space count:" << space_count << std::endl;
std::cout << "punct count:" << punct_count << std::endl;
std::cout << "num count:" << num_count << std::endl;
std::cout << "alpha count:" << alpha_count << std::endl;
std::cout << "enter count:" << enter_count << std::endl;
std::cout << "other count:" << other << std::endl;
return 0;
}

25. fstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
#include <fstream>

int main() {
std::ofstream out_file;
out_file.open("aa");

const int MAX_LINE_SIZE = 100;
char* line = new char[MAX_LINE_SIZE];
while (std::cin) {
std::cin.getline(line, MAX_LINE_SIZE);
out_file << line << std::endl;
}
out_file.close();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <string>
#include <fstream>

int main() {
std::ifstream in_file;
in_file.open("bb");
if (!in_file.is_open()) {
std::cout << "Error: open bb error" << std::endl;
}
// seek到倒数第0个字符
in_file.seekg(0, std::ios_base::end);
int length = in_file.tellg();
std::cout << length << std::endl;
in_file.seekg(0, std::ios_base::beg);

std::ofstream out_file;
out_file.open("aa");
if (!out_file.is_open()) {
std::cout << "Error: open aa error" << std::endl;
}

int count = 0;
const int MAX_LINE_SIZE = 10;
char* buffer = new char[MAX_LINE_SIZE];
while (in_file.good() && out_file.good()) {
int size = length - count;
size = size > MAX_LINE_SIZE ? MAX_LINE_SIZE : size;
in_file.read(buffer, size);
out_file.write(buffer, size);
count += size;
std::cout << "write size=" << size << std::endl;
if (count >= length) break;
}

delete []buffer;

in_file.close();
out_file.close();


return 0;
}

26. 数组作为函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>
#include <fstream>

void modify(int arr[], int len);
void print_arr(int arr[], int len);

int main() {
const int len = 10;
// 如果被当作指针,则sizeof返回8
//int* arr = new int[len];
//std::cout << sizeof(arr) << std::endl;

// 如果被当作数组,则sizeof返回40
int arr[len] = {};
std::cout << sizeof(arr) << std::endl;

print_arr(arr, len);
modify(arr, len);
print_arr(arr, len);
return 0;
}

void modify(int arr[], int len) {
for (int i=0; i<len; i++) {
arr[i] += 1;
}
}

void print_arr(int arr[], int len) {
// 编译错误,数组作为函数参数,会被当作指针
//std::cout << sizeof(arr) << std::endl;
for (int i=0; i<len; i++) {
std::cout << arr[i] << ", ";
}
std::cout << std::endl;
}

27. const指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// int* const src = new int[len];
// const int* src = new int[len];

#include <iostream>
#include <string>
#include <fstream>

void modify(int arr[], int len);
void print_arr(const int* arr, int len);

int main() {
const int len = 10;
// src的值不能改变
int* const src = new int[len];
// arr[i]的值不能改变
const int* arr = src;

print_arr(arr, len);
modify(src, len);
print_arr(arr, len);
return 0;
}

void modify(int arr[], int len) {
for (int i=0; i<len; i++) {
arr[i] += 1;
}
}

void print_arr(const int* arr, int len) {
//std::cout << sizeof(arr) << std::endl;
for (int i=0; i<len; i++) {
std::cout << arr[i] << ", ";
}
std::cout << std::endl;
}

28. 传指针 & 传引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
#include <fstream>

struct Dog {
const char* name;
int age;
};

void print_dog(Dog* dog);
void print_dog(Dog& dog);

int main() {
Dog dog = {"gouzi", 10};
print_dog(&dog);
print_dog(dog);
return 0;
}

void print_dog(Dog* dog) {
std::cout << &(dog->name) << "-->" << dog->name << std::endl;
std::cout << &(dog->age) << "-->" << dog->age << std::endl;
}

void print_dog(Dog& dog) {
std::cout << &(dog.name) << "-->" << dog.name << std::endl;
std::cout << &(dog.age) << "-->" << dog.age << std::endl;
}

29. 函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>
#include <fstream>

struct Dog {
const char* name;
int age;
};

void print_dog(Dog* dog) {
std::cout << &(dog->name) << "-->" << dog->name << std::endl;
std::cout << &(dog->age) << "-->" << dog->age << std::endl;
}

void print_dog(Dog&, void (*f)(Dog*));

int main() {
Dog dog = {"gouzi", 10};
print_dog(dog, print_dog);
return 0;
}

void print_dog(Dog& dog, void (*p_dog)(Dog*)) {
p_dog(&dog);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 使用typedef简化声明和定义的写法
#include <iostream>
#include <string>
#include <fstream>

struct Dog {
const char* name;
int age;
};

void print_dog(Dog* dog) {
std::cout << &(dog->name) << "-->" << dog->name << std::endl;
std::cout << &(dog->age) << "-->" << dog->age << std::endl;
}

// f是类型名
typedef void (*f)(Dog*);
typedef Dog& RDog;
void print_dog(RDog, f);


int main() {
Dog dog = {"gouzi", 10};
print_dog(dog, print_dog);
return 0;
}


void print_dog(RDog dog, f p_dog) {
p_dog(&dog);
}

30. 内联函数inline

1
2
3
4
5
// 当函数调用的开销,占函数执行的开销比重比较大的时候,可以使用内联,否则意义不大
inline void print_dog(Dog* dog) {
std::cout << &(dog->name) << "-->" << dog->name << std::endl;
std::cout << &(dog->age) << "-->" << dog->age << std::endl;
}

31. 变量引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
#include <fstream>

struct Dog {
const char* name;
int age;
};

int main() {
Dog dog = {"gouzi", 10};
Dog& dog1 = dog;
Dog* dog2 = &dog;
std::cout << &dog << std::endl;
std::cout << &dog1 << std::endl;
std::cout << dog2 << std::endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <string>

void swap(int& a, int& b);
void swap_ptr(int* a, int* b);

int main() {
int a = 3;
int b = 5;
std::cout << a << ", " << b << std::endl;

swap(a, b);
//swap_ptr(&a, &b);
std::cout << "-->" << a << ", " << b << std::endl;

return 0;
}

void swap_ptr(int* a, int* b) {
int tmp = *a;
*a = *b;
*b = tmp;
}

void swap(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Dog和Dog&是两个类型
#include <iostream>
#include <string>
#include <cstring>

struct Dog {
char* name;
int age;
};

int main() {
Dog dog = {"gouzi", 20};
std::cout << &dog << std::endl;

// 这个等号表示拷贝
Dog dog1 = dog;
std::cout << &dog1 << std::endl;

// 这个是起别名
Dog& dog2 = dog;
std::cout << &dog2 << std::endl;

return 0;
}

32. 函数返回引用类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <string>
#include <cstring>

struct Dog {
char* name;
int age;
};

void print_dog(Dog& dog);
Dog& clone(Dog& dog);
void destory_dog(Dog* dog);

int main() {
Dog dog = {"hello", 20};
print_dog(dog);

Dog& dog1 = clone(dog);
std::cout << "clone1:" << &dog1 << "---" << dog1.name << std::endl;
print_dog(dog1);

destory_dog(&dog1);
return 0;
}

void print_dog(Dog& dog) {
std::cout << &(dog.name) << "-->" << dog.name << std::endl;
}

Dog& clone(Dog& dog) {
Dog* dog1 = new Dog;
int len = std::strlen(dog.name);
dog1->name = new char[len + 1];
std::strncpy(dog1->name, dog.name, len);
dog1->name[0] = 'M';
dog1->age = dog.age;
std::cout << "clone:" << dog1 << "---" << dog1->name << std::endl;
return *dog1;
}

void destory_dog(Dog* dog) {
delete [](dog->name);
delete dog;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
#include <cstring>

std::string& func1(const std::string&, const std::string&);

int main() {
std::string input;
getline(std::cin, input);
std::string& res = func1(input, "nani");
std::cout << res << std::endl;

delete &res;
return 0;
}

std::string& func1(const std::string& s1, const std::string& s2) {
std::string* s = new std::string(s1);
*s += s2;
return *s;
}

33. 函数模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
#include <cstring>

template <typename T>
void swap(T&, T&);

int main() {
std::string a = "aaa";
std::string b = "bbb";
std::cout << a << ", " << b <<std::endl;
swap(a, b);
std::cout << a << ", " << b <<std::endl;
return 0;
}

template <typename T>
void swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 具体化模版
#include <iostream>
#include <string>
#include <cstring>

struct Dog {
std::string name;
int age;
};

template <typename T>
void swap(T&, T&);

void swap(Dog&, Dog&);

int main() {
std::string a = "aaa";
std::string b = "bbb";
std::cout << a << ", " << b <<std::endl;
swap(a, b);
std::cout << a << ", " << b <<std::endl;

std::cout << "----------" << std::endl;

Dog dog1 = {"dog aa", 5};
Dog dog2 = {"dog bb", 7};
std::cout << dog1.name << ", " << dog1.age <<std::endl;
std::cout << dog2.name << ", " << dog2.age <<std::endl;
swap(dog1, dog2);
std::cout << dog1.name << ", " << dog1.age <<std::endl;
std::cout << dog2.name << ", " << dog2.age <<std::endl;
return 0;
}

void swap(Dog& a, Dog& b) {
swap(a.name, b.name);
}

template <typename T>
void swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}

34. 命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>
#include <cstring>

namespace ming {
void hello() {
std::cout << "ming hello" << std::endl;
}

namespace mad {
void hello() {
std::cout << "ming mad hello" << std::endl;
}
}
}

void hello();

int main() {
ming::mad::hello();
return 0;
}


void hello() {
std::cout << "global hello" << std::endl;
}

35. 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef DOG_H_
#define DOG_H_

#include <string>

class Dog {
public:
Dog(std::string name, int age);
~Dog();
std::string say();
std::string getName();
int getAge();

private:
std::string m_name;
int m_age;
};

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <string>
#include "Dog.h"

Dog::Dog(std::string name, int age) {
this->m_name = name;
this->m_age = age;
std::cout << "Dog() called..." << std::endl;
}

Dog::~Dog() {
std::cout << "~Dog() called..." << std::endl;
}

std::string Dog::say() {
return "My name is " + this->m_name;
}

std::string Dog::getName() {
return this->m_name;
}

int Dog::getAge() {
return this->m_age;
}
1
2
3
4
5
6
7
8
9
#include <iostream>
#include "Dog.h"

int main() {
Dog* dog = new Dog("gouzi", 10);
std::cout << dog->say()<< std::endl;
delete dog;
return 0;
}

36. 对象数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
#include "Dog.h"

int main() {
Dog** arr = new Dog*[5];
for (int i=0; i<5; i++) {
arr[i] = new Dog("dog_"+ std::to_string(i), i);
}
std::cout << arr[0]->say()<< std::endl;
for (int i=0; i<5; i++) {
delete *(arr+i);
}
return 0;
}

37. 声明对象常量和类常量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef DOG_H_
#define DOG_H_

#include <string>

class Dog {
public:
static const int NANI = 99;
Dog(const std::string& name, int age);
~Dog();
std::string say();
std::string getName();
int getAge();

private:
const int SIZE = 100;
std::string m_name;
int m_age;
};

#endif

38. 运算符重载 & 友元函数 & 重载<<

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef TIME_H_
#define TIME_H_

#include <ostream>

class Time {
public:
Time();
Time(int hour, int minute);
~Time();
void show() const;
Time to_time(const int minutes) const;
int to_minute(const Time& t) const;
Time operator+(const Time& t) const;
Time operator-(const Time& t) const;
// 添加末尾的const,表示此函数不会修改当前对象成员变量
Time operator*(const double val) const;
// 友元函数不是成员函数,所以不能加末尾的const
friend Time operator*(const double val, const Time& t);
friend std::ostream& operator<<(std::ostream& os, const Time& t);

private:
int m_hour;
int m_minute;
};

#endif // TIME_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include "Time.h"

Time::Time() {
this->m_hour = 0;
this->m_minute = 0;
}

Time::Time(int hour, int minute) {
this->m_hour = hour;
this->m_minute = minute;
}

Time::~Time() {

}

void Time::show() const {
std::cout << this->m_hour << ":" << this->m_minute << std::endl;
}

Time Time::to_time(const int minutes) const {
Time tmp(minutes / 60, minutes % 60);
return tmp;
}

int Time::to_minute(const Time& t) const {
return t.m_hour * 60 + t.m_minute;
}

Time Time::operator+(const Time& t) const {
Time tmp;
tmp.m_minute = this->m_minute + t.m_minute;
tmp.m_hour = this->m_hour + t.m_hour;
if (tmp.m_minute >= 60) {
tmp.m_hour += 1;
tmp.m_minute %= 60;
}
return tmp;
}

Time Time::operator-(const Time& t) const {
return to_time(to_minute(*this) - to_minute(t));
}

Time Time::operator*(const double val) const {
Time tmp;
tmp.m_hour = this->m_hour * val;
tmp.m_minute = this->m_minute * val;
if (tmp.m_minute >= 60) {
tmp.m_hour += tmp.m_minute / 60;
tmp.m_minute %= 60;
}
return tmp;
}

// 友元函数的定义不用加friend
Time operator*(const double val, const Time& t) {
return t * val; // 又调用成员函数
}

// 友元
std::ostream& operator<<(std::ostream& os, const Time& t) {
os << t.m_hour << ":" << t.m_minute;
return os;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include "Time.h"

int main() {
Time t1(2, 30);
Time t2(1, 20);

(t1 + t2).show();
(t1 - t2).show();
t1.operator-(t2).show();

(t1 * 2).show();
(2 * t1).show(); // 调用友元函数

std::cout << t2 << std::endl;

return 0;
}

39. 类型转化(对象–>int || int –> 对象)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef DOG_H_
#define DOG_H_

#include <ostream>

class Dog {
public:
Dog(int age);
~Dog();
// Dog转化成int
// explicit表示强制显示类型转化
explicit operator int() const;
friend std::ostream& operator<<(std::ostream& os, Dog& dog);
private:
int m_age;
};
#endif //DOG_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "Dog.h"

Dog::Dog(int age) {
this->m_age = age;
}

Dog::~Dog() {

}

std::ostream& operator<<(std::ostream& os, Dog& dog) {
return os << dog.m_age;
}

Dog::operator int() const {
return this->m_age;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include "Dog.h"

int main() {
Dog dog = 100;
std::cout << dog << std::endl;

dog = (Dog) 101;
std::cout << dog << std::endl;

dog = Dog(102);
std::cout << dog << std::endl;

int age = dog;
std::cout << age << std::endl;

int age1 = (int) dog;
std::cout << age1 << std::endl;

int age2 = int(dog);
std::cout << age2 << std::endl;

return 0;
}

40. 拷贝构造 & =运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef MYSTRING_H_
#define MYSTRING_H_

#include <ostream>
#include <istream>

class Mystring {
public:
Mystring();
Mystring(const Mystring& s);
Mystring(const char* s);
~Mystring();

void create(const char* s);
int getLen() const;

Mystring& operator=(const Mystring& s);
Mystring& operator=(const char* s);
char& operator[](const int i);
const char& operator[](const int i) const;
friend std::ostream& operator<<(std::ostream& os, const Mystring& s);
friend std::istream& operator>>(std::istream& is, const Mystring& s);
friend int operator>(const Mystring& s1, const Mystring& s2);
friend int operator<(const Mystring& s1, const Mystring& s2);
friend int operator==(const Mystring& s1, const Mystring& s2);

static int howMany();


private:
char* m_str;
int m_len;
static int s_count;
};

#endif //MYSTRING_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include "Mystring.h"
#include <cstring>
#include <iostream>

int Mystring::s_count = 0;

Mystring::Mystring() {
create("c++");
s_count++;
std::cout << "Mystring() c++" << std::endl;
}

void Mystring::create(const char* s) {
m_len = strlen(s) + 1;
m_str = new char[m_len];
strncpy(m_str, s, m_len);
}

Mystring::Mystring(const Mystring& s) {
create(s.m_str);
s_count++;
std::cout << "copy Mystring()" << s.m_str << std::endl;
}

Mystring::Mystring(const char* s) {
create(s);
s_count++;
std::cout << "Mystring()" << s << std::endl;
}

Mystring::~Mystring() {
std::cout << "~Mystring()" << m_str << std::endl;
delete []m_str;
m_str = NULL;
m_len = 0;
s_count--;
}

Mystring& Mystring::operator=(const Mystring& s) {
std::cout << "operator=(const Mystring&)" << std::endl;
if (this == &s) return *this;
delete this->m_str;
create(s.m_str);
std::cout << "operator=" << m_str << std::endl;
return *this;
}

Mystring& Mystring::operator=(const char* s) {
std::cout << "operator=(const char*)" << std::endl;
create(s);
return *this;
}

int Mystring::getLen() const {
return this->m_len;
}

std::ostream& operator<<(std::ostream& os, const Mystring& s) {
return os << s.m_str;
}

std::istream& operator>>(std::istream& is, const Mystring& s) {
return is >> s;
}

int operator>(const Mystring& s1, const Mystring& s2) {
return std::strcmp(s1.m_str, s2.m_str) > 0;
}

int operator<(const Mystring& s1, const Mystring& s2) {
return std::strcmp(s1.m_str, s2.m_str) < 0;
}

int operator==(const Mystring& s1, const Mystring& s2) {
return strcmp(s1.m_str, s2.m_str) == 0;
}

char& Mystring::operator[](const int i) {
std::cout << R"(char& Mystring::operator[](const int i))";
return m_str[i];
}

const char& Mystring::operator[](const int i) const {
std::cout << R"(const char& Mystring::operator[](const int i) const)" << std::endl;
return m_str[i];
}

int Mystring::howMany() {
return s_count;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include "Mystring.h"

int main() {
Mystring s1 = "12345";
std::cout << s1 << std::endl;
std::cout << "--------" << std::endl;

Mystring t2 = "aaa000";
Mystring* s2 = new Mystring(t2);
std::cout << *s2 << std::endl;
delete s2;
std::cout << "--------" << std::endl;

Mystring t3 = "bbb000";
Mystring s3(t3);
std::cout << "--------" << std::endl;

Mystring t4 = "ccc000";
Mystring s4;
s4 = t4;
std::cout << "--------" << std::endl;

Mystring t5 = "abcd";
std::cout << t5[2] << std::endl;
std::cout << "--------" << std::endl;

Mystring t6 = "abc";
Mystring t7 = "abc";
std::cout << (t6 > t7) << std::endl;
std::cout << (t6 < t7) << std::endl;
std::cout << (t6 == t7) << std::endl;
std::cout << "--------" << std::endl;

Mystring t8 = "xyz";
t8[1] = 'z';
std::cout << t8 << std::endl;
std::cout << "--------" << std::endl;

const Mystring t9 = "opq";
std::cout << t9[2] << std::endl;
std::cout << "--------" << std::endl;

Mystring t10;
t10 = "www";
std::cout << "--------" << std::endl;

std::cout << Mystring::howMany() << std::endl;
std::cout << "--------" << std::endl;

std::cout << "main finish" << std::endl;
return 0;
}

41. 定位new运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include "mystring/Mystring.h"

const int BUF_SIZE = 512;
int main() {
char* buffer = new char[BUF_SIZE];

Mystring* s1 = new (buffer) Mystring("abc");
std::cout << *s1 << "-->" << s1 << std::endl;
//delete s1;

Mystring* s2 = new Mystring("bcd");
std::cout << *s2 << "-->" << s2 << std::endl;
delete s2;

delete []buffer;
return 0;
}

42. 类继承 & 多态 & 虚析构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef ANIMAL_H_
#define ANIMAL_H_

#include <string>

class Animal {
public:
Animal();
Animal(const std::string& name, int age);
// 这个要写成虚析构, 当释放父类指针的时候,才会去调用子类的析构
virtual ~Animal();
// 多态的标准动作, 虚函数
virtual void eat(const std::string& something);
std::string& whoami();

private:
std::string m_name;
int m_age;
std::string m_info;
};

class Dog : public Animal {
public:
enum Color {
BLACK,
WHITE,
OTHER
};
Dog();
Dog(const std::string& name, int age, Color color=BLACK);
Dog(Animal& animal, Color color=BLACK);
~Dog();
virtual void eat(const std::string& something);
Color getColor();
private:
Color m_color;
};

#endif //ANIMAL_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include "Animal.h"

Animal::Animal() {
std::cout << "Animal()" << std::endl;
}

Animal::Animal(const std::string& name, int age) {
std::cout << "Animal(name, age)" << std::endl;
m_name = name;
m_age = age;
}

Animal::~Animal() {
std::cout << "~Animal()" << std::endl;
}

void Animal::eat(const std::string& something) {
std::cout << "animal eat " << something << std::endl;
}

std::string& Animal::whoami() {
m_info = m_name + "," + std::to_string(m_age);
return m_info;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include "Animal.h"

Dog::Dog() : Animal() {
std::cout << "Dog()" << std::endl;
}

Dog::Dog(const std::string& name, int age, Color color) : Animal(name, age), m_color(color) {
std::cout << "Dog(name, age, color)" << std::endl;
}

Dog::Dog(Animal& animal, Color color) : Animal(animal), m_color(color){
std::cout << "Dog(animal, color)" << std::endl;
}

Dog::~Dog() {
std::cout << "~Dog()" << std::endl;
}

Dog::Color Dog::getColor() {
return m_color;
}

void Dog::eat(const std::string& something) {
std::cout << "Dog eat " << something << std::endl;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include "Animal.h"

int main() {
Animal* animal = new Animal("nani", 10);
std::cout << animal->whoami() << std::endl;
animal->eat("meat..");
delete animal;

std::cout << "--------" << std::endl;

Dog* dog = new Dog("what", 6);
std::cout << dog->whoami() << std::endl;
dog->eat("meat..");
delete dog;
std::cout << "--------" << std::endl;

Animal* p = new Dog("hello", 3);
std::cout << p->whoami() << std::endl;
delete p;

return 0;
}

43. 多态的实现

1
2
// 编译的时候,为每个类添加了一个成员,就是虚表指针。
// 创建对象的时候,为每个对象创建了虚函数表,保存了指向函数的地址,当调用函数的时候,就会使用真实对象的虚标指针去查表,然后调用

44. 纯虚函数

1
2
3
4
// 父类
virtual void fuck(Animal& animal) = 0;
// 子类
void fuck(Animal& dog);

45. 对象做成员 & valarray模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#ifndef STUDENT_H_
#define STUDENT_H_

#include <string>
#include <valarray>
#include <ostream>
#include <istream>

class Student {
private:
std::string m_name;
std::valarray<int> m_scores;

public:
Student(std::string name, std::valarray<int> scores);
~Student();
int getAgv() const;
const std::string& getName() const;
int& operator[](int i);
int operator[](int i) const;
friend std::ostream& operator<<(std::ostream& os, Student& stu);
friend std::istream& operator>>(std::istream& is, Student& stu);
};

#endif //STUDENT_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "Student.h"
Student::Student(std::string name, std::valarray<int> scores): m_name(name), m_scores(scores) {

}

Student::~Student() {

}

int Student::getAgv() const {
return m_scores.sum() / m_scores.size();
}

const std::string& Student::getName() const {
return m_name;
}

int& Student::operator[](int i) {
return m_scores[i];
}

int Student::operator[](int i) const {
return m_scores[i];
}

std::ostream& operator<<(std::ostream& os, Student& stu) {
return os << stu.m_name;
}

std::istream& operator>>(std::istream& is, Student& stu) {
return is >> stu.m_name;
}
1
2
3
4
5
6
7
8
#include <iostream>
#include "Student.h"

int main() {
Student s1("ming", {10, 20, 30, 40});
std::cout << s1 << "-->" << s1.getAgv() << std::endl;
return 0;
}

46. 私有继承 & 保护继承 & 公有继承

1
2
// 子类对外的接口,是基类和子类的叠加,选择较小的范围
// 对于私有继承,如果想访问基类的public成员,则可以使用using 把方法声明成public的

47. 类模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef STACK_H_
#define STACK_H_

template <class T>
class Stack {

public:
Stack();
bool isEmpty() const;
bool isFull() const;
bool push(T&);
bool pop(T&);

private:
static const int MAX_SIZE = 10;
T m_arr[MAX_SIZE];
int m_top;
};

#endif //STACK_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "Stack.h"

template<class T>
Stack<T>::Stack() {
m_top = 0;
}

template<class T>
bool Stack<T>::isEmpty() const {
return m_top <= 0;
}

template<class T>
bool Stack<T>::isFull() const {
return m_top >= Stack::MAX_SIZE;
}

template<class T>
bool Stack<T>::push(T& item) {
if (isFull()) return false;
m_arr[m_top++] = item;
return true;
}

template<class T>
bool Stack<T>::pop(T& item) {
if (isEmpty()) return false;
item = m_arr[--m_top];
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
#include "Stack.cpp"

int main() {
Stack<std::string> s;
std::string x = "aaa";
s.push(x);
std::string y = "bbb";
s.push(y);

std::string c;
s.pop(c);
std::cout << c << std::endl;

std::string b;
s.pop(b);
std::cout << b << std::endl;

return 0;
}

48. 模版 实例化&具体化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1.隐式实例化
Stack<std::string> s;

// 2.显示实例化
template <> class Stack<std::string>;

// 3.显示具体化
template <> class Stack<const char*> {
//...
}
// 因为有些操作,指针类型和实际类型没法做一个统一模版,只能查分处理
Stack<int> s;
Stack<const char*> s;

// 4.部分具体化
// 只具体化第二个类型
template <class T1, class T2> class Pair;
template <class T1> class Pair<T1, int>;

49. 成员模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 即模版类中的成员的类型,使用另一个模版类创建
#ifndef BETA_H_
#define BETA_H_

#include <iostream>

template <typename T> class beta {
private:
template <typename V> class Hold {
private:
V val;
public:
Hold(V v=0) : val(v) {}
V getVal() const {
return val;
}
void show() {
std::cout << val << std::endl;
}
};
Hold<T> a;
Hold<int> b;

public:
beta(T t, int x) : a(t), b(x) {}
void show() {
a.show();
b.show();
}
template <typename U>
U blab(U u, T t) {
return (a.getVal() + b.getVal()) * u / t;
}
};

#endif // BETA_H_
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include "beta.h"

int main() {
beta<double> obj(2.1, 10);
obj.show();

double x = obj.blab(3.0, 4.0);
std::cout << x << std::endl;
return 0;
}

50. 模版当参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifndef CRAB_H_
#define CRAB_H_

template <typename T>
class S {
public:
bool isEmpty() const;
bool isFull() const;
bool push(T&);
bool pop(T&);
};

// int 和 double也可以改成参数
// template <template <typename T> class S, typename I, typename D>
template <template <typename T> class S>
class crab {
private:
S<int> s1;
S<double> s2;

public:
bool push(int a, double b) {
return s1.push(a) && s2.push(b);
}

bool pop(int& a, double& b) {
return s1.pop(a) && s2.pop(b);
}
};

#endif // CRAB_H_
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include "stack/Stack.cpp"
#include "crab.h"

int main() {
// 相当于用Stack替换模版S
crab<Stack> what;
what.push(10, 5.5);

int a = 20;
double b = 3.5;
what.pop(a, b);
std::cout << a << ", " << b << std::endl;

return 0;
}

51. 模版类的友元

1
// 

52. 友元类

1
// 类中的所以方法都是友元函数

53. 嵌套类

1
// 没啥可说的

54. 异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

int test(int, int);

int main() {
int res = 0;
try {
res = test(9, 0);
} catch(const char* e) {
std::cout << "ming:" << e << std::endl;
}
std::cout << res << std::endl;
return 0;
}

int test(int a, int b) {
if (b == 0) {
throw "Error: b is zero!";
}
return a / b;
}

55. RTTI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#ifndef WD_H
#define WD_H

#include <string>
#include <iostream>

class Wolf {
protected:
std::string m_name;
public:
Wolf(const std::string name) : m_name(name) {

}

virtual void showName() {
std::cout << "wolf-->" << m_name << std::endl;
}
};

class Dog : public Wolf {
public:
Dog(const std::string name) : Wolf(name) {

}

void showName() {
std::cout << "dog-->" << m_name << std::endl;
}
};

#endif //WD_H


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include "wd.h"

int main() {
Wolf* f = new Wolf("haka");
f->showName();

Wolf* d = new Dog("bulu");
d->showName();

Dog* dp = dynamic_cast<Dog*>(d);
std::cout << dp << std::endl;

std::string* ep = dynamic_cast<std::string*>(d);
std::cout << ep << std::endl;

std::cout << "-------------" << std::endl;

int res = typeid(d) == typeid(f);
std::cout << res << std::endl;

int res1 = typeid(d) == typeid(ep);
std::cout << res1 << std::endl;

return 0;
}

56.类型转换

1
2
3
4
5
6
7
8
9
10
11
12
// 1. dynamic_cast: 父子类的转换 

// 2. const_cast: 同种类型的带const和不带const的转换(包含volatile)
const Wolf* a = new Wolf("haka");
Wolf* b = const_cast<Wolf*>(a);

// 3. static_cast: 隐式转换合法时,才能用

// 4. reinterpret_cast: 危险的转换
struct {short a; short b} dat;
long val = 0xA0123456;
dat* val = reinterpret_cast<dat*>(&val);

57. string类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <string>

using namespace std;
int main() {
string s1("wha's hhhh aabb ends well");
cout << s1 << endl;

string s2(10, 'a');
cout << s2 << endl;

string s3(s1);
cout << s3 << endl;

string s4 = "b";
s4 += s3;
cout << s4 << endl;

cout << s1[1] << endl;

string s5(s1, 10);
cout << s5 << endl;

string s6(&s1[2], &s1[11]);
cout << s6 << endl;

string s7(s1, 2, 12);
cout << s7 << endl;
return 0;
}
1
2
3
4
5
6
7
8
wha's hhhh aabb ends well
aaaaaaaaaa
wha's hhhh aabb ends well
bwha's hhhh aabb ends well
h
aabb ends well
a's hhhh
a's hhhh aab

58. getline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <fstream>
#include <string>

int main() {
std::ifstream fin;
fin.open("test.cpp");
if (fin.is_open() == false) {
std::cout << "open file error!" << std::endl;
return 1;
}

std::string tmp;
getline(fin, tmp, ';');
while (fin) {
std::cout << tmp << std::endl;
getline(fin, tmp, ';');
}
fin.close();
std::cout << "Done" << std::endl;
return 0;
}

59. 智能指针模版类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include "Dog.h"
#include <memory>

int main() {
Dog* dog = new Dog("gouzi");
dog->show();
{
// unique_ptr
// share_ptr
std::auto_ptr<Dog> pd(new Dog("wana"));
pd->show();
}
std::cout << "ending" << std::endl;
return 0;
}

60. vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <vector>
#include <algorithm>
#include "Dog.h"

using namespace std;

void show(Dog& dog) {
dog.show();
}

int main() {
vector<Dog> list;
Dog arr[] = {Dog("wawa1"), Dog("wawa2"), Dog("wawa3")};
for (int i=0; i<3; i++) {
list.push_back(arr[i]);
}

//vector<Dog>::iterator ip;
//for (ip=list.begin(); ip!=list.end(); ip++) {
// ip->show();
//}

cout << "---------" << endl;
for_each(list.begin(), list.end(), show);

return 0;
}

java面试准备

java基础

1.继承, 封装, 多态

2.集合框架list,map,set

  • ArrayList 底层是数组,默认10个元素,每次增加原来size的一半

  • LinkedList 底层是双向列表,可以当作列表用(add/remove), 可以当做队列用(offer/poll),也可以当作栈用(push/pop)

  • HashSet 底层用HashMap实现的

  • TreeSet 使用TreeMap实现

  • HashMap 哈希表的实现

  • Hashtable 相当于HashMap加了synchronized

  • LinkedHashMap 继承了HashMap,每个Entry加了前驱和后继指针,用于保持插入顺序

  • TreeMap 底层是二叉搜索树

  • Vector 与ArrayList类似,是线程安全的,一般不用

  • Stack 继承了Vector,栈的实现

3.反射

Class.forName(“xxx”);

4.泛型

泛型是一种类模版,就比如ArrayList,如果不用泛型,数组只能定义为Object,如果这样,编译的时候,就没办法做类型检查。为了兼容老版本的java,使用了类型擦除,也就是字节码中,不保存元素的类型信息。

参考

6.注解

7.其他

java并发

1. 并发编程的挑战

  • 并发是什么

    并发就是同时执行两段指令。

  • 为什么要用并发

    因为cpu的速度远远超过存储器和其他硬件的速度,如果不用并发,大部分时间,cpu是空闲的,为了充分利用cpu,所以并发。

  • 并发面对的问题

    1. 开了太多线程,线程切换会消耗一些资源,如果耗费在线程切换上资源太多,性能下降太快。

    2. 死锁,主要的死锁场景包括:

      • 一个线程获取多个锁
      • 一个线程,在锁内,占用了多个资源

2. java并发的实现原理

  1. volatile
  • 保证了原子性和可见性。
  • 加了volatile的变量,每次都会把缓存写会主内存,其他内核会嗅探到缓存改变,其他线程再读这个变量时,会从主内存读取
  1. synchronized
  • 保证了原子性,可见性,顺序性
  • 对象方法是对当前对象加锁,类方法是对class对象加锁。代码块是对配置的对象加锁
  • markword中保存锁信息,最后两位保存了锁标记
  • 锁升级,无锁(对象头中保存hashcode,分代年龄,锁标记),偏向锁(偏向线程id,偏向时间戳,分代年龄,锁标记),轻量级锁(指向栈中锁记录的指针),重量级锁(指向重量级锁的指针)
  1. 原子操作

    1. 锁, synchronized可以保持原子性
    2. CAS, 对于单个变量可以保持原子性。存在的问题:ABA问题,循环时间长和只能处理一个变量
  2. DCL单例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
    if (instance == null) {
    synchronized(Singleton.class) {
    if (instance == null) {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }
  3. java并发解决的三个性质

    1. 原子性, 原子操作
    2. 可见性, 工作内存,主内存
    3. 顺序性, 指令重排序, 内存屏障
  4. java线程状态机

  • New
  • Runnable
  • Terminated
  • Blocked
  • Wait
  • Time wait
  • 怎样查看线程状态?先jps找到对应的进程id,再jstack pid
  • juc包里的Lock,阻塞时,是wait状态,因为Lock使用的LockSupport.park
  1. 生产者消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  public class PC {

public static void main(final String[] args) {
final PC pc = new PC();

for (int i = 0; i < 2; i++) {
new Thread(pc.new Productor(), "product_thread_" + i).start();
}

for (int i = 0; i < 20; i++) {
new Thread(pc.new Customer(), "custom_thread_" + i).start();
}
}

private int count = 0;
private final Object lock = new Object();

private class Productor implements Runnable {
@Override
public void run() {
while (true) {
synchronized (lock) {
while (count == 10) {// 这个要用while, 防止下次被同类唤醒后,再次累加
try {
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
count++;
lock.notifyAll();// 这里要用notifyAll, 防止唤醒的线程是同类,会导致假死
}
}
}
}

private class Customer implements Runnable {
@Override
public void run() {
while (true) {
synchronized(lock) {
while (count == 0) {
try {
lock.wait();
} catch (final Exception e) {
e.printStackTrace();
}
}
count--;
lock.notifyAll();
}
}
}
}

}
  1. join()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TestJoin {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("nani");
}
});
t.start();

try {
t.join();// join内部循环判断t线程是否isAlive, 如果还活着则调用wait
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread");
}
}
  1. Lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
public static void main(String[] args) {
TestLock ins = new TestLock();
for (int i=0; i<5; i++) {
new Thread(ins.new Productor(), "productor-" + i).start();
}
for (int i=0; i<5; i++) {
new Thread(ins.new Customer(), "customer-" + i).start();
}
}

private boolean on = true;
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition fullCond = lock.newCondition();
private Condition emptyCond = lock.newCondition();

private class Productor implements Runnable {
@Override
public void run() {
while (on) {
try {
lock.lock();
while (count == 10) {
try {
fullCond.await();
} catch (Exception e) {
e.printStackTrace();
}
}
count++;
emptyCond.signalAll();
} finally {
lock.unlock();
}
}
}
}

private class Customer implements Runnable {
@Override
public void run() {
while (on) {
try {
lock.lock();
while (count == 0) {
try {
emptyCond.await();
} catch (Exception e) {
e.printStackTrace();
}
}
count--;
fullCond.signalAll();
} finally {
lock.unlock();
}
}
}
}
}
  1. 读写锁(读读共享,读写互斥, 写写互斥)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class TestRWLock {
public static void main(String[] args) {
TestRWLock test = new TestRWLock();

Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
test.read();
}
}, "t1");
t1.start();

Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
test.write();
}
}, "t2");
t2.start();
}

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

private void read() {
try {
lock.readLock().lock();
System.out.println("read " + System.currentTimeMillis());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.readLock().unlock();
}
}

private void write() {
try {
lock.writeLock().lock();
System.out.println("write " + System.currentTimeMillis());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.writeLock().unlock();
}
}
}
  1. 队列同步器(AQS)

    • 独占状态的获取和释放: 同步器维护了一个双向链表,头节点是获取到锁的线程,如果有新线程请求锁失败,则加入到队尾,并且保持自旋。当头节点释放锁后,会唤醒后继节点,尝试获取锁。
    • 共享状态的获取和释放
  2. ConcurrentHashMap

  3. ConcurrentLinkedQueue

  4. 阻塞队列

  5. 原子操作类

  6. 线程池

    • 工作原理:当添加新任务时,先判断核心线程池是否满了,如果是再判断阻塞队列是否满了,如果是再判断线程池中的线程是否全再工作,如果是则抛出拒绝任务的异常

JVM

  • java内存布局:

    1. 方法区
    2. 程序计数器
    3. 虚拟机栈
    4. 本地方法栈
  • GC的几个问题:

    1. 怎样找到垃圾
      通过搜索GC root,如果没有引用被GC根指向,则认为是垃圾。

      引用的类型分四种:

      • 强引用, 永远不回收
      • 软引用, 主要用与缓存,如果gc一次之后,还是内存不足,才会回收。
      • 弱引用, 只能生存到下一次gc之前
      • 虚引用, 相当于没有引用,只是为了回收前,收到一个通知

      GC root是什么:

      • 虚拟机栈空间中,局部变量表的引用
      • Navite栈中,变量的引用
      • 方法区中,静态变量的引用
      • 方法区中,常量的引用
    2. 怎样回收
      分代回收。

      回收算法:

      • 标记清除
      • 标记整理
      • 拷贝

      分代:

      • 新生代, 8:1:1,使用拷贝算法,每次把需要保留的对象拷贝到幸存区, minor gc 频繁
      • 老年代, 使用标记整理算法, full gc 不频繁
      • 永久代, 主要是对不用的常量和对类的卸载,很少做回收
    3. 何时回收

    4. 怎样分配对象内存

      1. 优先在eden区分配,如果eden内存不足,则gc一次
      2. 如果age超过阈值,则移动到老年代
      3. 如果是大对象,直接分配到老年代
      4. 动态分配,如果幸存区中的对象,相同age的总和超过一半,就把大于或等于这个年龄的对象移动到老年代
    5. 垃圾回收器:G1,CMS。。。

  • 类加载

    1. 加载的是什么
    2. 怎样加载
    3. 加载之后,怎样运行,栈帧是怎样的

shell script

shell

0.man

1
2
3
4
5
6
7
8
9
# 1、Standard commands (标准命令)
# 2、System calls (系统调用)
# 3、Library functions (库函数)
# 4、Special devices (设备说明)
# 5、File formats (文件格式)
# 6、Games and toys (游戏和娱乐)
# 7、Miscellaneous (杂项)
# 8、Administrative Commands (管理员命令)
# 9 其他(Linux特定的), 用来存放内核例行程序的文档。

1.type 查看是否是内建命令

1
2
3
#查看命令查找的顺序
type -a ls
type -a cd

2.echo 打印变量

1
2
hello=kkk
echo $hello

3.export/unset 设置/取消环境变量

1
2
export hello
unset hello

4.反单引号

1
2
myver=kkk`uname -r`
myver=aaa$(uname -r)

5.set 查看所有变量

6.env/export 查看环境变量

7.声明一个int类型变量,并设置为10以内的随机数

1
2
declare -i num=$RANDOM%10
echo $num

8.当前shell的pid

1
echo $$

9.上一个命令的回传码

1
echo $?

10.等待用户输入

1
2
3
4
read myname
# -p 提示信息
# -t 等待秒数
read -p "kkk-->" -t 3 myname

11.声明某个类型的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#声明int类型
declare -i sum=1+2

#声明一个环境变量
declare -x sum

#声明为只读变量,只有注销再登陆,才能恢复
declare -r sum
sum=kkk
-bash: sum: readonly variable

#设置环境变量
declare -x sum
#取消环境变量
declare +x sum

#查看变量类型
declare -p sum

#定义数组
num[0]=a
num[1]=b
num[2]=c
echo ${num[2]}

12.设置配额

1
ulimit -a

13.变量内容的删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
#test=$PATH

#删除变量内容
#从前向后,最短匹配
echo ${test#/*bin:}
#从前向后,最长匹配
echo ${test##/*bin:}

#从后向前,最短匹配
echo ${test%:*sbin*}
#从后向前,最长匹配
echo ${test%%:*sbin*}

mypath=/var/spool/mail/root
#如果只想保留路径
echo ${mypath%/*}
#如果只想保留文件名
echo ${mypath##/*/}

14.变量内容的替换

1
2
#替换, 把kkk替换成aaa
echo ${mypath/kkk/aaa}

15.测试变量

1
2
3
4
5
6
7
8
#如果hello不存在或者是空,则打印默认值ming
echo ${hello:-ming}

#如果hello不存在或者为空,则把两个变量都改成默认值
echo ${hello:=ming}

#如果hello不存在或者为空,则把默认值输出到标准错误
echo ${hello:?ddd}

16.命令的别名

1
2
3
4
5
#新增别名
alias lm='ls -l | more'

#删除别名
unalias lm

17.history

1
2
#查看命令历史
history

18.登陆的欢迎信息

1
2
3
#/etc/issue
#/etc/issue.net
#/etc/motd

19.重定向(>, >>, <, <<)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 标准输出重定向到11.log
find / -name .bashrc > 11.log

# 标准输出追加到11.log
find / -name .bashrc >> 11.log

# 标准输出追加到11.log, 标准错误重定向到err.log
find / -name .bashrc > 11.log 2>err.log

# 标准输出追加到11.log, 标准错误重定向到黑洞
find / -name .bashrc > 11.log 2> /dev/null

# 标准输出追加到11.log, 标准错误重定向到标准输出
find / -name .bashrc > 11.log 2>&1

# 标准输入从11.log中读取,标准输出重定向到22
cat > 22 < 11.log

# <<表示如果输入nani,则结束输入,相当于输入EOF
cat > 22 << "nani"

20.多条命令(;/,/&&/||)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 按顺序执行多条命令
hello=aaa; echo $hello; echo ${hello}bb

# 如果ls aa返回0,则不执行创建目录,否则,执行创建
ls aa || mkdir aa
# 如果不存在,则把标准错误扔进黑洞
ls aa 2> /dev/null || mkdir aa

# 如果aa存在,则在aa下创建22
ls aa && touch ./aa/22

# 无论bb是否存在,第三个命令都会执行
ls bb || mkdir bb && touch ./bb/11

# 如果cc存在,则显示exist, 否则显示not exist
ls cc 2>/dev/null && echo "exist" || echo "not exist"

21.管道

1
2
# 把前一个标准输出作为下一个命令的标准输入
ls -l /etc | grep sys* | less

22.cut, grep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
# 输出第二段/usr/local/bin
echo $PATH | cut -d ":" -f 2

# 把环境变量,去掉declare -x
export | cut -d ' ' -f 3

# 功能同上
export | cut -c 12-

# 获取登陆列表中的第一段
last | cut -d ' ' -f 1

# 过滤包含ming的行
last | grep "ming"

# 取出环境变量中,包含PATH的行,并且只显示key=value部分
export | grep "PATH" | cut -d ' ' -f3

# 筛选文件11中,包含k或s的行
grep -n "[ks]" 11

23.sort, wc, uniq

1
2
3
4
5
6
7
8
9
10
11
12
# 对/etc/passwd文件按:分隔的第3列, 按数字大小排序,然后,输出包含bash的行
cat /etc/passwd | sort -t ":" -k 3 -n | grep "bash"

# 对登陆列表排序,去重,统计行数
last | cut -d " " -f 1 | sort | uniq -c

# 统计标准输入中的行数-l, 单词数-w, 字符数-m
cat 11 | wc -l

# 统计这个月登陆了几次
last | grep "Dec" | wc -l
last | grep $(date | cut -d " " -f 2) | grep "[a-zA-Z]" | grep -v "wtmp" | wc -l

24.双重重定向tee

1
2
# 保存一份标准输出到文件
ls -l | tee -a 11.log

25.字符删除或替换tr

1
2
3
4
5
6
7
8
# 小写改大写
last | tr "a-z" "A-Z"

# 把PATH中的:替换成换行
echo $PATH | tr -s ":" "\n"

# 然后删除所有的/
echo $PATH | tr -s ":" "\n" | tr -d "/"

26.grep

1
2
3
4
5
6
7
8
9
# 搜索包含the的行, 忽略大小写-i, 并显示行号-n
grep -ni "the" regular_express.txt

# 搜索正则,并显示行号
grep -n "t[ae]st" regular_express.txt

# 搜索文件中,非空白行,且不是#号开头的行
cat /etc/systemd/user.conf | grep -v "^$" | grep -v "^#"

27.sed(新增,删除,替换)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 删除文件中2-5行
nl /etc/passwd | sed "2,5 d"

# 删除第5到最后一行
nl /etc/passwd | sed "5,$ d"

# 在2-5行末尾追加what kkk
nl /etc/passwd | sed "2,5 a what kkk"

# 在第2行前面插入what kkk
nl /etc/passwd | sed "2 i what kkk"

# 把2-5行替换成一行bbbbbb
nl /etc/passwd | sed "2,5 c bbbbbb"

# 打印2-5行
nl /etc/passwd | sed -n "2,5 p"

# 把忽略大小写的the替换成THE
nl regular_express.txt | sed "s/[tT][hH][eE]/THE/g" | grep -i "THE"

# 截取ip
/sbin/ifconfig eth0 | grep "inet" | sed "s/^.*inet.//" | sed "s/ netmask.*//"

# 搜索文件中带有MAN的行,删掉#之后的注释,再删掉空白行
cat /etc/manpath.config | grep "MAN" | sed "s/#.*//g" | sed "/^$/d"

# 把末尾的.替换成!
sed -i "s/\.$/\!/g" regular_express.txt

# 在最后一行追加kkkkkbbb
sed -i "$ a kkkkkbbb" regular_express.txt

28.egrep, fgrep

1
2
3
4
5
6
7
8
9
10
11
# grep对+要转义才能当作正则的+
grep "go\+d" regular_express.txt

# egrep直接支持+
egrep "go+d" regular_express.txt

# fgrep完全不支持正则, 单纯搜索go+d
fgrep "go+d" regular_express.txt

# 搜索文件中包含*的文件的文件名
find / -type f | xargs -n 10 fgrep -l "*"

29.printf

30.awk

1
2
3
4
5
6
# 获取登陆信息的第1和3列
last | awk '{print $1 "\t" $ 3}'

# 获取第三列小于10的行
cat /etc/passwd | awk 'BEGIN {FS=":"} $3<10 {print $1 "\t" $3}'

31.diff

1
2
3
4
5
6
7
# 删除第4行,并替换第6行
cat /etc/passwd | sed -e "4 d" -e "6 s/^.*$/no six line/" > passwd

diff /etc/passwd passwd

cmp /etc/passwd passwd

32.date

1
2
# 格式化日期
date +"%Y%m%d"

33.test

1
2
# -e 测试文件是否存在
test -e test1.sh && echo "exist" || echo "not exist"

34.shell script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/bin/bash
# author:ming

#echo 'hello world'

#read -p "please input your name:" filename
#touch "${filename}_$(date +'%Y%m%d')"

#read -p "please input first value:" first
#read -p "please input second value:" second
#echo $(( $first * $second ))

#pwd
#read -p "please input filename:" filename
##test ! -e $filename && echo "$filename does not exist" && exit 0
#test -e $filename || (echo "$filename does not exist" ; exit 0)
#test -d $filename && echo "$filename is directory" || echo "$filename is regular file"
#test -x $filename && echo "executable" || echo "no execute"

#str1=ming
#str2=ming11
#[ "$str1" == "$str2" ]
#echo $?

#read -p "Please choise Y/N:" res
#[ "$res" == "Y" -o "$res" == "y" ] && echo "OK, continue" && exit 0
#[ "$res" == "N" -o "$res" == "n" ] && echo "Oh, interrupt!" && exit 0
#echo "I don't know what your choice is" && exit 0

#echo "program name:$0"
#echo "args count:$#"
#echo "args count:$@"
#shift 2
#[ "$#" -lt "2" ] && echo "args too short"
#echo "args:$*"
#echo "first:$1"
#echo "second:$2"

read -p "Please chose Y/N:" res
if [ "$res" == "Y" ] || [ "$res" == "y" ]; then
echo "OK, continue"
elif [ "$res" == "N" ] || [ "$res" == "n" ]; then
echo "Oh, interrupt"
else
echo "I don't know what your choice is"
fi


exit 0

35.用户管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 创建用户
useradd -g 初始组 -G 次要组 用户名

# 设置密码
passwd ming2

# 查看当前用户所属的组
groups

# 切换有效组, exit可以切换回上一次的结果
newgrp ming
exit

# 删除用户, -r 删除用户主文件夹
userdel -r ming

# 用户指纹
finger ming

36.定时任务at

1
2
3
4
5
6
7
8
9
10
11
# 启动atd服务
/etc/init.d/atd start

# 5分钟后,执行任务
at now + 5 minutes

# 查看任务列表
atq

# 删除4号任务
atrm 4

37.循环定时任务crontab

1
2
3
4
5
6
7
8
9
10
11
# 查看服务进程是否启动
/etc/init.d/cron status

# 查看所有任务
crontab -l

# 编辑任务
crontab -e

# 每隔一分钟,追加一行日志文件
*/1 * * * * echo "$(date)" >> /home/ming/test/corn.log

38.前后台程序jobs/fg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 在后台执行tar命令, 标准错误重定向到标准输出,标准输出写入到tar.log中
tar -cvf test.tar test/ > tar.log 2>&1 &

# 查看当前shell及其子进程
ps -l

# ctrl + z可以把vim 11.log切换到后台并暂停
vim 11.log

# 查看所有后台进程
jobs -l

# 把号码为2的后台进程切换到前台
fg %2

# 查看信号的值
kill -l

# 正常方式终止1号任务
kill -15 %1

39.nohup

1
2
3
4
5
6
7
# 忽略父进程的信号
nohup ping www.google.com &

# 杀掉ping的后台进程
kill -2 $(ps -aux | grep ping | awk '{ print $2 }' | head -n 1)


40.进程管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查看所有进程
ps -aux

# 查看进程树
ps axjf

# 查看当前的shell进程之下的子进程
ps -l

# top 命令,每隔2秒刷新一次,$$ bash pid
top -d 2 -p $$

# 查看最耗资源的进程
top -d 2
shift + p

# 查找僵尸进程
pstree -Aup

41.查看系统信息

1
2
3
4
5
6
# 查看系统信息
uname -a

# 查看系统负载
uptime

42.查看网络状态

1
2
3
4
5
# 查看tcp的连接
netstat -aptl

# 查看udp的连接
netstat -apul

43.查看内核产生的信息

1
dmesg | more

44.查看正在使用当前文件的进程

1
fuser -uv .

45.查看正在被使用的文件

1
lsof -u ming -Ua

46.linux启动流程

1
2
3
4
# 1.BIOS
# 2.grub
# 3.kernel
# 4.systemd

47.systemd

1
2
# http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html

48.chown

1
2
# 修改 用户:组
chown ming:ming android/

49.tar

1
2
3
4
5
6
7
8
# 压缩
tar -cvf test.tar test/

# 解压到当前目录
tar -xvf test.tar

# 不解压, 只列出压缩包内的文件列表
tar -tvf test.zip

面试-JNI

Android NDK 从入门到精通(汇总篇)(https://blog.csdn.net/afei__/article/details/81290711)

1.JNI demo

1.1 写java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestJni {
static {
System.load("/Users/liuming/_work/java/demo/jni/libhello.so");
}

private native static String hello(String message);

public static void main(String[] args) {
String text = hello("ming");
System.out.println("Test jni-->" + text);

String text1 = hello(null);
System.out.println("Test jni-->" + text1);
}
}

1.2 生成头文件

1
2
# 自动生成native方法的头文件
java -h . TestJni.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJni */

#ifndef _Included_TestJni
#define _Included_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJni
* Method: hello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_TestJni_hello
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif

1.3 实现头文件中声明的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "TestJni.h"
#include <string.h>
#include <stdlib.h>
#include "/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h"

JNIEXPORT jstring JNICALL Java_TestJni_hello(JNIEnv * env, jclass cls, jstring str) {
if (!str) {
return (*env)->NewStringUTF(env, "");
}

const char* s = (*env)->GetStringUTFChars(env, str, 0);
const char* head_str = "nani:";
char* dest = malloc(strlen(head_str) + strlen(s) + 1);
if (dest == NULL) {
fprintf(stderr, "%s\n", "error: malloc fail!");
return (*env)->NewStringUTF(env, "");
}

strcpy(dest, head_str);
const char* res = strcat(dest, s);

if (!dest) free(dest);
if (!s) (*env)->ReleaseStringUTFChars(env, str, s);

return (*env)->NewStringUTF(env, res);
}

1.4 编译

1
2
3
4
5
6
7
8
# -I表示在这个目录中搜索用到的头文件, 这因为没写完整的jni.h的全路径,所以要加上
gcc -c -I "/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers" hello.c

# 把hello.o打包成libhello.so
gcc -shared -o libhello.so hello.o

# .java文件编译成.class
javac TestJni.java

1.5 运行

1
java TestJni

2. 静态绑定&动态绑定

静态绑定

java_包名_类名_方法名
1
JNIEXPORT jstring JNICALL Java_TestJni_hello(JNIEnv * env, jclass cls, jstring str);

动态绑定

原理:当java层调用System.load(“xxx.so”)时,会在so中搜索JNI_OnLoad, 然后被调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
jstring stringFromJNI(JNIEnv *env, jclass clazz) {
return (*env)->NewStringUTF(env, "what are you fucking doing here?");
}

jint add(JNIEnv *env, jclass clazz, jint a, jint b) {
return a + b;
}

// 方法签名
// | java类型 | 类型描述符 |
// | ---- | ---- |
// | int | I |
// | long | J |
// | byte | B |
// | short | S |
// | char | C |
// | float | F |
// | double | D |
// | boolean | Z |
// | void | V |
// | 其他引用类型 | L+类全名+; |
// | 数组 | [ |
// | 方法 | (参数)返回值 |
JNIEXPORT jint JNICALL
RegisterNatives(JNIEnv *env) {
jclass clazz = (*env)->FindClass(env, "TestJni");
if (clazz == NULL) {
printf("con't find class: TestJni");
return JNI_ERR;
}
JNINativeMethod methods_TestJni[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},
{"add", "(II)I", (void *) add}
};
int len = sizeof(methods_TestJni) / sizeof(methods_TestJni[0]);
return (*env)->RegisterNatives(env, clazz, methods_TestJni, len);
}

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jint result = RegisterNatives(env);
printf("RegisterNatives result: %d\n", result);
return JNI_VERSION_1_6;
}

3.Android framework里,主要的jni的点

数据结构和算法-字符串匹配

1. BF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
char[] arr1 = haystack.toCharArray();
char[] arr2 = needle.toCharArray();
for (int i=0; i<arr1.length; i++) {
int p1 = i;
int p2 = 0;
while (p1 < arr1.length && p2 < arr2.length && arr1[p1] == arr2[p2]) {
p1++;
p2++;
}
if (p2 == arr2.length) return i;
}
return -1;
}

2. RK

3. BM

4. KMP

5. Trie树

6. AC自动机

数据结构和算法-动态规划

怎样判断是否是动态规划问题

  1. 有多少种走法
  2. 求最大最小值
  3. 是否存在

线性DP

1. 单串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int lengthOfLIS(int[] nums) {
int len = nums.length;
if (len == 0) return 0;
int res = 0;
int[] dp = new int[len];
for (int i=0; i<nums.length; i++) {
// 从nums[0]...nums[i-1]中,找到小于nums[i]的项,并且从中选择最大的一个
int max = 0;
for (int j=0; j<i; j++) {
if (nums[j] < nums[i]) {
max = Math.max(max, dp[j]);
}
}
dp[i] = max + 1;
res = Math.max(res, dp[i]);
}
return res;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int maxSubArray(int[] nums) {
if (nums.length == 0) return 0;
int[] dp = new int[nums.length];
dp[0] = nums[0];
int res = dp[0];
for (int i=1; i<nums.length; i++) {
int preSum = (dp[i-1] > 0 ? dp[i-1] : 0)
// 连续子序
dp[i] = preSum + nums[i];
// 不连续子序
// dp[i] = preSum + (nums[i] > 0 ? nums[i] : 0);
res = Math.max(res, dp[i]);
}
return res;

1
2
3
4
5
6
7
8
9
10
11
12
public int rob(int[] nums) {
int N = nums.length;
if (N == 0) return 0;
int[][] dp = new int[N][2];
dp[0][0] = 0;
dp[0][1] = nums[0];
for (int i=1; i<N; i++) {
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1]);
dp[i][1] = (i < 2 ? 0 : Math.max(dp[i-2][0], dp[i-2][1])) + nums[i];
}
return Math.max(dp[N-1][0], dp[N-1][1]);

区间DP

树型DP

背包dP

排序

1. 冒泡排序

1
2
3
4
5
6
7
8
9
10
// 从后向前冒泡,每次选出一个最小的
private static void bubbleSort(int[] arr) {
for (int i=0; i<arr.length-1; i++) {
for (int j=i; j<arr.length-1; j++) {
if (arr[j] > arr[j+1]) {
swap(arr, j, j+1);
}
}
}
}

2. 插入排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 对于一个已经排好序的数组,先找到插入的位置,然后移动后续的元素,最后插入
private static void insertSort(int[] arr) {
for (int i=1; i<arr.length; i++) {
int tmp = arr[i];
int index = -1;
for (int j=i-1; j>=0; j--) {
if (tmp < arr[j]) {
arr[j+1] = arr[j];
index = j;
} else {
break;
}
}
if (index >= 0) arr[index] = tmp;
}
}

3. 选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 每次扫描数组,找到最小的值,然后交换
private static void selectSort(int[] arr) {
for (int i=0; i<arr.length-1; i++) {
int min = arr[i];
int index = -1;
for (int j=i+1; j<arr.length; j++) {
if (arr[j] < min) {
min = arr[j];
index = j;
}
}
if (index > 0) swap(arr, i, index);
}
}

4. 希尔排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 相当于拓展了插入排序的步长
public static void shellSort(int[] arr) {
int length = arr.length;
int temp;
for (int step = length / 2; step >= 1; step /= 2) {
for (int i = step; i < length; i++) {
temp = arr[i];
int j = i - step;
while (j >= 0 && arr[j] > temp) {
arr[j + step] = arr[j];
j -= step;
}
arr[j + step] = temp;
}
}
}

5. 归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 先拆分,再合并
private static int[] mergeSort(int[] sourceArr) {
int[] arr = Arrays.copyOf(sourceArr, sourceArr.length);
if (arr.length < 2) return arr;
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
return merge(mergeSort(left), mergeSort(right));
}

// 合并两个有序数组
private static int[] merge(int[] arr1, int[] arr2) {
int[] res = new int[arr1.length + arr2.length];
int p1 = 0;
int p2 = 0;
int i = 0;

while (p1 < arr1.length && p2 < arr2.length) {
if (arr1[p1] < arr2[p2]) {
res[i++] = arr1[p1++];
} else if (arr1[p1] > arr2[p2]) {
res[i++] = arr2[p2++];
} else {
res[i++] = arr1[p1++];
res[i++] = arr2[p2++];
}
}

while (p1 < arr1.length) {
res[i++] = arr1[p1++];
}

while (p2 < arr2.length) {
res[i++] = arr2[p2++];
}

return res;
}

6. 快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static void quickSort(int[] arr) {
qsort(arr, 0, arr.length-1);
}

// 分治法,先拆分分区,然后,分别对分区排序
private static void qsort(int[] arr, int left, int right) {
if (right < left) return;
int index = partition(arr, left, right);
qsort(arr, left, index-1);
qsort(arr, index+1, right);
}

private static int partition(int[] arr, int left, int right) {
// 设定基准值(pivot)
int pivot = left;
int index = pivot + 1;
for (int i = index; i <= right; i++) {
if (arr[i] < arr[pivot]) {
swap(arr, i, index);
index++;
}
}
swap(arr, pivot, index - 1);
return index - 1;
}

7. 堆排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 每次堆化后,都能选出一个最大的元素
// 1. 堆化,对所有非叶子节点,做堆化
// 2. 删除头节点
private static void heapSort(int[] arr) {
for (int n=arr.length; n>1; n--) {
heapify(arr, n);
// 把堆顶的元素,移动到末尾,然后重新堆化
swap(arr, 0, n-1);
}
}

// 堆化大顶堆
private static void heapify(int[] arr, int n) {
for (int i=(n/2)-1; i>=0; i--) {
// 找到比较小的叶子节点的索引
int j = i * 2 + 1;
if (i*2+2 < n && arr[i*2+1] < arr[i*2+2]) {
j = i * 2 + 2;
}
if (arr[j] > arr[i]) {
swap(arr, i, j);
}
}
}

8. 计数排序

1
2
// 每个桶只存储单一键值
// 比如按年龄排序

9. 桶排序

1
// 每个桶存储一定范围的数值

10. 基数排序

1
2
// 根据键值的每位数字来分配桶
// 把每个数的值,当作数组索引

设计模式

创建型模式

  • 单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static volatile Singleton instance;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
  • 简单工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ShapeFactory {

//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
  • 抽象工厂
1
2
3
4
5
6
7
8
9
10
11
// 创建工厂的工厂
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
  • 建造者模式
1
// 使用多个简单的对象一步一步构建成一个复杂的对象
  • 原型模式
1
// 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能
  • 适配器模式
1
// 统一一套接口给外部使用,不同的适配器,可以把不同的数据类型整合到一个接口里
  • 桥接模式
1
// 把变化的部分隔离开,当发生变化的时候,互不影响
  • 过滤器模式
1
// XXXFilter 就做简单数据筛选
  • 组合模式
1
2
// View里包含子view
// 组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象
  • 装饰器模式
1
2
// java中的IO流, 一层套一层的装饰
// 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构
  • 外观模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;

public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}

public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
  • 享元模式
1
2
// 共享原型的模式
// JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面
  • 代理模式
1
// 为其他对象提供一种代理以控制对这个对象的访问
  • 责任链模式
1
// okhttp里面的拦截器
  • 命令模式
1
2
// Message就是命令,Message里持有Handler的引用
// 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
  • 解释器模式
1
2
3
// 编译器
// 解析外链
// 提供一个接口,解析表达式
  • 迭代器模式
1
2
3
// JAVA 中的 iterator。
// hasNext/next
// 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示
  • 中介者模式
1
2
// 多对多关系,转成多对1关系
// 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
  • 备忘录模式
1
// 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
  • 观察者模式
1
// 点击监听
  • 状态模式
1
// 就是状态机
  • 策略模式
1
2
3
4
5
6
7
8
9
10
11
12
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}

ARTS_第5周

Algorithm

Review

Tip

  1. libev是什么,怎么用,什么场景下用?
  • libev是一个事件驱动库
  • 任何场景下都可用,著名的ss就是基于这个库写的
  • 以io事件为例,使用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
int main(int argc, char const *argv[])
{
init();
int listenfd = create_and_bind();

struct ev_loop *loop = ev_default_loop(0);
struct ev_io w_accept;
ev_io_init(&w_accept, accept_cb, listenfd, EV_READ);
ev_io_start(loop, &w_accept);
ev_run(loop, 0);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
int clientfd;
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
clientfd = Accept(watcher->fd, (struct sockaddr *)&clientaddr, &len);
print_accept_info(clientfd, &clientaddr);

struct ev_io *w_client = (struct ev_io*) malloc (sizeof(struct ev_io));
ev_io_init(w_client, client_cb, clientfd, EV_READ);
ev_io_start(loop, w_client);

clients[clientfd] = clientfd;
}

void client_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
int n = handle_client(watcher->fd);
if (n == 0) {
int fd = watcher->fd;
ev_io_stop(loop, watcher);
Close(fd);
free(watcher);
clients[fd] = -1;
printf("client=%d, client exit...\n", fd);
}
}
  1. gcc 链接第三方库,第三方库的名字从哪来?
1
gcc -v -o ev_server file_server.c ../demo/wrap.c -lev
  • -lev表示静态链接libev.a
  • 库路径: /usr/lib/x86_64-linux-gnu/libev.a
  • 另外,gcc -v 可用打印详细信息, 可用看到库搜索路径:
1
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/9/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/9/../../../:/lib/:/usr/lib/
  1. 段错误的core文件,怎么分析, 出错的位置显示了一个内存地址?

  2. tcpdump怎么用?

  3. nc怎么用

  4. find命令,搜索文件, 默认递归搜索

1
find / -type f -name 'libev*'

Share