c++学习笔记,不一定每一个都写案例,主要是补充和留下印象吧。
第一个c++程序
//引入头文件
#include <iostream>
//入口
int main()
{
//输出一句话
/**
1.std::cout 向控制台输出内容
2.<< 输出运算符
3."" 输出内容
4.hello world
5.\n 回车
6.; 结束
*/
std::cout << "Hello World!\n";
}
C++输出数据
std::cout << "Hello World!\n";
使用变量
int a;
a = 10;
注释
1.//
2./**/
常量
const int a = 10;
数据分类
(1)数字:0-9
(2)字符:单引号括起来的 ''
(3)字符串:双引号括起来的 ""
输出数据步骤
(1)Std::cout可以输出各种类型的数据
(2)操作符拼接多个数据项
(3)Std::endl换行使用
(4)Using namespace std;缺省命名空间
程序的注释
(1) 程序添加说明文字,
①单行注释
②多行注释
使用变量
(1) 变量是存储内存变量的简称,用于存放临时数据的。
(2) 属于之前需要先声明
语法:数据类型 变量名
(3)常用的数据类型有:int、float、double、char、string、bool
(4)代码
#include <iostream>
int main()
{
char c = 'A';
int num = 10;
std::string name = "垃圾";
bool sex = false; //ture为1 false为0
std::cout << c << " " << num << " " << name << " " << sex;
std::cout << "Hello World!\n";
}
标识符命名
C++输入数据
(1)C++输出类型
①控制台节目输入
②文件中读取
③数据库中读取
④网络中读取
⑤输入方式
(2) 输出案例
Std::cin >>变量名
(3)注意
- bool只能填入0和非0
- 输入数据和变量数据类型不匹配,导致结果不确定
- 代码
string name;
cin >> name;
std::cout << "Hello World!\n"<<name;
算术运算
基本运算
(1) 算术运算包含了:+ - * / %
(2) 只适用于整数和浮点数
(3) 取模只能使用于整数
自增和自减运算
(1) ++变量名、变量名++、--变量名、变量名--
赋值运算
1.+=
2.-=
c++11初始化赋值
old
int a = 100;
int b(100);
int c = (100);
new
```c++
int a = 100;
int b{100};
int c = {100};
函数
函数的位置
(1) 在main上面定义,下来写内容
(2) 在main的上面写内容
(3) 函数可以多次声明,但是实现只有一个
(4) 函数的声明也可以写main函数中,必须放在使用之前,否则会报错。
函数参数的传递
(1) 函数调用,调用这将参数传递给函数的参数
① 实参:可以是常量变量表达式
② 形参:函数调用的参数
(2) 函数中修改形参的值不会影响实参。
(3) 代码
#include <iostream>
int max(int a, int b);
int min(int a, int b);
int main()
{
int a = 10;
int b = 11;
std::cout << "max:" << max(a,b) << std::endl;
std::cout << "Hello World!\n";
}
int max(int a, int b) {
return a > b ? a : b;
}
int min(int a, int b) {
return a < b ? a : b;
}
函数分文件编写
(1) 使用头文件和源文件在编写代码
(2) 在使用的时候通过#将头文件加进去
(3) 可以写到头文件中的东西:头文件、命名空间、全局变量、函数声明,数据结构和类的声明等
(4) 源文件:函数的定义、类的定义。
Sizeof运算符
(1) Sizeof(数据类型) 得到数据类型或者变量占用的内存空间
(2) 32位和64为可能结果不一样
(3) String不是基本数据类型,sizeof(string)没意义
(4) 代码
cout << sizeof(int) << endl;
cout << sizeof(a) << endl;
C++的基本数据类型
(1) int float double char bool string(不是基本数据类型,但是比较重要)
int类型
整型的基本概念
(1) C++使用int关键字声明变量,实际是integer的简写
(2) 声明的时候可以在int关键字之前加signed、unsigned、short、long四种修饰符
① Signed:有符号的,可以表示正数和负数
② Unsigned:无符号的
③ Short:短的
④ Long:长的
(3) C++中没有整数的取值范围,取值返回根据组合来计算得到
整数的书写
(1) 二进制:书写必须以0b(B)开头
(2) 八进制:
① 必须以0开头
② 0-7
(3) 十进制
(4) 十六进制:
① 0x开头
整数的坑
(1) 书写十进制不要在前面加上0,会被认为是八进制
longlong类型
- vs中long是4个字节
- liunx中long是8个字节
- long long 类型的整数,至少是8个字节,并且与long的长度一样长
浮点型
- 浮点型包括float 、double、long double
- 字符数据类型;
a) 内存占用一个字节,使用单引号
b) 不存放字符本身,只存放编码
c) 内存中存储都是二进制 - ASCII:33个控制字符 95个可显示字符
- 字符的本质
a) 字符本质是整数,取值范围是0-127
b) 书写的时候使用单引号,也可以使用整数
c) 显示的时候可以是整数,也可以是整数
d) 可以和整数进行运算
原始字面量
是不需要转义,直接使用他的直接含义
#include <iostream>
using namespace std;
int main()
{
string path = "c:\\user";
string path = R"(c:\user)";
//前后必须一致 否则会报错
string path1 = R"aaa(c:\user;)aaa";
cout << "Hello World!\n";
}
字符串
(1) C++风格:string var = “字符串”; 类
(2) C风格:char[] ch = “字符串”; 字符数组
数据类型的转换
(1)计算机进行运算的时候,要求操作数类型相同的大小和存储方式
(2)转换方式:
i. 自动转换
ii. 强制转换
数据类型的别名
1.别名的目的
i.创建别名,方便书写和记忆
ii. 创建与平台无关的数据类型,提高程序的兼容性
2.兼容性的写法
typedef long int lint;判断使用的机器
3.别名可以原原本本的代替原名
4.语法
typedef 数据类型名 别名
指针
(1) C++通过&得到变量的其实地址
(2) 语法
i. &变量名 取得数据的地址
(3)指针用于存放变量的地址 (起始地址)
(4)函数中使用
void printInfo(int* a) {
cout << *a << endl;
*a = -1;
}
int main()
{
int a = 0;
printInfo(&a);
cout << a << endl;
}
(5)优点:可以提升性能,较少数据的copy
(6)没有指定值的指针,值是乱七八糟的
(7)指针存储的是地址,所有指针的变量名存储的是地址
(8)*对指针进行解引用
指针用于函数的参数
(1)指针是指向的地址的,如果函数使用指针作为参数,那么使用地址传参
(2)既然是指针,那么在使用的使用的时候,通过解引用得到值。
(3)如果在函数中是通过解引用改变值,那么也会改变原来的值
(4)好处
- 函数中修改实参的值
- 减少拷贝,提升性能
值传递和地址传递
使用常量
常量是程序中固定不变的数据
(1)const修饰
(2)在程序的任何地方都可以的
(3)使用它修饰指针
常量指针
(1)语法:const 数据类型 变量名
i. 不可以使用解指针的方法设置值
ii. 注意
(2)指向的变量可以改变,值不可以改变(p =xx值不可以改变)
(3)一般用于修饰函数的形参,表示不希望改变值
(4)如果用于型参,虽然指向对象可以改变,但是没意义
指针常量
(1)语法:数据类型 *const 变量名
(2)指向的变量对象不可以改变
(3)注意
i. 定义的时候必须初始化,否则没意义
ii. 可以通过解引用方法修改地址中的值
iii. 代码
Int* const p = &a;
*p = 13;初始化必须设置值 并且值可以修改
常指针常量
(1)语法:const int *const int
(2)宏常量
i. 一般在main函数之上声明的,使用大写字母
ii. 语法:
#define NAME C++;
void关键字
(1)C++中,void表示无类型
(2)作用:
i.函数返回值void
ii.参数设置为void,表示不需要参数
iii.void*作为函数参数,表示可以使用任意类型的参数
(3)注意:
i. 不能使用void声明变量,不可以代表一个真实的变量
ii. 不能对void*指针直接解引用
iii. 把其它类型的指针赋值给void*指针不需要转换
iv. 把void*指针赋值给其他类型的需要转换
内存空间
内核空间 栈空间 堆空间 常量 代码
堆和栈的区别
(1)管理方式不同:栈是自己回收的,出作用域就会自己回收,堆需要自己释放
(2)空间大小不同:堆是物理空间决定的,栈一般只有8M
(3)分配方式不同
(4)是否存在碎片
动态分配内存new和delete
(1)使用堆内存的步骤
i. 声明指针
ii. 使用new向系统申请一个内存,让指针指向这块内存
iii. 通过解引用的方法,使用这一块内存
(2)语法:new 数据类型(初始值)
(3)申请成功分配一块地址,如果申请失败,就是一块空地址
(4)释放delete 地址
(5)注意
i. 动态分配内存没有变量名,只能通过指针来操作内存中的数据
ii. 如果动态分配内存不使用了,必须要释放
iii. 动态分配的内存生命周期与程序相同
iv. 指针作用域失效,指向的内存也不会释放
v. 指针跟踪分配内存的时候,不可以跟丢。
二级指针
空指针
(2)在c或者是c++中,都可以使用0或者NULL表示空指针,在没有赋值之前给它设置空,代表没有指向任何地址
(3)使用空指针的后果
i. 程序崩溃
ii. 对空指针执行delete,系统会忽略该操作,不会出现异常,内存释放后,也应该设置为null。
(4)为什么指向空指针会崩溃
i. Null指针分配的分区,范围是0x0000-0x0000ffff这个区域是空闲的,没有任何物理存储与之对应,任何读写都会发生异常
(5) C++11的nullptr
i. 使用0或者NULL会产生歧义,所以建议使用(void*)0
野指针
(1)野指针就是一个没有指向一个有效合法的地址
(2)如果访问野指针就会发生崩溃
(3) 出现野指针的情况
i. 指针定义的时候没有初始化,值是不确定的
ii. 使用了动态分配的内存,内存释放了,指针不会置空,但是指向已经失效
iii. 指针指向变量已经超过了作用域
(4)规避方法
i. 指针在定义的时候,如果无指向,就指向nullptr
函数指针
(1)函数的二进制代码存放在内存四区中代码段,函数的地址是它在内存中的起始地址,如果将函数地址作为参数传给函数,就可以灵活的调用其他函数
(2)使用函数指针的步骤
i. 声明函数指针
ii. 让函数指针指向函数的地址
iii. 通过函数指针调用函数
(3)代码演示
i.声明函数指针
int max(int a,int b);
int (*maxa)(int,int); 可以和上面一样,也可以直接这么写就完事了
int max(int a,int b){
return a > b ? a : b;
}
int a = 10;
int b = 20;
//使用函数指针名
void (*pmax)(int,int);
pmax = max; //函数名字就是函数的地址
pmax(a,b) //c++写法
(*pmax(a,b));//c的写法
max(a,b);
(4)函数指针有什么作用
我也解释不清楚,直接一个案例把
void log(){
}
void printLog(void (*plog)()){
logpre();
log
logend();
}
2.必须提供指针类型(返回值和函数列表)
数组
数组:一维数组 二维数组 多维数组
创建数组
(1)声明数组:数据类型 数组名[数字长度]
- 数组的长度必须是整数 常熟 表达式
int bh[3];
string name[3];
数组的赋值
bh[0] = 1;
占用空间
sizeof(bh); 类型 * 长度
sizeof只适用于基本数据类型,其他的没有意义
初始化
声明的时候初始化
类型 nameArr[length] = {v1,v2,……}; 指定值
类型 nameArr[] = {v1,v2,……};
类型 nameArr[] = {0}
数组清零
memset函数将数组清零
void *memset(void *s,int v,int size)
memset(bh,0,sizeof(bh));
数组复制
memcpy()将数组中的全部元素复制到另一个相同的数组
## 引用
### 引用概念以及作用
1.引用是c++新增的复合类型
2.它是已定义变量的别名
3.主要用途是函数的形参和返回值。
4.语法
```c++
数据类型 &引用名 = 原变量名
5.引用取值
int a = 3;
int &ra = a;
cout<<"a地址:" <<&a << "a的值:" << a <<endl;
cout<<"ra地址:" <<&ra << "a的值:" << ra <<endl;
数组和指针
地址+1,一般加的是地址+数据类型大小,所以数组+1是下一个数据的值。
输出地址的方法
cout<< (long long)&a[0] <<endl;
cout<< (long long)&a[1] <<endl;
cout<< (long long)&a[2] <<endl;
cout<< (long long)&a[3] <<endl;
int *p = a;
cout<< (long long)p <<endl;
cout<< (long long)p+1 <<endl;
cout<< (long long)p+2 <<endl;
cout<< (long long)p+3 <<endl;
数组的越界
合法的数组下标
一维数组用于函数参数
数组下标取值解释为首地址+下标解引用取值
地址加下标 p[1] 当前地址开始向后一个 p[2]当前地址向后两个
数组操作:
char a[20];
int *p = int(*)a; //char类型变为了int类型 操作不超过20,就不会存在问题
数组用于函数
传递的数组,在函数中是指针,使用sizeof得不到数组的长度
new创建数组
普通数组在栈上分配,更多的在堆上
数组类型 *指针= new 数据类型[size];
释放:
delete[] 指针;
遍历指针
*(arr+i)遍历数组 指针解引用
注意
- 动态创建的,不可以使用sizeof运算符
- 可以使用数组和指针的方法操作数组
- 必须使用delete释放
- 栈上分配空间,如果内存超出会异常
- 大量数据需要在堆上分配
new分配可以堆超出的,给出一个空地址
int *a = new(nothrow)int[maxsize];
if(a == nullptr){
}else{
}
c风格字符串
- c++字符串是string,可以自动扩展,不需要担心内存
- string封装了c的字符串
- c风格的更加方便/高效
c风格的字符串
如果char数组末尾包含了\0,那么就是一个字符串。所以使用c字符的时候,需要多留一个存放\0
char name[20];
cout<<name<<endl; 会显示遇到0结束(c/c++都是)
清空字符串就是将所有的数据都设置为0
方法一:
char name[10] = {0}; //清空
方法二:
memset(name,0,sizeof(name));
方法三:
将第一个设置为0
字符串赋值或复制
strcpy(name,"hello");
strncpy(name,"hello",3);//指定个数小于字符长度没有问题 ,但是小于就会存在问题
毕竟字符出结尾是\0,所以建议每次最好清零
字符串拼接
strcat(dst,src);
结构体
定义结构体就是告诉编译器,定义了一个新的数据类型,每个成员是什么类型
结构体声明变量
struct People{
string name;
int age;
}
//声明变量 c中必须struct c++中可以不写
struct People p;
访问成员
p.name = "wk";
p.name = 10;
cout << "name :" << p.name;
注意:
- 结构体名是标识符
- 结构体成员可以是任意类型的数据
- 定义结构体可以放在代码的任何地方,一般放在main的上面或者头文件中
- 结构体一般放在使用之前
- 结构体成员可以使用c++的类,但是不提倡
- 结构体中可以有函数,但是不提倡(c++)
- 结构体可以给初始值(c++11)
结构体的细节
- 创建的时候可以设置值
People p = {v1,v2,……};
Poeple p1 = {0};
- 创建结构体的时候,创建结构体变量
struct People {
char name[20] = "kw";
int age = 100;
} p;
- 还可以初始化
struct People {
char name[20] = "kw";
int age = 100;
} p = {"cs",100};
1.占用内存的情况
使用ziseof可以计算结构体的大小。但是结构体占用内存不一定等于全部成员占用的内存大小,因为需要内存对齐
内存对齐,
2.清空结构体
创建结构体没有初始化,成员变量中存在垃圾值,使用memset清楚数据中的值,只使用于c++中的数据类型
memset(&strp,0,sizeof(strp))
3.结构体的复制
- memcpy()将结构体中的成员复制到另一个相同类型的结构体中
- 也可以使用==
结构体指针
结构体是一种自定义的数据类型,使用结构体可以创建变量
(1)基本语法
struct People p; //声明结构体变量
struct People *pst = &p; // 结构体指针
(2)访问成员
指针名里面是地址,解引用是结构体,结构体.
(*指针名).成员名
指针名-> 成员变量
(3)用处:
作为函数参数, 用于动态分配内存
枚举
enum 枚举名{v1,v2}
enum colors {
red,vv,ss
};
//值为012
colors f = red; //对
colors xx = 0; //x
值虽然是0123,但是不可以直接设置
(2)注意事项和细节
- 枚举创建变量只能在枚举范围内
- 枚举和变量作用域一样
- 可以显示枚举的值
- 给枚举指定值
- 整数强制转换为枚举 枚举类型(整数)
引用
引用是c++新增的符合类型,是已经定义变量的别名,主要用途是函数形参以及返回值
1.声明语法:
数据类型 &引用名 = 原变量名
int a = 0;
int &ra = a;
2.注意事项
- 引用的数据类型与变量名一致 int int
- 引用名和原变量名可以互换
cout<< "a的地址"<< &a << "a的值:"<< a << endl;
cout<< "ra的地址"<< &ra << "ra的值:"<< ra << endl;
两个输出都是相同的
ra = 9;
他们的值都发生了改变
- 必须在声明的时候初始化
引用初始化后值是不变的
int a = 10;
int ra = &10;
int b = 3;
ra = b; // a = 3
- c/c++使用&符号来指示/取变量的地址
引用的本质
指针常量的伪装
int a = 3;
int &b = a;
int * const rb = &a;
引用用于函数参数
将函数形参声明为引用,调用的时候,形参为实参的别名,这个叫引用传递,引用的本质是指针,传递的是地址,形参会影响到实参
优点:
- 传引用代码简洁
- 不需要传递二级指针
- 引用的属性和特别之处
指针:用于函数参数和动态分配内存
传值案例
//值传递 f(1,2)
int f (int a ,int b){
a = 10;
b = 0;
return a;
}
//指针传递 f(&a,&b)
int f (int* a ,int* b){
*a = 10;
*b = 0;
return a;
}
//引用传递 f(a,b)
int f(int &no,int &ss){
a ,b
}
引用用于返回值
函数返回值引用语法
int *fun(){
}
调用
int &d = fun();
注意:
- 如果返回值是局部的,那就是野指针
- 可以返回
- 引用参数
- 类的成员
- 全局变量
- 静态变量
注意事项
1.引用数据类型与原来的数据类型相同
2.引用与原变量名可以互换,值与内存单元相同
3.必须在声明之前初始化,初始化后不可变
引用的本质
他是指针常量的伪装