1】、变量地址:
&+变量名 获取变量在内存中的起始地址
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a;
char b;
bool c;
string d;
//16进制表示
cout<<"变量a的地址:"<<(void*)&a<<endl;
cout<<"变量b的地址:"<<(void*)&b<<endl;
cout<<"变量c的地址:"<<(void*)&c<<endl;
cout<<"变量d的地址:"<<(void*)&d<<endl;
//10进制表示
cout<<"变量a的地址:"<<(long long)&a<<endl;
cout<<"变量b的地址:"<<(long long)&b<<endl;
cout<<"变量c的地址:"<<(long long)&c<<endl;
cout<<"变量d的地址:"<<(long long)&d<<endl;
return 0;
}
2】、指针变量
用于存放变量在内存中的起始地址
表示: 数据类型 *变量名
3】、对指针赋值(指向某变量,被指向的变量的数据类型称为"基类型")
表示: 指针=&变量名
#include<bits/stdc++.h>
using namespace std;
int main()
{
int *pa = &a;//(让指针pa指向变量a,pa的基类型是int)
char *pb = &b;
bool *pc = &c;
string *pd = &d;
cout<<"变量a的地址:"<<(long long)pa<<endl;
cout<<"变量b的地址:"<<(long long)pb<<endl;
cout<<"变量c的地址:"<<(long long)pc<<endl;
cout<<"变量d的地址:"<<(long long)pd<<endl;
//如果指针的数据类型与基类型不符,会报错,但是可以强制转换类型
int*pe = (int*)&b;
return 0;
}
4】、指针占用的内存
在64位操作系统中,不管什么类型的指针,占用的内存都是8字节
#include<bits/stdc++.h>
using namespace std;
int main()
{
cout<<sizeof(pa)<<endl;
cout<<sizeof(pb)<<endl;
cout<<sizeof(pc)<<endl;
cout<<sizeof(pd)<<endl;
cout<<sizeof(int*)<<endl;
cout<<sizeof(char*)<<endl;
cout<<sizeof(bool*)<<endl;
cout<<sizeof(string*)<<endl;
return 0;
}
5】、指针的使用
(指针存放变量的地址,因此,指针名表示的是地址,就像变量名可以表示变量的值一样)
*运算符被称为间接值或解引用值,将它用于指针,可以得到该地址的内存中存储的值
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a = 3;
int *p = &a;
//p是a的地址
//*p相当于a
cout<<"a="<<a<<endl;
cout<<"*p="<<*p<<endl;
*p = 8;//更改值
cout<<"a="<<a<<endl;
cout<<"*p="<<*p<<endl;
return 0;
}
变量和zhizhend
如图:
(!但是也不是很贴切,因为变量和指向变量的指针并不是一对一的关系,多个指针可以指向同一个变量)
6】、const修饰指针
1、常量指针 (最常用)
const 数据类型*变量名
1)不能通过解引用的方法修改内存地址中的值(用原始的变量名可以修改)
2)指向的变量可以改变(之前指向变量a的,后来可以改为指向变量b)
3)一般用于修饰函数的形参,表示不希望在函数里修改内存地址中的值
4)如果用于形参,虽然指向的对象可以改变,但没有意义
5)如果形参的值不需要改变,建议加上const修饰,程序可读性更好
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a = 3, b = 8;
const int*p = &a;
*p = 13;//会报错
return 0;
}
2、指针常量(引用)
数据类型*const变量名
指向的变量(对象)不可改变
但是可以通过解引用的方法修改内存地址中的值
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a = 3, b = 8;
int*const p = &a;
*p = 13;
p = &b;//会报错
return 0;
}
3、常指针常量 (常引用)
const数据类型*const变量名
指向的变量(对象)不可改变,不能通过解引用的方法修改内存地址中的值
//三种总结对比
7】、void关键字的用途
void表示无类型
1、函数的参数用void*,表示接受任意数据类型的指针(重要)
2、函数返回值用void,表示函数没有返回值
3、函数参数用void,表示函数不需要参数(或者让参数列表空着)
#include<bits/stdc++.h>
using namespace std;
//显示变量的十六进制地址的函数,varname-变量名,addr-变量的地址
void fun(string varname, int&p)
{
cout<<varname<<"的地址是:"<<p<<endl;
}
int main()
{
int a;
char b;
cout<<"a的地址是:"<<&a<<endl;
cout<<"b的地址是:"<<&b<<endl;
fun("a", &a);
fun("b", &b);//会报错,需要强制转换
return 0;
}
调用b会报错
但是如果改为void,则不会有问题
#include<bits/stdc++.h>
using namespace std;
//显示变量的十六进制地址的函数,varname-变量名,addr-变量的地址
void fun(string varname, void&p)
{
cout<<varname<<"的地址是:"<<p<<endl;
}
int main()
{
int a;
char b;
cout<<"a的地址是:"<<&a<<endl;
cout<<"b的地址是:"<<&b<<endl;
fun("a", &a);
fun("b", &b);
return 0;
}
1)不能用void声明变量,它不能代表一个真实的变量
2)不能对void*指针直接解引用(需要转换成其他类型指针)
3)把其它类型的指针赋值给void*指针不需要转换
4)把void*指针赋值给其他类型的指针则需要转换
8】、c++内存模型
9】、 动态分配内存 new和delete
(在程序中,局部变量存放在栈区,栈区的效率很高,但是空间很小,如果需要处理大量的数据,就必须使用堆区的内存)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int *p = new int(5);
cout<<"*p="<<*p<<endl;//5
*p = 8;
cout<<"*p="<<*p<<endl;//8
delete p;//释放这块内存
return 0;
}
10】、二级指针
(指针是指针变量的简称,也是变量,是变量就有地址)
(指针是用来存放普通变量的地址,二级指针是用来存放指针变量的地址)
(在函数中,如果传递普通变量的地址,形参用指针;传递指针的地址,形参用二级指针)
声明二级指针的语法:数据类型**指针名
使用指针的目的:1)传递地址;2)存放动态分配的内存的地址
#include<bits/stdc++.h>
using namespace std;
int main()
{
int ii = 8;
cout<<"ii="<<ii<<",ii的地址是:"<<&ii<<endl;
int *pii = ⅈ
cout<<"pii="<<pii<<",pii的地址是:"<<&pii<<endl;
int **ppii = &pii;
cout<<"ppii="<<ppii<<",ppii的地址是:"<<&ppii<<endl;
return 0;
}
将分配内存的代码放在另一个函数中:
#include<bits/stdc++.h>
using namespace std;
void func(int **pp)
{
*pp = new int(3);//为形参pp分配内存
cout<<"pp="<<pp<<",*pp="<<*pp<<endl;
}
int main()
{
int*p=new int(3);//将内存中的值设置为3
cout<<"p="<<p<<"*p="<<*p<<endl; //*p=3
//如果业务要求把分配内存的代码放在另一个函数中
//需要把指针p的地址传给另一个函数
func(&p);
return 0;
}
11】、空指针
1)对空指针解引用,程序会崩溃;
int*p=0;
在函数中,应该有判断形参是否为空指针的代码,目的是保证代码的健壮性
c++11中用nullptr代替0 ,在linux平台下,编译需要加-std=c++11参数
12]、野指针
13】、一维数组和指针
1、指针的算数
将一个整型变量加1后,其值将增加1
但是,将指针变量(地址的值)加1后,增加的量等于它指向的数据类型的字节数
#include<bits/stdc++.h>
using namespace std;
int main()
{
char a; //sizeof 1字节
short b; //sizeof 2字节
int c; //sizeof 4字节
double d; //sizeof 8字节
cout<<"a的地址是:"<<(void*)&a<<endl;
cout<<"a+1的地址是:"<<(void*)(&a+1)<<endl;
cout<<"b的地址是:"<<(void*)&b<<endl;
cout<<"b+1的地址是:"<<(void*)(&b+1)<<endl;
cout<<"c的地址是:"<<(void*)&c<<endl;
cout<<"c+1的地址是:"<<(void*)(&c+1)<<endl;
cout<<"d的地址是:"<<(void*)&d<<endl;
cout<<"d+1的地址是:"<<(void*)(&d+1)<<endl;
return 0;
}
2、数组的地址
1)数组在内存中占用的空间是连续的
2)c++将数组名解释为数组第0个元素的地址(和数组首地址的取值是相同的)
3) 数组第n个元素的地址是:数组首地址+n
4)c++编译器将数组名[下标]解释为*(数组首地址+下标)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[5];
cout<<"a的值是:"<<(long long)a<<endl;
cout<<"&a的值是:"<<(long long)&a<<endl;
cout<<"a[0]的地址是:"<<(long long)&a[0]<<endl;
cout<<"a[1]的地址是:"<<(long long)&a[1]<<endl;
cout<<"a[2]的地址是:"<<(long long)&a[2]<<endl;
cout<<"a[3]的地址是:"<<(long long)&a[3]<<endl;
cout<<"a[4]的地址是:"<<(long long)&a[4]<<endl;
return 0;
}
3、数组的本质
数组是占用连续空间的一块内存,数组名被解释为数组第0个元素的地址。
c++操作这块内存的方法:数组解释法和指针表示法,它们是等价的。
4、数组名不一定被解释为地址
将sizeof运算符用于数组时,将返回整个数组占用内存空间的字节数
可以修改指针的值,但是数组名是常量,不可修改
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[5];
int *p = a;
cout<<a<<endl;//0x6ffe00
cout<<p<<endl;//0x6ffe00
p++;
cout<<p<<endl;//0x6ffe04
a++;//报错
return 0;
}
14】、一维数组用于函数的参数
1、指针的数组表示
1)c++编译器把 数组名[下标] 解释为 *(数组首地址+下标)
2)c++编译器把 地址[下标] 解释为 *(地址+下标)
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a[5] = {3, 6, 5, 8, 9};
//用数组表示法操作数组
for(int ii = 0; ii < 5; ii++)
{
cout<<"a["<<ii<<"]的值是:"<<a[ii]<<endl;
}
cout<<(&a[2])[0]<<endl;//第二个元素的地址+[0] 解释为*(第二个元素的地址+0)
//用指针表示法操作数组
int *p = a;//p是整型指针
for(int ii = 0; ii < 5; ii++)
{
cout<<"*(p+"<<ii<<")的值是:"<<*(p+ii)<<endl;//数组名[下标] 解释为 *(数组首地址+下标)
cout<<"p["<<ii<<"]的值是:"<<p[ii]<<endl;//地址[下标] 解释为 *(地址+下标)//把整型指针p当成了数组名来使用
}
return 0;
}
2、一维数组用于函数的参数
#include<bits/stdc++.h>
using namespace std;
int main()
{
char a[20];
int *p = (int*)a;
for(int i = 0; i < 5; i++)
{
p[i] = i+300;
}
for(int i = 0; i < 5; i++)
{
cout<<p[i]<<endl;
cout<<*(p+i)<<endl;
}
return 0;
}
15】、用new动态创建一维数组
普通数组在栈上分配内存,栈很小;如果需要存放更多的元素,必须在堆上分配内存
动态创建一维数组的语法:数据类型*指针=new 数据类型[数组长度]
释放一维数组的语法:delete[]指针
注:
1)普通数组有数组名,可以用sizeof运算得到整个数组占用内存空间的大小;但是,动态创建的数组没有数组名,只有一个指针,不能用sizeof运算符
2)可以用数组表示法和指针表示法两种方式使用动态创建的数组
#include<bits/stdc++.h>
using namespace std;
int main()
{
int *arr=new int[8];//创建八个元素的整型数组
for(int i = 0; i < 8; i++)
{
arr[i] = 100+i;
cout<<"arr["<<i<<"]="<<*(arr+i)<<endl;//指针表示法
}
delete[]arr;
//用栈 空间很小
int a[1000001];
//用堆放内存 数组加到100亿程序才会崩溃
int *a = new int[10000000001];
//但是如果用(std::nothrow),不会崩溃,会返回nullptr
int *a=new(std::nothrow) int[10000000001];
//再判断指针是否为空
if(a == nullptr)
{
cout<<"分配内存失败。\n";
}
else
{
a[10000000001]=8;
delete[]a;
}
return 0;
}
16】、二维数组用于函数的参数
回顾:
int*p;//整型指针
int*p[3];//一维整型指针数组,元素是3个整型指针(p[0]p[1]p[2])
int*p();//函数p的返回值类型是整型的地址,调用函数时可以用一个整型指针接收它的返回值
int(*p)(int,int);//p是函数指针,函数的返回值是整型
(指针是容器,用于存放地址,不要把两者混淆)
1、行指针(数组指针)
声明:数据类型(*行指针名)[行的大小];//行的大小即数组长度
int(*p)[3]//p是行指针,用于指向数组长度为3的int型数组
double(*p2)[5]//p2是行指针,用于指向数组长度为5的double型数组
一维数组名取地址得到的是数组的地址,是行地址
int main()
{
int a[10];
//存放数组a的地址,不能用普通指针
int*p1=a;
cout<<p1<<endl;//首地址
int*p2=&a;//报错
cout<<p2<<endl;
int(*p3)[10]=&a;
cout<<p3<<endl;//首地址
return 0;
}
2、二维数组名是行地址
int main()
{
int bh[2][3]={{11,12,13},{21,22,23}};
//bh是二维数组名,该数组有两元素,每一个元素本身又是一个数组长度为3的整型数组
//bh被解释为数组长度为3的整型数组类型的行地址
//如果存放bh的值,要用数组长度为3的整型数组类型的行指针
int(*p)[3]=bh;//可以
return 0;
}
3、把二维数组传递给函数
如果把bh传给函数,函数的声明如下:
void func(int(*p)[3], int len);
void func(int p[][3], int len);
#include<bits/stdc++.h>
using namespace std;
void func(int p[][3], int len)
{
for(int i = 0; i < len; i++)
for(int j = 0; j < 3; j++)
cout<<"p["<<i<<"]["<<j<<"]="<<p[i][j]<<endl;
}
int main()
{
int bh[2][3]={{11,12,13},{21,22,23}};
func(bh, 2);//二维数组名,二维数组的行数
return 0;
}
17】、函数指针
如果把函数的地址作为参数传递给函数,就可以在函数中灵活的调用其它函数
使用函数指针的三个步骤:
1)声明函数指针
int(*pfa)(int,string);
2)让函数指针指向函数的地址
3)通过函数指针调用函数
#include<bits/stdc++.h>
using namespace std;
void func(int no, string str)
{
cout<<no<<' '<<str<<endl;
}
int main()
{
int bh = 3;
string message = "hello";
func(bh,message);
void(*pfunc)(int,string);//声明函数的函数指针
pfunc=func;//对函数指针赋值,语法是函数指针名=函数名
pfunc(bh,message);//对函数指针名调用函数
return 0;
}
以上是我对于学习指针相关内容做的笔记
如有不正确 欢迎指正
希望留个赞~
谢谢阅读