C语言指针
C语言指针
C语言指针
1.指针与内存地址的区别:
指针是带有解释方式的内存地址,它不但指出了数据存放在何处,还指出了这个内存 地址存放的是什么类型的数据,而内存地址仅仅是个地址,至于数据是什么类型,数据 有多大,并没有解释。
2.指针访问和数组下标访问谁快谁慢:
使用指针访间接访问的程序运行速度要比使用数组下标访问快,这是不正确的, 使用指针间接访问内存数据速度要小于等于使用数组下标访问,这是因为使用下 标访问时,由CPU先算出要访问数据的内存地址,然后进行一次内存访问,就可以 得到指定内存地址处的数据,而是用指针间接访问时,要先进行一次内存访问, 拿出指针所指向的内存地址,然后得到该内存地址后,在进行一次访存才能得到 指定内存地址处的数据,共两次访问内存,所以通过数组下标访问要比使用指针 间接访问要快。
3.指针使用注意事项
定义指针时要赋合适的初值,如果没有合适的初始值,那就赋间接访问时能发生错误 的值,一般赋NULL,在新的C++11标准中规定了关键字nullptr表示空指针。 指针使用完后要置无效,使用指针时一定要让其处于可控状态。
4.void类型指针
void *型指针仅仅只有内存地址值,没有类型信息,不能做下标运算 void*型指针可以接受任何类型的指针,将一个有类型指针赋给void*型指针时,只保留 内存地址,类型信息丢失;但一个void*型指针如果不经强转是不能赋值给其它类型的 指针,void*型指针通常作为复制数据函数的形参,如C库函数memcpy的形参就是void* 类型。
5 指针运算
-
指向基本数据类型的指针,加减运算后得到同类型指针
设有指针Type* ptr=....; Type为基本数据类型,N为整数
那么ptr+N=(Type*)((int)ptr+sizeof(Type)*N); -
两个同类型指针相加无意义,但两者相减结果的绝对值为两者之间间隔的同类型数据的个数
例:
下面观察下内存:
由于在Debug版下指针pA和pB之间间隔了8字节,由于pB所在的内存地址比pA要低
所以相减后得到-3,如果是pA-pB将得到3,所以可以推出两指针相减的公式:
设有指针Type* ptr1,*ptr2:
ptr1-ptr2 = ((int)ptr1-(int)ptr2)/sizeof(Type); -
指针不能进行乘除运算,编译时无法通过
常量指针与指针常量以及指向常量的常指针
-
常量指针
无法使用该指针修改它所指向数据,但可以改变该指针的指向,使它指向另一个变量;
定义时const关键字在""号左边,则定义的是常量指针,形如const Type * 或者
Type const; -
指针常量
该指针本身是个常量,定义时必须进行初始化,无法修改该指针的指向,但是可以
修改该指针所指向变量的数据
定义时const关键字在"*"号右边,则定义的是指针常量,形如Type * const; -
指向常量的常指针
无法使用该指针修改它所指向的数据,也无法修改该指针的指向
定义时"*"左右都有const关键字,形如Type const * const或者const Type *const;
指针数组与数组指针的区别
- 指针数组
数组中每个元素都是指针,例如:int* PtrAry[8]; - 数组指针
一个指向数组的指针,例如:int nAry[M][N]; 定义一个指向该数组的
指针: int (*pAry)[N] = &nAry;
数组元素内存地址的计算
计算数组元素的内存地址时要熟练记住一下规则:
- 规则1:[数组名]是[数组第0个元素类型]的地址常量
- 规则2:[某类型]指针取内容得到[某类型]
- 规则3:[某类型]指针下标运算得到[某类型]
- 规则4:[某类型]指针和整型相加得到[某类型]指针常量
- 规则5:[某类型]指针减去[某类型]指针得到两者之间间隔的同类型元素的个数
- 对于多维数组,例如:int nAry[3][4][5],我们可以把它视为一维数组,元素类型为int[4][5]类型
例:
int nAry[2][3]= { {10,20,30}, {40,50,60} }; int(*pAry)[3] = nAry; int *pInt = nAry[0]; printf("%08X\r\n", nAry); printf("%08X\r\n", nAry[0]); printf("%08X\r\n", nAry[0][0]); printf("%08X\r\n", pAry[0]); printf("%08X\r\n", *pAry[0]); printf("%08X\r\n", pAry[1]); printf("%08X\r\n", *pAry[1]); printf("%08X\r\n", (*pAry)[1]); printf("%08X\r\n", pAry + 1); printf("%08X\r\n", *pAry + 1); printf("%08X\r\n", (*pAry)[1] + 1); printf("%08X\r\n", *(pAry[1]) + 1);
如上图所示,在监视窗口中可以看的数组nAry的首地址为0x00EFF7F0:
-
nAry是数组名,其值为数组首地址,所以printf("%08X\r\n", nAry)输出的结果就是00EFF7F0
-
nAry是数组名,指向nAry[2][3]中第0个元素,第0个元素类型为int[3],根据规则3可知nAry[0]指向一个
int[3]类型,这是一个int类型的一维数组,所以nAry[0]int类型指针,该指针指向nAry[2][3]中的元素10
因为元素10位于数组首地址处,所以nAry[0]指向的内存地址为0x00EFF7F0,printf("%08X\r\n", nAry[0])
输出结果为00EFF7F0 -
nAry[0][0]做了两次下标运算,由上一步可知nAry[0]为int类型指针,那么根据规则3,nAry[0][0]得到int
类型,所以printf("%08X\r\n", nAry[0][0])输出0000000A -
pAry指向数组nAry,pAry指向元素为int[3]类型的数组,故pAry是一个int[3]类型指针,pAry[0]得到int[3]
类型,int[3]为一维数组,所以pAry[0]为int类型指针,指向一维数组{10,20,30}中的元素10,而元素10位于
数组首地址处,所以printf("%08X\r\n", pAry[0])输出结果为00EFF7F0 -
由上面的推导可知pAry[0]是一个int类型的指针,实际上它指向的就是{10,20,30}中的元素10,根据规则
"[某类型]指针取内容得到[某类型]",对pAry[0]取内容得到10,所以printf("%08X\r\n", *pAry[0])
输出结果为0000000A -
pAry是一个int[3]类型的指针,pAry[1]指向数组nAry[2][3]中第1个元素,所以pAry[1]指向的内存
地址值为:00EFF7F0+sizeof(int[3])=00EFF7FC,所以printf("%08X\r\n", pAry[1])输出结果为00EFF7FC -
pAry[1]是一个int类型的指针,指向数组nAry中第1个元素也就是{40,50,60}中的40,那么该类型指针
取内容得到int类型,也就是40,所以printf("%08X\r\n", *pAry[1])输出00000028 -
再来看看(*pAry)[1],因为(*pAry)加了"()",所以优先计算pAry,pAry是一个int[3]类型指针,根据
规则2,对pAry取内容得到int[3]类型,所以pAry是int类型指针,根据规则3可知(*pAry)[1]得到
{10,20,30}中的20,printf("%08X\r\n", (*pAry)[1])输出结果为00000015 -
pAry是一个int[3]类型指针,那么根据规则4可知pAry+1得到一个int[3]类型指针,指向的内存地址为
00EFF7F0 + sizeof(int[3])=00EFF7FC,所以printf("%08X\r\n", pAry + 1)输出00EFF7FC -
pAry是一个int[3]类型指针,根据规则2可知,*pAry得到一个int类型指针,在根据规则4可知,*pAry+1
得到一个int类型指针,指向的内存地址为: 00EFF7F0 + sizeof(int) = 00EFF7F4,所以
printf("%08X\r\n", *pAry + 1)输出00EFF7F4 -
*pAry为int类型指针指向{10,20,30}中的元素10,根据规则3可知(*pAry)[1]取得{10,20,30}中的20
所以printf("%08X\r\n", (*pAry)[1] + 1)输出00000015 -
pAry[1]为int类型指针,指向{40,50,60}中的40,根据规则2可知*(pAry[1])为40,所以
printf("%08X\r\n", *(pAry[1]) + 1)输出00000029
结果验证:
二级指针
指向一级指针的指针称为二级指针,例如int**p=NULL;就是定义了一个二级指针,二级指针可作为函数
参数传入,函数内部可通过间接访问,来修改二级指针所指向的一级指针,使用场景较少,至于三级指针
和其它的多级指针则几乎没有使用场景.
函数指针
函数指针的声明应该包括三部分:返回值类型 (调用约定*标识符)(参数类型列表)
例:
int _stdcall Test(int nAry[], char ch) { return nAry[0] * ch; }
定义函数指针为:int (_stdcall * pfnTest)(int*,char);