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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// 写一个打印数组的方法
void print(int* arr,int length){
// 获取数组的大小
for (int i = 0; i < length; i++)
{
printf("%d ",arr[i]);
}
}

struct MyStruct
{
char* name;
};

typedef struct MyStruct Student;// 给 MyStruct 取一个别名叫做 Student

// int32_t ,可以对基本数据类型取别名
typedef int int32;

//数组作为参数传递
/*void main(){
int a;// 告诉c和c++编译器开辟一块连续大小的 4 字节的内存空间
int arr[] = {1,2,3,4,5,6};
// 求数组的大小?
int size = sizeof(arr) / sizeof(int);
printf("size = %d\n",size);
print(arr,size);//数组作为参数传递,会退化成为一个指针,传递的是首地址,所以必须传递数组的长度过去,否则无法打印数组
getchar();
}
//数据类型分析

void main(){
int a;// 告诉c和c++编译器开辟一块连续大小的 4 字节的内存空间
int arr[] = { 1, 2, 3, 4, 5, 6 };// arr 数据类型的内存大小空间 24

a = 10;

// 12 , 16 , 12 , 36 36?
printf("%d, %d, %d ,%d", arr, arr+1, &arr, &arr+1);


getchar();
}
// 2.数据类型剖析
// 数据类型的本质:一块连续大小的内存空向
// 数据类型的别名:int32_t
// void指针数据类型:vid"代表任意的数据类型的指针
// 3.变量的本质
// 变量的本质:固定内存块大小(一块连续大小的内存空向) 的别名,通过变量可以去操作一块内存上的数据变量的三要素:内存大小,名称,作用域
void main(){
Student stu = { "Darren" };

int32 number1 = 100;

printf("number1 = %d",number1);

// 获取 Bitmap ,锁定画布 void**
// void number;// 错误

// memcpy();// 拷贝内存,什么用 void*

getchar();
}


char* getStr1(){
char* str = "12345";
return str;
}

char* getStr2(){
char* str = "12345";
return str;
}

char* getStr3(){
char buff[128];

strcpy(buff,"12345");

return buff;
}

char* getStr4(){
char* buff = malloc(128); // char* buff char* 是四字节的数据类型,存的是堆区开辟的 128 个字节的首地址
strcpy(buff, "12345");

return buff;
}

void main(){

char* str1 = NULL;

char* str2 = NULL;

str1 = getStr1();

str2 = getStr2();

char* str3 = getStr3();

char* str4 = getStr4();

printf("%d , %d, %s, %s",str1,str2,str3,str4);// str3 = 12345

getchar();
}
*/


void main(){
int a = 10;

int b = 10;
// 0508 , 0496 a 的地址要大于 b 的地址 (debug)
// 016 , 020 a 的地址要小于 b 的地址 (release)
// buffer 数据类型跟栈的开口方向无关(都是向上)

char buff[10];
// buff+1 > buff
printf("%p , %p, %p, %p",&a,&b,buff,buff+1);

int a = 100;
int* ff = &a;// 虽说是 a 的地址,但是我也可以把 ff 看做一个数组

// char** 二维数组 char** 二级指针

ff += 1;

getchar();
}
// 理解指针的关键还是在于内存 ,指针的大小是 4 字节 int* double* 指针存放的是一块内存的地址 4 字节 ,但是也会有数据类型

4.1 程序运行的流程:

1
2
3
4.1.1操作系统会把物理硬盘上的代码 load 到内存
4.1.2加载到内存后会把c代码分成4个区
4.1.3然后系统会找到main 函数的入口去执行

4.2 四驱模型:

1
2
3
4
4.2.1栈区:由编译器自动分配的,存放一些局部变量值和函数,这个里面内存是会自动进行回收的
4.2.2 堆区:一般都是由我们自己去开辟的,这个里面的内存需要手动进行释放 malloc free new delete
4.2.3 全局区:静态的一些常量,字符串等等
4.2.4程序代码区:存放的是函数体的二进制代码

p65-p76没看,讲的c进阶,有点懵不如后面自己看c的完整教程

