**
2020-10-15 C++知识点累积(六)
**
**
一、指针
**
#include <iostream>
int main()
{
int a{ 5200 };
std::cout <<"a的值:"<< a<<std::endl;
int* pa = &a;
std::cout <<"pa的值:" <<pa<<"\n*pa的值:"<<*pa << std::endl;
*pa = 500;
std::cout << "a的值:" << a <<"\n*pa的值:"<<*pa<<"\n&a的值:"<<&a<< std::endl;
(*pa)++;
std::cout << "a的值:" << a << "\n*pa的值:" << *pa << "\n&a的值:" << &a << std::endl;
}
结果:
int* pa pa是int类型的指针
int* pa=&a 是把a的地址赋值给pa
*pa=500 是改变地址内的值,因为a的地址和pa的地址一样,所以a的值也改变了
pa=a的地址
*pa=a的值
&a=a的地址
(*pa)++使用运算符需要把*pa括起来表示一个整体
**
*pa++和(*pa)++区别
**
指针+1的时候 数值的变化是+1*指针类型的大小
如:
#include <iostream>
int main() {
int a[5]{ 1001,2001,3001,4001,5001 };
int* pa{ &a[0] };
std::cout << ++(*pa) << std::endl;
std::cout << pa << std::endl;
*pa++;
std::cout << *pa << std::endl;
std::cout << pa << std::endl;
return 0;
}
结果:
pa++前和pa++后地址差4,因为1*4(int为4个字节)
**
常量指针和指针常量
**
#include <iostream>
int main() {
//常量指针(const 变量类型* 变量名)
const int a{ 10001 };
int b{ 10002 };
const int* ptrA{&b};
std::cout << "ptrA:" << *ptrA<<std::endl;
ptrA = &a;
std::cout <<"ptrA:"<<*ptrA << std::endl;
//指针常量(变量类型* const 变量名)
int* const ptrB{ &b };
std::cout << "ptrB:" << *ptrB << std::endl;
*ptrB = -1;
std::cout << "ptrB:" << *ptrB << std::endl;
return 0;
//指向常量的常量指针(const 变量类型* const 变量名)
const int* const ptrC{&b};
std::cout << "ptrC:" << *ptrC<< std::endl;
}
结果:
在第一次赋值之后:
常量指针能改变指针的指向,不能改变指针地址里的值;
指针常量能改变指针地址里的值,不能改变指针的指向;
指向常量的常量指针既不能改变指针的指向,也不能改变指针地址里的值
**
指针与数组
**
#include <iostream>
int main() {
int a[5];
int* ptr{ a };
a[0] = 5;
a[2] = 10;
std::cout << a[0]<< "\n";
std::cout << *(ptr + 2)<<"\n";
std::cout << ptr[0];
return 0;
}
结果:
a[0]的地址为a的地址偏移0个位子,所以a其实是一个数组的起始地址;
ptr指向a的地址,所以ptr也可以像a一样控制数组,如ptr[0],ptr[1];(ptr[1]==a[1])
ptr也可以*(ptr+2),等同于a[2];
多维数组也一样如:int a[2][2]{},a[1][1]==ptrA[3];
结论:
**
指针数组和数组指针
**
#include <iostream>
void main() {
int a[2][5]{};
a[0][2] = 1001;
int* ptrB[5]{&a[0][0],&a[0][1] ,&a[0][2] ,&a[0][3] ,&a[0][4] };//指针数组代表能存五个int类型的指针的数`在这里插入代码片`组(本质上是数组)
int(* ptrC)[5]{ a };//数组指针代表指向int[5]的数组(本质上是指针)
std::cout << *ptrB[2] <<std::endl;
std::cout << ptrC[0][2] << std::endl;
}
结果:
**
动态内存
**
C语言动态内存分配(c++也可以用)
malloc
功能:分配内存空间
格式:void* malloc(size_t size)
size代表分配的内存大小,返回值是内存分配的地址,如果分配失败则返回0
示例:
#include <iostream>
#include <string>
void main() {
int* a = (int*)malloc(10 * sizeof(int));//分配了10个int类型的大小(10*4)返回的为10个int内存空间的起始地址(类似于数组)
int* b = (int*)malloc(999999999 * sizeof(int));//分配了10个int类型的大小(10*4)返回的为10个int内存空间的起始地址(类似于数组)
if (a == 0)
{
std::cout << "分配内存失败";
}
if (b == 0)
{
std::cout << "分配内存失败";//b内存分配失败原因为分配的空间超过了可分配空间最大值(32位最大值为4GB)
}
}
代码中a成功分配内存空间10*4字节大小,b分配内存空间失败
calloc
功能:分配内存空间
格式:void* calloc(size_t count,size_t size)
count代表分配的个数,size表示每个个数分配的大小,返回值与malloc一样,分配失败返回值也和malloc一样
malloc和calloc的区别
calloc会将分配好的内存区域的值设置为0,而malloc不会处理内存空间内的值,但是malloc会比calloc运行效率高
#include <iostream>
int main()
{
int* pm = (int*)malloc(10*sizeof(int));
int* p = (int*)calloc(10,sizeof(int));
std::cout << pm[0] << std::endl;
std::cout << p[0] << std::endl;
}
结果:
realloc
功能:重新分配内存空间
格式:void* realloc(void* _Block,size_t _Size)
_Block代表需要重新被分配的内存,_Size是重新分配的内存大小,返回值和malloc一样
#include <iostream>
int main()
{
int* p = (int*)malloc(10*sizeof(int));
std::cout << p[0] << std::endl;
p=(int*)realloc(p,100*sizeof(int));
}
memcpy
功能:复制内存
格式:void* memcpy(void* _Dst,const Void* _Src,size_t _Size)
把_Sre区域的内存复制到_Det的区域,_Size需要复制的长度(可以复制_Sre部分内存区域到_Det,但是不要复制超出_Sre的内存区域)
#include<iostream>
int main() {
int a[2]{8,9};
int* b=new int[2];
memcpy(b, a, 8);
std::cout << a[0] << std::endl;
std::cout << a[1] << std::endl;
std::cout << b[0] << std::endl;
std::cout << b[1] << std::endl;
}
结果:
memset
功能:设置内存
格式:void* memset(void* _Dst,int val,size_t _Size)
_Dst代表设置内存的地址,val表示设置内存的值,_Size需要设置的长度(字节)
memset可以将指定内存区域每一个字节的值都设置为val,所以只能设置为一个字节大小的东西
#include <iostream>
int main()
{
int a[5]{ 1,2,3,4,5 };
int* b = new int[5];
memset(b, 0x0, 5 * sizeof(int));
for (int i = 0; i < 5; i++)//设置未超过一个字节的值
{
std::cout <<std::hex << b[i] << std::endl;
}
std::cout << "---------------------------------" << std::endl;
memset(b, 0x12345, 5 * sizeof(int));//设置超过一个字节的值,会截取最后两位,因为一个字节最大0xFF
for (int i = 0; i < 5; i++)
{
std::cout << std::hex << b[i] << std::endl;//每次显示4个字节的值(int为4字节)
}
}
结果:
free
功能:释放内存
格式:void free(void* _Block)
_Block表示需要释放的内存
#include <iostream>
int main()
{
int* p = (int*)malloc(10*sizeof(int));
std::cout << p[0] << std::endl;
p=(int*)realloc(p,100*sizeof(int));
free(p);
p=0;
}
释放内存每个内存只能释放一次,一般释放完会把指针赋为0,防止出现悬挂指针问题
C++动态内存分配
new
功能:分配内存
格式:数据类型* 变量名 = new 数据类型
int* pa = new int;//只分配了一个int类型的内存空间
数据类型* 变量名 = new 数据类型[数量]
int* pa = new int[5];//分配一段能存放5个int变量类型的内存空间
数据类型* 变量名 = new 数据类型(数据)
int* pa = new int(5);//分配一个int类型的内存空间,并赋值5
数据类型* 变量名 = new 数据类型{数据}
int* pa = new int{5};//分配一个int类型的内存空间,并赋值5
delete
功能:释放内存
格式:如果内存分配的是用new则用delete+需要释放的内存
delete pa;
如果分配内存用的是new[]则用delete[]+需要释放的内存
delete[] pa;
例子:
#include <iostream>
int main()
{
int* p = new int;
std::cout << p[0] << std::endl;
delete p;
p=new int[5];
delete[] p;
}
**
引用
**
引用的概念
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
引用 vs 指针
引用很容易与指针混淆,它们之间有三个主要的不同:
1.不存在空引用。引用必须连接到一块合法的内存。
2.一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
3.引用必须在创建时被初始化。指针可以在任何时间被初始化。
引用的作用
假如变量名称是变量附属在内存位置中的标签,可以把引用当成是变量附属在内存位置中的第二个标签。因此,您可以通过原始变量名称或引用来访问变量的内容
例如:
#include <iostream>
int main()
{
int i = 999;
int& li = i;
li++;
std::cout << i << std::endl;
std::cout << li << std::endl;
}
结果:
改变引用变量li的值,i的值也会发生变化,并且引用变量的地址和被引用变量的地址相同,引用变量无法二次改变引用
智能指针
**
std::unique_ptr:唯一智能指针
**
实例:
#include <iostream>
int main()
{
int* ptr = new int{6};
std::unique_ptr<int> a{ std::make_unique<int>(5)};//给指针a赋值5
std::unique_ptr<int[]> b{ std::make_unique<int[]>(5) }; //给指针数组b分配5个元素
std::unique_ptr<int[]> bCopy{ std::make_unique<int[]>(5) };
b[1] = 500;
std::cout << "a的地址:" <<a << " " << "a的值:" << *a<<std::endl;
std::cout << "b的地址:" << b << " " << "b[1]的值:" << b[1] << std::endl;
std::cout << "bCopy的地址:" << bCopy << " " << "bCopy[1]的值:" << bCopy[1] << std::endl;
std::cout << "ptr的地址:" << ptr << " " << "ptr的值:" << *ptr << std::endl;
ptr=a.get();//get()的功能是获取a的指针
a.reset();//reset()的功能是释放内存空间,并且把a设为nullptr
std::cout << "Get后ptr的地址:" << ptr << " " << "Get后ptr的值:" << *ptr << std::endl;
std::cout << "reset后a的地址:" << a << " " << "reset后a的值:" << *a << std::endl;
bCopy=std::move(b);//把b指针转移给bCopy
std::cout << "Move后bCopy的地址:" << bCopy << " " << "Move后bCopy[1]的值:" << bCopy[1] << std::endl;
ptr=bCopy.release();//release的功能是获取bCopy的指针,并且把bCopy设为nullptr,但是不释放其内存
std::cout << "release后bCopy的地址:" << bCopy << " " << "release后bCopy的值:" << bCopy << std::endl;
std::cout << "release后ptr的地址:" << ptr << " " << "release后ptr的值:" << ptr << std::endl;
}
注意:智能指针具有唯一性,不能将两个智能指针存放同一个内存地址
**
std::shared_ptr:共享智能指针
**
**
字符处理(字符串)
**
char声明字符串
char str[6]{"Hello"};//==>'H','e','l','l','o','\0'
const char* strA{ "Hello" };
char *strB = new char[6]{ "Hello" };
因为字符串以\0结尾,所以声明初始化数组数量要+1
strlen()
功能:获取字符串长度
用法:strlen(const char* str);