C语言中的指针:掌握内存的钥匙

C语言中的指针:掌握内存的钥匙

引言

       C语言是一种结构化编程语言,它提供了对硬件底层的直接访问,其中最强大的特性之一就是指针。指针允许程序员直接操作内存地址,这对于理解程序的内部工作原理以及优化代码性能至关重要。本文将深入探讨C语言中指针的概念、使用方法以及一些高级技巧。

什么是指针

       指针就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其声明。

声明形式:

type *pointName;

其中type是指针的基类型,它必须是一个有效的C数据类型,pointName是指针变量的名称。用来声明指针的星号*与乘法中使用的星号是相同的,但是在c语句中,星号是用来指定一个变量是指针。
C语言中的指针
例如:指针简单使用
C语言中的指针
输出结果
C语言中的指针

指针与变量

内存区的每一个字节都有一个编号,这就是"地址",如果在程序中定义了一个变量,在对程序进行编译运行时,系统就会给这个变量分配一个内存单元,并确定它的内存地址也就是一串这个地址独有的编号,指针是内存单元的编号,指针变量是存放地址的变量,通常我们会把指针变量称为指针,其实指针与指针变量含义式不一样的。

指针变量赋值

指针变量在使用之前, 一定要先赋值,也就是让指针变量指向一个地址。注意,给指针变量赋值只能式地址,通过"&"符号来获取普通变量的地址。“&” - 取地址运算符,“*” - 指针运算符,或称为间接访问运算符,取地址的值。
例子:
C语言中的指针
输出结果:上面代码演示了如何在C语言中使用指针,第一步定义一个整型变量i和一个指向整型变量的指针p。然后将i的值初始化为10,并将p指向i的地址,然后打印出i,和p的内存地址以及i的值和通过指针p访问到的值。
C语言中的指针
i = 0x7ffd55fc953c 变量i的存储地址, p = 0x7ffd55fc953c是指针变量p指向的地址,以为代码中把i的地址赋给了p,所以p指向i的存储地址。i=10i赋值10*p = 10 指针变量p指向地址里的值,p指向的是地址0x7ffd55fc953c,而这个地址里存储的值为10,所以*p=10

通过地址取值:*((unsigned char *)p))

C语言中的指针
输出结果:((unsigned char *)7c3d726c))代表把 0x7c3d726c 这个值,强制转换成一个无符号字符类型指针,相当于告诉编译器,这是一个地址,强制转换的类型和变量a的数据类型保持一致,因为要通过地址,把变量i的值读出来,最后通过*把这个地址的数据读出来,结果7c3d726c = 10
C语言中的指针

通过指针改变某个内存地址里的值

上面说了如何获取内存地址的值,那也可以通过指针改变某个内存地址里面的值。格式: *指针变量 = 数值; 【*p = 20】
C语言中的指针
输出结果:
C语言中的指针
案例2:输入a和b两个整数,然后按先大后小顺序输出a和b。不交换整型变量的值,而是交换两个指针变量的值。
C语言中的指针
分析:输入a=5,b=6,由于a<b,将p1和p2交换,注意的是,a和b的值并未交换,他们仍然保持原值,但是p1和p2的值改变了,p1的值原来为&a,后来变成了&b, p2的值原来为&b后来变成了&a,这样在输出p1和p2的时候,实际上是输出了变量b和a的值,所以先输出6后输出5。
C语言中的指针

指针变量作为函数参数

函数的参数不仅可以是整型,浮点型,字符型,还可以是指针类型,它的作用将一个变量的地址传送到另一个函数中。

案例:通过指针类型的数据作为函数的参数,对输入的两个整数按大小顺序输出。【通过指针实现交换两个变量的值】
C语言中的指针
案例:输入三个整数a,b,c,要求按由大到小的顺序输出。
C语言中的指针

指针与一维数组

一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占有存储单元,它们都有相应的地址,指针变量既然可以指向变量,当然也可以指向数组元素,把某一元素的地址放到一个指针变量中,所谓的数组元素的指针就是数组元素的地址。c语言定义数组时,编译器会分配连续地址的内存,来存储数组里的元素,实际上数组本质上也是指针。

