C语言学习笔记05——指针01( 函数指针,数组指针)

指针 

指针是一种数据类型,通过它可以定义一个指针变量,变量里存的是地址,通过这个地址就可以找到所需的变量单元。

但注意,指针本身也是数据,所以这意味着它也有属于自己的地址。


为什么要使用指针: 

1)函数之间的命名空间互相独立,但函数之间的存储空间是统一的,使用指针可以做到跨函数共享变量 。(全局变量不宜多用,它可以被所有函数访问,可能会被其他函数无意间修改,且在程序运行期间一直占用存储单元,耗费空间)

2)指针可以接收malloc分配函数返回的内存首地址,即占用的是堆内存,内存的申请和释放是受程序员控制的。

3)函数之间有传参是值传递(内存拷贝),使用指针可以优化参数的传递效率。


如何使用指针: 定义:  类型*   变量名;(变量名_p)

                         赋值: 指针变量名 = 地址;

                         取值:  *指针变量名 (变量名相当于地址,前面加个*表示取这个地址的内容)

                       1)类型决定了通过指针来访问内存时访问的字节量

                       2)*表示是指针变量 一个*搭配一个变量名 切不可偷懒int* p1,p2,p3 这是错误的

                      3)指针变量的默认值是随机的(野指针),如果在定义指针后没有立即指向某块合法地址,为了安全起见建议初始化为NULL(空)

举个例子:

int num = 10;

首先定义一个整型变量,那么编译器在内存中就开辟一个整型单元(4字节)存放变量num,假设num的地址为0x001,这块地址存放着数据10。接下来定义指针:

int* p = malloc(sizeof(int)); 
p = #

第一句定义指针  用malloc函数在堆内存中开辟一块整型单元给指针变量p  这里假设指针变量的地址为0x008。第二句赋值,即将num的地址作为数据存放到指针中  即0x008这块地址存放的数据是num的地址0x001,也就是我们说的指针p指向了num,我们可以通过指针访问到num 。

p = NULL;

如果定义指针后发现暂时没有地址需要指向,切记要么不定义,要么一定要将指针初始化为NULL。NULL在指针中表示空,即这是一个空指针,没有指向任何东西。当你在写程序时,用到一个外来指针时也一定要记得判断其是否为空指针(多一点勤劳,日后少一点查找bug的苦):

 if(NULL == p) return;


那么,如果我定义指针后,既没有指向地址,也没有初始化为NULL,会怎么样呢?朋友,那你就危险了。这就会诞生一个怪物,它叫野指针。

产生野指针的后果是不可预料的:指针是个很强大的工具,操作不当的野指针甚至可能会引起系统崩溃。因为指针定义后没有初始化,其默认值是随机的,也就是说它想指哪里就指向哪里,运气好他可能指向一块空白的内存区域,运气差点指向存放重要数据的内存区或者是操作系统的关键内存区,那么你对你的指针的再操作,将有可能会导致你数据丢失,系统紊乱。

产生野指针的原因:

1)定义指针未初始化;

2)指针指向的是局部变量的地址;(局部变量 即函数中定义的变量 存储在栈区 随函数运行结束而释放)

3)指针被free后没有置为NULL;(内存被释放不代表指针会消亡或置为NULL,勤劳!!!)

                                 没有天生的野指针,只有偷懒或粗心创造野指针的程序员 ,勤劳!


指针运算:

            指针+整数 = 指针+(宽度 * 整数)    //宽度为指针类型对应所占的字节数

            指针-整数 = 指针-(宽度 * 整数) 

            指针-指针 = (指针-指针)/宽度   //两个指针之间相隔多少个元素

            指针+指针 // 无意义


指针与数组:

           数组名是一种特殊的指针,它的值不能修改,地址是对应关系,不是存储关系,对应的就是数组的第一个元素的地址。 因为数组名就是指针,所以数组名与指针的使用方式可以互换。

arr[i] == *(arr+i);
            数组作为函数的参数时:当把数组作为参数传递给函数时,就脱变为了指针。需要额外增加一个参数(int len),把数组的长度传递过去。
