1. 变量与常量
变量/常量
- 变量
数据类型 变量名=初始值
- 常量
const 数据类型 变量名=常量值constexpr
传统const的问题在于“双重语义”,既有“只读”的含义,又有“常量”(不可改变)的含义,而constexpr严格定义了常量 为不可改变。
const for poiter
1
2
3
4
5
6
int b = 1;
//C++规定const在类前和类后是一样的。
const int *a = &b; //该指针可以指向其它的变量但无法修改它们的值。
int const *a = &b; //同上一行
int* const a = &b; //无法指向别的地址。
const int* const a = &b; //既不能指向其它变量的地址,也不能修改值。
const for function
1
2
3
const int func(){}; //函数返回const int类型
int const func(){}; // 函数返回const int类型
void func() const{}; //通常是在类中,表示该函数不修改成员变量
const for class 类的const实例只能调用类的const成员函数.
1
2
3
4
5
6
7
8
class A{
public:
void test1() const;
void test2();
};
const A classA;
classA.test1();//正确
classA.test2();//错误
变量属性
作用域
在 C++源代码中,一个标识符能被语句使用的区域范围称为此标识符的作用域。一个标识符只有在其作用域内才是“可见”的,在作用域以外不能访问.
- 分类
- 局部作用域(块作用域): 局部作用域是指在函数、块或类内部定义的变量,其作用域仅限于定义它的代码块。
- 局部变量
loop/ main/ class/ struct/ if/ func - 函数形参
- 局部变量
- 全局作用域: 全局作用域中的变量和函数在程序的任何地方都可见, 全局变量在所有函数和类中可访问.
::varN
-
函数作用域
-
类作用域: 在类内部定义的成员变量和成员函数具有类作用域, 它们在类的所有成员函数中可见.
- 命名空间作用域
namespace: 特定的块声明成“命名空间”,将标识符的作用域限制在块内1 2 3 4
nameapce ns { }
- 就近原则,跳出最近作用域后改变
- 局部作用域(块作用域): 局部作用域是指在函数、块或类内部定义的变量,其作用域仅限于定义它的代码块。
生存期
一个动态的概念,说明了标识符表示的数据对象存在的时间区间
- 影响因素:存储类别
- 动态存储 不自动初始化
- 静态存储 初始化只进行一次 没有初始化其值,则系统自动赋值 0x0
宏定义
1. 数据宏定义
1
#define LEN 1024
| 已有宏 | 含义 |
|---|---|
| LINE | 这会在程序编译时包含当前行号。 |
| FILE | 这会在程序编译时包含当前文件名。 |
| DATE | 形式为 month/day/year 的字符串, 表示编译的日期。 |
| TIME | 形式为 hour:minute:second 的字符串, 表示程序被编译的时间 |
2. 类型宏定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef type alias
typedef struct LNode *LinkLink
typedef struct Sname {
type1 var1;
type2 var2;
}alias1,alias2;
struct Sname{
type1 var1;
type2 var2;
}instant1, instant2;
typedef struct 和 struct 的区别在于 -
- 使用时是否可以省去struct关键字(C), CPP不做区分
- typedef: 别名; struct: 实例化;
实例
1
2
3
4
5
6
7
8
9
10
11
12
typedef Node{
}Node,*LinkList;
struct Node{
};
typedef struct Node Node;
typedef struct Node *LinkList;
Node *list;
LinkList list;
3. 函数宏定义
1
2
3
4
5
6
7
#define DEBUG_ON
#ifdef DEBUF_ON
#define PRINT_DEBUG(a) print("%d",a)
#else
#define PRINT_DEBUG()
#endif
枚举
枚举
1
2
3
4
5
6
7
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数]
} 枚举变量;
联合体
- 声明
1 2 3 4 5
union Data { char a[4]; int b; };// 32bit,可通过a操控8bit
- 性质
- 空间占用: 内部最大成员的大小
- 内部所有属性共用一个空间, 无内存对齐.
- 作用
- 判断大小端
2. 数据类型
基本类型
- 整型 : 描述整数
- short
- int
- long
- longlong
long long z =42LL;
- 无符号整型 : 首位不表正负,因而所表数字更多
- unsigned int
unsigned int x = 42u; - unsigned short
- unsigned long
- unsigned longlong
- unsigned int
- 浮点型 : 描述小数
- float
float abc=123f - double
- float
- 字符型 : 描述单个字母
- char
- 字符串型 描述多个字母
- string
string abc ="aaa"
- string
- 布尔类型 : 表示真假
- bool
bool abc=true/false - 输出字符true/false
- bool
- 空类型
- void
转义字符
- 转义字符 特殊 ASCII 字符
\n换行\\\\t水平制表 整齐输出\b退格\r回车\0空字符\'单引号\"双引号\ddd1~3 位八进制数所代表字符\xhh1~2 位十六进制数所代表字符\t\x16 进制 charch = ‘\01a’\08 进制\a发出报警音
类型转换
- 强制/显式类型转换explicit conversion
(type)var// C风格转换语句type(var)//C++风格转换语句- 问题 : 目标类型表示范围小于原类型
- 数值截断
- 精度丢失
- 自动/隐式类型转换implicit conversion
赋值动作中,数据类型与变量类型不一致时自动发生- 隐式转换
- 不同类型的算数运算中
1 2
int m;doule n; m+n; //先将m转为double再进行加法
- 不同类型的赋值运算中
1 2
int m;doule n; n=m; //先将m转为double再进行赋值
- 不同类型的算数运算中
- 数字类型默认int和double, 类型转换发生于:
- 初始化与赋值:
int counter = 2.718; float f =2111222333; - 函数调用:传递参数与返回值
- 算术运算符求解:
7 / 2.718;
- 初始化与赋值:
- 隐式转换
- 示例
- 整型to 浮点: 由于浮点数有效数字位数较少,转换可能导致精度降低
- 浮点to 整型: 浮点数不超出整型表示的数据上限时截断;否则结果没有规律
- 小整型vs 大整型: 只保留右边字节存储的数据
- 尽量避免“大类型”向“小类型”的自动转换
- C++ 中四种强制类型转换
- ``
数组
- 存放相同类型数据元素
- 由连续内存位置组成
- 一维数组: 一个数组中数据类型相同
- 数组定义
数据类型 数组名[数组长度]; //定义数组名[数据编号]=123; //赋值数据类型 数组名[ ]={值 1,值 2,值 3,值 4}; //定义,数组长度自动填写数据类型 数组名[数组长度]={值 1,值 2,值 3,值 4}; //定义
- 数组定义
- 二维数组
- 数组定义
数据类型 数组名 [行] [列] ; //定义数据类型 数组名 [行] [列]=123 //赋值数据类型 数组名 [行] [列]= { {1,2,3} , {4,5,6} }; //二维数组可视化数据类型 数组名 [行] [列]={1,2,3,4,5,6};数据类型 数组名 [ ] [列]={1,2,3,4,5,6}; //自动算出行数
- 数组定义
- 指针数组, 可用以实现:
- 堆区二维数组
- 堆区一维数组
指针
记录数据地址, 32bit: 4字节, 64bit: 8字节.
- 定义
数据类型*指针变量名; 指针变量名=&变量名;int * p; p=&num1;
数据类型*指针变量名=&变量名;int * p=&num1;
- 使用: 通过解引用找到指针指向内存
*指针变量名
- 常见指针
- 空指针指向地址0 , 指向的内存不可访问(0-255 为系统占用,不可访问)
int \*p=NULL;
- 野指针
int _ p=(int_)0x1100;
- const
- const 修饰常量 : 指针常量
指针指向(p)不可改,指针指向的值(*p)可改.int * const p= &num1
- const 修饰指针 : 常量指针
指针指向(p)可改,指针指向的值(*p)不可改.const int *p= &num1
- 既修饰常量, 又修饰指针:
指针指向
(p)不可改,指针指向的值(*p)不可改const int * const p= &num1
- const 修饰常量 : 指针常量
- 空指针指向地址0 , 指向的内存不可访问(0-255 为系统占用,不可访问)
- 指针按指向分类
- 堆区指针
- new类
p-> - new数组
p[ ] - new其他
*p
- new类
- 栈区指针
- 堆区指针
引用
- 引用
- 使多个变量指向同一块内存
- 对数据的操控等效
- 引用本质为指针常量
- 引用使用
type &alias = ori;
- 引用注意
- 引用必须初始化(赋初值)
int &c; //错误,引用必须初始化
- 引用在初始化后,(指向)不可以改变
int &c = a; //一旦初始化后,就不可以更改c = b; //这是赋值操作,不是更改引用
- 引用必须初始化(赋初值)
- 引用作用
- 引用作函数参数: 与地址传递功能相同, 让形参修饰实参
1 2 3 4 5 6 7
void mySwap03(int& a, int& b) { int temp = a; a = b; b = temp; } mySwap03(a, b);
- 引用做函数返回值: 不可返回局部变量的引用, 可返回静态变量(存放在全局区)
- 返回全局变量
1 2 3 4
int &func(){ return a; } int &a=func();
-
返回类的引用
- 接受引用返回值的对象能修改该值
- 接受引用返回值该使用引用
1 2 3 4 5
student &func(student other){ if(1){return *this;} else {return other;} } student&ssss=s.func(1);
- 返回全局变量
- 函数调用作左值
func()=1000;
函数返回值为引用时, 则为函数返回的引用赋值
- 引用作函数参数: 与地址传递功能相同, 让形参修饰实参
- 常量引用: 修饰形参(指向和数值都不能改变), 可用以引用常量
- 用在 man 中
main: int&b=10; //错误main: const int&b=10; //正确
- 用在函数中
1 2 3
void showValue(const int& v) { } main: showValue(a);
- 成员函数中,前 const 无法修改 s1,后无法修改 this,返回值改为常引用
1 2 3 4
const student&func(const student&s1)const { return *this; }
- 用在 man 中
函数指针
- 函数指针是指指向函数的指针.
- 回调函数无非是对函数指针的应用,用函数指针来调用一个函数.
- 注册函数则是将函数指针作为参数传进去,便于其它函数调用.
1
2
3
4
5
6
7
8
int funcOri(tpye1 para1,type2 para2,...){
return 0;
}
int main(){
returnType (*funcPoi)(type1 para01,type2 para2,...); //指针参数类型
funcPoi=funcOri; //指针指向函数
funcPoi(var1,var2,...); //通过指针调用函数
}
使用typedef:
1
2
typedef void (*fp)(void);
fp b[] = {t1,t2,t3};
3. 运算符号
- 赋值运算符
=赋值+=-=/=*=%=^=
- 比较运算符
cout 中加( )优先运算==等于!=不等于> /<大于/小于> =/<=大于等于/小于等于
- 算数运算符
1
2
3
4
5
*
/
%
var++;//返回值后自增
++var;//先自增再返回
- 逻辑运算符
!非&&与||或
- 三目运算
condition ? val1 : val2
4. 程序流程结构
-
顺序结构
-
选择结构
-
if 语句
1 2 3 4 5 6 7
if(condition){block;} if(condition){block;}else{block;} if(condition1){block1} else if(condtion2){block2} else{blcok3}
-
三目运算符: 返回变量( #左值),可以继续赋值
表达式 1?表达式 2:表达式 3;
-
switch
1 2 3 4 5 6 7
switch( ) { case 1: case 99: case 44: default: } // case 中语句过长时用{}括起来
-
-
循环结构
-
while
1 2 3
while(condition){ block; }
-
for
1 2 3
for(initBlock;continueCondition;loopAdd){ block; }
-
do while
1 2 3
do { block; } while(condition);
-
-
跳转语句
-
break跳出选择或循环- switch 结构中
- 终止 case 跳出 switch
- 循环语句中
- 跳出当前循环语句
- 嵌套循环中
- 跳出最近的内层循环语句
- switch 结构中
-
continue在循环语句中跳过其后的执行语句 -
goto跳转到标记处(极少使用)1 2 3 4
goto FLAG; //code FLAG: //标记用冒号 //code
-
5. 函数
函数基础
- 函数
-
定义
1 2 3 4
returnType funcName(paraType paraVar,...){ block; return val; }
-
声明: 声明可以写多次,定义只有一次
1
returnType funcName(paraType paraVar,...);
-
调用
funcName(paraVar,...);
-
参数传递
-
值传递(直接传递拷贝)
- 值传递时,函数形参改变不影响实参
- 函数内部数据不影响外部,仅通过输出(return)影响
-
地址传递
-
传入数组
-
传入指针
-
引用传递
returnType funcName(int &a,...)
-
-
常见样式
- 有返
- 无返
进阶
- 函数默认参数(形参)
- 返回值类型 函数名 (参数= 默认值){}
retTyp funcN(para=defaultVal)- 作用
- 可以使调用函数时传入的参数少于所需参数
- 未传数值使用默认值
- 注意
- 1.如果某个位置参数有默认值,那么从这个位置往后都要有默认值
-
- 函数声明和函数实现只能其中一个有默认参数
-
函数的占位参数
- 占位参数也可以有默认参数
1 2 3 4
void func(int a, int) {} int main{ func(10,10); //占位参数必须填补 }
1 2 3 4
void func(int a, int=10) {} int main{ func(10); }
-
函数重载
-
满足条件
- 函数名相同
- 同一个作用域下
- 函数名称相同
- 函数参数类型不同/个数不同/顺序不同.
- 函数的返回值不同不可函数重载,仍为同一函数
-
注意事项
-
引用作函数重载条件
1 2 3 4 5 6 7 8
retType funcName(int&a){} int main{ (变量) } retType funcName(const int&a){} int main{ (常量) //例如 (10) }
-
函数重载与默认参数 函数重载最好不写默认参数
1 2 3 4 5
void func2(int a, int b = 10){ cout << "func2(int a, int b = 10) 调用" << endl;} void func2(int a){ cout << "func2(int a) 调用" << endl;} func2(10); //碰到默认参数产生歧义,需要避免
-
-
inline 函数内联
避免相同函数重写多次的麻烦,减少了运行时间但是增加了空间开销。
类内的成员函数,默认都为inline.
-
inline使用限制 inline 的使用是有所限制的,inline 只适合涵数体内代码简单的函数使用,不能包含复杂的结构控制语句例如 while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)
-
inline仅是一个对编译器的建议
-
建议 inline 函数的定义放在头文件中 因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。
-
声明跟定义要一致:如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为。如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定。所以,最好将内联函数定义放在头文件中。
-
类中的成员函数与inline 定义在类中的成员函数默认都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的。
6. 结构体
即自定义数据类型
-
结构体
- 定义
- struct 结构体名{结构体成员列表};
struct humans { int age; string sex; };
- struct 结构体名{结构体成员列表};
- 创建
- 先定义再单独赋值
struct humans hum1; hum1.age= 24 ; hum1.sex= "男" ;
- 定义时直接赋值
struct humans hum1={24,"男"};
- 定义时创建
struct humans { int age; string sex }hum3;
- 先定义再单独赋值
- 定义
-
结构体数组
- 创建
- struct 结构体名 数组名[ ]= { {12,”女” }, {16,”男”}, {99,”女”}, };
- 创建
-
结构体指针: 访问结构体成员
(*p).namep->name
-
结构体作函数参数
-
值传递: 实参不变
1 2 3 4
void printstudent(struct student xzy) { cout<<xzy.name; }; printstudent(xzy);
-
地址传递: 实参改变
1 2 3 4
void printstudent(struct student *pxzy) { cout<<pxzy->name; }; printstudent(&xzy)
-
-
结构体中 const 的使用
只读状态,防止错误. 与地址传递结合,节省数据量,且值不变.(常量指针).1 2 3 4 5
void printstudent(const struct student *pxzy) { cout<<pxzy->name; }; printstudent(&xzy)
7. 限定符
static
- 变量
- global
- 只有本文件能访问该变量(默认即为 static)
- local
- 程序开始时初始化
- class. val
- 可以通过
类名::变量名直接访问, 实现多个不同的类实例之间的数据共享.
- 可以通过
- global
- 函数
- global
- 文件外不能访问该函数
- class.method
- 可以通过
类名::方法名直接访问.
- 可以通过
- global
extern
- 变量
- global
- 文件外能访问
- global
- 函数
- global function
- 文件外能访问(默认)
- global function
8. 变量作用域
9. 文件与 I/O
CPP
输入输出
-
转化进制
标记使 cout 对所有整型变量都使用指定的方式输出(直到被下一个标记改变位置),不仅限于标记之后的一个变量- 16
cout<<hex<<var; - 10
dec - 8
oct
- 16
-
数据输入
cin>>a;cin.getline(str, len);cin.get(char)
-
数据输出
cout.put(a);a 为一个已定义字符或’a’
读写文件
- 文件流
| 数据类型 | 描述 |
|---|---|
| ofstream | 该数据类型表示输出文件流,用于创建文件并向文件写入信息。 |
| ifstream | 该数据类型表示输入文件流,用于从文件读取信息。 |
| fstream | 该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能 |
- 流状态成员函数
eof(): 表示如果读文件到达文件末尾,返回truebad():如果在读写过程中出错,返回 truefail():读写过程出错或格式错误时返回true
-
打开文件
1 2
fstream ofm("fileName", ios::mode1 | ios::mode2); fs.open("fileName", ios::mode1 | ios::mode2);
| 模式标志 | 描述 |
|---|---|
| ios::app | 每次写操作定位到文件末尾。 |
| ios::ate | 文件打开后定位到文件末尾。 |
| ios::in | 打开文件用于读取。 |
| ios::out | 打开文件用于写入。 |
| ios::trunc | 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 |
| Ios::binary | 二进制 |
-
写文件
1
outfile << data << endl;
-
读文件
1 2
infile >> data; getline(infile,tmpStr);
-
关闭文件
1
fs.close()
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 写
fstream obj;
obj.open("./test.txt", ios::app);
for (auto i = 0; i < 3; i++)
{
obj << str << endl;
}
obj.close();
// 读
fstream obj2;
obj2.open("./test.txt", ios::in);
while (obj2 >> str)
{
cout << str<<endl;
}
obj2.close();
C
- 流
- 输入流
- 输出流
- 文件流
输入输出
以两输出
1
2
char tmp = 0xFF;
printf("%02X ",(int)(unsigned char)tmp);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// in
scanf();//格式化读取
gets();//读取一行
fget();//从指定流读取一行
fgetc();
getc();
getchar();
// out
printf();
print();
puts();
fputs();
putc();
fputc();
putchar();
//
fflush()
文件读写
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
// 文件的打开
fopen() // 打开文件
// 文件的关闭
fclose() // 关闭文件
// 文件的读写
fgetc() // 读取一个字符
fputc() // 写入一个字符
fgets() // 读取一个字符串
fputs() // 写入一个字符串
fprintf() // 写入格式化数据
fscanf() // 格式化读取数据
fread() // 读取数据
fwrite() // 写入数据
// 文件状态检查
feof() // 文件是否结束
ferror() // 文件读/写是否出错
clearerr()// 清除文件错误标志
ftell() // 文件指针的当前位置
// 文件指针定位
rewind() // 把文件指针移到开始处
fseek() // 重定位文件指针
字符串操作
- code
1
2
3
4
strcmp(str1,str2){
return 0 if same
return -1 if notsame
}
1
2
3
4
5
strcpy(char* dst,char*src);
strncpy(char*dst,char*src,int copyCharNum);
strcat(char*ori,char*append);
char*p = strstr(char* str,char* substr);
strlen();
10. 项目管理
- 分文件编写
- .h 头文件中写声明
1 2
#include using namespace std;
- .cpp 源文件中写函数定义
1 2
#include"头文件模板.h" //以下写函数定义
- .h 头文件中写声明
. 其他
随机数生成
1
2
#include<ctime>
srand((unsigned int)time(null));
内存占用
- 内存大小
sizeof()
系统
- SYSTEM
- ` system (“pause”); `
- ` system(“cls”); `