分别打印了arr&arr[0]的地址,发现地址是一样的,既然是个地址,那就可以使用指针的形式,去访问地址里存储的值,然后打印*arr的值,得到的结果为1,正好和arr[0]的值对应,最后得出数组也可以使用指针的形式去使用。

数组元素的引用

        引用数组元素可以使用下标法,也可以使用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能使程序质量高,占用内存少,运行速度快。

	p = &a; // p的值是a[0]的地址
	p = a; // p的值是数组a首元素即a[0]的地址

C语言中的指针
注意:程序中的数组名不代表整个数组,只代表数组首元素的地址。上面的p = a的作用是把a数组首元素的地址赋给指针变量p,而不是把数组a各元素的值赋给p,可以简写为 int *p = &a[0];也可以写成int *p = a; 也可以写成两行的形式int *p; p = &a[0];他们都有一个作用:将a数组首元素即a[0]的地址赋给指针变量p(而不是*p)。
C语言中的指针

  • 下标法 a[i]的形式
  • 指针法如*(a+i)*(p+i)。其中a是数组名,p是指向数组元素的指针变量。
    C语言中的指针
    或者用指针变量指向数组元素
    C语言中的指针
    注意:在使用指针变量指向数组元素的时候,可以通过改变指针变量的值指向不同的元素,例如上面代码中的方法是使用指针变量p来指向元素,用p++使p的值不断改变宠儿指向不同的元素。
    换一种想法,如果不用p变化的方法而用数组名a变化的方法如a++行不行呢。for(p=a;a<(p+10);a++) printf("%d",*a);答案是不行的,因为数组名a代表数组首个元素的地址,他是一个指针型常量,它的值在程序运行期间是固定不变的,既然a是常量,所以a++是无法实现的。
    例:通过指针变量输出整型数组a的10个元素。
    C语言中的指针
    很明显,输出的数值并不是a数组中各个元素的值,因为在执行第二个for循环读入数据后,p已指向a数组的末尾,因此在执行第二个for循环时候,p的起始值不是&a[0]了,而是a+10
    C语言中的指针
    解决上面问题只要在第二个for循环之前加一个赋值语句即可
    C语言中的指针

常用指针引用数组元素的情况

  • (1) p++; p; p++使p指向下一个元素a[i],然后再执行p,则得到下一个元素a[i]的值。
  • (2) p++; 由于++和同优先级,结合方向为自右而左,因此它等价于*(p++)。先引用p的值,实现*p的运算,然后再使p自增1。
  • (3) (p++)与(++p)作用,前者先取p值,然后使p加1。后者是先使p加1,再取p。若初始值为a即&a[0],若输出*(p++)得到a[0]的值,而输出*(++p)得到a[1]的值。
  • (4) ++(*p) 表示p所指向的元素值加1,如果p = a,则++(*p)相当于++a[0],若a[0]的值为3,则在执行++(*p)即++a[0]后的值为4。
  • (5) 如果p当前指向a数组中第i个元素a[i],则:
            *(p--) 相当于a[i--],先对p进行运算(求p所指向的元素的值),再使p自减。
            *(++p) 相当于a[++i],先使p自加,在进行
    运算。
            *(--p) 相当于a[–i],先使p自减,再进行*运算。
    将++和–运算符用于指针变量十分有效,可以使指针变量自动向前或向后移动,指向下一个或上一个数组元素。

在引用数组元素时指针的运算

