前言
最近学习C++和C语言关于指针的一些知识,学了指针才发现,什么牛鬼蛇神都出来了,一些常用的知识只要和指针一联合,就搞不懂了,尤其是常用的常量、数组以及函数,因此花了很长时间,做出的一个总结,不足之处,还请各位指教。
正文
首先,需要了解一个优先级顺序:() > [] > * 。这里对了解数组以及函数和指针的关系至关重要。
指针常量与常量指针
指针常量 ---- 指针类型的常量
类型名 * const 指针名
定义:其实就是一个指针类型的常量,本质上还是一个常量,因此常量p指向的地址就不可以更改(地址是一个常量),也就是说指针的值不可以更改,因为指针的值就是地址。而同一个地址可以有不同的值,因此经过*p解引用之后的值可以更改,因为现在已经不是地址值了。
代码示例:
int a = 10,b = 20;
int * const p = &a // 常量必须初始化
p = &b; // 错误,地址不可更改
*p = 30; // 正确,值可以更改
常量指针 ---- 常量类型的指针
两种表示方式:
1、const 类型名 * 指针名
2、类型名 const * 指针名
定义:这里指的是常量的指针,也就是说,本质上是一个指针,这里定义的指针p是指向常量的(值是一个常量),因此*p解引用,其实就是一个常量,不可以进行更改。而同一个值可以有不同的地址,因此p其实是一个指针的值,也就是地址,可以更改。
代码示例:
int a = 10,b = 20;
const int * p = &a; / int const *p = &a; // 常量必须初始化
p = &b; // 正确,地址可更改
*p = 30; // 错误,值不可以更改
注意事项 ---- 指针与字符串常量
上述中使用的只是简单的数值常量,了解和指针的关系之后,我们可以知道对于指针常量来说,指针经过解引用之后的值是可以进行更改的,因为在内存管理中,数值常量主要存放在数据段,或者堆栈中,因此可以进行修改。
然而,对于字符串常量来说,它被分配在代码段的只读存储区内,通常这块内存区域用于只读,不可对其进行随意修改。因此,虽然在指针数值常量中的数值是可以进行修改的,对于指针字符串常量,str指向的是字符串常量"Hello World"的首地址,而*str就是首个字符"H",然而字符常量是不可以进行修改。另外,指针的地址更是不可以更改,因为这里的指针是一个常量,不可以对地址值修改。
代码示例:
int main()
{
char * const str = "Hello World"; // 定义了一个指针类型的字符串常量
* str = "Hello Earth"; // 错误,不可以对字符串的值进行修改
str = "Hello Earth"; // 错误,不可以对指针的值进行修改
cout << str << endl;
}
指针函数与函数指针
指针函数 ---- 指针类型的函数
下面代码中声明了两个函数,第一个就是一个普通的函数声明,返回值是一个 int 类型;第二个主要的区别就是加了一个指针 " * " 符号,也就是返回值是一个 int 类型的指针,是一个地址而已。因此,两者也就是用于接收返回值的时候,定义的 int 类型变量有些不同。
类型名 * 函数名 (参数列表)
1、int fun(int a,int b);
2、int *fun(int a,int b);
定义:一个指针类型的函数,本质上还是一个函数,只是它的返回值是一个地址值,也就是返回一个指针,因此必须使用相同类型名的指针变量接收这个返回值。
代码示例:
struct Data
{
int m_A;
int m_B;
};
Data *fun(int &a,int &b)
{
// 定义Data类型的指针接收指针函数的返回值
Data *data = new Data;
data->m_A = a;
data->m_B = b;
return data;
}
int main()
{
// 调用指针函数
Data * myData = fun(10,20);
cout<<myData->m_A<<" "<<myData->m_B<<endl;
delete myData;
}
注:在调用指针函数时,必须使用一个相同类型的指针来接收这个函数的返回值。
函数指针 ---- 函数类型的指针
类型名 (*函数名) (参数列表)
int (*pfun) (int a,int b);
定义:一个函数类型的指针,指针指向的是函数,本质上是一个指针,由前面的优先级可知,()优先级最高,因此函数名与指针 * 结合,定义的也就是一个指针。原理就是当定义一个函数时,此时的函数命就是该函数的首地址,既然函数名也是一个地址,就可以定义一个指针变量来存放。
函数指针使用的时候需要将函数的地址赋给声明的函数指针变量,由于前面提到一个函数名就代表了函数的首地址,因此一般使用直接赋值的方式即可。
// 定义一个函数
int fun (int a, int b)
{
return a*b;
}
声明一个函数指针
int (*pfun) (int a,int b);
pfun = fun; // pfun是一个存放函数地址的指针,而fun又是定义的一个函数的名称,
// 本身也是地址,可以直接相等
代码示例:
int Min (int x,int y)
{
int z;
if(x > y){
z = y;
}
else{
z = x;
}
return z;
}
int main()
{
int (*pfun) (int x, int y);
pfun = Min; // 都是地址,可以直接相等
int a,b,c;
cin>>a>>b;
c = (*pfun)(a,b); // 调用函数时,可以使用函数指针的方式进行调用,此时解引用出来的是个值,不是地址
cout<< "Min = "<<c<<endl;
return 0;
}
注意事项 ---- 函数指针与++、--运算
我们知道,在普通的数值型的指针变量中,指针是可以进行自增和自减,自增(向后加4个字节,32位操作系统中,左右类型指针均占4个字节),自减则相反方向。然而由于函数指针指向的是一个代码段,每个函数的大小是不确定的,因此无法确定递增或递减的步长。也就是下方代码中我们声明的函数指针pfun无法进行++或者--操作,如果需要进行此操作,可以采用定义一个整型变量用来保存每个函数的代号,然后进行函数调用操作。
void func1()
{
cout << "This is func1." << endl;
}
void func2()
{
cout << "This is func2." << endl;
}
void func3()
{
cout << "This is func3." << endl;
}
int main()
{
void (*pfun)() = func1; // 声明一个函数指针
int index = 1;
// 递增函数指针的索引
index++;
// 将函数指针指向相应的函数
if (index == 1)
{
pfunc = func1;
}
else if (index == 2)
{
pfunc = func2;
}
else if (index == 3)
{
pfunc = func3;
}
// 调用函数指针指向的函数
pfunc();
return 0;
}
指针数组与数组指针
指针数组 ---- 指针类型的数组
指针类型的数组,本质上是一个数组。然而数组中的元素都是指针,其实就是说数组中存放的元素都是地址而已,如果需要操作这个数组,就必须让里面的元素等于地址。
类型名 *数组名 [参数];
int *arr [8]; // 这里定义了一个有8个int类型元素的数组,并且数组中的元素都是指针
代码示例:
int main()
{
int *pArr[4];
int Arr[4] = { 1,2,3,4 };
pArr[0] = &Arr[0]; // 对Arr中的首元素取地址赋给pArr首元素
cout << pArr[0] << endl; // 输出地址 0071F8AC
cout << *pArr[0] << endl; // 输出值 1
return 0;
}
数组指针 ---- 数组类型的指针
本质上是一个指针。并且这个指针指向一个数组,数组中的元素还是数值,但是使用一个指针进行管理。
类型名 (*数组名) [参数];
int (*pArr) [8]; // 声明了一个指针,并且这个指针指向了一个具有8个int型元素的数组
下例中,声明一个指针指向 int [4] 类型。然后,我们将 Arr 的地址给 pArr,这样 pArr 就指向了数组的第一行。接着,我们使用 ++pArr 将 pArr 指向数组的下一行,然后使用 **(++pArr) 来访问这一行的第一个元素,输出结果为 5。
代码示例:
int main()
{
int Arr[3][4] = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
int (*pArr)[4];
pArr = Arr;
cout << **(++pArr)<< endl; // 输出 5
system("pause");
return 0;
}
总结
定义不同
指针常量本质是一个常量,地址是一个常量,地址不可改(&指向不可改),值可以改。
常量指针本质是一个指针,值是一个常量,值不可改,地址可以改(&指向可以改)。
指针函数本质是一个函数,但是返回值是指针类型,使用时,必须定义同类型指针变量接收。
函数指针本质是一个指针,声明一个函数指针,可以为其赋给其他函数地址。使用(*fun)调用函数
指针数组本质是一个数组,数组中的元素是指针,占有多个指针的存储空间。
数组指针本质是一个指针,指针管理数组的元素,占有内存中一个指针的存储空间。
如果想简单一点辨别,也可以看出我写的时候,凡是本质上是指针的,名称都是指针在后,本质上是其本身的名称都是指针在前。
写法不同
指针常量:int * const p = &a;
常量指针:const int * p = &a; / int const * p = &a;
指针函数:int * fun(int a,int b);
函数指针:int (*pfun) (int a,int b);
指针数组:int *Arr[10];
数组指针:int (*pArr)[10];