C++-指针

224 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天点击查看活动详情

指针

	//在栈中申请了一个int指针变量a a有他自己的地址存储在栈中 
	//然后new int是在堆中申请了一个内存 用a存储 而栈中地址存储的就是new int申请的内存地址
	int* a = new int;
	cout << "a:" << a << "	" << "&a:" << &a << endl;
		//在堆中 a存储的值      在栈中 是a的地址

指针指向的地址实际上是一个虚拟地址 进程结束后就会释放所有的内存空间 操作系统会进行映射

nullptr(空指针)

nullptr和NULL,0的区别是在逻辑判断的时候能知道这是一个空指针

	int* a = new int;
	delete a;//删除之后a存储的值其实并不是0或者NULL 是一个其他的值
	//a = nullptr;//如果不写这个就会执行1
	if (a)//1
	{
		cout << "&a:" << &a << endl;
		cout << "a:" << a << endl;
		//在堆中 a存储的值      在栈中 是a的地址
	}
	else//2
	{
		cout << "NULL" << endl;
	}

为什么要指针类型

32位中任何类型的指针都是四个字节 64位是八个字节 例如int a;chaa 那么他们有什么区别呢 指针只是指向了一个内存地址,但是当从内存中取值的时候,系统不知道你要从当前指针指向的地址,取几个字节,指定了指针的类型后,系统就知道取几个字节了。char类型取1个字节,short类型取2个字节,int类型取4个字节。 定义了类型也会决定他的操作方式 指针移位方式 取值方式

函数指针

本质是指针 指向一个函数 函数的定义是存在于代码段,因此,每个函数在代码段中,也有着自己的入口地址 函数指针就是指向这个入口地址

例子

int fun(int b)
{
	return b;
}
void main()
{
	int b = 5;
	int(*a)(int)		;
	a = fun;
	cout<<a(b);
}
void fun()
{
	cout << "fun" << endl;
}
void main()
{
	void(*a)()		;
	a = fun;
	a();
}
/*如何声明函数指针*/
//void (*pf)();//就是正常的声明一个函数一样的加了一个*号
int *fun(int b)//这是一个返回值为int*类型的函数
{
    //cout << "salfh";
    int* a = new int;
    *a = b;
    return a;
}
void main()
{
    int* (*pf)(int);//指向返回值为int型指针的函数指针
    pf = fun;
    cout << (*pf)(10) << endl;//地址 这里调用的就是指针函数 返回了返回值(指针指向的地址)
    cout << *(*pf)(10) << endl;//10 这里返回的就是指针里面存储的值了

    /*函数指针数组*/
    int* (*pa[2])(int) = {pf,nullptr};//指向返回值为int型指针的函数指针数组
    cout << *(*pa[0])(10) << endl;//10


}

将函数指针作为函数形参

int prin(int a, int b)
{
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
    return 0;
}
int fun(int a, int b,int(*func)(int, int))
{
    func(a,b);
    return 0;
}
void main()
{
    int a = 1;
    int b = 2;
    fun(1, 2, prin);
}

在这里插入图片描述

回调函数

int func_sum(int n)
{
        int sum = 0;
        if (n < 0)
        {
                printf("n must be > 0\n");
                exit(-1);
        }
        for (int i = 0; i < n; i++)
        {
                sum += i;
        }
        return sum;
}

//这个函数是回调函数,其中第二个参数为一个函数指针,通过该函数指针来调用求和函数,并把结果返回给主调函数
int callback(int n, int (*p)(int))
{
        return p(n);
}

int main(void)
{
        int n = 0;
        printf("please input number:");
        scanf("%d", &n);
        printf("the sum from 0 to %d is %d\n", n, callback(n, func_sum));       //此处直接调用回调函数,而不是直接调用func_sum函数 
        return 0;
}

指针函数

就是返回值是指针的函数

指针数组

	int *p[];//这个需要看成 p变量是一个数组 是一个int*类型的数组 所以他每个元素应该存储int*变量 
    int* a[2];//存储了两个指针的数组 这个*是和int结合
    int* b;
    int c = 10;
    b = &c;
    a[0] = b;
    cout << *(a[0]);

简便用法

int *pnum(&num1);//将num1的地址传递给pnum
int * &rnum = pnum;//rnum是pnum的别名 实际上就是在int*rnum = pnum的基础上加了个引用符号
rnum = &num2;//rnumhe pnum指向同一片内存  改变了rnum就相当于改变了pnum

关于* a++和 *(a+1)

a是一个数组名 int a[5]; *a++中 ++优先于 *所以这个++是对a地址进行+1 这是不被允许的 数组a = a+1是不行的 但是 int*b = a; b++;是可以的 而*(a+1)则并没有改变a的值 只是取了a前一位地址的值 也就是a[1]

指针+1

在这里插入图片描述

int (*a)[10] 和 int *a[10]的区别

a是优先和[10]结合

int *a[10]

int* a[10] 很明显就是a[10]是一个数组 而数组里面的元素都是int*类型

    int* d1, * d2, * d3;
    int* d[3] = { d1,d2,d3 };

int (*a)[10]

首先是(*a)是和[10]先结合 那么 我们看最前面可以知道[10]里面存储的是10个int 那么就是 a[10] 和 a[10]的区别 很明显a[10]是一个指针 指向这个存储10个int的数组

//int(*a)[3] = {3,5,7};//这样是不对的
    int b[3] = {3 ,5,7};
    //int(*a)[3] = b;//不合法
    int(*a)[3] = &b;//和int*a = &b区别很大 这个a是指向一个数组 很像一个 int a[1][3]这样的数组
    //int b[4];
    //int(*a)[3] = &b;//这样会报错 一定只能指向包含三个元素的数组 定死了 四个也不行
    int* c = b;
    cout << *a + 1;//是一个地址
    cout << **a + 1;//4
    cout << c+1;//是一个地址
    cout << *c+1;//4

数组(常量指针)

数组名是指针常量,指针是指针变量

常量指针是指向常量的指针 也就是他指向的地址存储的值不能改变 指针常量是指针是一个常量 是不能改变指向的

数组名是一个指向数组首元素地址的常量指针 数组名一经定义就没办法改变

    int a[3] = {3,5,7};
    cout << "a:" << a << endl;//00F3F790
    cout << "&a:" << &a << endl;//00F3F790
    cout << "&a[0]" << &a[0] << endl;//00F3F790
    cout << "*a:" << *a << endl;//3

系统在使用数组下标对数组成员变量进行访问时,开销比较大,指针的访问效率是远远大于数组名的访问效率的

局部性原理

cpu会将一部分连续的地址传入高速缓冲区 所以数组比链表更快些