在C语言中,有很多极易混淆的概念(比如:内存和指针、函数指针、指针函数、数组指针、指针数组),也正是这些概念使得很多人学习起来很累,今天,笔者分享一下自己对这些的概念的理解。
一、定义
1、内存:随机存取存储器,也叫主存,是与CPU直接交换数据的内部存储器。
2、指针:是编程语言中的一类数据类型及其对象或变量,用来表示一个存取器地址,这个地址的值直接指向存在该地址对象的值。
3、函数指针:是一种在C、C++(类C语言)中指针,可以像一般的函数一样传参、调用、返回变量。
4、指针函数:返回值是一个指针的函数称为指针函数。
5、数组指针:当数组名作为取地址&运算符的操作数,则表达式的值为指向整个数组的指针。
6、指针数组:一个数组,数组中的各个元素都是指针。
二、理解
关键:代码中一切都是数据,而数据只有两个属性:首地址和长度,至于怎么读取这个数据与数据本身无关。
如果你理解了上面这一句话,那么C语言中很多看似复杂的概念就很好理解了,那么接下来,我们来看下面这几句古文。
① 下雨天留客天留我不留
这句话可以拆解成很多总句式:最常见的就是“下雨天,留客天,留我不?留!”,“下雨天留客,天留我不留。”。
② 黄河远上白云一片孤城万仞山羌笛何须怨杨柳春风不度玉门关
上面这一段古文是一段千古佳话,传闻纪晓岚为人画扇,写下了上面这一段文字,要知道当时是没有标点符号的,所以纪晓岚也是写得一手大板书,细心的朋友可能已经发现了,如果纪晓岚写得是王之涣的《凉州词》,那么他少些了一个“间”字,王之涣的《凉州词》原文是:
黄河远上白云间,一片孤城万仞山。
羌笛何须怨杨柳,春风不度玉门关。
这个问题也是立马就被纪晓岚的朋友发现了,当即指了出来,纪晓岚心想,这回可是糗大了,但是细细一看,纪晓岚灵机一动,立马把这个尴尬的词给圆了过去:
黄河远上,白云一片,孤城万仞山。
羌笛何须怨,杨柳春风,不度玉门关。
古代汉语没有标点符号,写在纸上,就如同数据(0和1)存储在内存中,从上面我们可以知道:同一段内存空间,我们不同的读法,读取到的结果是不一样的,按照王之涣《凉州词》的读法,纪晓岚很明显就是错的,如果按照纪晓岚的那个读法,又是合情合理,也符合断句,也是朗朗上口。再者就是都是从“下”字(首地址)开始读,一次读取三个字是“下雨天”,一次读取五个字就是“下雨天留客”。如果把“下”字(首地址)比作内存地址(0xA12D1000)呢?你又能有什么体会?
一:数据 = 首地址 + 长度
二:所有的数据类型本质上就两种:单字节数据、多字节数据。
从这个角度去从新理解上面的定义:
1、内存:一个或者一组容器,能够存取数据的容器。
2、指针:一个首地址。
3、函数指针: 一个首地址。
4、指针函数:返回 首地址 的函数。
5、数组指针:一个首地址
6、指针数组:一个数组,数组中的各个元素都是首地址。
这样一来,你会发现上面的很多定义都成了首地址,既然都是首地址,那么那么他们的区别就是长度和读取方式的不同了。
这里需要面对一个不可逃避的问题:首地址也是一个数据。
相信很多人,又会开始疑惑了,如果首地址也是一个数据,数据又只是首地址和长度......,那这岂不是成了无底洞?
我的答案是,不是这样的,内存中每一个字节都有自己的唯一的地址,地址是唯一的,不过从某个地址上开始存储多少个字节的数据,这个数据是可变的,既然是可以变动的,那么自然也是可以吧另一个地址的值存到这里来。
那我们再次更新一下上面的理解:
1、内存:存取所有的数据或者一段数据的容器。
2、指针:数据。
3、函数指针: 数据。
4、指针函数:返回 数据 的函数。
5、数组指针:数据
6、指针数组:一个数组,数组中的各个元素都是数据。
帮助理解(切记,不要在实际运用中使用):
using namespace std;
//定义一个函数指针
typedef void(*pFUNCTION)(void);
void HelloWord(void)
{
cout << "Hello Word!" << endl;
}
void GoodBye(void)
{
cout << "Good Bye!" << endl;
}
int main(int argc, char** argv)
{
pFUNCTION hello = HelloWord;
pFUNCTION bye = GoodBye;
//可以理解为,从函数void HelloWord(void)的首地址按照 int类型 的方式读取四个字节
int addrHello = (int)hello;
int addrBye = (int)bye;
int myarray[2] = { addrHello, addrBye };
//从addr内存中的首地址开始按照 pFUNCTION类型 的方式读取一定长度的数据
pFUNCTION helloInt = (pFUNCTION)addrHello;
pFUNCTION byeInt = (pFUNCTION)myarray[1];
pFUNCTION byeInt1 = (pFUNCTION)*(myarray + 1);
helloInt();
byeInt();
byeInt1();
//你甚至可以这么干,当然,能不能运行我就不管了。
pFUNCTION func = (pFUNCTION)65536;
func();
return 0;
}