int len = (sizeof(arr)/sizeof(arr[0]));


数组指针与指针数组:   

变量类型判断:变量和哪个关键字先结合,该变量就是什么类型

(1)int (*p)[n];   //p先和*结合,故是一个数组指针 ,即指针指向一个数组

(2) int* p[n];    //p先和[n]结合,故是一个指针数组,即数组里面的成员是指针

        (1) 数组指针,也称行指针,即指向一维数组的指针,这个一维数组的长度为n,n也叫做p的步长,因此,当p+1时就代表跨越了n个int型内存单元,即指向下一行的数组(在二维数组中),举个栗子:

#include<stdio.h>

int main(int argc, char const *argv[])
{
	int b[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; 
	int (*p)[4];//定义一个步长为4的数组指针 
	p=b; //赋值
	printf("%d %d\n",(*p)[0],(*(p+1))[0]); //输出第一行第一个值 和第二行第一个值
}

在运行这段代码时,我发现其实 (*p)[1] == (*p+1)[0]  可以自己揣测下原由。

     (2)指针数组,说明这是一个整型指针数组,它有n个指针类型的数组元素,此时不可用p=arr来赋值,因为数组中只有元素p[0],p[1],p[2]....p[n-1]。因此要通过循环来分别赋值。 

int* p[4];   //定义一个指针数组 里面包含四个指针变量元素 p[0],p[1],p[2],p[3]
int arr[4][5]; //定义一个4行5列的二维数组
for(int i=0;i<4;i++)
{
    p[i] = arr[i];
}  //将二维数组的四行分别赋值给指针数组中的指针   


函数指针与指针函数:

           同理,函数指针和指针函数

int (*func)(int x); //()优先级高 先与*结合 所以这是一个指针 所以是函数指针
int* func(int x); // 由函数的定义可以知道这是一个返回值为int型指针的函数 所以是指针函数

            (1) 函数指针。首先,之前说过函数是一段具有功能性的代码,其最终会被翻译成一堆二进制数据。既然函数也是一块数据,那么自然也可以用指针来指向它,这里函数名就是它的首地址。函数指针的作用就是提供不同实现的统一接口。(在多人合作开发中可以应用到,以及一些接口问题) 

举个例子:

假如你是开发,上层用户要求你做一个加法小程序,好的你开始做,终于做完了。

#include <stdio.h>

int add(int a,int b)
{
	return a+b;
}

int main()
{
  int a,b;
  scanf("%d",&a);
  scanf("%d",&b);
  int (*func_p)(int a,int b);
  func_p = add;
  printf("%d",(*func_p)(a,b));
  return 0;
}
这时,上层用户突然说不要加法了,要减法。这时你不仅要写个减法程序,还要将调用过加法的地方全部改成减法,这是多么的麻烦。而当我们使用函数指针时,上层用户想让一个函数所做的东西会变化时,我们只需要改变底层实现,并用函数指针指向新的实现就行了。


如下:

#include <stdio.h>

int add(int a,int b)
{
   return a+b;
}

int sub(int a,int b)
{
   return a-b;
}

int main()
{
  int a,b;
  scanf("%d",&a);
  scanf("%d",&b);
  int (*func_p)(int a,int b);
  func_p = add;
  func_p = sub;
  printf("%d",(*func_p)(a,b));
  return 0;
}


看到一个比喻很恰当,函数指针就相当于一只可以切换武器的手,前期的时候你可能没有太多武器,但是到了后期,武器多了,应对的困难也需要不同的武器解决,函数指针就能帮你做到切换不同武器去对付不同场景,让你更有效率的升级。


 写到这里,一开始还以为自己已经理顺了,然后看了几个例子又蒙了。

int (*p)[num](int a);
int (*p[num])(int a);
int (*(*p)[num])(int a);

能分别说出上面三个都是什么吗?

答案放下篇博客吧,我也要好好琢磨琢磨。


猜你喜欢

转载自blog.csdn.net/canger_/article/details/80536877
今日推荐