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);

  • 两个同类型指针相加无意义,但两者相减结果的绝对值为两者之间间隔的同类型数据的个数
    例:
    指针相减.png
    下面观察下内存:
    指针相减1.png
    由于在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
", nAry);
 printf("%08X
", nAry[0]);
 printf("%08X
", nAry[0][0]);
 printf("%08X
", pAry[0]);
 printf("%08X
", *pAry[0]);
 printf("%08X
", pAry[1]);
 printf("%08X
", *pAry[1]);
 printf("%08X
", (*pAry)[1]);
 printf("%08X
", pAry + 1);
 printf("%08X
", *pAry + 1);
 printf("%08X
", (*pAry)[1] + 1);
 printf("%08X
", *(pAry[1]) + 1);

C语言指针_数组首地址.png
如上图所示,在监视窗口中可以看的数组nAry的首地址为0x00EFF7F0:

  • nAry是数组名,其值为数组首地址,所以printf("%08X ", 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 ", nAry[0])
    输出结果为00EFF7F0

  • nAry[0][0]做了两次下标运算,由上一步可知nAry[0]为int类型指针,那么根据规则3,nAry[0][0]得到int
    类型,所以printf("%08X ", 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 ", pAry[0])输出结果为00EFF7F0

  • 由上面的推导可知pAry[0]是一个int类型的指针,实际上它指向的就是{10,20,30}中的元素10,根据规则
    "[某类型]指针取内容得到[某类型]",对pAry[0]取内容得到10,所以printf("%08X ", *pAry[0])
    输出结果为0000000A

  • pAry是一个int[3]类型的指针,pAry[1]指向数组nAry[2][3]中第1个元素,所以pAry[1]指向的内存
    地址值为:00EFF7F0+sizeof(int[3])=00EFF7FC,所以printf("%08X ", pAry[1])输出结果为00EFF7FC

  • pAry[1]是一个int类型的指针,指向数组nAry中第1个元素也就是{40,50,60}中的40,那么该类型指针
    取内容得到int类型,也就是40,所以printf("%08X ", *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 ", (*pAry)[1])输出结果为00000015

  • pAry是一个int[3]类型指针,那么根据规则4可知pAry+1得到一个int[3]类型指针,指向的内存地址为
    00EFF7F0 + sizeof(int[3])=00EFF7FC,所以printf("%08X ", pAry + 1)输出00EFF7FC

  • pAry是一个int[3]类型指针,根据规则2可知,*pAry得到一个int类型指针,在根据规则4可知,*pAry+1
    得到一个int类型指针,指向的内存地址为: 00EFF7F0 + sizeof(int) = 00EFF7F4,所以
    printf("%08X ", *pAry + 1)输出00EFF7F4

  • *pAry为int类型指针指向{10,20,30}中的元素10,根据规则3可知(*pAry)[1]取得{10,20,30}中的20
    所以printf("%08X ", (*pAry)[1] + 1)输出00000015

  • pAry[1]为int类型指针,指向{40,50,60}中的40,根据规则2可知*(pAry[1])为40,所以
    printf("%08X ", *(pAry[1]) + 1)输出00000029

结果验证:
C语言指针_运行结果.png

二级指针

指向一级指针的指针称为二级指针,例如int**p=NULL;就是定义了一个二级指针,二级指针可作为函数
参数传入,函数内部可通过间接访问,来修改二级指针所指向的一级指针,使用场景较少,至于三级指针
和其它的多级指针则几乎没有使用场景.

函数指针

函数指针的声明应该包括三部分:返回值类型 (调用约定*标识符)(参数类型列表)
例:

int _stdcall Test(int nAry[], char ch)
{
  return nAry[0] * ch;
}

定义函数指针为:int (_stdcall * pfnTest)(int*,char);

C语言指针_函数指针.png

原文地址:https://www.cnblogs.com/UnknowCodeMaker/p/11022291.html