在引入数组元素时会遇到指针的算术运算,当指针指向数组元素的时候,譬如指针变量p指向数组元素a[0],我想用p+1表示指向下一个元素a[1],如果能实现这种运算会对引用数组元素提供很大的便利。
在指针指向一个数组元素时可以进行以下运算

  • 加一个整数用+或者+=,如p+1
  • 减一个整数用-或者-=,如p-1
  • 自加运算,如p++,++p
  • 自减运算,如p--,--p
  • 两个指针相减,如p1-p2 如果p1p2都指向同一个数组中的元素时才有意义。
      解释:
        (1)如果指针变量p已经指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p-1指向同一数组中的上一个元素,执行p+1时并不是将p的值或地址简单的加1而是加上一个数组元素所占用的字节数,例如数组元素是float型,每个元素占4个字节,则p+1意味着p的值是地址加上4个字节,使它指向下一个元素,p+1实际上代表p+1*dd是一个数组元素所占的字节数。
        (2)如果p的值为&a[0],则p+ia+i就是数组元素a[i]的地址,或者说他们指向a数组序号为i的元素。这里要注意的是a代表数组首元素地址,a+1也是地址,它的计算方法同p+1,即它的实际地址为a+1*d
        (3)*(p+i)*(a+i)p+ia+i所指向的数组元素,即a[i]。实际上在编译的时候,对数组元素a[i]就是按*(a+i)处理的,即按数组首元素的地址加上相对位移量得到要找的元素地址,然后找出该单元中的内容。
        (4)如果指针变量p1和p2都指向同意数组中的元素,如果执行p2-p1,结果是p2-p1的值两个地址之差,除以数组元素的长度。两个地址不能进行p1+p3是毫无意义的。
        
  • 指针的解引用:可以通过指针的解引用来访问它所指向的变量的值,解引用的操作符是"*",与乘法运算符不同。
    例如:
            int value = 10;
            int *p = &value;
            printf(“The value is %d”,*p); // 输出value的值
  • 指针的加减法:我们可以对指针进行加减操作,让指针移动到数组中的其他元素上。
            int arrPtr = arr;
            printf(“The second element is %d\n”,
    (arrPtr+1)); // 这里*(arrPtr+1)实际上就是指向数组第二个元素的地址,并解引用获取其值。
    值得注意的是,指针算数的步长取决于指针所指向的数据类型。例如arrPtr指向的是一个整型的指针,那么arrPtr+1实际上是在当前地址基础上加上了整型的大小。
  • 指针 - 指针 :得到的数值的绝对值是指针和指针之间元素的个数。
    C语言中的指针:掌握内存的钥匙

其他指针使用方法:

C语言中的指针

指针与一维数组地址关系

C语言中的指针
指针与一维数组值的关系
C语言中的指针

用数组名做函数参数的情况

第一种,数组元素做实参的情况,假设swap是将两个形参x,y进行交换使用

swap(a[0],a[1]);
void swap(int x,int y);

得出:与用变量作为实参的情况一样,是”值传递“方式,将a[0]a[1]的值单向传递给xy。当xy的值改变时a[0]a[1]的值不改变。

第二种: 数组名做函数形参的情况,实参数组名代表该数组首元素的地址,而形参是用来接受实参传递过来的数组首元素地址的。因此形参应该是一个只恨变量,只有指针变量才能存放地址,实际上c语言的编译都是将形参数组名作为指针变量来处理的。

void fun(int arr[],int n); === void fun(int *arr,int *n);

C语言中的指针
在函数被调用时,系统会在fun函数中建立一个指针变量arr,用来存放从主调函数传递过来的实参数组元素的地址。如果在fun函数中用运算符siezof确定arr所占字节数,可以发现sizeof(arr)的值为8,这就这个名了系统把arr作为指针变量来处理的。当arr接受了实参数组的首元素地址后,arr就指向实参数组的首元素,也就是指向了brr[0]。因此,arr就是brr[0]
注意: 实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。在函数调用进行虚实结合后,形参的值就是实参数组首元素的地址,在函数指向期间,它可以在被赋值。
C语言中的指针
常用这种方法通过调用一个函数来改变实参数组的值。

第三种:变量名做函数参数和用数组名做函数参数比较

  • 1.当实参类型是变量名时,要求形参类型也是变量名,通过形参传递的信息是变量的值,通过函数调用不能改变实参量的值。
  • 2.当实参类型是数组名时,要求形参的类型是数组名或者指针变量,通过形参传递的信息是实参数组首元素地址,通过函数调用能改变其实参变量的值。

总结 :说明c语言调用函数时虚实结合的方法都是采用“值传递”方式,当变量名作为函数参数时传递的是变量的值。当用数组名作函数参数的时候,由于数组名代表的是数组首元素的地址,因此传递的值是地址,所以要求形参为指针变量。

