## C++指针学习
个人对于指针的学习与理解,仅供参考,也请路过的大牛给与指导。指针相关的大家不明白的也欢迎留言,大家共同学习
指针的基本理解
代码:
int a = 10;
int* p = &a;
运行结果:
a:10
&a:00CFFB00
p:00CFFB00
*p:10
&p:00CFFAF4
总结:
变量名 | 变量类型 | 变量值 | 变量地址 |
---|---|---|---|
a | int | 10 | 00CFFB00 |
p | int* | 00CFFB00 | 00CFFAF4 |
q | int** | 00CFFAF4 | ...... |
无论什么变量,编辑器都会为这个变量申请一个地址。这个地址就像是现实中的房子,变量地址是房子的门牌号,每个房子都有唯一的门牌号,而且这个门牌号不重名,变量值是这个房子实际存放的东西,a放的是整型数值10,指针类型里面放的东西有些奇特放的都是别人家房子的门牌号。多重指针q房子里放的更奇特放的是指针p的变量地址。
整型变量在这个地址中存放的是一个整型的值,整型指针在这个地址中存放的是一个整型变量的地址,因此p = &a都是地址,指针的本质就是地址
*加在指针变量前面代表解引用,意思找到指针p指向的值
*p=50
代码:
int a = 10;
int* p = &a;
*p = 60; //正确
int* q = nullptr;
*q = 50; //异常
int* m = 50; //异常
总结:
指针的本质就是地址,第一种情况为指针赋予了明确的地址,因此可以通过*p=60的方式修改地址中的值。而剩余的两种方式都没有为指针赋予明确的地址,因此不是异常就是报错。
常量指针和指针常量
代码:
int a = 10;
int b = 20;
const int * p1 = &a;//常量指针
*p1 = 100; //错误
p1 = &b; //正确
int * const p2 = &a;//指针常量
*p2 = 100; //正确
p2 = &b; //错误
总结:
const int* p1:指向可修改,指向的值不可修改
const 常量 int *指针(常量在前指针在后):常量指针,常量的指针
const int* p1:此时const修饰的是*p1,因此 *p1不可改,p1可改
int * const p2:指向不可修改,指向的值可修改
int *指针 const 常量 (指针在前常量在后):指针常量,指针的常量
int* const p2:此时const修饰的是p2,因此 p2不可改,*p2可改
指针与数组
代码:
int arr[5] = { 1, 2, 3, 4, 5 };
int* p1 = arr;
int* p2 = &arr[0];
int* p3 = &arr[1];
int* p4 = &*(p1 + 1);
运行结果:
arr:001BFC6C
p1:001BFC6C
*p1:1
&p1:001BFC60
p2:001BFC6C
*p2:1
&p2:001BFC54
p3:001BFC70
*p3:2
p4:001BFC70
*p4:2
总结:
数组名arr本身就是一个指针,但是这个指针不是指向整个数组,而是指向数组的首元素的地址,因此p1与p2相等
从p1和p2两个指针的地址可以看出,虽然两个指针指向的地址相同,但是两个指针存在不同的地址下,是不同的指针
p3与p4相等,因此可以认为对于数组而言,p3[i]与*(p3+i)两者是等价的
使用函数对整型变量赋值:
代码:
void func3(int a)
{
a = 888;
std::cout << "func3 &a:" << &a << std::endl;
}
void func4(int &a)
{
a = 888;
std::cout << "func4 &a:" << &a << std::endl;
}
int num = 10;
func3(num);
std::cout << "num:" << num << std::endl;
std::cout << "&num:" << &num << std::endl;
int num1 = 10;
func4(num1);
std::cout << "num1:" << num1 << std::endl;
std::cout << "&num1:" << &num1 << std::endl;
运行结果:
func3 &a:010FF928
num:10
&num:010FFA08
func4 &a:010FF9FC
num1:888
&num1:010FF9FC
总结:
在函数中对变量进行赋值,并将更改后的结果传出,需要形参为&a
根据在不同函数中的参数地址可以发现,当形参为&a时,函数内的参数地址与参数外的地址相同,此时在函数内对参数进行修改实际上是对函数外参数地址中的内容进行修改
使用函数对数组进行赋值:
代码:
void func(int array[5], int num)
{
for (int i = 0; i < num; i++)
{
array[i] += 100;
}
}
void func1(int* array, int num)
{
for (int i = 0; i < num; i++)
{
*(array + i) += 100;
}
}
int arr[] = { 1, 2, 3, 4, 5 };
int arr1[] = { 1, 2, 3, 4, 5 };
int length = sizeof(arr) / sizeof(int);
func(arr, length);
func1(arr1, length);
std::cout << "length:" << length << std::endl;
std::cout << "arr[0]:" << arr[0] << std::endl;
std::cout << "arr1[0]:" << arr[0] << std::endl;
运行结果:
length:5
arr[0]:101
arr1[0]:101
总结:
func与func1两种形参都能够实现在函数中对数组赋值
可以使用int length = sizeof(arr) / sizeof(int);获得数组的长度
与使用函数对整型变量赋值相比形参并没有取地址,这是因为数组名arr本身就是一个指针,指向数组的首元素的地址
p1++与(p1++)是等价的,这是因为++的运算符优先级比要高,因此不管你加不加括号,都会优先执行p++,然而p++是先返回p的值,再与结合,最后p再向后移动一位。
不过在这里要特别注意,有一种情况下我们是不能通过sizeof操作符来计算数组的长度的,就是当数组名作为函数参数传递的时候:
void test(int arr[])
{
int lenth = sizeof(arr) / sizeof(int);
}
上面这行代码语法上没有问题,但是得出的结果却不是我们想要的结果,为什么呢,这是因为数组名作为函数传递的时候,会退化成一个指针,如果是二维数组的话,会退化成指向一维数组的指针,所以sizeof(arr)计算出来的结果就不是数组的字节长度了。所以说,在c/c++中传递数组的时候,一般我们也会把数组的长度作为形参传递过去。
指针数组与数组指针
代码:
int *p1[10]; //指针数组
int (*p2)[10]; //数组指针
定义说明:
指针数组:*这是因为[]的优先级比要高。p1 先与[]结合,构成一个数组的定义,数组名为p1,int 修饰的是数组的内容,即数组的每个元素。因此int *p1[10]是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。
数组指针:在这里括号的优先级比[]高,括号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。因此p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。
指针数组代码:
int arr1[] = { 11, 12, 13, 14, 15 };
int arr2[] = { 21, 22, 23, 24, 25 };
int m = 5;
int * q[3];
q[0] = arr1;
q[1] = arr2;
q[2] = &m;
std::cout << "arr1:" << arr1 << std::endl;
std::cout << "&arr1:" << &arr1 << std::endl;
std::cout << "q[0]:" << q[0] << std::endl;
std::cout << "q[1]:" << q[1] << std::endl;
std::cout << "q[2]:" << q[2] << std::endl;
std::cout << "*q[0]:" << *q[0] << std::endl;
std::cout << "*q[1]:" << *q[1] << std::endl;
std::cout << "*q[2]:" << *q[2] << std::endl;
std::cout << "&q[0]:" << &q[0] << std::endl;
std::cout << "&q[1]:" << &q[1] << std::endl;
std::cout << "&q[2]:" << &q[2] << std::endl;
std::cout << "q[0][2]:" << q[0][2] << std::endl;
std::cout << "*(q[0] + 2):" << *(q[0] + 2) << std::endl;
std::cout << "*(*(q + 0) + 2):" << *(*(q + 0) + 2) << std::endl;
指针数组运行结果:
arr1:0044F888
&arr1:0044F888
q[0]:0044F888
q[1]:0044F86C
q[2]:0044F860
*q[0]:11
*q[1]:21
*q[2]:5
&q[0]:0044F84C
&q[1]:0044F850
&q[2]:0044F854
q[0][2]:13
*(q[0] + 2):13
*(*(q + 0) + 2):13
指针数组说明:
数组中元素就是指针,可以按照指针的正常操作进行赋值,例如示例中赋予整型和数组型
可以通过下列方式获得数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
数组指针代码:
int arr[] = { 1, 2, 3, 4, 5 };
int(*p)[5] = &arr;
int(*p)[5] = arr;//失败
std::cout << "arr:" << arr << std::endl;
std::cout << "&arr:" << &arr << std::endl;
std::cout << "p:" << p << std::endl;
std::cout << "*p:" << *p << std::endl;
std::cout << "(*p)[3]:" << (*p)[3] << std::endl;
std::cout << "&p:" << &p << std::endl;
std::cout << std::endl;
int* p1 = arr;
std::cout << "ar:" << arr << std::endl;
std::cout << "&ar:" << &arr << std::endl;
std::cout << "p1:" << p1 << std::endl;
std::cout << "*p1:" << *p1 << std::endl;
std::cout << "*(p1 + 3):" << *(p1 + 3) << std::endl;
std::cout << "&p1:" << &p1 << std::endl;
指针数组运行结果:
arr:001AFCC0
&arr:001AFCC0
p:001AFCC0
*p:001AFCC0
(*p)[3]:4
&p:001AFCB4
ar:001AFCC0
&ar:001AFCC0
p1:001AFCC0
*p1:1
*(p1 + 3):4
&p1:001AFCA8
指针数组说明:
数组指针与(定义一个数组,再定义一个指针,指针指向这个数组)没啥大区别呀,几乎可以认为两者相等。区别就是数组指针指向的是整个数组的首地址,而普通指针指向的是数组首元素的首地址
&a 是整个数组的首地址,a是数组首元素的首地址,其值相同但意义不同。
参考链接:https://www.cnblogs.com/mq0036/p/3382732.html
与二维数组代码:
int m[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
int(*p)[3];
p = m;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++){std::cout << *(*(p + i) + j) << " ";}
std::cout << std::endl;
}
std::cout << std::endl;
int *q[3];
q[0] = m[0];
q[1] = m[1];
q[2] = m[2];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++){std::cout << *(*(q + i) + j) << " "; }
std::cout << std::endl;
}
与二维数组运行结果:
1 2 3
4 5 6
7 8 9
1 2 3
4 5 6
7 8 9
关于对数组指针赋值取地址的说明:
代码:
int m[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
int(*p)[3];
p = m;
int n[3] = { 14, 15, 16 };
int(*q)[3];
q = &n;
q = n;//异常
说明:
将一维数组赋值给数组指针时,他俩类似于差了一个维度(其他要么都是一个,要么都是两个),因此需要对数组取地址
双重指针
概念理解的参考链接:
https://blog.csdn.net/majianfei1023/article/details/46629065
将双重指针作为二维数组使用:
void func(int **a, int ROW, int COL)
{
for (int i = 0; i < ROW; i++)
for (int j = 0; j < COL; j++)
a[i][j] = i * 10 + j;
}
int ROW = 5;
int COL = 4;
int **a;
//a = (int **)malloc(sizeof(int*) *ROW);//C语言写法
a = new int*[ROW];
for (int i = 0; i < ROW; i++)
{
//a[i] = (int *)malloc(sizeof(int) *COL);
a[i] = new int[COL];
}
func(a, ROW, COL);
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
std::cout << a[i][j] << " ";
std::cout << std::endl;
}
for (int i = 0; i < ROW; i++)
{
delete a[i];
//free(a[i]);
}
delete a;
//free(a);
指针函数与函数指针
指针函数代码:
int *func()
{
int a = 10;
int *p = &a;
return p;
}
指针函数说明:
指针函数:返回值类型是指针的函数
函数指针代码:
int func(int a, int b)
{
return a + b;
}
int(*pFun)(int, int) = func; //定义一个函数指针
std::cout << (*pFun)(2, 5) << std::endl; //用函数指针调用函数
std::cout << pFun(2, 5) << std::endl; //用函数指针调用函数
函数指针说明:
函数指针:指向函数的指针
我们定义了一个函数指针,在最后进行调用函数的时候,有两种方法,一种是用*pFun来调用,一种是直接用pFun来调用,可见两种方法结果都一样。
参考链接:
AI算法与图像处理