工具配置

配置Toolchains

打开Clion,File->Settings->Build->Toolchains然后添加minggw64的路径

1
https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/sjlj/x86_64-8.1.0-release-posix-sjlj-rt_v6-rev0.7z/download

中文乱码

settings->Editor->File Encodings,Global Encoding设置utf-8,Project Encoding设置utf-8,点击+把代码文件也设置utf-8,配置文件也改成utf-8,按住Ctrl+Shift+Alt+/选中Registry,然后取消掉run.processes.with.pty后面的√,点击close即可

变量

常量

const int a =100;

宏常量

#define name "john" ,宏常量在预编译的时候就替换进去了,反编译的代码里不会有这个

整型

int(4字节), short int == short(2字节), long int == long(Windows为4字节,32位Linux为4字节、64位Linux为8字节), long long int == long long(8字节)

溢出的话,就是short类型赋值为65536,就会丢失数据

取数据大小,sizeof(变量名)

浮点型

0.105可以写成.105

代码中直接出现的浮点数默认为double

使用float b = .105f,可以指定为float类型

cout输出默认显示6位有效数字,要显示更多需要配置,但是这种情况下会精度丢失

1
2
3
std::out.setf(std::ios::fixed);
std::out.setf(std::ios::showpoint);
std::out.precision(20);

字符型

使用单引号,只能写一个字符,不能是中文,占一个字节(Java里2个字节)

本质上也是数值,存的ASCII码的值,可以加减

有符号数和无符号数

signed和unsigned,不写默认有符号数

字符串

1
2
3
4
5
char cc[10] = "测试的";//长度为9报错,默认字符串结尾添加字节0代表结束
char cc[] = "测试的";
char* str = "测试的";
std::string ccc = "test";
ccc.c_str(); //C++ string转C char*

sprintf

用来格式化输出的

1
2
char aa[10];
sprintf(aa,"%x",100); //%x是十六进制

bool类型

C++才有,C没有,占一个字节

Java类型和C类型

比较

1
2
3
基本数据类型,两者通用

对于Java的引用类型,需要jni转换

jni用来解决Java和C之间的相互调用的问题

1
2
3
4
5
程序最终处理的都是数据

Java处理数据后的结果,只要转成C认识的类型,那么C就能接着处理

jni就规定了Java数据和C数据的映射关系,Java语言调用c语言的规则,并且提供了一系列的方法,用于转换数据、C调用Java等
Java 字节数 C
int 4 int
short 2 short
byte 1 char
long 8 long long
double 8 double
float 4 float
char 2 short
boolean 1 0代表假 1代表真

数据输入

cin >> a

数组

1
2
3
4
5
6
7
8
数据类型 数组名[数组长度]

数据类型 数组名[数组长度] = {值1,值2...}

数据类型 数组名[] = {值1,值2...}

char a[] = {'a','b','c','\0'}; //最后要有\0
或者 char a[] = {'abc'}; //最后有没有\0都行

下标越界

计算数组长度sizeof(arr)/sizeof(arr[0])

数组地址

1
2
3
std::cout << arr << std::endl; //数组首地址_十六进制
std::cout << (long long)arr << std::endl; // 数组首地址_十进制
std::cout << &a[0] << std::endl; //数组首个元素的地址

字符串数组,char *代表字符串

1
char* a[4] = {'hello','you','yyyyyy','aaaaaaaa'};

指针

就是地址

定义和初始化

1
2
3
4
5
6
int *p;
p = &a;
或者
int *p = &a;

*p可以访问到p所指向的变量的值

运算符*和&

1
2
3
4
5
6
7
8
9
10
&:取地址符
a:变量a本身
p:指针变量p本身
&a:变量a的地址值
*p:指针变量p所指向的变量,也就是变量a
&p:指针变量p的地址值,无意义
*a:把a看作指针,*a代表指针指向的变量,无意义

int a = 0x84025789(假定是合法地址)
*a不合法,a根本不是指针变量

指针类型和所指向的变量类型必须匹配否则结果不可预知

空指针指向地址0,野指针不可预知

指针的大小,32位是4字节,64位8字节

指针与const

1
2
3
4
5
int a = 100;

const int* p1 = &a; // 常量指针,p1本身可以变,但是p1的指向不能变,*p1 = 300 不合法
int* const p2 = &a; //指针常量,p2被定义为常量,那么p2的内容不能变,p2 = &b 不合法
const int* const p3 = &a; // 都不能变

指针应用

1
2
3
4
5
6
7
实现函数参数引用传递

实现函数多个返回值

处理字符串

表示结构体等复杂的数据结构,在作为参数传递的时候更节省内存

数组指针

数组的地址,int arr[] = {1,2,3},那么arr&arr&arr[0] 都表示数组的首地址

数组指针 int * arr_p = arr;

依次取数据的成员,arr_p++; printf(*arr_p) ,指针+1,加的是1个数据类型的长度(注意*p+1*(p+1) 不一样,后者才是对的)

1
2
3
4
char* a[4] = {'hello','you','yyyyyy','aaaaaaaa'};
// char*类型,想指向它就得是char**类型,就这么理解
char** arr_p = a;
*arr_p访问到的就是hello