第四种:数组名和指针变量作为函数的形参,在c语言中,用下标法和指针法都可以访问一个数组,如果有一个数组a,则a[i]*(a+i)无条件等价,用数组名作形参,以便于实参数组对应,比较直观便于理解。从应用的角度看,用户可以认为有一个形参数组,他从实参数组哪里得到起始地址,因此形参数组与实参数组共占同一段内存单元,在调用函数期间,如果改变了形参数组的值,也就改变了实参数组的值,在主调函数中就可以利用这些已经改变的值。
例:将数组an个整数按相反顺序存放,用一个函数reversal来实现交换。实参用数组名a,形参可用数组名,也可以用指针变量。
C语言中的指针
上面代码中,在main函数中定义整型数组a,并赋初值,函数reversal形参数数组名为x。在定义reversal函数时,可以不指定形参数组x的大小。因为形参数组名实际上是一个指针变量,并不是真的开辟一个数组空间。reversal函数的形参n用来接受需要处理的元素个数。在main函数中有函数调用语句reversal(a,10);表示要求将a数组的10个元素颠倒排列。
改写上面代码 ,将reversal中的形参改成指针变量,函数reversal的形参有数组名x[]变为指针变量*x,相应的实参仍是数组名a,即数组a首元素的地址,将它传递给形参指针变量x,这个时候x就指向x[0]x+ma[m]元素的地址。
C语言中的指针
总结归纳:
C语言中的指针
C语言中的指针
C语言中的指针
C语言中的指针
例:用选择排序+指针方法对是个整数由大到小顺序排序:
C语言中的指针

通过指针引用多维数组

        指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素,多维数组的指针比一维数组的指针要复杂一些。

多维数组元素的地址

以二维数组为例:int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}a是二维数组名,a数组包含三行,即三个元素,每一行元素又是一个一维数组,它包含4个元素即4列元素。可以看出二维数组是数组的数组,即二维数组a是由3个一维数组组成的。
C语言中的指针
从二维数组角度来看,a代表二维数组首元素的地址,现在的元素不是一个简单的整型元素,而是由四个整型元素所组成的一维数组,因此a代表的是首行即序号为0的行的起始地址。a+1代表序号为1的行的起始地址。如果二维数组的首行的起始地址为2000,一个整型数据占4个字节,则a+1的值应该是2000+4*4=2016 因为第0行有4个整型数据。a+1指向a[1],或者说a+1的值是a[1]的起始地址。a+2代表a[2]的起始地址,它的值是2032
C语言中的指针
上面说了如何表示首行地址,那么a[0]是一维数组名,该一维数组中序号为1的元素的地址显然应该用a[0]+1来表示。此时a[0]+1中的1代表一列元素的字节数,即4个字节。a[0]的值是2000,a[0]+1的值是2004而不是2016,这是因为现在在一维数组范围内讨论问题的,正如有一个一维数组x,x+1是其第一个元素x[1]的地址一样。a[0]+0,a[0]+1,a[0]+2,a[0]+3分别是a[0][0],a[0][1],a[0][2],a[0][3]元素的地址,即(&a[0][0],&a[0][1],&a[0][2],&a[0][3])
C语言中的指针
一维数组与指针的时候已经说了a[0]*(a+0)无条件等价,a[1]*(a+1)无条件等价,a[i]*(a+i)无条件等价。因此a[0]+1*(a+0)+1都是&a[0][1]a[1]+2*(a+1)+2的值都是&a[1][2]。但是要注意不要将*(a+1)+2错写成*(a+1+2),后者相当于a[3]
        再深一步解析,既然a[0]+1*(a+0)+1a[0][1]的地址,那么*(a[0]+1)就是a[0][1]值。同理,*(*(a+0)+1)*(*a+1)也是a[0][1]的值。*(a[i]+j)*(*(a+i)+j)a[i][j]的值。*(a+i)a[i]是无条件等价。
        a[i]从形式上看是a数组中序号为i的元素。如果a是一维数组名,则a[i]代表a数组序号为i的元素存储单元。a[i]是一个有确定地址的存储单元。但是如果a是二维数组,则a[i]是一维数组名,它只能是一个地址,并不代表一个存储单元,也不代表存储单元中的值如同一维数组名只有一个指针常量一样。a,a+i,a[i],*(a+i),*(a+i)+j,a[i]+j都是地址,而*(a[i]+j)*(*(a+i)+j)是二维i数组元素a[i][j]的值。