1.c和c++的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream> // c++的包
#include "Student.h"
using namespace std; // 命名的空间,java中的类部内
/*
// 1. c++ 代码中可以混编 c 代码,c++ 里面写 c 代码,也可以调用
// 2. c++ 面向对象(Java),c 面向过程
// 3. 很多的开源框架大部分都是 c++ 写的 (大部分基于 c++ )
void main(){
// printf("Hello World");// c 的打印
// c++ 打印 opencv << 操作符重载
std::cout << "Hello World!" << std::endl;
getchar();
}

2.常量

1
2
3
4
5
6
7
8
9
void main(){
const int number = 10;
// number = 20;
// int* numberP = &number;// 不能通过地址去修改值,但是某些编译器上面能通过,但是也不能修改值
// c 是能够修改的,在 c 中可以说是一个伪命题
// *numberP = 20;
// printf("%d", number);
getchar();
}

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
27
28
29
30
31
32
33
34
35
36
// 引用
void swap(int &number1, int &number2){
cout << "swap number1的地址" << &number1 << endl;
int temp = 0;
temp = number1;
number1 = number2;
number2 = temp;
}
void main(){
int number1 = 10;
int number2 = 20;
// 引用: 四驱模型值的拷贝,引用其实是地址赋值 ,可以看成同一块内存的另外一个变量
cout << "main number1的地址" << &number1 << endl;
swap(number1,number2);
cout << "number1 = " << number1 << " , number2 = " << number2 << endl;
getchar();
}
//------------------------------------------------------------------------------------------------
//常量引用
typedef struct
{
char name[20];
int age;
}Student;

void insertStu(const Student &stu){// stu 不想改 常量引用
// 可以修改 stu 的值
// strcpy(stu.name,"Jack");
// 就变成了只读
cout << stu.name << "," << stu.age << endl;
}
void main(){
Student stu = {"Darren",20};
insertStu(stu);
getchar();
}

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
25
26
// 1.函数重载和默认参数
int add(int number1, int number2 = 200, bool cache= 0){ // number2 = 200 默认的参数 kotlin 很像
return number1 + number2;
}
// 错误 1 error C2084: 函数“int add(int,int)”已有主体 c 不允许函数的重载
int add(int number1, int number2, int number3){
return number1 + number2 + number3;
}

void main(){
int number1 = 100;
int number2 = 200;
int sum = add(number1,200);//报错

// bool 类型 0是true 和 !0 都是false
bool cache = -100;
if (cache){
cout << "true" << endl;
}else
{
cout << "false" << endl;
}
// printf("sum = %d", sum);

getchar();
}

5.类的初探

1
2
3
1.class 定义类,跟java 几乎类似
2.真正的开发过程中我们的 pp 或者 文件,最终 d 或者 库供调用者使用,所以为了确保类能正常被调用,我们般需要定义.h 头文件
3实现方法的使用要用命名空间 xxx类名::xxx方法

Student.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student{ // 结构体类似
private: // 私有,包装,影响下面所有的属性
char* name;
int age;
public:
void setAge(int age);

void setName(char* name);
// private: // 影响到下面的所有方法

int getAge();

char* getName();
};

Student.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "Student.h"
// 写实现 ,定义了另外一个方法
void Student::setAge(int age){// 实现 Student 的 setAge 方法
this->age = age;
}
void Student::setName(char* name){
this->name = name;
}
int Student::getAge(){
return this->age;
}
char* Student::getName(){
return this->name;
}

Simple.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "Student.h"
// 0 1 2 , 0 打印, 1 上传到后台, 2 友盟和服务器
void printLog(char* content,int){// 自己平台打印,产品说了下个版本可能要上传到服务器,友盟也要备一份
cout << content << endl;
// 补功能 有可能
}
void main(){
// new ,new 出来的是一个指针
Student *stu = new Student();
printLog("xxxx",0);// 100
printLog("xxxx", 1);// 100
printLog("xxxx", 2);// 100
stu -> setName("Darren");
stu -> age = 24;
cout << stu -> getName() << " , " << stu -> getAge() << endl;
getchar();
}
// 补充:开发过程中(重要)直接写的问题所在 Student.cpp 是一个单独类 Student.cpp
// 会编译成一个 so 库,必须要有一个头文件

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
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
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
// 1.构造函数
class Student
{
public:
// 构造函数
Student(){// 空参数构造函数
cout << "空参数构造函数"<< endl;
}

Student(char* name):age(0){// 一个参数构造函数, 相当于 this->age = 0
cout << "一个参数构造函数" << endl;
this->name = name;
this->age = 0;
}
Student(char* name, int age){// 两个参数构造函数
cout << "两个参数构造函数" << endl;
this->name = name;
this->age = age;
}
//默认参数赋值
// Student(char* name):age(0),sex(1){// 一个参数构造函数, 相当于 this->age = 0
// cout << "一个参数构造函数" << endl;
// this->name = name;
// this->age = 0;
// }

//析构函数:临终遗言,对象被回收的时候会被调用
//如果有在对象内部开辟堆内存,可以在析构函数中释放内存
~Student(){
cout << "析构函数" << endl;
// 释放内存
free(this->name);
this->name = NULL;
}

private:
int age;
char* name;
public:
int getAge(){
return this->age;
}

char* getName(){
return this->name;
}

void setAge(int age){
this->age = age;
}

void setName(char* name){
this->name = name;
}
};
/*
void main(){
// Student stu;// 1. 默认调用空参的构造函数
// stu.setAge(24);
// stu.setName("Darren");
// Student stu("Darren",24); // 2. 调用两个参数的构造函数
// 3. 用 new 关键字,返回的是一个 Student 的一级指针
// Student *stu = new Student("Darren",24);
// 4. 用 malloc 的方式,并没有调用空参的构造函数
// Student *stu = (Student*)malloc(sizeof(Student));
// stu->setAge(24);
// stu->setName("Darren");
//5.构造函数相互调用,注意:先会调用两个参数的构造函数,然后才会执行当前构造函数
Student *stu = new Student("Darren");
cout << stu -> getName() << " , " << stu->getAge() << endl;

getchar();
}

7.malloc、free、new、delete 区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// malloc、free、new、delete 区别
void main(){
// Student stu;// 栈中开辟内存
// malloc、free、 new、delete 都是开辟和释放内存
// 1. malloc/free 他们是一套, new/delete 它们也是配套
// 2. malloc/free 不会去调用构造函数和析构函数
// Student *stu = (Student*)malloc(sizeof(Student));
// free(stu);
// 3. new/delete 会调用构造函数和析构函数
// Student *stu = new Student();
// delete(stu);
// 4. 如果用了 new ,那么一定要记得 delete(释放内存)
getchar();
}

8.可变参数

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 <stdarg.h>
using namespace std;
// 1. 可以变参数
void sum(int count,...){// int ... , c++ ...
va_list vp;
// 可变参数开始方法,count 代表从哪里开始
va_start(vp, count);
int number = va_arg(vp,int);
cout << number << endl;//2
// 读取下一个
number = va_arg(vp, int);
cout << number << endl;//1
// 读取下一个 ,超出了默认是 0
number = va_arg(vp, int);
cout << number << endl;//0
}

void main(){
int number = sum(2,1,2);
cout << number << endl;
getchar();
}

//----------------------------------------------------------------------------------------------
int sum(int count, ...){// count数组长度
va_list vp;
// 可变参数开始方法,count 代表从哪里开始
va_start(vp, count);
int sum = 0;
for (int i = 0; i < count; i++)
{
sum += va_arg(vp, int);
}
// 结尾,释放内存
va_end(vp);
return sum;
}
void main(){
int number = sum(5,1,2,4);
cout << number << endl;
getchar();
}

9.静态属性static

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
class Student
{
public:
char* name;
int age;
// 静态
static int tag;// 定义
Student(){
tag = 12;
}
public:
static void change(){
tag += 12;
}
void change1(){
this -> change();
}
};
// 静态属性在 c++ 中必须要初始化,初始化必须这么写
int Student::tag = 12;
void main(){
Student stu;
// stu.tag = 12;
// c++ 操作静态语法 ::
// Student::tag += 12;
// Student::change();
stu.change1();
cout << Student::tag << endl;
getchar();
}
//总结:
// 静态 可以直接用类名去操作 ::
// 静态的属性必须要初始化 (实现)
// 静态的方法只能去操作静态的属性或者方法

10.类的大小

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
class A
{
public:
double b;
int a;
char c;
};
class B
{
public:
double b;
int a;
char c;
static double d;
};
class C
{
public:
double b;
int a;
char c;
C(int a){
this->a = a;
}
public:
int getA(){
return this->a;
}
};
// 1. 对象的大小与结构体的计算方式类似
// 2. static 静态变量和方法并没有算到类的大小中
// 3. 栈,堆,全局(静态,常量,字符串),代码区 ,类的大小只与普通属性有关系
void main(){
cout << "A 的大小:" << sizeof(A) << endl;
cout << "B 的大小:" << sizeof(B) << endl;
cout << "C 的大小:" << sizeof(C) << endl;
C c1(12);
C c2(24);
cout << c1.getA() << endl;
cout << c2.getA() << endl;
getchar();
}
  1. 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

// this 指针:代表当前的对象,因为类的方法存放在代码区,大家一起共享的,所以要有 this 做区分
class Student{
public:
char* name;
int age;
public:
// this = const Student *const this
// 第一个 const :常量指针,代表值不能修改
// 第二个 const :指针常量,代表指针的地址不能修改
void change() const{// const 在() 之后主要用来限制 this 关键字
// this -> age += 12; // 不能对类的属性进行修改
// this = (Student*)0x0012;
}
// this = Student *const this
void change1(){
// this = (Student*)0x0012;
}
};
// 5. const 修饰函数
void main(){
Student stu;
stu.change();
cout << stu.age << endl;
getchar();
}

12.友元函数和类

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
class Person
{
private:
int age = 0;
public:
// 如果有自己写构造函数,那么会默认覆盖无参的构造函数
Person(int age){
this->age = age;
}
int getAge(){
return this->age;
}
// 友元函数的声明
friend void friend_change(Person *person, int age);
};
// 友元函数的实现
void friend_change(Person *person, int age){
// 修改一下 age 的值
// 在类的内部才能访问私有属性
// 如果该方法声明成友元函数那么是可以在外部访问其私有属性的
person->age = age;
}
void main(){
Person person = Person(24);
friend_change(&person,36);
cout << person.getAge() << endl;
getchar();
}

13.友元函数与普通函数的区别

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
33
34
#ifndef STUDENT_H
#define STUDENT_H

class Student{
private:
int age;
char* name;
public:
// 静态属性的声明
static int tag;

public:
Student();
Student(char* name);
Student(char* name,int age);
// 析构函数
~Student();
// 拷贝构造函数
Student(const Student& stu);

public:
void setAge(int age);
void setName(char* name);

int getAge();
char* getName();

void print() const;

// 静态和友元
static void changeTag(int tag);
friend void changeAge(Student *stu,int age);
};
#endif // STUDENT_H

Student.cpp

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
#include "Student.h"
#include <iostream>

// 一个一个来实现
int Student::tag = 0;

// 构造函数
Student::Student(char* name) :Student(name,0){// :age(0) 赋默认值
this->name = name;
}

Student::Student(char* name,int age){
this->name = name;
this->age = age;
}

// 析构和拷贝构造函数
// 析构函数
Student::~Student(){

}
// 拷贝构造函数
Student::Student(const Student& stu){

}

// 普通方法
void Student::setAge(int age){
this->age = age;
}

void Student::setName(char* name){
this->name = name;
}

int Student::getAge(){
return this->age;
}

char* Student::getName(){
return this->name;
}

void Student::print() const{
std::cout << this->name << " , " << this->age << std::endl;
}

// 静态和友元
void Student::changeTag(int tag_replace){
tag = tag_replace;
}

// 实现友元方法
void changeAge(Student *stu, int age){
stu->age = age;
}

Simple.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <stdarg.h>
using namespace std;
#include "Student.h"

void main(){
Student *stu = new Student("Darren",24);
// Student::changeTag(36);
changeAge(stu,36);
stu->print();
delete(stu);
getchar();
}

14.友元类

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
class  ImageView
{
public: // B 是 A 的友元类
friend class Class;
private:
int a;
};

class Class
{
public:
ImageView aObj;
void changeA(int number){
aObj.a = number;//拿到友元类的私有属性
}
int getA(){
return aObj.a;
}
};

void main(){
Class b;
b.changeA(12);
cout << b.getA() << endl;
getchar();
}

15.operator重载运算符

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <iostream>

using namespace std;

/*
class Vector
{
public:
Vector(int x, int y){
this->x = x;
this->y = y;
}

Vector(const Vector &vector){
this->x = vector.x;
this->y = vector.y;
cout << "拷贝构造函数" << endl;
}
private:
int x;
int y;

public:
void setX(int x){
this->x = x;
}
void setY(int y){
this->y = y;
}

int getX(){
return this->x;
}
int getY(){
return this->y;
}

// 重载减号运算符
// 为什么要用引用,为了防止重复创建对象
// const 关键常量,为了防止去修改值
Vector operator - (const Vector &vector){
int x = this->x - vector.x;
int y = this->y - vector.y;
Vector res(x, y);
return res;// 不建议返回引用
}

// 自增减运算符
void operator ++ (){// ++X
this->x = this->x++;
this->y = this->y++;
}

void operator ++ (int){// X++
this->x = this->x++;
this->y = this->y++;
}

// 自减
// 输出运算符
friend ostream & operator << (ostream &_Ostr, const Vector &vector){
_Ostr << vector.x << "," << vector.y << endl;
return _Ostr;
}

// 条件运算符
bool operator == (const Vector &vector){
return (this->x == vector.x && this->y == vector.y);
}

// 括号运算符
};

// 定义在类的外面,一般来讲我们定义在类的里面
// 重载运算 + :operator +
Vector operator + (Vector vector1, const Vector vector2){
int x = vector1.getX() + vector2.getX();
int y = vector1.getY() + vector2.getY();
Vector vector(x,y);
return vector;
}

void main(){
Vector vector1(2, 3);
Vector vector2(2, 3);

// java 中 string + string

// char* str = "123" + "456";

// 重载运算符 +
// Vector vector = vector1 - vector2;

// Vector vector(1, 2);
// vector++;
// ++vector;

// cout << vector.getX() << " , " << vector.getY() << endl;
// cout << vector << vector;
bool isEqual = vector1 == vector2;
cout << isEqual << endl;
// 可以重载加其他对象 Person
getchar();
}

16.括号运算符

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

// 括号运算符
class Array
{
public:
Array(int size){
this->size = size;
this->array = (int*)malloc(sizeof(int)*size);
}
~Array(){
if (this->array){
free(this->array);
this->array = NULL;
}
}

Array(const Array& array){
this->size = array.size;
this->array = (int*)malloc(sizeof(int)*array.size);

// 值的赋值
for (int i = 0; i < array.size; i++)
{
this -> array[i] = array.array[i];
}
}

private:
int size;
int* array;

public:
void set(int index,int value){
array[index] = value;
}

int get(int index){
return this->array[index];
}

int getSize(){
return this->size;
}

// 操作符[]
int operator[](int index){
return this->array[index];
}
};

void printfArray(Array array){
for (int i = 0; i < array.getSize(); i++)
{
cout << array[i] << endl;
}
}

void main(){

Array *array = new Array(5);

array->set(0,0);
array->set(1, 1);
array->set(2, 2);

printfArray(*array);

delete(array);
getchar();
}

17.类继承

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
class Person{
// 变量修饰符
public:// 本类中使用
// protected :子类中能使用 (默认)
// public :公开,任何地方都可以
char* name;
int age;

public:
Person(char* name,int age){
this->name = name;
this->age = age;
cout << "Person 构造函数" << endl;
}

public:
void print(){
cout << this->name << " , " << this->age << endl;
}
};

// 类继承 语法 :
// 类继承修饰符 public
class Student : public Person
{
public:
// : Person(name,age) 调用构造函数初始化父类的属性
Student(char* name,int age):Person(name,age){// 调用父类构造函数
cout << "Student 构造函数" << endl;
}

void test(){
print();
}
};
void main(){
Student stu("Darren",24);

stu.name = "Jack";

getchar();
}

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 初始化属性
class Person{
// 变量修饰符
public:// 本类中使用
// protected :子类中能使用 (默认)
// public :公开,任何地方都可以
char* name;
int age;

public:
Person(char* name, int age){
this->name = name;
this->age = age;
cout << "Person 构造函数" << endl;
}

public:
void print(){
cout << this->name << " , " << this->age << endl;
}
};


class Student : public Person
{
private:
char* course;
public:
// : Person(name,age) 调用构造函数初始化父类的属性
// 不光可以给父类初始化属性,还可以给本类的属性进行初始化,用 , 隔开即可
Student(char* name, int age, char* course) :Person(name, age), course(course){// 调用父类构造函数
cout << "Student 构造函数" << endl;
}

void print(){
cout << "course: " << course << endl;
}
};

class Teacher: public Person
{
public:
Teacher(char*name,int age):Person(name,age){

}
};

void main(){
Student stu("Darren",24,"语文");
stu.print();
getchar();
}

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
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
#include <iostream>

using namespace std;

// 1. 属性初始化
/*
class Person
{
protected:
char* name;
int age;
public:
Person(char* name, int age){
this->name = name;
this->age = age;
}
};

class Course
{
private:// java String
string name;
public:
Course(string name){
this->name = name;
}

public:
string _name(){
return this->name;
}
};

class Student : public Person
{
private:
// char* courseName;
Course course;
public:
Student(char* name, int age, string courseName) : Person(name, age), course(courseName){ // 初始化父类的属性
// this->courseName = courseName;
}

void print(){
cout << name << "," << age << "," << course._name().c_str() << endl;
}
};
void main(){
Student *stu = new Student("Darren",24,"math");
delete stu;
getchar();
}

20.多继承

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
class Person{
private:
char * name;
public:
Person(char * name){
this->name = name;
}
char* _name(){
return this->name;
}
};
class Child
{
int age;
public:
Child(int age){
this->age = age;
}
int _age(){
return this->age;
}
};
class Student : public Person, public Child // 多继承 , 并没有实现
{
public:
Student(char* name,int age):Person(name),Child(age){
}
};

void main(){
// 构造函数:先父类 -> 再子类
// 析构函数:先子类 -> 再父类
Student *stu = new Student("Darren",24);
delete stu;
getchar();
}

21.多继承-二义性(virtual虚继承)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A{
public:
char* name;
};
class B : virtual public A{ //不加virtual会运行时报错,virtual确保继承过来的相同属性或者函数,只存在一份拷贝
};
class C :virtual public A{
};
class D : public B ,public C
{
};
void main(){
D d;
d.name="darren";
cout<<d.name<<endl;
getchar();
}

22.多态

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
class Activity
{
public:
virtual void onCreate(){ // 支持多态,虚函数
cout << "Activity 中的 onCreate" << endl;
}
};

class MainActivity : public Activity
{
public:
void onCreate(){
cout << "MainActivity 中的 onCreate" << endl;
}
};

class WelcomeActivity : public Activity
{
public:
void onCreate(){
cout << "WelcomeActivity 中的 onCreate" << endl;
}
};

void startActivity(Activity* activity){
activity->onCreate();
}

void main(){
Activity *activity1 = new MainActivity();// 父类 = new 子类对象
Activity *activity2 = new WelcomeActivity();

// activity->onCreate();
// c++ 中的多态是怎样的,默认情况下不存在
// 父类指向子类的引用,重写 ,里氏替换原则
// 程序在编译期间并不知晓运行的状态(我需要运行那个函数),只要在真正运行的过程中才会去找需要运行的方法
startActivity(activity1);
startActivity(activity2);

// c++ 多态:动态多态(子父类),静态多态(函数的重载)(编译过程确定性的区别)
getchar();
}

23.继承中的抽象类

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
// java 中类似的 抽象类,接口 纯虚函数
class BaseActivity // 跟 java 中的抽象类一个概念
{
public:
void onCreate(){// 普通函数
initView();
initData();
}
// 子类必须要实现
virtual void initData() = 0;// 虚函数,声明,没有实现的,类似于 java 中的抽象方法,如果子类不实现会报错
virtual void initView() = 0;
};

// 如果不实现父类的纯虚函数,那么 MainActivity 也会变成抽象类,抽象类不允许实例化
class MainActivity : public BaseActivity
{
public:
void initData(){//实现
cout << "initData" << endl;
}
void initView(){
cout << "initView" << endl;
}
};

void main(){
BaseActivity *m_a = new MainActivity();
m_a->onCreate();
getchar();
}

24.接口

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
class ClickListener{// 所有的函数都是虚函数,那么就可以认为是接口
public:
virtual void click() = 0;
};
class ImageClickListener : public ClickListener
{
public :
void click(){
cout << "图片点击" << endl;
}
};
void click(ClickListener *listener){
listener->click();
}
void click(){
cout << "click点击" << endl;
}
// 函数指针 07 次
void click(void(*c)()){// 函数指针作为参数传递 返回值(函数名)(参数)
// 压缩开始
c();// 输出压缩进度
// 压缩结束
}

void main(){
// 函数指针的时候:回调可以用 指针函数作为回调,纯虚函数类进行回调(接口)
// ClickListener *listener = new ImageClickListener();
// listener->click();
// click(listener);
// 自己再去了解加深一下
click(click);
getchar();
}

25.构造函数和析构函数

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
class Person
{
protected:
char* name;
int age;
public:
Person(char* name, int age){
this->name = name;
this->age = age;
cout << "Person 的构造函数" << endl;
}
~Person(){
cout << "Person 的析构函数" << endl;
}
};

class Student : public Person
{
public:
Student(char* name, int age) : Person(name, age){ // 初始化父类的属性
cout << "Student 的构造函数" << endl;
}
~Student(){
cout << "Student 的析构函数" << endl;
}
};

void main(){
// 构造函数:先父类 -> 再子类
// 析构函数:先子类 -> 再父类
Student *stu = new Student("Darren",24);
delete stu;
getchar();
}

26.模版函数(java 中的泛型)

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
int add(int number1, int number2){
return number1 + number2;
}

double add(double number1, double number2){
return number1 + number2;
}

float add(float number1, float number2){
return number1 + number2;
}
// 模板函数 算法4 看一遍敲一遍
// 当普通函数和模板函数同时存在的时候,优先会调用普通函数
template <typename T>// 模板函数的定义
T add(T number1, T number2){
return number1 + number2;
}

void main(){
int sum1 = add(1,2);
cout << sum1 << endl;
int sum2 = add(1.0, 2.0);
cout << sum2 << endl;
int sum3 = add(1.0f, 2.0f);
cout << sum3 << endl;
getchar();
}

//day25

27.模板类 语法,跟模板函数非常类型

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
template <typename T>
class Callback
{
public:
void onError(){
}
void onSucceed(T result){
cout << result << endl;
}
};

// 模板类继承 ,子类如果也是模板类
// 如果子类不是模板类
class HttpCallback : public Callback<int>
{
};
template <class T>
class HttpCallback : public Callback<T>
{
};

void main(){
HttpCallback<int> *callback = new HttpCallback<int>();
callback->onSucceed(12);
getchar();
}

28.类型转换

1
2
3
4
5
1.1 static_cast 静态转换  用于基本数据类型之间的转换,如把int转换成char
1.2 const_cast 常量转换 用于修改常量的值
1.3 reinterpret_cat 强制类型转换 ,用于转换任意类型
1.4 dynamic_cast 动态转换 ,更安全,转换成功返回类型,失败返回空 ,
必须要包含多态类型和 static_cast 很类似,但是更安全
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
#include <iostream>
using namespace std;
class Person{
public:
string name;
int age;
public:
Person(string name, int age){
this->name = name;
this->age = age;
}
};

class Student : public Person{
public:
Student(string name, int age) : Person(name,age){
}
};

class Worker : public Person{
};

void main(){
double number1 = 20.02;
// 直接转换
// int number2 = number1;
// 1. 用于基本数据类型之间的转换,如把int转换成char
// int number2 = static_cast<int>(number1);
// cout << number2 << endl;
// 2. 把类型转换成另一种类型,用于类层次结构中基类和派生类之间指针或引用的转换
// Student *stu = new Student("Darren",24); // jobejct -> objectArray
// Person *person = stu;
// Person *person = static_cast<Person *>(stu);

// 待会再试
Person person = Person("Darren", 24);
// 转成Student
// Student stu = person;
cout << person.name.c_str() << " , " << person.age << endl;
getchar();
}

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// NDK 异常总结
// 1. 在 c++ 层如果是自己写的代码或者调用别人的方法,记得要 try 住, 如果不 try 在 java 层 try 是没有意义的
// 2. 如果异常需要往外抛给 java 层,一定要按照java层抛异常的方式
// 3. 如果是自己写的 NDK 层的代码,最好抛自己写的异常,声明异常
// 4. 如果是做 c++/c , 或者是帮 c/c++ 写代码,最好抛系统定义好的异常或者继承系统的异常
// 5. 系统异常的体系 exception 基类 https://www.cnblogs.com/QG-whz/p/5136883.html
class Exception
{
public:
string msg;
public:
Exception(string msg){
this->msg = msg;
}
public:
const char *what(){
return this->msg.c_str();
}
};
// 异常的处理
void main(){
// c++ 中有自己一套异常的体系,不要去强记
// 但是 c++ 可以抛任何数据类型 try{}catch(数据类型 变量名){}
// throw 抛异常
try{
int i = -1;
if (i == 0){
throw Exception("出异常了");
}
if (i< 0){
throw 12.5f;
}
}
catch (int number){
cout << "捕捉到异常" <<number << endl;
}
catch (Exception exception){
cout << "捕捉到异常:" << exception.what() << endl;
}
catch (...){
cout << "捕捉到其他异常:" << endl;
}
getchar();
}

30字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void main(){
// C++ STL(Standard Template Library) 准模板库 :容器 + 迭代器 + 算法
// 1. 对象的构建
string str1 = "123";
string str2("123");
string str3(5, 'A');// 5 个 A = AAAAA
string *str4 = new string("123");

// cout << str1.c_str() <<endl;
// cout << str2.c_str() << endl;
// cout << str3.c_str() << endl;
// cout << str4->c_str() << endl;

// string 与 char* 相互之间转换 c_str()
// const char* c_str1 = str1.c_str();
// cout << c_str1 << endl;

// char* -> string
char* c_str = "Darren";
string str(c_str);// 对象
cout << str.c_str() << endl;

getchar();
}

31.字符串遍历

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
#define D_SCL_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

void main(){
string str("1234567");
// 1. 字符串的遍历
for (int i = 0; i < str.length(); i++)
{
cout << str[i] << endl;
}
// 迭代器遍历
for (string::iterator it = str.begin(); it < str.end(); it++)
{
cout << *it << endl;
}

try{
for (int i = 0; i < str.length()+2; i++)
{
cout << str.at(i) << endl;// 如果越界会抛异常
}
for (int i = 0; i < str.length()+2; i++)
{
cout << str[i] << endl;// 会导致程序宕机,AS里面是可以的
}
}
catch (...){
cout << "异常了" << endl;
}
getchar();
}

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
45
46
void main(){
// 添加
string str1 = "123";
string str2 = "456";
// str1 = str1 + str2;
// str1.append(str2);
cout << str1.c_str() << endl;

// 删除
string str1 = "123 abc 123 abc 123";
// str1.erase(0,3);// 第一个参数:从哪里开始 ; 第二个参数:删除几个(默认值,字符串的结尾)
// 迭代器删除 2 bc 123 abc 123 解释
for (string::iterator it = str1.begin(); it<str1.begin()+3; it++)// 删除一个字后都会从头开始计算
{
str1.erase(it);
}
cout << str1.c_str() << endl;

//替换
string str1 = "123 abc 123 abc 123";
// 第一个参数:从哪里开始
// 第二个参数:替换几个
// 第三个参数:替换成谁
str1.replace(0,6,"1234");
cout << str1.c_str() << endl;

//查找
string str1 = "123 abc 123 abc 123";
// 查找谁,从哪里开始
// int position = str1.find("123",0);
// 从后面往前面查
int position = str1.rfind("123");
cout << position << endl;

//#include <algorithm>// STL 算法包
//#include <cctype>
//大小写转换
string str1 = "AAA abc BBB abc 123";
// 转换成大写
// transform(str1.begin(), str1.end(), str1.begin(), toupper);
transform(str1.begin(), str1.end(), str1.begin(), tolower);
cout << str1.c_str() << endl;

getchar();
}

33.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
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
#include <iostream>

#include <vector>
#include <stack>
#include <queue>
#include <list>
#include <functional>
#include <set>

using namespace std;

/*
void main(){ // vector 数组

// vector
// 1. vector<int> v;
// 2. vector<int> v(10);
// 3. vector<int> v(10,0);
vector<int> v;
// 插入数据
// v.begin() 迭代器的开始位置
v.insert(v.begin(),12);
v.insert(v.begin(), 22);
v.insert(v.begin(), 32);

v.insert(v.end(), 42);

// 引用当左值当右值 (修改)
v.front() = 33;//最前
v.back() = 44;//最后

v.push_back(55);//最后处添加
// 移除最后的元素,并没有返回值
// v.pop_back();
// 通过迭代器位置进行移除
v.erase(v.begin());

// 获取数据 for 循环
for (int i = 0; i < v.size(); i++)
{
cout << v[i] << "\t";// 越界程序宕机
}
cout << endl;
//
for (int i = 0; i < v.size(); i++)
{
cout << v.at(i) << "\t"; // 越界抛异常 out_of_range
}
cout << endl;

// 通过迭代器
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << "\t"; // 越界抛异常 out_of_range
}
cout << endl;

getchar();
}

34.stack 栈, 先进后出 (链表,数组)

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
void main(){
stack<int> s;
// 压栈 ,压入指定位置
s.push(12);
s.push(22);
s.push(32);

// 并不能够通过角标去插入获取值,如果在开发的过程中如果的确需要通过角标去获取值,需要自定义
for (int i = 0; i < s.size(); i++)
{
cout << s. << endl;
}
// 迭代器也没有,并不支持循环的 ,非得循环

for (stack<int>::iterator it; i < s.size(); i++)
{
cout << s. << endl;
}

int top = s.top();
cout << top << endl;

// 弹出栈顶
s.pop();
top = s.top();
cout << top << endl;

while (!s.empty())
{
int top = s.top();
cout << top << endl;
s.pop();
}

getchar();
}

35.queue 队列 ,先进先出(链表,数组)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void main(){

queue<int> q;
q.push(12);
q.push(44);
q.push(32);

q.front() = 42;
cout << q.front() << endl;
q.pop();
cout << q.front() << endl;
// 最后,最后加入的
int back = q.back();
cout << back << end
while (!q.empty())
{
cout << q.front() << endl;
q.pop();
}

getchar();
}

36.优先级队列 (数据结构 数组,排序的方式堆排序)

1
2
3
4
5
6
7
8
9
10
11
12
13
//priority_queue优先队列是一种容器适配器,采用了堆这样的数据结构,保证了第一个元素总是整个优先队列中最大的(或最小的)元素。
void main(){
// int 存放的数据 vector<int> 数据类型(数组) greater 从大到小 less 从小到大
priority_queue<int,vector<int>,greater<int>> pq;
pq.push(12);
pq.push(44);
pq.push(32);
pq.push(10);
// 最大值
cout << pq.top() << endl;

getchar();
}

37.list 链表, 双向链表(有没有回环)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void main(){
list<int> l;
// 插入
l.push_front(11);
l.push_back(22);
l.insert(l.begin(),10);
// 修改
l.back() = 33;
l.front() = 44;
// 不能通过角标去访问,也不能去修改
// 移除
l.erase(l.begin());
l.pop_front();
l.pop_back();
// 循环
for (list<int>::iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << endl;
}

getchar();
}

38.set 容器(红黑树结构) ,会对你存入的数据进行排序,但是不允许元素相同

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
void main(){
// set<int,less<int>> s;// 从小到大排序 ,默认就是 less
set<int, greater<int>> s;//从大到小排序

// 添加参数 , 不需要用迭代器,也不需要指定位置
s.insert(3);
s.insert(5);
s.insert(4);
// 重复的插入,并不会报错,返回两个值 插入迭代器的位置 ,是否插入成
pair<set<int, greater<int>>::iterator, bool> res = s.insert(5);
// res.first; 获取第一个参数
bool insert_succeed = res.second;
if (insert_succeed){
cout << "插入成功" << endl;
}
else{
cout << "插入失败" << endl;
}
// int count = s.count(5);
// s.find();

for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << endl;
}

getchar();
}
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
class Student
{
public:
string name;
int grade;

public:
Student(string name, int grade){
this->name = name;
this->grade = grade;
}
};

// 谓词(函数谓词) :按找特定的规则所编写的函数谓词
bool compare(const Student& _Left, const Student& _Right){
return _Left.grade > _Right.grade;
}

// 函数对象 仿函数
struct comparefuction
{
// 函数重载了 () 运算符,函数对象,仿函数
bool operator()(const Student& _Left, const Student& _Right) const{
return _Left.grade > _Right.grade;
}
};

// 基本数据类型 ,对象数据类型
void main(){
set<Student, comparefuction> s;

Student s1("Darren1", 2);
Student s2("Darren2", 9);
Student s3("Darren3", 5);

s.insert(s1);
s.insert(s2);
s.insert(s3);

for (set<Student>::iterator it = s.begin(); it != s.end(); it++)
{
cout << it->name.c_str() << "," << it->grade << endl;
}

getchar();
}

39.multiset容器 , 允许重复 ,用法和 set 一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main(){
// set<int,less<int>> s;// 从小到大排序 ,默认就是 less
multiset<int, greater<int>> ms;

// 添加参数 , 不需要用迭代器,也不需要指定位置
ms.insert(3);
ms.insert(5);
ms.insert(4);
ms.insert(4);
ms.insert(3);

for (set<int>::iterator it = ms.begin(); it != ms.end(); it++)
{
cout << *it << endl;
}

getchar();
}

40.map

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<map>
#include<set>
#include<vector>
#include<algorithm> // 预定义函数 ,已经实现好的一些算法头文件
using namespace std;
/*
void main(){
// map 会对 key 排序 ,二叉树算法
map<int, string> map1;

// 添加数据 - 第一种
map1.insert(pair<int,string>(01,"01"));

// 第二种方式
map1.insert(make_pair(02,"02"));

// 第三种方式
map1.insert(map<int,string>::value_type(03,"03"));

// 区别,如果用前面三种 key 重复添加 ,不生效
// map1.insert(map<int, string>::value_type(03, "30"));

// 第四种方式 = map1[key] = value
map1[04] = "04";
//常用的是第一种和第四种
// 第四种是会覆盖的
map1[04] = "40";

// 如果要判断添加是否成功 - 自己思考,参考上次课内容
map1[00] = "00";

// 循环 - 迭代器
for (map<int,string>::iterator it = map1.begin(); it != map1.end(); it++)
{
cout << it->first << " " << (it->second).c_str() << endl;
}

cout << " 遍历结束" << endl;

// 删除 ,查找
map<int,string>::iterator find_it = map1.find(0);

// cout << find_it->first << " " << (find_it->second).c_str() << endl;
if (find_it != map1.end()){
cout << find_it->first << " " << (find_it->second).c_str() << endl;
}
else{
cout << " 找不到 " << endl;
}

getchar();
}
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
//map案例
void main(){
multimap<int, string> map1;
// 案例,1 (11,12,13),2 (21,22,23),3(31,32,33)

map1.insert(pair<int, string>(1,"11"));
map1.insert(pair<int, string>(1, "12"));
map1.insert(pair<int, string>(1, "13"));

map1.insert(pair<int, string>(3, "31"));
map1.insert(pair<int, string>(3, "32"));
map1.insert(pair<int, string>(3, "33"));

map1.insert(pair<int, string>(2, "21"));
map1.insert(pair<int, string>(2, "23"));
map1.insert(pair<int, string>(2, "22"));


// 遍历
for (map<int, string>::iterator it = map1.begin(); it != map1.end(); it++)
{
cout << it->first << " " << (it->second).c_str() << endl;
}
cout << " 遍历结束" << endl;

// 分组查询 多个数据
multimap<int, string>::iterator find_it = map1.find(3);
while (find_it != map1.end()){
cout << find_it->first << " " << (find_it->second).c_str() << endl;
find_it++;
// 不是我们要找的内容
if (find_it == map1.end() || find_it -> first != 3){
break;
}
}

getchar();
}

41.一元谓词,二元谓词,仿函数

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
class Person
{
public:
string name;
char* test;
public:
Person(){}

Person(string name){
this->name = name;
test = (char*)malloc(12);
}

// 析构函数
~Person(){
free(test);
cout << "析构函数"<< &test << endl;
}

public:
void setName(string name){
this->name = name;
}

private:

};

// 3. 容器对象拷贝构造函数 , 就想存同一对象,在任何地方改变,集合的数据也相应发生变 (指针)
void main(){
// java 中把对象添加到了集合
// c++ 中会调用对象的拷贝构造函数,存进去的是另一个对象
// 第一个错误:没有默认的构造函数
// 第二个错误:析构函数也可能回调用多次,如果说在析构函数中释放内存,需要在拷贝构造函数中进行深拷贝
vector<Person> vector1;

Person person("Darren");
vector1.push_back(person);

person.setName("Jack");

Person person1 = vector1.front();

cout << person1.name.c_str() << endl;

getchar();
}


class Compare
{
// 重载了括号运算符
public:
void operator()(){
cout << "仿函数" << endl;
}
};

void compare1(){
cout << "普通函数" << endl;
}

// 函数对象(仿函数) 一元谓词,二元谓词
void main(){
Compare compare;

// 跟函数非常类似
compare();
// 普通函数调用
compare1();

getchar();
}

// 一元谓词
void print(int number){
cout << number << endl;
}

// 仿函数 - 一元谓词 (能够记录状态)
class PrintObj
{
public:
int count = 0;
public:
void operator()(int number){
cout << number << endl;
count++;
}
};

// 回调函数和仿函数的区别
void main() {

set<int> set1;
set1.insert(1);
set1.insert(2);
set1.insert(3);
set1.insert(4);

// for_each 迭代器 ,非常重要的一点就是:仿函数如果要保存记录状态,要确保对象一致,可以用返回值
// for_each(set1.begin(),set1.end(),print);
PrintObj printObj;
printObj = for_each(set1.begin(), set1.end(), printObj);
cout << "个数:" << printObj.count << endl;

getchar();
}


class CompareObj
{
public:
int count = 0;
public:
bool operator()(const string str1, const string str2){
return str1 < str2;
}
};

void main(){
// 二元谓词的仿函数
set<string, CompareObj> set1;
set1.insert("aaa");
set1.insert("aAa");
set1.insert("ccc");
set1.insert("ddd");
// 是否包含 aaa , 遍历比较 , 找方法
for (set<string>::iterator it = set1.begin(); it != set1.end(); it++)
{
cout << (*it).c_str() << endl;
}
getchar();
}

42.预定义函数对象和函数适配器 p118-p121

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#define _SCL_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<set>
#include<functional>
#include<algorithm>
using namespace std;

/*
// 自定义重载 () 运算符
// 1. 预定义函数对象和函数适配器
void main(){
// c/c++ 提供了很多定义好的函数对象
// 常见的几个 less ,greater,plus,equal_to
plus<string> strAdd;
string str = strAdd("aaa","bbb");

// cout << str.c_str() << endl;

set<string, greater<string>> set1;
set1.insert("aaa");
set1.insert("bbb");
set1.insert("ccc");

// 判断是不是包含 aaa
// 怎么写仿函数,一定要确定好你的仿函数的参数
// bind2nd 函数适配器 , aaa 相当于 equal_to 中的 right

template<class _Ty = void>
struct multiplies
: public binary_function<_Ty, _Ty, _Ty>
{ // functor for operator*
_Ty operator()(const _Ty& _Left, const _Ty& _Right) const
{ // apply operator* to operands
return (_Left * _Right);
}
}
set<string, greater<string>>::iterator find_it = find_if(set1.begin(), set1.end(),bind2nd(equal_to<string>(),"aaa"));
if (find_it != set1.end()){
cout << "找到了" << (*find_it).c_str() << endl;
}
else
{
cout << "没有找到" << endl;
}

getchar();
}


// 1,种方式自定义仿函数(函数对象)
class Equal
{
private:
int equal_number;
public:
Equal(int equal_number){
this->equal_number = equal_number;
}
public:
bool operator()(const int& number){
return number == equal_number;
}
};

void main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(2);
vector1.push_back(4);
vector1.push_back(2);

// 找集合中 等于 2 的个数
int count = count_if(vector1.begin(), vector1.end(), Equal(2));
cout << "count = " << count << endl;

// 预定义好的函数对象 + 函数适配器
count = count_if(vector1.begin(), vector1.end(), bind2nd(equal_to<int>(),2));
cout << "count = " << count << endl;

getchar();
}


void print(int number){
cout << number << endl;
}

// 进行修改
int transform_print(int number){
// cout << number << endl;
return number + 3;
}

// foreach,transform,find,find_if,count,count_if,megre,sort,random_shuffle,copy,replace
// 常用预定义算法 循环,增,删,改,查
void main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);

// for_each(vector1.begin(), vector1.end(),print);
vector<int> vector2;
vector2.resize(vector1.size());

transform(vector1.begin(), vector1.end(), vector2.begin(), transform_print);

for_each(vector2.begin(), vector2.end(), print);

getchar();
}


// find,find_if
void main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);

vector<int>::iterator find_it = find(vector1.begin(), vector1.end(), 2);

if (find_it != vector1.end()){
cout << "包含" << endl;
}
else
{
cout << "不包含" << endl;
}

// 有没有大于2的,自定义函数对象,预定义函数对象+函数适配器,省略...

getchar();
}


// count,count_if
void main(){
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(2);
vector1.push_back(4);

int number = count(vector1.begin(), vector1.end(), 2);

cout << "等于2的个数:" << number << endl;

number = count_if(vector1.begin(), vector1.end(), bind2nd(less<int>(), 2));

cout << "小于2的个数:" << number << endl;

number = count_if(vector1.begin(), vector1.end(), bind2nd(greater<int>(), 2));

cout << "大于2的个数:" << number << endl;

getchar();
}

class _merge
{
public:
bool operator()(int number1,int number2){
return true;
}

};

void print(int number){
cout << number << endl;
}

// megre,sort,random_shuffle,copy,replace
void main(){

// 两个有序数组进行合并 - 归并排序
vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);

vector<int> vector2;
vector1.push_back(4);
vector1.push_back(5);
vector1.push_back(6);

vector<int> vector3;
vector3.resize(6);
merge(vector1.begin(), vector1.end(), vector2.begin(), vector2.end(), vector3.begin());
for_each(vector3.begin(), vector3.end(), print);

getchar();
}


void print(int number){
cout << number << endl;
}

void main(){

vector<int> vector1;
vector1.push_back(1);
vector1.push_back(3);
vector1.push_back(2);
vector1.push_back(4);

sort(vector1.begin(),vector1.end(),less<int>());
for_each(vector1.begin(), vector1.end(), print);

cout << "循环结束" << endl;

// 打乱循序
random_shuffle(vector1.begin(), vector1.end());
for_each(vector1.begin(), vector1.end(), print);

getchar();
}


void print(int number){
cout << number << endl;
}

// copy,replace
void main(){

vector<int> vector1;
vector1.push_back(1);
vector1.push_back(2);
vector1.push_back(3);
vector1.push_back(4);

vector<int> vector2;
vector2.resize(2);
copy(vector1.begin(), vector1.begin() + 2, vector2.begin());
// for_each(vector2.begin(), vector2.end(), print);

replace(vector1.begin(), vector1.end(), 2, 22);
for_each(vector1.begin(), vector1.end(), print);

getchar();
}

// 头部的封装 ToolBar 思考:自定义 View 4中样式
// 思考问题 ,方式去解决 NavigationBar (各种源码各种框架)

// 过度设计 :扩展性不高实用性不强 ,继承(代码)过于复杂,花哨

// 模板类 _InIt 名字 , _First, _Last , 返回值 是一个类型
// _Pr _Pred 这是什么?
template<class _InIt,
class _Pr> inline
_InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred)
{ // find first satisfying _Pred
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Pred);
return (_Rechecked(_First,
_Find_if(_Unchecked(_First), _Unchecked(_Last), _Pred)));
}
template<class _InIt,
class _Pr> inline
_InIt _Find_if(_InIt _First, _InIt _Last, _Pr _Pred)
{ // find first satisfying _Pred
for (; _First != _Last; ++_First)
// if 返回值是一个 bool 类型 ,往 _Pred 里面传递了一个参数 ,要么就是回调函数要么就是仿函数
if (_Pred(*_First))
break;
return (_First);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.  基本概念
计算机视觉,智能识别,机器学习
2. 银行卡识别步骤:(拍照,照片)
c/c++ 语法编程基础 (比较重要)
opencv 常用基础 (较重要)
图像的算法基础 (不是很重要)
2.1. 截取到银行卡区域(扫描,可以不要这一步)
2.2. 截取到银行卡号区域
2.3. 对银行卡号区域进行特征分析提取识别
3. opencv 集成
https://www.jianshu.com/p/fe8dbb9f72ef
4. 截取到银行卡区域(扫描,可以不要这一步)
思路:会有多种:
4.1. 轮廓增强(梯度增强)
4.2 进行二值化轮廓过滤
4.3 截取卡号区域
4.3.1 找到银联区域(精度强,90%)
4.3.2 可以直接截取卡号 (1/2,3/4)区域尽量大一些;

p122-p128 直接看项目源代码,opencv具体流程还是不够清楚;

p129-p207讲的算法,过多未看;

p208-p291 opencv及ndk实战 未看

NDK 学到什么程度,该怎么学 ?

1
2
3
4
5
6
c/c++ 少不了,基础,进阶,linux 内核,shell 脚本,cmake 语法
opencv ,fmpeg ,opengl,android native
android 开发:c/c++工程师开发(精通),java 工程师(精通),native 和 java 层互通 NDK 开发熟悉c++.熟悉android)
知道:有这么个东西,你模糊
熟悉:知道这个东西,讲明白,项目中也能直接用上
精通:知道这个东西,讲明白,项目中也能直接用上,知道原理( 四种启动模式,怎么解析的,怎么压栈弹栈,声明周期怎调用的)

fitst.cpp文件:

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
#include <Windows.h>
//01默认格式
// int main()
// {
// printf("Hello World!\n");
// system("pause"); // 防止运行后自动退出,需头文件stdlib.h
// return 0;
// }

//02打印输出
// %d:用于输出整数。
// %f:用于输出浮点数。
// %c:用于输出字符。
// %s:用于输出字符串。
// %p:用于输出指针。
// %x:用于输出十六进制数。
//\n换行
// int main(){
// int a=100;
// double b=100.1;
// float c=100.2;
// long d=200;
// short e=200;
// char f='a';

// printf("a=%d\n",a);
// printf("b=%f\n",b);
// printf("c=%f\n",c);
// printf("d=%d\n",d);
// printf("e=%d\n",e);
// printf("f=%c\n",f);
// system("pause");
// return 0;

// //输出结果:
// // a=100
// // b=100.100000
// // c=100.199997
// d=200
// e=200
// f=a

// }

//03变量地址 &代表取变量的地址
// int main(){
// int a=100;
// printf("a=%d\n",a);//值
// printf("a=%p\n",&a);//地址:a=000000000061FE1C
// system("pause");
// return 0;
// }

// 04 指针变量和获取地址的值
// int main(){
// int a=100;
// int* p =&a;
// printf("p的值是:%d\n", *p);//获取值
// printf("p的值是:%d\n", p);//获取地址
// printf("p的值是:%d\n", *(&p));//获取地址
// system("pause");
// return 0;
// }

// 05 通过指针修改值
// int main(){
// int a=100;
// int* p=&a;
// printf("a=%d\n",a);
// printf("*p=%d\n",*p);

// *p=200;
// printf("a1=%d\n",a); //a1=200
// printf("p1=%d\n",*p);//p1=200
// system("pause");
// return 0;
// }

// 06 方法修改值和指针修改值
// void change(int i){// 通过方法修改值
// printf("change1 中i的地址是:%p\n",&i);
// i = 200;
// }

// void changePoint(int* p){// 通过地址来修改
// printf("change2中i的地址是:%p\n",p);
// *p = 300;
// }
// int main(){
// int i = 100;
// printf("i的地址是:%p\n", &i);
// printf("i的值是:%d\n", i);
// change(i);//方法内值改变不影响传入的变量本身
// changePoint(&i);//指针方式方法内值改变改变的就是值本身,地址一致
// system("pause");
// }

// 07 题目:写个方法对两个值进行交换
// void change(int* a,int* b){
// int temp = *a;
// *a = *b;// a的值 = b 的值
// *b = temp;
// }
// int main(){
// int a = 100;
// int b = 200;
// // 1 两个值进行交换
// // change(&a,&b);
// // printf("就交换后ab的值分别是:%d,%d",a,b);
// // 2 面试题:对 a,b 两个值进行交换,但是不开辟额外的内存(不能用中间变量)
// a = a + b;// a = 300
// b = a - b;// b = 100
// a = a - b;// a = 200
// printf("就交换后ab的值分别是:%d,%d",a,b);
// system("pause");
// }

//08 指针地址改变
// int main(){
// int a = 100;
// int b = 200;
// // a = b 把b变量的值复制给a
// printf("a,b的地址分别是:%p,%p\n",&a,&b);
// //p1=a的地址,p2=b的地址
// int* p1 = &a;
// int* p2 = &b;
// printf("p1,p2的地址分别是:%p,%p\n", p1, p2);//打印指针不能加&号不然变成int **了
// p1 = p2;// 只是p1 的地址改了,并没有改动 a 的地址
// printf("a,b的值分别是:%d,%d\n", a, b);
// printf("p1,p2的值分别是:%d,%d\n", *p1, *p2);//打印指针的值
// system("pause");
// }

// 09 二级指针和多级指针
// int main(){
// int num = 12;
// // int* 指针 & 取地址
// int* p = &num;
// int** s_p = &p;// 对一级指针取地址
// printf("num 的地址是:%p\n",&num);//0023fe40
// printf("p 的地址是:%p\n",p);//0023fe40
// printf("s_p 的地址是:%p\n",s_p);//0061FE08
// printf("s_p 的地址是:%p\n",*s_p);//0023fe40,二级指针取地址
// printf("s_p 的地址是:%p\n",**s_p);//0000000C
// printf("获取最终的值是:%d", **s_p);//12,二级指针取值
// system("pause");
// }

// 10 数组与数组指针
// int main(){
// int arr[] = {1,2,3,4};
// // 遍历数组, linux 就有问题
// for (int i = 0; i < 4;i++){
// printf("%d\n",arr[i]);
// }
// // for循环在 c 和 C++ 中的正确写法
// int i = 0;
// for (; i < 4; i++){
// printf("%d\n", arr[i]);
// }
// // 看一种现象: arr 地址的值 = arr[0]的地址的值(首地址)
// printf("arr& = %p\n", &arr);//000000000061FE00
// printf("arr[0]& = %p\n", &arr[0]);//000000000061FE00
// printf("arr[1]& = %p\n", &arr[1]);//000000000061FE04

// int* arr_p = arr;// 数组指针指向的是数组的首地址
// // 做一系列的操作
// printf("%d\n",*arr_p);//1
// // 对指针进行 ++
// arr_p++;
// printf("%d\n", *arr_p);//2
// // 指针再往后逻动两位
// arr_p += 2;
// printf("%d\n", *arr_p);//4

// system("pause");
// }

//11 采用指针遍历数组循环赋值
// int main(){
// int arr[] = {1,2,3,4};
// int* arr_p = arr;
// int i = 0;
// for (; i < 4;i++){
// printf("位置%d的值是:%d\n", i, *(arr_p+i));
// }
// system("pause");
// }
// int main(){
// int arr[4];// 指定数组[4]
// int* arr_p = arr;
// int i = 0;
// for (; i < 4; i++){
// *(arr_p + i) = i;
// }
// i = 0;
// for (; i < 4; i++){
// printf("位置%d的值是:%d\n", i, arr[i]);
// }
// system("pause");
// }

// 12 指针为什么要有类型? 指针其实就是一块地址而且他的值都差不多
// int main(){
// //原因是取值的时候我要知道怎么取,按4字节取,还是8字节或16字节去取,不同字节取出的值不一样;
// int num = 12;
// int* num_p = &num;
// printf("num_p=%d\n",*num_p);//12
// double* num_p_d = num_p;
// printf("num_p=%d\n",*num_p);//12
// system("pause");
// }

// 13函数指针
// void add(int num1,int num2){
// printf("num1 + num2 = %d\n",(num1+num2));
// }
// void mins(int num1, int num2){
// printf("num1 - num2 = %d\n", (num1 - num2));
// }
// // 方法指针,传两个数
// void opreate(void(method)(int,int),int num1,int num2){
// method(num1, num2);
// }
// int main(){
// // add(1,2);
// // 方法指针怎么定义?方法的返回(*方法的名称)(方法的参数)
// opreate(add, 1, 2);
// opreate(mins, 1, 2);
// system("pause");
// }

// 14 监听数据压缩回调
// void call_back(int current,int total){
// printf("压缩的进度是:%d/%d",current,total);
// }
// void compress(char* file_name,void(*callback)(int,int)){
// callback(12,24);
// }

// int main(){
// void(*call)(int, int);// 声明了一个函数
// call = call_back;// 给函数指针赋值
// // call(1,2);// 通过函数指针间接的调用call_back
// compress("1.png", call);
// system("pause");
// }

// day08
// 15 生成随机数

// int main(){
// // 可以随机但是每次运行,随机数都一致,解决的方式就是初始化发生器
// // 初始化随机发生器, 每次发生器都是当前时间,啰嗦,实用场景,在 C 层生成秘钥(安全)
// srand((unsigned)time(NULL));//初始化发生器
// int i;
// for (i = 0; i<10; i++)
// printf("%d\n", rand() % 100);
// system("pause");
// }

//16 静态内存开辟
// void staticlloc(){
// int arr[5];// 静态开辟,静态开辟内存内存大小无法更改,使用后自动回收
// int i = 0;
// for (; i < 5; i++)
// {
// arr[i] = i;
// printf("%d, %p\n", *(arr + i), arr + i);
// }
// }

// int main(){
// // Stack overflow 栈溢出 如何判断一块内存中有没有写入数据,判 NULL?,判越界
// // int arr[10*1024*1024];// 占用内存是多少? 大小?10M数组大小, int占用4字节,40M空间

// // c 有分区,四驱模型,栈,堆
// // 栈:占用内存空间最大值 2M ,开辟内存的方式是静态内存开辟 int arr[10*1024*1024],方法结束会自动回收
// // 堆:占用内存空间最大值 80% ,开辟内存的方式是动态内存开辟 ,不会自动回收必须手动回收(内存不再使用的时候一定要手动回收)
// while (true)
// {
// Sleep(500);
// staticlloc();
// }
// system("pause");
// }

//17 动态内存开辟
// void dynamiclloc(){//malloc动态开辟内存
// int* arr = (int*)malloc(1 * 1024 * 1024 * sizeof(int)); // 40M
// // 第二个重要的方法,手动释放 free , 释放arr这一块内存
// free(arr);
// }

// int main(){
// // int arr[10 * 1024 * 1024];
// // 操作
// while (true)
// {
// Sleep(100);
// dynamiclloc();
// }

// system("pause");
// }

// 18 改变内存空间大小,新开辟的内存地址会复制旧的内存地址内的东西并释放旧的内存地址;
// int main(){
// int num;
// printf("请输入数的个数:");
// // 获取用户输入的值
// scanf("%d", &num);
// // 5 个值
// int* arr = (int*)malloc(sizeof(int)*num);
// int i = 0;
// int print_num;
// for (; i < num; i++){
// arr[i] = i;// arr[i] = *(arr+i)
// }
// printf("开辟内存的指针:%p\n",arr);
// // 再加点内存
// printf("请输入新增的个数:");
// // 获取用户输入的值
// int new_num;
// scanf("%d", &new_num);// 5
// // 原来那块内存的指针,总的大小(原来的大小+新的大小)
// int* new_arr = (int*)realloc(arr, sizeof(int)*(num+new_num));

// if (new_arr){// = if(new_arr != NULL)
// i = new_num;
// for (; i < (num + new_num); i++){
// arr[i] = i;// arr[i] = *(arr+i)
// }

// printf("新开辟内存的指针:%p\n", new_arr);

// // 打印数组
// i = 0;
// for (; i < num + new_num; i++){
// printf("%d, %p\n", *(arr + i), arr + i);
// }

// }
// if (new_arr){
// // 如果成功了,只要释放新的地址就行
// free(new_arr);
// new_arr = NULL;
// }
// else{
// // 如果没有成功,释放原来的内存
// free(arr);
// }

// // 不要反复去释放,那块地址一旦被释放,就有可能会被新的应用占用,不要去再次释放
// // free(new_arr);
// system("pause");
// }

//day09
// 19 字符串定义
// int main(){
// // 第一种 字符串数组
// char str[] = {'D','a','r','r','e','n','\0'};
// str[2] = 'y';
// printf("%s\n",str);// 字符串结尾是 '\0'
// // 第二种
// char *str1 = (char*)"Darren";
// str1[2] = 'y';//不能修改会报错
// printf("%s", str1);
// // 区别,一个能修改一个不能修改
// system("pause");
// }

// 20字符串长度获取
// int strlen_(char *str){
// // 怎么获取字符串的长度? 不断读取字符,判断末尾 '\0'
// int len = 0;
// while (*str != '\0')
// {
// len++;
// str++;
// }
// return len;
// }
// int main(){
// char *name = "Darren is";
// char str[] = { 'D', 'a', 'r', 'r', 'e', 'n', '\0','i','s' };
// // 怎么获取长度?有一种方式计算(不好使)
// // printf("length is %d",sizeof(str)/sizeof(char));
// printf("name length is %d\n", strlen(str));
// // 自己来写一个方法读取字符串的长度
// // strlen_(name);
// printf("name length is %d", strlen_(str));
// system("pause");
// }

//21 字符串的转换
// int main(){
// // char * num = "1"; -> int float double
// char *num_str = "12.0xxx";
// int number0 = atoi(num_str);// 如果不能转换就是 0 ,后面如果有其他不是数字的就会被剔除 12xxx->12 , xx12->0
// printf("number0 is %d\n",number0);//12

// char *num_str1 ="12.5f";
// float number1 = atof(num_str1);// 如果不能转换返回的是默认值 0.000000
// printf("number1 is %lf\n",number1);//12.500000

// char *num_str2 = "12.5xx";
// double number2 = strtod(num_str2, NULL);
// printf("number2 is %lf",number2);//12.500000
// system("pause");
// }

// 22字符串的比较
// int main(){
// char* str1 = "Darren";
// char* str2 = "darren";
// // 大于小于等于
// int rc = strcmp(str1, str2);// 区分大小写比较
// printf("rc=%d\n",rc);//-1,=0相等

// int rc1 = _strcmpi(str1, str2);//不区分大小写比较
// printf("rc1=%d\n",rc1);//0
// // 比较前几个
// char* str3 = "Darren";
// char* str4 = "Darren is";
// int rc2 = strncmp(str3, str4,7);// count 代表的是比较字符串前几个是否相等
// int rc3 = _strnicmp(str3, str4, 6);
// printf("rc2=%d\n",rc2);//0
// printf("rc3=%d\n",rc3);//0
// // if (rc == 0){
// // printf("相等");
// // }
// // else{
// // printf("不相等");
// // }
// system("pause");
// }

// 23 字符串查找,包含
// int main(){
// char* str = "name is Darren";
// char *substr = "is";
// char* pos = strstr(str, substr);// 返回的是字符串第一次出现的位置(位置指针), 如果没有找到返回的是空
// // 求一下位置 int 怎么办? strstr
// int postion = pos - str;
// printf("第一次出现的位置是:%d\n",postion);
// // 包含?pos 是不是空就可以了
// if (pos){
// printf("%s", "包含");
// }
// else{
// printf("%s\n", "不包含");
// }
// system("pause");
// }

// 24拼接,截取,大小写转换
// int main(){
// // strcpy(); copy进来
// char* str = "darren";
// char *str1 = " is";
// int len = strlen(str);// cpy[len] android studio 是可以的
// printf("len:%d\n",len);//6
// char cpy[len];
// // str 会 copy 到 cpy 里面
// strcpy(cpy, str);
// printf("%s\n",cpy);//darren
// // 拼接
// strcat(cpy,str1);
// printf("%s", cpy);//darren is
// system("pause");
// }

// 25字符串的截取
// char * substr(char * str,int start,int end){
// // 开辟一个字符串去存储我们的数据,开辟多大计算
// // char sub[end-start];
// int len = end - start;
// char* sub = (char*)malloc(len*sizeof(char)+1);// 记得加上1 ,在 NDK 一般会采用静态的数组存储 char sub[len]
// // malloc 一定要 free
// // 遍历赋值
// str += start;
// for (int i = 0; i < len; i++)
// {
// sub[i] = *str;
// str++;// 指针往后逻一位
// }
// // 标记字符串结尾,否则 print 无法判断结尾
// sub[len] = '\0';
// printf("%p\n",sub);
// // free(sub);
// return sub;
// }

// int main(){
// char *str = "Darren is";
// // 截取第三个位置到第五个位置 3,5
// char *sub = substr(str,0,5);
// printf("%p\n", sub);
// printf("%s\n",sub);
// // 一定要 free ,因为你的 substr 有动态开辟内存,但是真正开发过程中并不会这么做,自己的方法尽量要自己处理好内存
// free(sub);
// system("pause");
// }


//26大小写转换
// dest 用来存放结果,大小自己指定,source 需要转换的字符串
// void lower(char* dest,char* source){
// while (*source != '\0'){
// // 拿当前字符
// char ch = *source;
// // 转完赋值给 dest
// *dest = tolower(ch);// a -> a A -> a
// // 接着指针 ++ ,遍历下一个
// source++;
// dest++;
// }
// // 标记字符串结尾
// *dest = '\0';
// }
// int main(){
// char* name = "daRRen";
// char dest[20];
// lower(dest,name);
// printf("%s",dest);
// system("pause");
// }

//day10
// 27.结构体的定义和使用
//第一种方式
// struct Worker{// 定义一个结构体,相当于 java 的 class
// char name[10];
// int age;
// double salary;
// };
// 第二种方式,可以直接取名字
// struct Worker{// 定义一个结构体,相当于 java 的 class
// char name[10];
// int age;
// double salary;
// }darren = {"Darren",23,1000}, jack;
// 第三种方式,结构体嵌套
// struct Work{
// char name[10];
// char grade[10];
// };
// struct Worker{// 定义一个结构体,相当于 java 的 class
// char name[10];
// int age;
// double salary;
// // 工作, name grade
// Work work;
// };
// 第四种方式,结构体嵌套
struct Worker{// 定义一个结构体,相当于 java 的 class
char name[10];
int age;
double salary;

struct Work{
char name[10];
char grade[10];
}work;
// 工作, name grade
// Work work;
};
// int main(){
//第一种方式
// struct Worker worker;// 初始化 , 没有初始化的情况下,那么里面所有的属性都是没有初始值的
// 赋值
// worker.age = 24;
// worker.name = "Darren";////没有初始化不能直接修改
// strcpy_s(worker.name,"Jack");
// worker.salary=1.1;
// printf("name = %s, age = %d, salary = %lf", worker.name, worker.age, worker.salary);//name = Jack, age = 24, salary = 1.100000请
// 第二种方式
// printf("name = %s, age = %d, salary = %lf", darren.name, darren.age, darren.salary);//name = Darren, age = 23, salary = 1000.000000
// jack.name = "Jack";//没有初始化不能直接修改
// strcpy_s(jack.name, "Jack");
// jack.age = 24;
// jack.salary=2.2;
// printf("name = %s, age = %d, salary = %lf", jack.name, jack.age, jack.salary);//name = Jack, age = 24, salary = 2.200000
// 第三种方式
// struct Worker worker = { "Darren", 23, 1000, {"Android","中级"} };// 嵌套的初始
// printf("name = %s, grade = %s", worker.work.name, worker.work.grade);//name = Android, grade = 中级
// 第四种方式
// struct Worker worker = { "jeck", 20, 100, {"JAVA","中级"} };// 嵌套的初始
// printf("name = %s, grade = %s", worker.work.name, worker.work.grade);//name = JAVA, grade = 中级
// system("pause");
// }

// 28 结构体指针
// int main(){
// struct Worker worker = {"Darren",23};
// Worker* worker_p = &worker;
// // 通过结构体指针去操作数据
// // worker.age = 24;
// worker_p->age = 24;// 结构体操作的另外一种方式,一般这种比较常用
// strcpy_s(worker_p->name,"Jack");
// printf("name = %s, age = %d", worker_p->name, worker_p->age);
// system("pause");
// }

// 29 动态内存开辟
// struct Worker{// 定义一个结构体,相当于 java 的 class
// char name[10];
// int age;
// double salary;
// };
// int main(){
// // Worker* worker;// 创建了一个结构体指针 , 不能直接使用,因为没有初始化
// Worker* worker = (Worker*)malloc(sizeof(Worker));
// worker->age = 24;
// strcpy_s(worker->name, "Jack");
// printf("name = %s, age = %d", worker->name, worker->age);
// // 释放
// if (worker){
// free(worker);
// worker = NULL;
// }
// system("pause");
// }

// 30 结构体的数组
// struct Worker{// 定义一个结构体,相当于 java 的 class
// char name[10];
// int age;
// double salary;
// };
// int main(){
// // 动态内存开辟
// // Worker worker[10] = { {"Darren",23,1000},{},{},{},{} };// 创建了 10 个 ,静态开辟
// // worker[9] = {"Darren9",24,200};
// // printf("name = %s, age = %d\n", worker[9].name, worker[9].age);

// // 动态内存开辟 Worker* 结构体的指针
// Worker* worker = (Worker*)malloc(sizeof(Worker)*10);
// strcpy_s(worker->name,"Darren0");
// worker->age = 10;
// // 对第十个数据进行操作?
// worker += 9;
// strcpy_s(worker->name, "Darren9");
// worker->age = 9;
// printf("name = %s, age = %d", worker->name, worker->age);
// system("pause");
// }

// 31 结构体大小计算(字节对齐)
// struct Worker{// 定义一个结构体,相当于 java 的 class
// char name[18]; // 10
// int age; // 4
// double salary;// 8
// };

// int main(){
// int size = sizeof(Worker);// 16 字节
// // 计算的规则:
// // 1. 按照最大的字节去计算
// // 2. 算得时候只会按照基本数据类型去算
// // 3. 首先会把所有字节数加起来,是否能够整除最大属性的字节数,如果不够为网上累加,一直加到能整除位置
// printf("size = %d", size);
// system("pause");
// }

// 32结构体大小计算(字节对齐)
// struct date{ // 12
// int year;
// int month;
// int day;
// };
// struct student{
// int number;// 4
// char sex;// 1
// int age;// 4
// char name[10];// 10
// struct date birthday;// 12
// // 31%4
// };

// int main(){
// int size = sizeof(student);// 36 字节
// // 计算的规则:
// // 1. 按照最大的字节去计算
// // 2. 算得时候只会按照基本数据类型去算
// // 3. 首先会把所有字节数加起来,是否能够整除最大属性的字节数,如果不够为网上累加,一直加到能整除位置
// // 4. 进阶,留一个思考
// printf("size = %d", size);
// system("pause");
// }


//33 结构体和结构体指针取别名
// struct Worker_{// 定义一个结构体,相当于 java 的 class
// char name[10];
// int age;
// double salary;
// };
// typedef Worker_ Worker_;
// // 对结构体的指针取别名
// typedef Worker_* Worker;
// int main(){
// Worker worker = (Worker_*)malloc(sizeof(Worker_));
// printf("%s,%d,%lf",worker->name,worker->age,worker->salary);
// system("pause");
// }

//34 联合体
// union Person{
// // 最能出现一个 ,不用累加,找最大值 10,12字节
// //联合体大小计算 (不用累加,取的最大值,最终是否够基本数据类型整除)
// char name[10]; // 10
// int age; // 4
// double salary;// 8
// };
// // 联合体的定义和使用,联合体只能存在一个,要么是 age ,要么是 name
// int main(){
// Person person = {"Darren"};
// // Person person;
// // strcpy_s(person.name,"Darren");
// // person.age = 24;
// // printf("name = %s,age = %d",person.name,person.age);
// printf("%d", sizeof(Person));
// system("pause");
// }

// 35 枚举的定义和使用 枚举有点类似于 int
// enum CommentType
// {
// TEXT = 10,TEXT_IMAGE,IMAGE
// };
// int main(){
// CommentType commentType = TEXT;
// CommentType commentType1 = TEXT_IMAGE;
// CommentType commentType2 = IMAGE;
// printf("%d,%d,%d", commentType, commentType1, commentType2);
// system("pause");
// }

//day11
// 36文件读取
// int main(){
// // 打开文件 (文件名,模式)mode r(读) w(写) rb(作为二进制读) wb(作为二进制写)
// // FILE *fopen( const char *filename, const char *mode );
// char* fileName = "C:\\Users\\11046\\OneDrive\\桌面\\test.txt";
// FILE *file = fopen(fileName, "r");
// if (!file){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// char buffer[10];
// // 缓冲区buffer,长度10 ,文件指针
// while (fgets(buffer, 10, file)){
// printf("%s",buffer);
// }
// // 关闭文件
// fclose(file);
// system("pause");
// }

// 37文件写入
// int main(){
// char* fileName = "C:\\Users\\11046\\OneDrive\\桌面\\test.txt";
// FILE *file = fopen(fileName, "w");
// if (!file){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// fputs("I am tyl",file);
// // 关闭文件
// fclose(file);
// system("pause");
// }

//38 文件复制,当做二进制文件来操作
// int main(){
// char* fileName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image.jpg";
// char* fileCopyName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_copy.jpg";
// FILE *file = fopen(fileName, "rb");// 流指针
// FILE *file_copy = fopen(fileCopyName, "wb");// 新建一个文件 0kb
// if (!file || !file_copy){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// // size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
// int buffer[512];// 2048 = 4*514
// int len;
// printf("%d",sizeof buffer);
// while ((len = fread(buffer, sizeof(int), 512, file))!=0){
// fwrite(buffer, sizeof(int), len, file_copy);
// }
// fclose(file);
// fclose(file_copy);
// system("pause");
// }

//39 获取文件的大小
// int main(){
// char* fileName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image.jpg";
// FILE *file = fopen(fileName, "rb");// 流指针
// if (!file){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// // 没有一个函数是可以直接拿文件大小
// // 思路:将文件的指针移动到最后,然后再去计算偏移量
// // 三个参数 SEEK_SET(开头), SEEK_CUR(当前), SEEK_END(移动到最后)
// fseek(file, 0, SEEK_END);
// // 计算偏移的位置,ftell 从 0 开始统计到当前(SEEK_END)
// long file_size = ftell(file);
// printf("文件大小:%ld",file_size);
// fclose(file);
// system("pause");
// }

// 40文件加密
// 图片服务器(使用第三方的),为了防止图片泄密,图片进行加密
// int main(){
// char* fileName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image.jpg";
// char* fileEncrpyName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_en.jpg";
// FILE *file = fopen(fileName, "rb");// 流指针
// FILE *file_encrpy = fopen(fileEncrpyName, "wb");// 新建一个文件 0kb
// if (!file || !file_encrpy){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// // 破坏文件,还原
// // 思路:把每一个字节都拿出来,对每一个字节都处理,把某部分字节拿出来,进行处理
// // 10^5 异或 加密过程
// // 1010
// // ^0101
// // 1111 加密

// // 解密 同样的去异或 5
// // 1111
// // ^0101
// // 1010 解密后 10
// int c;// EOF end of file
// while ((c = fgetc(file))!=EOF){
// fputc(c ^ 5, file_encrpy);
// }
// system("pause");
// }

// 41文件的解密
// int main(){
// char* fileEnName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_en.jpg";
// char* fileDeName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_de.jpg";
// FILE *file = fopen(fileEnName, "rb");// 流指针
// FILE *file_decrpy = fopen(fileDeName, "wb");// 新建一个文件 0kb
// if (!file || !file_decrpy){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// // 思路:把每一个字节都拿出来,对每一个字节都处理,把某部分字节拿出来,进行处理
// // 10^5 异或 加密过程
// // 1010
// // ^0101
// // 1111 加密
// // 解密 同样的去异或 5
// // 1111
// // ^0101
// // 1010 解密后 10
// int c;// EOF end of file
// while ((c = fgetc(file)) != EOF){
// fputc(c ^ 5, file_decrpy);
// }
// system("pause");
// }

// 42 就改一个字节, 过不去
// int main(){
// char* fileEnName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image.jpg";
// // rwb
// FILE *file = fopen(fileEnName, "wb"); // 流指针,创建文件? 有没有其他办法 支付宝人脸识别 Mat 矩阵
// // file 流的头指针
// fputc(5, file);
// fclose(file);
// system("pause");
// }

//43字符串密码进行加密
// int main(){
// char* fileName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image.jpg";
// char* fileEncrpyName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_en.jpg";
// char* passWord = "123456";// 轮流进行^操作
// FILE *file = fopen(fileName, "rb");// 流指针
// FILE *file_encrpy = fopen(fileEncrpyName, "wb");// 新建一个文件 0kb
// if (!file || !file_encrpy){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// int c;// EOF end of file
// int index = 0;
// int pass_len = strlen(passWord);
// while ((c = fgetc(file)) != EOF){
// fputc(c ^ passWord[index%pass_len], file_encrpy);
// index++;
// }
// system("pause");
// }

// 44字符串密码解密
// int main(){
// char* fileName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_en.jpg";
// char* fileDeName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_de.jpg";
// char* passWord = "123456";// 轮流进行^操作
// FILE *file = fopen(fileName, "rb");// 流指针
// FILE *file_decrpty = fopen(fileDeName, "wb");// 新建一个文件 0kb
// if (!file || !file_decrpty){
// printf("文件打开失败");
// exit(0);// 退出程序
// }
// int c;// EOF end of file
// int index = 0;
// int pass_len = strlen(passWord);
// while ((c = fgetc(file)) != EOF){
// fputc(c ^ passWord[index%pass_len], file_decrpty);
// index++;
// }
// fclose(file);
// fclose(file_decrpty);
// system("pause");
// }

// 45 文件的切割,思路类似于断点下载
// int getFileSize(char* fileName){
// FILE* file = fopen(fileName,"rb");
// if (!file){
// return 0;
// }
// fseek(file,0,SEEK_END);
// int file_size = ftell(file);
// fclose(file);
// return file_size;
// }

// int main(){
// // 百度云 不大于4G的文件? 8G 文件,扔进去
// // 大文件(断点续传)
// // 文件切割,把文件合作为一个思考
// // 计算每个文件需要写入多少?
// char* fileName = "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image.jpg";
// FILE* file = fopen(fileName,"rb");
// int file_size = getFileSize(fileName);
// // 定义 切多少,计算大小,指定文件名
// int file_number = 3;
// // 二维数组,char[] char*
// // image_0.jpg
// // image_1.jpg
// // image_2.jpg
// // char* file_name
// // char*
// char** file_names = (char**)malloc(sizeof(char*)*file_number);
// int preFileSize = file_size / file_number;
// // 进行复制
// int i = 0;
// for (; i < 3; i++)
// {
// file_names[i] = (char*)malloc(sizeof(char)*100);// '\0'
// sprintf(file_names[i], "C:\\Users\\hcDarren\\Desktop\\android\\NDK\\image_%d.jpg", i);
// printf("%s\n", file_names[i]);
// }
// i = 0;
// for (; i < file_number; i++)
// {
// // 从源文件中往切割文件写入数据
// FILE* cur_file = fopen(file_names[i],"wb");
// // 写多少?
// int start = i*preFileSize;
// int end = (i + 1)*preFileSize;
// if (i == file_number - 1){
// end = file_size;
// }
// // 第一个循环 file 读,第二个循环还是从 file 里面
// for (int j = start; j < end; j++)
// {
// // 从file 里面读,写如到 cur_file
// fputc(fgetc(file),cur_file);
// }
// fclose(cur_file);
// free(file_names[i]);
// }
// fclose(file);
// free(file_names);
// system("pause");
// }

//day12
// p45-p53课开始jni的开发流程,没看懂,用的工具和自己用的不一样

//46 常量指针/指针常量
// 常量变量:被常量修饰的变量,不能再次被赋值 (Java )
// 常量指针:const 在*之前,指针的地址是可以被再次赋值的( 可以修改的),指针地址上面的值(变量)是不能被修改,常量指针的常量是
不能被改变的,
// 指针常量:const 在*之后,指针的地址是不可以被再次赋值的(不可以修改的),指针地址上面的值(变量)能被修改的,指针常量的指针地
址是不能被改变的。
//const在谁前面谁就不能修改;
//int main(){
// // 常量,不能去修改
// const int number = 100;
// // number = 200;
// int number1 = 100;
// int number2 = 200;
// // 常量指针
// // int const * n_p = &number2;
// // n_p = &number1;
// // printf("n_p = %p",n_p); // 地址是可以重新被赋值的
// // *n_p = 300; 值是不能改的
//
// // 指针常量
// int * const n_p = &number2;
// // n_p = &number1; 地址是不能被重新赋值
// *n_p = 300;
// printf("number2 = %d", number2);
// system("pause");
//}

//p54-p57面试题没看懂,太啰嗦
//day14


title:2_jni基础

JNI的一般开发流程:(java访问native)

1
2
3
4
5
1.1定义好本地的 native 方法
1.2javah 命令生成h 头文件
1.3 拷贝 xxx.h、jni_md.h、jih 到 VS 的工程目录并添加依赖进来
1.4实现我们头文件中的 native 方法
1.5 生成 dll 动态,java 引 dl 动态库运行即可

流程细节:

// ecplise

1.定义好本地的 native 方法(NdkSimple.java)

1
2
3
4
5
6
7
8
9
package com.tyl.myStudy;
public class NdkSimple {
public static void main(String[] args) {
NdkSimple ndkSimple = new NdkSimple();
String signaturePassword = ndkSimple.getSingnaturePassword();
System.out.println("秘钥 = "+signaturePassword);
}
public static native String getSingnaturePassword();
}

2.cmd中cd到本文件的路径下执行javah,查看支持的命令;

1
2
3
4
5
6
错误:'javah' 不是内部或外部命令
原因:jdk版本太新了,在jdk10及以上的版本中,已经删除了javah,使用"javac -h"命令替代"javah"
解决方案:javac -h ./ NdkSimple.java
(错误: 编码 GBK 的不可映射字符 (0x9B)
原因:不能有中文哪怕是注释,添加-encoding UTF-8
解决方案:javac -h ./ NdkSimple.java -encoding UTF-8

3.本地路径上出现新生成的文件:com_tyl_myStudy_NdkSimple.h

// visualStudio

4.将com_tyl_myStudy_NdkSimple.h复制到visualStudio的项目根目录中(如:D:\C++Projects\C++Study\Project1)

5.本地java的安装目录中搜索jni,然后将jni.h及jni_md.h文件复制到visualStudio的项目根目录中(如:D:\C++Projects\C++Study\Project1)

6.vs中点击头文件->添加->现有项->将com_tyl_myStudy_NdkSimple.h和jni.h及jni_md.h文件添加进来;

image-20230710095206945

image-20230710095236313

7.生成dll动态库

7-1:调试–>调试属性->常规->配置类型->动态库(.dll)->应用->确定

image-20230710093341443

7-2:生成->配置管理器->平台处新建->x64或其他根据本地环境->确定

image-20230710093615611

7-3:生成->生成解决方案;提示:生成: 1 成功,0 失败,0 最新,0则成功,文件在项目根目录-x86-debug下的dll文件就是

//ecplise NdkSimple.java

8.引入加载我们的本地库

1
2
3
4
5
static{
//System.loadlibrary:android加载apk中的libs目录下.so库
// System.load:更载一个具体路径上的.so 库,可以去服务器上下载后下载
System.load("D:\\C++Projects\\C++Study\\Project1\\x64\\Debug\\Project1.dll");
}

9.执行调用

image-20230710100300639

10.com_tyl_myStudy_NdkSimple.h头文件字段说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h" // "" 引入自己工程的头文件 <> 引入系统的头文件
/* Header for class com_tyl_myStudy_NdkSimple */
// 用来打一个标记,c在编译的时候会把头文件 copy 到你引入的地方,不管是重复引用还是相互引用都只会 copy 一次
#ifndef _Included_com_tyl_myStudy_NdkSimple
#define _Included_com_tyl_myStudy_NdkSimple
#ifdef __cplusplus// 相当于 if 语句 c++(如果是c++执行下列的方法,使用c的编译方式)
// 不管是 c 还是 c++ 统一都是采用 c 的编译方式,因为在c里面是不允许函数重载的,但是在 c++ 里面可以
extern "C" {
#endif
/*
* Class: com_tyl_myStudy_NdkSimple
* Method: getSingnaturePassword
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_tyl_myStudy_NdkSimple_getSingnaturePassword
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

11.jni.c的字段说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//1.实现我们的 native 方法
#include "com_tyl_myStudy_NdkSimple.h"
//2.将.h中的方法复制过来修改为实现方法
// JNIEXPORT JNI 一个关键字,不能少(编译能通过),标记为该方法可以被外部调用
// jstring : 代表 java 中的 String
// JNICALL: 也是一个关键字,可以少的 jni call
// JNIEnv: 这个是 c 和 java 相互调用的桥梁,所有 function 搞清
// jobject: java传递下来的对象,就是本项目中 JniSimple java 对象
// jclass: java传递下来的 class 对象,就是本项目中的 JniSimple.class
JNIEXPORT jstring JNICALL Java_com_tyl_myStudy_NdkSimple_getSingnaturePassword
(JNIEnv* env, jclass jobj) {//JNIEnv*, jclass
// JNIEnv * 其实已经是一个二级指针了,所以 -> 调用的情况下必须是一级指针 *取值
return (*env)->NewStringUTF(env,"5555");
}

12.native修改java参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
JNIEXPORT void JNICALL Java_com_darren_ndk12_NdkSimple1_changeName
(JNIEnv *env, jobject jobj){
// 获取 name 属性然后修改为 Jack
// 3.获取 jclass
jclass j_clz = (*env)->GetObjectClass(env, jobj);
// 获取 jfieldId (JNIEnv *env, jclass clazz, const char *name, const char *sig)
// name 获取哪个属性的属性名
// 2.sig 属性的签名
jfieldID j_fid = (*env)->GetFieldID(env, j_clz, "name", "Ljava/lang/String;");
// 1.获取 name 属性的值
jstring j_str = (*env)->GetObjectField(env, jobj, j_fid);
// 打印字符串 jstring -> c_str
char* c_str = (*env)->GetStringUTFChars(env,j_str,NULL);
printf("name is %s",c_str);
// 修改成 jack
jstring jackName = (*env)->NewStringUTF(env,"Jack");
(*env)->SetObjectField(env, jobj, j_fid, jackName);
}

13.native调用java方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//java:
public int add(int numberl,int number2){
return numberl+number2;
}

//native:
JNIEXPORT void JNICALL Java_com_darren_ndk12_NdkSimple1_callAddMathod
(JNIEnv *env, jobject jobj){
jclass j_clz = (*env)->GetObjectClass(env,jobj);
// 获取 methodid
jmethodID j_mid = (*env)->GetMethodID(env, j_clz, "add", "(II)I");
// 去调用 java 的方法
jint sum = (*env)->CallIntMethod(env, jobj, j_mid,2,3);
printf("sum = %d",sum);
}

Point.java

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
package com.darren.ndk.day13;

import java.io.PrintWriter;

public class Point {
private int x;
private int y;

public Point(int x,int y){
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}

public void setY(int y) {
this.y = y;
}

public int getY() {
return y;
}
}

Simple1.java

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
package com.darren.ndk.day13;

import java.util.UUID;

public class Simple1 {

public static void main(String[] args) {
// callStaticMethod();

Point point = createPoint();

System.out.println("point: x = "+point.getX()+" , y = "+point.getY());

// android 用反射 ,jni 设置属性值,反射的原理?
}

private native static Point createPoint();

private native static void callStaticMethod();

// 小的思考:静态获取 uuid 的方法,然后再 c 调用这个方法获取uuid
public static String getUUID() {
return UUID.randomUUID().toString();
}

public static Point test(int x,float y){
return null;
}

static{
System.load("C:/Users/hcDarren/Desktop/android/NDK/NDK_Day13/x64/Debug/NDK_Day13.dll");
}
}

com_darren_ndk_day13_Simple1.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_darren_ndk_day13_Simple1 */

#ifndef _Included_com_darren_ndk_day13_Simple1
#define _Included_com_darren_ndk_day13_Simple1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_darren_ndk_day13_Simple1
* Method: callStaticMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_darren_ndk_day13_Simple1_callStaticMethod
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

Simple.c

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 "com_darren_ndk_day13_Simple1.h"


JNIEXPORT jobject JNICALL Java_com_darren_ndk_day13_Simple1_createPoint
(JNIEnv *env, jclass jclz){
// jclz -> Simple1

// 获取 Point 的 class ,name = "全类名"
jclass point_clz = (*env)->FindClass(env,"com/darren/ndk/day13/Point");
// 构建 java 层的 Point 对象,构造函数的id , 构造方法(百度) <init>
jmethodID j_mid = (*env)->GetMethodID(env,point_clz,"<init>","(II)V");

jobject point = (*env)->NewObject(env, point_clz, j_mid,11,22);

// 练习一下 y 重新付个值 ?调用 set 方法
j_mid = (*env)->GetMethodID(env, point_clz,"setY","(I)V");
/* va_list 集合
void (JNICALL *CallVoidMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
// jvalue
void (JNICALL *CallVoidMethodA)
(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
*/

(*env)->CallVoidMethod(env, point,j_mid,33);

// 作业:直接给属性赋值

return point;
}

p58-p61

com_darren_day15_Simple1.h

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 com_darren_day15_Simple1 */

#ifndef _Included_com_darren_day15_Simple1
#define _Included_com_darren_day15_Simple1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_darren_day15_Simple1
* Method: sort
* Signature: ([I)V
*/
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_sort
(JNIEnv *, jclass, jintArray);

#ifdef __cplusplus
}
#endif
#endif

Simple1.java

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
package com.darren.day15;

import java.util.Collections;

import javax.naming.ldap.SortControl;

public class Simple1 {
private static String name;// 一大堆变量
private static String name1;// 一大堆变量
private static String name3;// 一大堆变量

static {
System.load("C:/Users/hcDarren/Desktop/android/NDK/NDK_Day15_VS/x64/Debug/NDK_Day15_VS.dll");
}

public Simple1() {

}

public static void main(String[] args) {
// 1. 数组处理的一些细节
/*
* int[] arr = {11,22,-3,2,4,6,-15};
*
*
* sort(arr);
*
* for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+"\t");
* }
*
* // 2. 局部引用和全局引用 // localRef();
*
* // saveGlobalRef("Darren");
*
* // System.out.println(getGlobalRef());
*
* // 合适的时机去释放 // delteGlobalRef();
*
* // 再次获取 // System.out.println(getGlobalRef());
*
* // 3.缓存策略 static,native层有一大堆方法要去获取 name 属性 // 初始化全局静态缓存
* initStaticCache();
*
* staticLocalCache("Darren");
*
* System.out.println("name = "+name);
*
* staticLocalCache("Jack");// not null
*
* System.out.println("name = "+name);
*
* staticLocalCache("Rose"); // not null System.out.println("name = "
* +name);
*/

// 异常处理(简单讲,C++异常)

try {
exception();
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

System.out.println("name3 = " + name3);
}

private static final native void exception() throws NoSuchFieldException;

private static final native void initStaticCache();

private static final native void staticLocalCache(String name);

private static final native void delteGlobalRef();

private static final native String getGlobalRef();

private static final native void saveGlobalRef(String str);

private static final native void localRef();

private static final native void sort(int[] arr);
}

com_darren_day15_Simple1.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
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_darren_day15_Simple1 */

#ifndef _Included_com_darren_day15_Simple1
#define _Included_com_darren_day15_Simple1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_darren_day15_Simple1
* Method: sort
* Signature: ([I)V
*/
JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_sort
(JNIEnv *, jclass, jintArray);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_localRef
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_saveGlobalRef
(JNIEnv *, jclass,jstring);

JNIEXPORT jstring JNICALL Java_com_darren_day15_Simple1_getGlobalRef
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_delteGlobalRef
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_staticLocalCache
(JNIEnv *, jclass, jstring);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_initStaticCache
(JNIEnv *, jclass);

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_exception
(JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

Simple.c

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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "com_darren_day15_Simple1.h"
#include <stdlib.h>

int compare(const jint *number1, const jint *number2){
return *number1 - *number2;
}

// 全局变量
jstring globalStr;

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_sort
(JNIEnv * env, jclass jclz, jintArray jarray){

// 对 jarray 进行排序 (sort)
jint* intArray = (*env)->GetIntArrayElements(env,jarray,NULL);

int length = (*env)->GetArrayLength(env,jarray);

// 第一个参数:void* 数组的首地址
// 第二个参数:数组的大小长度
// 第三个参数:数组元素数据类型的大小
// 第四个参数:数组的一个比较方法指针(Comparable)
qsort(intArray, length, sizeof(int), compare);

// 同步数组的数据给 java 数组 intArray 并不是 jarray ,可以简单的理解为 copy
// 0 : 既要同步数据给 jarray ,又要释放 intArray
// JNI_COMMIT: 会同步数据给 jarray ,但是不会释放 intArray
// JNI_ABORT: 不同步数据给 jarray ,但是会释放 intArray
(*env)->ReleaseIntArrayElements(env,jarray,intArray,JNI_ABORT);
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_localRef
(JNIEnv *env, jclass jclz){
// 在 native 层构建的 Java 对象,你不用了该怎么管理?
// native 层开辟的内存由谁管理,你能开辟多大

// 字符串截取,String 对象
jclass str_clz = (*env)->FindClass(env,"java/lang/String");
jmethodID init_mid = (*env)->GetMethodID(env,str_clz,"<init>","()V");
jobject j_str = (*env)->NewObject(env, str_clz, init_mid);

// 还有 100 行代码

// jobject 不要再使用了,要回收 javaGC 的源码
(*env)->DeleteLocalRef(env,j_str);

// 删除了就不能再使用了,C 和 C++ 都需要自己释放内存(静态开辟的不需要,动态开辟的需要)

}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_saveGlobalRef
(JNIEnv *env, jclass jclz, jstring str){
// 保存全局变量,其他方法需要用到
globalStr = (*env)->NewGlobalRef(env, str);

// NewWeakGlobalRef (java 中的软引用很像) 无法保证对象不为空
}

JNIEXPORT jstring JNICALL Java_com_darren_day15_Simple1_getGlobalRef
(JNIEnv *env, jclass jclz){
return globalStr;
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_delteGlobalRef
(JNIEnv *env, jclass jclz){
(*env)->DeleteGlobalRef(env,globalStr);
}

/*JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_staticLocalCache
(JNIEnv *env, jclass jclz, jstring name){
// name属性 赋值操作
// static jfieldID f_id = NULL;// 局部缓存,这个方法会被多次调用,不需要反复的去获取 jfieldID OpenCV WebRtc
if (f_id == NULL){
f_id = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
}else{
printf("fieldID is not null\n");
}
(*env)->SetStaticObjectField(env, jclz, f_id, name);
}*/

// 全局静态缓存,在构造函数中初始化的时候会去缓存
static jfieldID f_name_id = NULL;
static jfieldID f_name1_id = NULL;
static jfieldID f_name2_id = NULL;



JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_staticLocalCache
(JNIEnv *env, jclass jclz, jstring name){
// 如果这个方法会反复的被调用,那么不会反复的去获取 jfieldID
(*env)->SetStaticObjectField(env, jclz, f_name_id, name);
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_initStaticCache
(JNIEnv *env, jclass jclz){
// 初始化全局静态缓存
f_name_id = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
f_name1_id = (*env)->GetStaticFieldID(env, jclz, "name1", "Ljava/lang/String;");
f_name2_id = (*env)->GetStaticFieldID(env, jclz, "name2", "Ljava/lang/String;");
}

JNIEXPORT void JNICALL Java_com_darren_day15_Simple1_exception
(JNIEnv *env, jclass jclz){

// 假设现在想给 ,name 赋值 name3
jfieldID f_id = (*env)->GetStaticFieldID(env, jclz, "name3", "Ljava/lang/String;");

// 好几种方式
// 1. 补救措施 ,name3 我拿 name
// 1.1 有没有异常
jthrowable throwable = (*env)->ExceptionOccurred(env);
/*if (throwable){
// 补救措施,先把异常清除
printf("有异常");
// 清除异常
(*env)->ExceptionClear(env);
// 重新获取 name 属性
f_id = (*env)->GetStaticFieldID(env, jclz, "name", "Ljava/lang/String;");
}*/

// 2. 想给 java 层抛一个异常
if (throwable){
// 清除异常
(*env)->ExceptionClear(env);
// Throw 抛一个 java 的 Throwable 对象
jclass no_such_clz = (*env)->FindClass(env,"java/lang/NoSuchFieldException");
(*env)->ThrowNew(env, no_such_clz,"NoSuchFieldException name3");

return;// 记得 return
}

jstring name = (*env)->NewStringUTF(env, "Darren");
(*env)->SetStaticObjectField(env, jclz, f_id, name);
}

Android.bp文件是Android系统的一种编译配置文件,是用来代替原来的Android.mk文件的。

【1】字符串表示
1
2
3
4
mk:
chips_dir := ../../../frameworks/opt/chips/res
bp:
chips_dir = ["../../../frameworks/opt/chips/res"]
1
2
3
4
mk:
res_dirs := $(chips_dir) $(color_picker_dir) $(timezonepicker_dir) res
bp:
res_dirs = chips_dir + color_picker_dir + timezonepicker_dir + ["res"]
1
2
3
4
mk:
src_dirs := src
bp:
src_dirs = ["src"]
【2】mk和bp模块对应表
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 $(BUILD_SHARED_LIBRARY) -> cc_library_shared {}

include $(BUILD_STATIC_LIBRARY) -> cc_library_static{}

include $(BUILD_HOST_SHARED_LIBRARY) -> cc_library_host_shared{}

include $(BUILD_HOST_STATIC_LIBRARY) -> cc_library_host_static{}

include $(BUILD_HEADER_LIBRARY) -> cc_library_headers{}

include $(BUILD_EXECUTABLE) -> cc_binary{}

include $(BUILD_HOST_EXECUTABLE) -> cc_binary_host{}

include $(BUILD_NATIVE_TEST) -> cc_test{}

include $(BUILD_HOST_NATIVE_TEST) -> cc_test_host{}

include $(BUILD_NATIVE_BENCHMARK) -> cc_benchmark{}

include $(BUILD_HOST_NATIVE_BENCHMARK) -> cc_benchmark_host{}

include $(BUILD_JAVA_LIBRARY) -> java_library_installable{}

include $(BUILD_STATIC_JAVA_LIBRARY) -> java_library{}

include $(BUILD_HOST_JAVA_LIBRARY) -> java_library_host{}

include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) -> java_library_host_dalvik{}

include $(BUILD_PACKAGE) -> android_app{}

include $(BUILD_RRO_PACKAGE) -> runtime_resource_overlay{}

include $(BUILD_CTS_EXECUTABLE) -> cc_binary{}

include $(BUILD_CTS_SUPPORT_PACKAGE) -> cts_support_package{}

include $(BUILD_CTS_PACKAGE) -> cts_package{}

include $(BUILD_CTS_TARGET_JAVA_LIBRARY) -> cts_target_java_library {}

include $(BUILD_CTS_HOST_JAVA_LIBRARY) -> cts_host_java_library{}
【3】mk和bp prebuilt 对应表
1
2
3
4
5
6
"SHARED_LIBRARIES": "cc_prebuilt_library_shared",
"STATIC_LIBRARIES": "cc_prebuilt_library_static",
"EXECUTABLES": "cc_prebuilt_binary",
"JAVA_LIBRARIES": "java_import",
"APPS": "android_app_import",
"ETC": "prebuilt_etc",
【4】mk和bp属性对应表
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
LOCAL_PACKAGE_NAME := Calendar -> name: "Calendar"
==================================================
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.calendar.* -> jacoco: { include_filter: ["com.android.calendar.*"],}
==================================================
mk:
LOCAL_SRC_FILES := $(call all-java-files-under, src)
bp:
srcs: ["src/**/*.java"],
==================================================
mk:
LOCAL_STATIC_JAVA_LIBRARIES := \
android-common \
libchips \
colorpicker \
android-opt-timezonepicker \
androidx.legacy_legacy-support-v4 \
calendar-common
bp:
static_libs: [
"android-common",
"libchips",
"colorpicker",
"android-opt-timezonepicker",
"androidx.legacy_legacy-support-v4",
"calendar-common",
],
==================================================
LOCAL_SDK_VERSION := current -> sdk_version: "current",
==================================================
mk:
LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
dp:
notice: LOCAL_PATH + "/NOTICE",
==================================================
mk:
# 混淆规则配置文件
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
bp:
optimize: {
proguard_flags_files: ["proguard.flags"],
},
==================================================
# true:将apk安装到priv-app下
LOCAL_PRODUCT_MODULE := true -> product_specific: true,
==================================================
mk:
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips
LOCAL_AAPT_FLAGS += --extra-packages com.android.colorpicker
LOCAL_AAPT_FLAGS += --extra-packages com.android.timezonepicker

bp:
aaptflags: ["--auto-add-overlay"] + [
"--extra-packages",
"com.android.ex.chips",
] + [
"--extra-packages",
"com.android.colorpicker",
] + [
"--extra-packages",
"com.android.timezonepicker",
],

==================================================

mk:
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.legacy_legacy-support-v13 \
androidx.legacy_legacy-support-v4 \
androidx.core_core
bp:
static_libs: [
"androidx.legacy_legacy-support-v13",
"androidx.legacy_legacy-support-v4",
"androidx.core_core",
],

==================================================

mk:
LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2-portability
LOCAL_STATIC_JAVA_LIBRARIES += xmp_toolkit
LOCAL_STATIC_JAVA_LIBRARIES += glide
LOCAL_STATIC_JAVA_LIBRARIES += guava
LOCAL_STATIC_JAVA_LIBRARIES += jsr305

bp:
static_libs: ["android-ex-camera2-portability"] + ["xmp_toolkit"] + ["glide"] + ["guava"] + ["jsr305"],

==================================================

mk:
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += $(call all-java-files-under, src_pd)
LOCAL_SRC_FILES += $(call all-java-files-under, src_pd_gcam)
bp:
srcs: ["src/**/*.java"] + ["src_pd/**/*.java"] + ["src_pd_gcam/**/*.java"],

==================================================

mk:
LOCAL_RESOURCE_DIR += \
$(LOCAL_PATH)/res \
$(LOCAL_PATH)/res_p
bp:
resource_dirs: [
"res",
"res_p",
],

==================================================

mk:
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--version-name "$(version_name_package)" \
--version-code $(version_code_package) \
bp:
aaptflags: [
"--auto-add-overlay",
"--version-name",
"\"" + version_name_package + "\"",
"--version-code",
] + version_code_package,

==================================================
# 使用SDK的hide的api
LOCAL_PRIVATE_PLATFORM_APIS := true -> platform_apis: true,
==================================================
LOCAL_CERTIFICATE := platform -> certificate: "platform",
==================================================
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy -> libs: ["org.apache.http.legacy"],
==================================================
mk:
LOCAL_JNI_SHARED_LIBRARIES := libjni_tinyplanet libjni_jpegutil
dp:
jni_libs: [
"libjni_tinyplanet",
"libjni_jpegutil",
],
==================================================
mk:
# 声明用到的注解
LOCAL_ANNOTATION_PROCESSORS := \
auto_value_plugin \
javapoet-prebuilt-jar \
dialer-dagger2 \
dialer-dagger2-compiler \
dialer-dagger2-producers \
dialer-glide-annotation \
dialer-glide-compiler \
dialer-guava \
dialer-javax-annotation-api \
dialer-javax-inject \
dialer-rootcomponentprocessor
bp:
plugins: [
"auto_value_plugin",
"javapoet-prebuilt-jar",
"dialer-dagger2",
"dialer-dagger2-compiler",
"dialer-dagger2-producers",
"dialer-glide-annotation",
"dialer-glide-compiler",
"dialer-guava",
"dialer-javax-annotation-api",
"dialer-javax-inject",
"dialer-rootcomponentprocessor",
],
==================================================
LOCAL_OVERRIDES_PACKAGES := Calculator -> overrides: ["Calculator"],
==================================================
LOCAL_MIN_SDK_VERSION := 26 -> min_sdk_version: "26",
==================================================
LOCAL_PRIVILEGED_MODULE := true -> privileged: true,
==================================================
LOCAL_SYSTEM_EXT_MODULE := true -> system_ext_specific: true,
==================================================
mk:
LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
bp:
overrides: [
"Home",
"Launcher2",
"Launcher3",
"Launcher3QuickStep",
],
==================================================
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3 -> required: ["privapp_whitelist_com.android.launcher3"],
==================================================
mk:
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/AndroidManifest.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
bp:
additional_manifests: [
LOCAL_PATH + "/AndroidManifest.xml",
LOCAL_PATH + "/AndroidManifest-common.xml",
],
==================================================
LOCAL_MANIFEST_FILE := go/AndroidManifest.xml -> manifest: "go/AndroidManifest.xml",
==================================================
mk:
# 混淆配置,默认为full obfuscation,全代码混淆,disabled不开启
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_PROGUARD_ENABLED := full obfuscation

dp:
optimize: {
enabled: false,
},
==================================================
mk:
LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_APPS)
bp:
local_module_path: {
var: "TARGET_OUT_PRODUCT_APPS",
},
==================================================
LOCAL_COMPATIBILITY_SUITE := general-tests -> test_suites: ["general-tests"],
==================================================
mk:
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
contextualcards:libs/contextualcards.aar
include $(BUILD_MULTI_PREBUILT)
bp:
android_library_import {
name: "contextualcards",
aars: ["libs/contextualcards.aar"],
}
【5】函数宏
1
2
3
4
5
6
7
LOCAL_PATH := $(call my-dir):返回Android.mk 的目录
include $(call all-java-files-under,<name>):返回位于<name>目录下的所有java文件。
如果不指定<name>,怎么返回my-dir目录下所有的java文件。
include $(call all-makefiles-under,<name>):返回位于当前 <name> 路径下所有目录中的 Android.mk 文件列表。
利用此函数,可以为构建系统提供深度嵌套的源目录层次结构。
默认情况下,系统只在 Android.mk 文件所在的目录中查找文件。
LOCAL_SRC_FILES := $(call all-logtags-files-under, src):返回 src 路径下所有 logtags 格式的文件。
【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
26
27
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
===========================================
# 开启AAPT2打包APK,AAPT是Android Asset Packaging Tool的缩写,AAPT2在AAPT的基础做了优化。
LOCAL_USE_AAPT2 := true
===========================================
# 在什么类型的版本下编译
# user/debug/eng: 指定该模块只在 user 版本下才编译(makePRODUCT-XXX-user)
# optional: 在所有版本下都会编译
LOCAL_MODULE_TAGS := optional
===========================================
引入aar:
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := contextualcards:libs/contextualcards.aar
include $(BUILD_MULTI_PREBUILT)
===========================================
# 声明 mk 要使用到的共享 JNI 库的名称
LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
===========================================
mk:
# 声明用到的注解器
LOCAL_ANNOTATION_PROCESSOR_CLASSES := \
androidx.room.RoomProcessor
===========================================
# protocol 相关配置
LOCAL_PROTOC_OPTIMIZE_TYPE
===========================================
Android.mk 最后一句:include $(call all-makefiles-under, $(LOCAL_PATH))

原文链接:https://www.jianshu.com/p/22cfdb1ad7b5

Application.mk中定义一些全局(整个项目)的配置,配置相对较少,比较简单。

下面是基本配置:

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
# 确定CPU架构
APP_ABI := armeabi-v7a

# 支持所有CPU架构:armeabi-v7a arm64-v8a x86_64 等等
# APP_ABI := all

# 一般对应Android SDK的最低版本
APP_PLATFORM := android-21

# c++_static 静态链接
# c++_shared 动态链接
# system 系统默认
# 如果生成的so库包含静态.a文件,这个属性要写成c++_static,否则可以不用写
APP_STL := c++_shared

# 用来指定C++功能
APP_CPP_FEATURES += exceptions rtti

ifdef APP_DEBUG
ifeq ($(APP_DEBUG),true)
CFLAGS+= -O0 -g
LOCAL_CFLAGS+= -D_DEBUG
APP_OPTIM := debug
else
CFLAGS+= -O2 -g
LOCAL_CFLAGS+= -DNDEBUG
APP_OPTIM := release
endif
endif

【1】APP_OPTIM

1
2
3
将此可选变量定义为 `release` 或 `debug`。
在构建应用的模块时可使用它来更改优化级别。
发行模式是默认模式,可生成高度优化的二进制文件。调试模式会生成未优化的二进制文件,更容易调试。

【2】APP_DEBUG

可以在AS中手动设置 Build Variants 改变的值。

img

1
2
如果选择 debug,APP_DEBUG等于true;
如果选择 release,APP_DEBUG等于false;

【2】APP_CFLAGS

1
为任何模块编译任何 C 或 C++ 源代码时传递到编译器的一组 C 编译器标志

【3】APP_CPPFLAGS

1
构建 C++ 源文件时传递到编译器的一组 C++ 编译器标志。

【4】APP_ABI

1
2
3
4
5
6
7
8
9
10
11
12
需要生成的cpu架构。

现在手机主要是armeabi-v7a。查看手机cpu架构的方法是:

adb shell cat /proc/cpuinfo
adb shell getprop ro.product.cpu.abi

apk在安装的时候,如果手机是armeabi-v7a的,则会首先查看apk中是否存在armeabi-v7a目录,
如果没有就会查找armeabi。

如果目标是armeabi-v7a,但是拥有一个armeabi的,也可以把它放到armeabi-v7a目录下。
但是反过来不行。
ABI(横 so)/CPU(竖 手机) armeabi armeabi-v7a arm64-v8a x86 x86_64
ARMV5 支持
ARMV7 支持 支持
ARMV8 支持 支持 支持
X86 支持
X86_64 支持 支持

【5】APP_PLATFORM

1
与app最小SDK版本对应即可。

img

【6】APP_STL

1
2
默认情况下,NDK 构建系统为 Android 系统提供的最小 C++ 运行时库 (`system/lib/libstdc++.so`) 
提供 C++ 功能。
名称 说明> 功能
libstdc++(默认) 默认最小系统 C++ 运行时库。 不适用
gabi++_static GAbi++ 运行时(静态)。 C++ 异常和 RTTI
gabi++_shared GAbi++ 运行时(共享)。 C++ 异常和 RTTI
stlport_static STLport 运行时(静态)。 C++ 异常和 RTTI;标准库
stlport_shared STLport 运行时(共享)。 C++ 异常和 RTTI;标准库
gnustl_static GNU STL(静态)。 C++ 异常和 RTTI;标准库
gnustl_shared GNU STL(共享)。 C++ 异常和 RTTI;标准库
c++_static LLVM libc++ 运行时(静态)。 C++ 异常和 RTTI;标准库
c++_shared LLVM libc++ 运行时(共享)。 C++ 异常和 RTTI;标准库

一般情况下,如果设置的配置不符合当前版本,会有明显的错误提示:

img

【7】在 build.gradle 中配置全局参数

全局参数不仅可以在Application.mk中配置,还可以在 build.gradle 中配置。

1
2
3
4
5
6
externalNativeBuild {
ndkBuild {
arguments "APP_STL=c++_static", "APP_PLATFORM := android-21"
abiFilters 'armeabi-v7a'
}
}

多个配置以逗号隔开。

原文链接:https://www.jianshu.com/p/4de7f04bc01c