函数指针

指向函数的指针

定义和初始化 void (*pFunc)(int*, int*) = nullptr; ,函数名就是函数的首地址,所以赋值 pFunc = &swap 也可以 pFunc = swap,函数指针代表的是这一类函数,返回值是void,参数是俩int *

调用 (*pFunc)(...) 或者 pFunc(...)

函数指针数组 void (*pFunc[2])(int*, int*); 只要是参数为两个int*的就能赋值

函数指针作为函数的参数

1
2
3
4
5
6
7
8
9
10
typedef void (*pFunc)(int*, int*); // 定义类型,后面直接用pFunc就可以
...

void call_Func(pFunc pf){
...
}

int main(){
pFunc = ...;
}

全局变量

1
2
在本文件中定义的全局变量
引用别的文件中定义的全局变量

extern

1
2
3
4
5
6
7
8
9
10
11
extern int a; 表示引用别的文件中的全局变量a

demo.h里写上 extern int a;,然后在demo.cpp里定义int a = 30;,然后在main.cpp里导入include "demo.h"
或者在main.cpp里不导入.h,直接写extern int a;也行(前提是demo.cpp里没有别的数据需要导入的)
第二种方法,编译器之所以知道去哪里找a变量,是在cmakelist.txt里确定的

extern "C" ..; 表示后续的代码以C语言的方式编译
extern "C" int swap(){
...
}
代表这个函数名在编译的时候不进行符号修饰

静态变量static

1
2
3
4
5
6
静态局部变量
swap()函数里定义了static int c = 1;c++; ,主函数连续调用三次swap函数,依次输出 1 2 3

静态全局变量,只能在当前文件内使用,在其他的cpp内不能用

静态函数,同上

内存区域

代码区,只读

全局区,全局变量、全局常量、静态变量、字符串常量

栈区,存放函数的参数和局部变量

堆区,由程序员分配和释放

char指针和char数组的区别

1
2
3
char *str1 = "test"; // 相当于常量,在全局区,不可被改变,*str1 = "new"会报错

char str2[] = "test"; // 本质是数组,可以修改

malloc和calloc

返回的都是void *,需要根据具体使用来强转类型

1
2
3
char* realResult = (char *)malloc(33);

char* realResult = (char *)calloc(33, 1);

注意不要把函数内的局部变量的地址赋值给函数外的指针

1
2
3
4
5
6
7
int test(){
int a =100;
return &a;
}
int main(){
int *p = test();//访问不到,局部变量被释放
}

多级指针

指向指针的指针

1
2
3
int a = 100; 
int *p = &a;
int **pp = &p;//访问a就是**pp

多级指针的应用

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
想把参数当返回值,传入的实参是char*等指针的时候
void swap(int* result){
*result = 300;
}

int main(){
int result = 100;
swap(&result);
}
上述的可以修改值

void swap(char* result){
result = "new";
}
int main(){
char* result = "old";
swap(result);// 无法修改值
}
上述的无法修改,因为在函数栈中新生成了局部变量result,这个局部变量被赋值为"old"字符串的地址,然后在函数内被修改为"new"字符串的地址,全程动的只是这个局部变量的值

void swap(char** result){
*result = "new";
}
int main(){
char* result = "old";
swap(&result);// 可以
}
上述的可以修改值


传入的实参是一级指针,那么想把参数当返回值,形参就得是二级指针

形参要比实参多一级指针才能把参数当返回值用

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void swap1(char** result){
char temp[3];
char* realResult = (char *)calloc(33, 1);
for(int i = 0; i < 16; i++){
int index = i;
sprintf(temp, "%.2x", index);
strcat(realResult, temp);
}
cout << "swap1: " << realResult << endl;
*result = realResult;
// 不能在函数内部free(realResult),不然访问不到了
}

int main() {
char* result = "kong";
swap1(&result);
cout << "main: " << result << endl;
free(result);
return 0;
}

也就是可以说,参数是二级指针,那么一般就是要改一级指针的指向,char **的话一般就是改字符串

结构体

相当于自定义数据类型

1
2
3
4
struct People {
string name;
int age;
}

使用

1
struct Student s1;// C++里struct可以忽略

结构体里还可以定义函数指针

结构体指针和结构体传参

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>
using namespace std;

struct Student{
string name;
};

typedef struct {
string name;
int age = 30;
bool sex;
Student s;
void (*pFunc)();
} testname;

void swap(){
cout << "this Func is Called" << endl;
}

void swap1(testname* x){
cout << x->name << endl;
}

int main() {

int a;
int b = 100;
testname p;
p.name = "john";
p.age = 31;
p.sex = true;
p.s.name = "rw";
p.pFunc = swap;
p.pFunc();
cout << p.name << endl;
cout << p.age << endl;
cout << p.sex << endl;
cout << p.s.name << endl;

Student sArr[2];
sArr[0].name = "rw";
sArr[1].name = "umr";
cout << sArr[0].name << endl;
cout << sArr[1].name << endl;

testname* pp = &p;
(*pp).pFunc();
pp->pFunc();// 这种访问方式也行

swap1(&p);

return 0;
}