下面是二维数组常用a的有关指针:
C语言中的指针根据上面解释,输出二维数组的有关数据【地址和元素的值】
C语言中的指针
案例:根据输入数组下标,显示二维数组中的值
C语言中的指针

用指向数组的指针作为函数参数

一维数组名可以作为函数参数,多维数组名也可以做函数参数,用指针变量作形参,以接受实参数组名传递来的地址,两种方法:

i. 用指向变量的指针变量。
ii. 用纸箱一维数组的指针变量。

例:一个班级,三个学生,各4门课,计算总平均分数以及第n个学生的成绩。
C语言中的指针
代码解释:先调用average函数求平均值。在函数average中,形参p被升为float *类型指向float型变量的指针变量,它的基类型是float型,实参用*score,即score[0]也就是&score[0][0],即score[0][0]的地址。把score[0][0]的地址传给p,使p指向score[0][0]。然后在average函数中使用p先后指向二维数组的各个元素,p每加1就改为指向score数组的下一个元素。
C语言中的指针
函数search的形参p的类型是float(*)[4],他不是指向整型变量的指针变量,而是指向包含4个元素的一维数组的指针变量。函数开始调用时,将实参score的值也就是数组0行的起始地址传给p,使p也只想score[0]
注意:实参与形参如果是真真类型,应当注意他们的基类型必须一致,不应把int *型的指针即数组元素的地址传递给int(*)[4]型(指向一维数组)的指针变量,反之亦然。
例:在上面的基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩。
C语言中的指针
代码分析:实参score和形参p的类型是相同的。在调用search函数时,p得到实参score的值,即score[0]的起始地址,也就是说p也指向score数组的第一行。然后p先后指向隔行包括每行学生的几门课的成绩。

通过指针引用字符串

字符串的引用方式

在c语言中,字符串是存放在字符数组中的,像引用字符串有两种方法

  1. 用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明%s输出该字符串。
    C语言中的指针
    实际上string[7]就是*(string+7),string+7就是一个地址,它指向字符“C”.
  2. 用字符指针变量指向一个字符串常量,通过字符指针变量引用字符串常量。
    C语言中的指针
    在上面代码中没有定义字符数组,只定义了一个char *型的字符指针变量string,用字符串常量"I love China!"对它初始化。c语言对字符串常量是按字符数组处理的,在内存中开辟一个字符数组用来存放该字符串常量,但是这个字符数组是没有名字的,因此不能通过数组名来引用,只能通过指针变量来引用。
    注意: 有人误认为string是一个字符串变量,以为在定义时把"I love China!"这几个字符赋给该字符串变量,这是不对的,在C语言中只有字符变量,没有字符串变量。string被定义一个指针变量,基类型为字符型。它指能指向一个字符类型数据,而不能同时指向多个字符数据,更不是把I love China!这些字符存放到string中,也不是把字符串赋给*string,只是把I love China! 的第一个字符的地址赋给指针变量string

例:将a串复制为字符串b,然后输出字符串b。
C语言中的指针
程序分析:程序中a和b都定义为字符数组,通过地址访问其数组元素。在for语句中先检查a[i]是否为'\0'。如果不等于'\0',表示字符串尚未处理完成,就将a[i]的值赋值给b[i],即复制一个字符串。

例:将a串复制为字符串b,然后输出字符串b。(用指针变量来处理)
C语言中的指针
代码解析p1p2时指向字符型数据的指针变量,现使p1p2分别指向字符串ab的第一个字符。*p1最初的值时字母“I”。赋值语句*p2 = *p1的作用是将字符串I赋值给篇所指向的元素,即b[0]。然后p1p2分别加上1,分别指向其下面的一个元素,知道碰到'\0'为止。

字符指针作函数参数

