指针
前言
总结一下目前在c++项目中使用到指针,由于之间使用C/C++写代码已经是好几年前的事了;这次老师的项目中需要使用C++调用一个光纤接口设置的驱动,并将其用JNI封装为在java中也可以调用的native方法。该驱动提供的消息发送和消息接收都是使用函数指针来实现的。因此该文章记录了在当前项目中使用函数指针的注意点,以及小小的总结下各类指针。
——PS:小小的小总结。。。
Overview
- 普通指针
- 变量
- 数组
- 结构体
- 函数指针
普通指针
变量
这里的普通指针指的是指某一变量地址的空间。即,如果在代码中声明了一个int类型的变量。并且变量中存放了“1”这个整型。
int a = 1;
这里假设 a 变量在内存中的地址为0x01;
现在声明一个int类型的指针 p,让其指向的地址为a的地址0x01;即p中存放的地址值为 0x01。
int *p = &a;//要获取某一变量的值需要使用 取地址符 &
//先声明,后初始化
int *p;
p = &a;
summary
- 声明指针
type *pointer_name;
- 初始化指针
type *pointer_name = &var_name;
数组
关于指针和数组需要了解的是数组指针和指针数组的区别。顾名思义
- 数组指针:指向一个数组的指针
int (*p)[5];
该指针p指向一个大小为5的int类型的数组。
使用时其实就完全可以看成是一个可增长的int类型的指针,指针的递增就是数组下标的递增。
- 指针数组:一个数组其数组中元素的类型为指针。
int *p[5];
由于[]的优先级比*高,首先它是一个数组,它的大小是5,它里面存放的数据类型是int *,也就是整型指针。
其实就是该数组的类型是指针类型。按照正常的访问操作就可以得到对应的指针,再对其进行解引用就可以访问该指针指向的地址中代表的值,结构体,函数等。
一维数组
数组的变量名可以代表数组的首地址,即等价于 &arr[0]。例如初始化如下一维数组。
int[5] arr = {0,1,2,3,4};
使用指针访问该数组中的值,这里要注意指针的类型要和数组的类型一致。而且在指针指向某个数组中的某一个值时,也可以执行"+","-"操作。
int *p;
p = arr;
//遍历
const int num = sizeof(arr) / sizeof(arr[0]);
for(int i = 0;i < 5;i < num;i++)
{
cout << *p << endl;
p++;
}
二维数组
二维数组的首地址也可以使用数组名来代替,等价于&arr[0][0];和一维数组有区别的是,二维数组相对于一维数组还具有行首地址&arr[0]。
int[2][3] arr = {{0,1,2},{3,4,5}};
arr和arr[0]的区别就是指针数组和数组指针的区别即
- arr的类型是 int * [2],即指针数组
- arr[0]的类型是 int *。
- &arr[0][0]的类型是int(*) [2]
- &arr[0][0]的类型是 int *。
所以实际上arr[0]和&arr[0][0]的类型相同,都是数组指针,因此可以相互赋值。而&arr[0]和arr 的类型相同,都是数组,因此可以相互赋值。
int *p;//数组指针
p = arr[0];
p = &arr[0][0];
int *q [2];
q = arr;
q = &arr[0];
访问二维数组
- 普通访问
int[2][3] arr = {{0,1,2},{3,4,5}};
int row = sizeof(arr) / sizeof(arr[0]);
int col = sizeof(arr[0]) / sizeof(arr[0][0]);
for(int i = 0; i < row; i++)
{
for(int j = 0; j < col; j++)
{
cout << arr[i][j] << endl;
}
}
- 指针访问
int[2][3] arr = {{0,1,2},{3,4,5}};
//数组指针
int *p[2];//2表示二行
p[0] = arr[0];//存放第0行
p[1] = arr[1];//存放第1行
直接使用数组名称访问
//获取arr[0][1]的值
int a01 = *(*arr + 1);//先解引用了arr,得到arr[0];arr[0]表示第一行的首地址,因此 + 1后表示第一行首地址&arr[0][0]向后移动变为&arr[0][1];最后解引用后得到a[0][1]的值。
int a10 = *(*(arr + 1));//arr + 1,执行后得到 &arr[1],即第二行的首地址,因此对其解引用后得到&arr[1][0],最后对&arr[1][0]解引用就得到了 arr[1][0]的值。
int b01 = *(&a[0][0] + 1)
summary
可以将二维数组看成是一个指针数组,该指针数组中放置的都是指向一个数组首地址的指针。
如上图所示:int* p[n]可以看为 arr 或是 &arr[0],int* 可以看为 &arr[0][0]。我们将从int*p[n]到int的路径看做是正方向,其中的每个类型我们称之为维度。所以每一次对arr进行解引用操作*,就会向正方向前进一步。而取地址符 & 则正相反,每进行一次&操作就会向反方向前进一步。
而对于指针来说的加减法则是在当前维度的下一个维度下进行的加减法。即对arr[0]进行+1,则不会指向arr[1],而是会指向arr[0][1]。所以,当使用指针遍历二维数组时要进行换行操作,就需要先进行升维度,即取地址操作。
这也就理解了为什么arr + 1会指向第二行,因为行现在位于第二维度。
数组在函数中的传递
函数中的传递包含如何用形参接收数组和如何返回数组。而在c/c++中,对于函数中变量的传递可以依靠值传递或是引用传递(指针)。
两者之间的区别主要是为了取决于是否想对实参中的值改变。如果是值传递则不会对实参的值造成影响,而指针则是可选的,可以使用const关键字对其变量进行“锁定”。
一维数组
#include <iostream>
#include <stdio.h>
using namespace std;
void show1DArray1(int arr[],int length);//常量接收
void show1DArray2(int *arr,int length);//指针接收
//常量指针接收,数组中的内容不可变
void show1DArray3(const int *arr,int length);
int main()
{
int arr[10] = {0,1,2,3,4,5,6,7,8,9};
show1DArray1(arr,10);
show1DArray2(arr,10);
show1DArray3(arr,10);
system("pause");
return 0;
}
void show1DArray1(int arr[],int length)
{
for (int i = 0; i < length; i++)
{
cout << arr[i] << " , ";
}
cout << endl;
}
void show1DArray2(int *arr,int length)
{
cout << sizeof(arr) << endl;
for (int i = 0; i < length; i++)
{
cout << * arr++ << " , ";
}
cout << endl;
}
void show1DArray3(const int *arr,int length)
{
cout << sizeof(arr) << endl;
for (int i = 0; i < length; i++)
{
if (i == 3)
{
//将arr[3] 的值改为 10
*arr = 10;//这里会报错,因为常量指针指向的内容被锁死,无法修改
}
cout << * arr++ << " , ";
}
cout << endl;
}
返回
作为数组,一般可以使用返回头指针的方式返回
二维数组
传递
#include <stdio.h>
#include <iostream>
using namespace std;
//void showArray(int array[][3],int row);//直接接受整个二维数组
void showArray(int (*p)[3],int row);//使用数组指针接收
void showArray(int *p,int row,int col);//使用指针接收
void showArray(int **p,int row,int col);//使用二重指针接收
int main()
{
//初始化一个二维数组
int arr[2][3] = {
{1,2,3},
{4,5,6}
};
cout<<"----------"<<endl;
//showArray(arr,2);
//showArray(arr,2);
//showArray((int *)arr,2,3);
showArray((int **)arr,2,3);
system("pause");
return 0;
}
//
//void showArray(int arr[][3],int row) {
// for (int i = 0; i < row;i++)
// {
// for (int j = 0; j < 3; j++)
// {
// cout<<arr[i][j];
// }
// cout << endl;
// }
//}
void showArray(int (*p)[3],int row)
{
for (int i = 0; i < row;i++)
{
for(int j = 0; j < 3; j++)
{
//cout << *(*(p + i) + j);// * 优先级大于 + ;p + i 选择行,j 表示列
//等价于
//cout << *(p[i] + j);
//等价于
cout << *((int *)p + i * 3 + j);
//这里实际上先将 p 从数组指针被强转为一个int类型的指针
//这时实际上是将这个二维数组从逻辑上的二维转为逻辑上的一维。因此只需要将其从头到尾遍历即可
}
cout << endl;
}
}
//这里实际上就是将arr强转在传参时就实现了。
void showArray(int *p,int row,int col)
{
for(int i = 0; i < row * col; i++)
{
cout<< *(p + i);
}
cout << endl;
}
//二重指针
void showArray(int **p,int row,int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j <col; j++)
{
cout << *((int *) p + i * col + j);
}
cout << endl;
}
}
返回
结构体指针
结构体在c++中和java中的pojo差不多,即只用来存放一些数据,不涉及业务流程操作。因此使用结构体指针就是相当于访问结构体中的数据。
struct teacher {
char* name;
int age;
}
struct student {
char* name;
int age;
struct teacher th;//结构体包含结构体
}
//结构体指针
student *str;
struct student s;
struct teacher th1;
str = &s;
//赋值
str->name = "xxx";
str->age = 12;
str->th = th1;
结构体中包含结构体指针
struct nc_card{
int id;
string name;
struct nc_card* next;
}
//调用
void showName(const nc_card *card)
{
cout << card->id << endl;
cout << card->name << endl;
struct nc_card *next = card->next_card;
cout << next->id << endl;
cout << next->name << endl;
}
函数指针
函数指针和指针函数
- 函数指针:本质上是一个指针,指向某一个函数,是该函数的入口
- 指针函数:本质上是一个函数,知识该函数的返回值是一个指针。
函数指针
声明
/*格式
return_type(*func_name)(parameter_typeA,parameter_typeB);
*/
//example
int (*swap)(int a,int b);
其类型的区分可以按照函数的类型去区分:无参无返回值,含参有返回值,含参无返回值,无参有返回值。参数部分对应这着parameter_type部分;返回值部分对应着return_type部分;没有返回值使用void,没有参数就使用形参;如果返回的参数使用的需要加const,即如下形式:
const int(*swap)(int a, int b);
简单使用
#include <iostream>
#include <stdio.h>
using namespace std;
//func_pointer callback function declaration
typedef void (*SWAP_CALLBACK)(int *a,int *b);//这里可以直接使用SWAP_CALLBACK作为形参类型
//real func_swap
void swap(int *a,int *b);
void show(int *a,int *b,SWAP_CALLBACK callback);//声明一个回调函数
int main()
{
int a = 10;
int b = 20;
SWAP_CALLBACK callback = swap;// init function pointer
show(&a,&b,callback);
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
//使用回调函数
void show(int *a,int *b,SWAP_CALLBACK callback)
{
cout << "开始调用回调函数:" << endl;
callback(a,b);
cout << "调用回调函数结束:" << endl;
}
void swap(int *a,int *b)
{
cout<< "回调函数执行"<< endl;
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 10;
int b = 20;
SWAP_CALLBACK callback = swap;// init function pointer
show(&a,&b,callback);
cout << a << endl;
cout << b << endl;
system(“pause”);
return 0;
}
//使用回调函数
void show(int *a,int *b,SWAP_CALLBACK callback)
{
cout << “开始调用回调函数:” << endl;
callback(a,b);
cout << “调用回调函数结束:” << endl;
}
void swap(int *a,int *b)
{
cout<< “回调函数执行”<< endl;
int temp = *a;
*a = *b;
*b = temp;
}