如果想把一个字符串从一个函数"传递"到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以引用改变后的字符串。

  1. 字符数组名作为函数参数
    例:用函数调用实现字符串的复制,用字符数组名作为函数参数。
    C语言中的指针
    程序分析:ab时字符数组,copy_string函数的作用是将from[i]赋给to[i],直到from[i]的值等于'\0'为止。
  2. 用字符型指针变量作实参
    C语言中的指针
    分析: 指针变量from的值时a数组首元素的地址,指针变量to的值时b数组的首元素的地址。他们作为实参,把a数组首元素的地址和b数组首元素的地址传递给形参数组名fromto(它们实质上也是指针变量)。
  3. 用字符指针变量作形参和实参
    C语言中的指针
    分析: 形参使用char *类型变量即字符指针变量,main函数中a时字符指针变量,指向字符串"I am a teacher"的首字符。b时字符数组,在其中存放了字符串"You arr a student."p是字符指针变量,它的值是b数组第一个元素的地址,因此也指向字符串"You are a student."的首字符。copy_string函数的形参fromto是字符指针变量,再调用copy_string的时候,将数组a首元素地址传递给from,把指针变量p的值即数组b元素的地址传给to。因此form指向a串的第一个字符a[0],to指向b[0],在for循环中,先检查from当前所指向的字符是否为'\0',如果不是,表示需要复制此字符,就执行*to = *from,每次将*from的值赋给*to,一致遇到'\0'结束。
    其实上面的字符串复制还可以写很多形式,如下
    C语言中的指针
    输出结果都是
    ![C语言中的指针(https://i-blog.csdnimg.cn/direct/6bdbe065eafa45ce928478f034eccabb.png)

调用函数时实参与形参的对应关系

C语言中的指针

使用字符指针变量和字符数组的比较

用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混淆,主要区别如下:

  • a.字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址),绝不是将字符串放到字符指针变量中。
  • b.赋值方式,可以对字符指针变量赋值,但是不能对数组名赋值。
  • c.初始化的含义,对字符指针变量赋初始值,数组可以在定义时对个元素赋初始值,但不能用赋值语句对字符数组中全部元素整体赋值。
  • d.存储单元的内容。编译时字符数组分配若干个存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元。
  • e.指针变量的值是可以改变的,而字符数组名代表一个固定的值(数组元素的地址)不能改变。
  • f.字符数组中各元素的值是可以改变的(可以不对他们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对他们再赋值)。
  • g.引用数组元素,对字符数组可以使用下标法(用数组名和下标)引用一个数组元素,也可以使用地址法(如*(a+5))引用数组元素a[5],如果定义了字符指针变量p,并使它指向数组a的首地址,则可以用指针变量带下标的形式引用数组元素(如p[5]),同样可以用地址法(如*(p+5))引用数组元素a[5],但是如果指针变量没有指向数组,则无法用p[5]*(p+5)这样的形式引用数组中的元素。这是若输出p[5]或者*(p+5)这样的引用形式引用数组中的元素。这时若输出p[5]*(p+5),系统将输出指针变量p所指的字符后面5个字节的内容。
  • h.用指针变量指向一个格式字符串,可以用它们替代printf函数中的格式字符串。
    C语言中的指针
    因此只要改变指针变量format所指向的字符串,就可以改变输入输出的格式。这种printf函数称为可变格式输出函数。如:
    C语言中的指针
    因此,用指针变量指向字符串的方式更为方便。

指向函数的指针

什么是函数的指针

       如果在c中定义了一个函数,在编译时会把函数的源代码转换为可只想代码并分配一段存储空间。这段内存存储空间有一个起始地址,也就是函数的入口地址,每次调用函数时都会从该地址入口开始执行此段函数的代码,函数名代表函数的其实地址,调用函数时从函数名导函数的其实地址,并执行函数代码。
       函数名就是函数的指针,代表着函数的起始地址,可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着指针变量指向该函数。如(int *p)(int,int); 定义p是一个指向函数的指针变量,它可以指向函数类型为整型且有两个整型参数的函数。此时指针变量p的类型用int(*)(int,int)表示。

用函数指针变量调用函数

如果想调用一个函数,除了可以通过函数名调用外,还可以通过指向函数的指针变量来调用该函数。
例:用函数求整数a和b中的最大值。
c语言指向函数的指针
通过指针变量调用它所指向的函数
通过指针变量调用它所指向的函数
上面两段代码,其中int(*p)(int,int);用来定义p是一个指向函数的指针变量,最前面的Int表示这个函数返回值是整型的,最后面的括号中有两个int,表示这个函数有两个int型参数。注意,*p两侧的括号不可省略,表示p先于*结合,是真真变量,然后在于后面的"()"结合,"()"表示是函数,即刻指针变量不是指像一般的变量,而是指向函数。
C语言中的指针:掌握内存的钥匙

如何定义和使用指向函数的指针变量

类型名 (*指针变量名)(函数参数列表); 
如:int(*p)(int,int);   // 这里的类型名是值函数返回值的类型。
    1. 定义指向函数的指针变量,并不意味着这个指针变量可以指向任何函数,它只能指向在定义时指定的类型的函数。如int (*p)(int)(int); 表示指针变量p可以指向函数返回值为整型且有两个整型参数的函数。
    1. 如果要用指针调用函数,必须先使用指针变量指向该函数。如 p = max;
    1. 在给函数指针变量赋值的时候,只需给出函数名而不必给出参数,如 p = max;,因为是将函数入口地址赋给p,而不牵涉实参与形参的结合问题。如果写成 p = max(a,b)就错误了,p =max(a,b);的作用是将调用max函数所得到的函数值赋给p,而不是将函数入口地址赋给p。
    1. 用函数指针变量调用函数时,只需将(*p)代替函数名即可(p为指针变量名),在(*p)之后的括号中根据需要写上真实实参。如 c = (*p)(a,b); 表示调用由p指向的函数,实参为a,b。得到的函数值赋给C,需要注意的是函数返回值的类型。从指针变量p的定义中可以知道,函数的返回值应该是整型的,因此将其值赋给整型变量c是合法的。
    1. 对指向函数的指针变量不能进行算术运算,如 p+n,p++,p--等运算是无意义的。
    1. 函数名调用函数,只能调用所指定的一个函数,而通过指针变量调用函数比较灵活,可以根据不同情况先后调用不同的函数。

例:输入两个整数,然后让用户选择1或2,选1时调用max函数,输出二者中的大数,选2时调用min函数,输出二者中的小数。
C语言中的指针:掌握内存的钥匙

用指向函数的指针作为函数参数

        指向函数的指针变量的一个重要用途是把函数的入口地址作为参数传递到其他函数。指向函数的指针剋作为函数参数,把函数的入口地址传递给形参,这样就能够在被调用的函数中使用实参函数。
        原理:有一个fun函数,它有两个形参x1,x2,定义x1,x2为指向函数的指针变量,在调用函数fun的时候,实参为两个函数名f1,f2,给形参传递的是函数f1f2的入口地址。这样在函数fun中就可以调用f1f2函数了。
在这里插入图片描述
fun函数中声明形参x1x2为指向函数的指针变量,x1指向的函数有一个整型形参,x2指向的函数有两个整型形参。ij是调用f1f2函数时所要求的实参,函数fun的形参x1x3指针变量在函数fun为被调用时不占内存单元,也不指向任何函数。在主函数调用fun函数时,把实参函数f1f2的入口地址传给形参指针变量x1x2,使x1x2指向函数f1f2,这时候在函数fun中用*x1*x2就可以调用函数f1f2(*x1)(i)就相当于f1(i)
用指向函数的指针作为函数参数
例:有两个整数a和b,由用户输入1,2或3。如果输入1,程序就给出a和b中的大者,如果输入2就给出a和b中的小者,输入三求a和b的和。

在这里插入图片描述
程序分析:在定义fun函数时,在函数首部使用int(*p)(int,int)声明形参p是指向函数的指针,该函数时整形函数,有两个整型形参,maxminadd都是已经定义的三个函数,分别用来实现求大数,小数,求和的功能。
用指向函数的指针作为函数参数

返回指针值的函数

        一个函数中可以返回一个整型值,字符串,实型值等, 也可以返回一个指针型的数据,即地址,概念与往常类似,只是返回值的类型时阵阵而已。如: int *a(int x,int y);a时函数名,调用它以后能得到一个int *型(指向整型数据)得指针,即整型数据的地址。xy时函数a的形参微整型。

定义返回指针的函数原型一般形式:

类型名 *函数名(参数列表)

例:有a个学生,每个学生有b门课程的成绩,要求在用户输入学生序号以后,能输出该学生的全部成绩,用指针函数来实现。
C语言中的指针:掌握内存的钥匙
函数search定义为指针型函数,它的形参p是指向包含4个元素的一维数组的指针变量,p+1指向score数组序号为1的行,*(p+1)指向10列元素。search函数中的p是指针变量,它指向float型便令而不是指向一维数组。main函数调用search函数,将score数组进行首行地址传给形参p(注意score也是指向行的指针,而不是指向列元素的指针)。k是要查找的序号,调用search函数后,main函数得到一个地址&[k][0]指向k个学生第0门课程,赋给p,然后此学生的四门课程成绩输出。

例:对于上面的学生,找出其中不及格课程的学生及其学号。
返回指针值的函数
程序分析:函数search的作用是检查一个学生有无不及格的课程,在search函数中的pointer是指向一维数组有四个元素的指针变量。pt指向float类型变量的指针变量,从实际参数传给形参p的是score+i,它是scorei行的首地址。
返回指针值的函数
search中,先使pt = NULL,即pt = 0;pt作为区分有无不及格课程的标志。若经过检查四门课中有不及格的,就使pt只想本行0列元素,即pt=&score[i][0];若无不及格则保持pt的值为NULL。将pt返回main函数,在main函数中,把调用search得到的函数值指针变量赋值给p。用if判断p是否等于*(score+i),若相等,表示所查的序号为i的学生有不及格的课程,p的值为*(score+i),即p指向i0列元素,就输出有不及格课程学生的四门课成绩,若无不及格,p的值为null不输出。
返回指针值的函数

指针数组和多重指针

一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中每一个元素都存放一个地址,相当于一个指针变量。如:int *p[4];由于[]比优先级高,因此p先于[4]结合,形成p[4]形式,这显然是数组形式,表示p数组有四个元素。然后再与p前面的结合,*表示此数组是指针类型的,每个数组元素相当于一个指针变量都可以指向一个整型变量。注意(*p)[4] 是指向一维数组的指针变量。

定义一维数组的一般形式:

类型名 * 数组名[数组长度]; ,类型名中应包括符号*,如 int *表示是指向整型数据的指针类型。

例子:将若干字符串按字母顺序由小到大输出。
在这里插入图片描述
程序分析:在main函数中定义指针数组name,他有五个元素,初始值分别是五个字符串的首字符的地址,sort函数作用是对字符串排序,sort函数的形参name也是指针数组名,接受实参传过来的name数组首元素即name[0]的地址。因此形参name数组和实参name数组指的是同一数组,用选择法对字符串排序,strcmp是系统提供的字符串比较函数,name[k]name[j]是第k个和第j个字符串首字符的地址。strcmp[name[k],name[j]) 的值如果name[k]所指的字符串大于name[j]所指的字符串,则此函数值为正值,若相等,则函数值为0,若小于则函数值为负值,if语句的作用是将两个串中的小的那个串的序号kj之一保留在变量k中。当执行完for循环后,从第i串到第n串这些字符串中,第k串最小,若k!=i就表示最小的串不是i串。故将name[i]name[k]对换,也就是将指向第i个字符串的数组元素的值与第k个字符串的元素的值兑换,也就是把他们的指向互换。

指向指针数据的指针变量

        已经了解了指针数组,也需要了解指向指针数据的指针变量,简称为指向指针的指针。如下图可以看到name是一个指针数组,它的每一个元素是一个指针型变量,其值为地址,name既然是一个数组,它的每一元素都应有相应的地址。数组名name代表该指针首元素的地址。name+iname[i]的地址。name+i就是指向指针型数据的指针。还可以设置一个指针变量p,它指向指针数组的元素,p就是指向指针类型数据的指针变量。
指向指针数据的指针变量

持续更新中。。。

  • 93
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值