C语言从“hello world”到深入【第四节·指针相关重难点】

本文详细介绍了C语言中的指针概念、声明与定义、野指针的避免、内存占用、指针操作(如加1移动)、二级指针、指针数组、数组指针、函数指针及其应用,包括函数指针数组的使用和其在编程中的重要作用。
摘要由CSDN通过智能技术生成

目录

认识指针

指针变量的声明

指针定义的格式

指针使用的符号

野指针

指针占用内存大小的问题

指针加1移动大小问题

二级指针

二级指针的定义格式

指针数组

指针数组:

指针数组的定义格式

指针数组与变量之间的关系

数组指针

数组指针的含义

数组指针的定义格式

数组指针占用内存大小及加1问题

指针函数和函数指针

指针函数

函数指针

总结:

函数指针数组

函数指针数组的定义格式


认识指针

    在C语言中,指针是一种变量,其值为另一个变量的地址。简单来说 指针也就是内存地址,指针变量是用来存放内存地址的变量,您必须在使用指针存储其他变量地址之前,对其进行声明。

指针变量的声明

要声明一个指针变量,需要在变量名前加上星号(*)表示指针。
例,int *p; 声明了一个名为 p 的整数指针。

指针定义的格式

<存储类型> <变量类型> *变量名;
例:
int *p; /* 一个整型的指针 */
       double *p; /* 一个 double 型的指针 */
       float *p; /* 一个浮点型的指针 */
       char *p; /* 一个字符型的指针 */

指针使用的符号

*:取地址中的值

&:对某个变量取地址

实例:使用指针输出变量的地址;

#include <stdio.h>

int main(int argc, const char *argv[])
{   
	int a = 10;//定义一个整形变量a,初始化为10;
	int *p; //定义一个整形指针p,
	p = &a;//将p指向变量a的地址

	printf("%p\n",p);//打印p指向变量a的地址;
	printf("%d\n",*p);//打印p指向变量a的值;

	
	return 0;
}

输出的结果为
0x7ffdc90be07c     //a存放的地址
10    //a的值

野指针

指的是一个指针变量指向了一个它不应该访问的内存位置,或者该内存位置已经被释放,但指针仍然保留着原来的地址。野指针可能会导致程序崩溃。所以在使用指针时应避免野指针的出现;
以下是导致野指针的常见情况:

内存释放后继续使用:当使用malloc(或其他动态内存分配函数)分配的内存被free释放后,如果指针没有立即设置为NULL,它将变成一个野指针。

局部变量的地址被返回:如果一个函数返回一个局部变量的地址,并且调用者试图通过这个地址访问该变量,那么它将成为野指针,因为局部变量在函数返回后会被销毁。

数组越界:当访问数组元素时超出了数组的边界,可能会得到一个指向无效内存的指针,这也可能导致野指针的情况。

指针未初始化:如果一个指针变量在使用前没有被初始化,它将包含一个随机的内存地址。
 

如何避免野指针

初始化指针:在声明指针变量时,最好立即将其初始化为NULL,以防止在分配内存之前意外使用它。

设置指针为NULL:在释放内存后,立即将指针设置为NULL。。

避免返回局部变量的地址:确保不要从函数中返回局部变量的地址,除非这个地址指向的是动态分配的内存或全局/静态变量。

检查数组边界:在访问数组元素之前,始终检查索引是否在有效范围内。

指针占用内存大小的问题

在32位系统上:所有的指针都是占用4字节

在64位系统上:所有的指针都是占用8字节

指针加1移动大小问题

指针加1移动的大小是它指向类型的大小。如

char *p; p+1移动1个字节

int *q;q+1移动4个字节

二级指针

在C语言中,二级指针(也称为指向指针的指针)是一个指针变量,它存储了另一个指针变量的地址。换句话说,它是一个指针,其指向的目标是另一个指针。

二级指针的定义格式

<存储类型> <变量类型> **变量名;

例:
int **p;

char **q;
说明:二级指针是指向一级指针地址的指针,

c语言中二级指针的作用;

  • 当你需要改变一个指针的值时,必须使用二级指针。
  • 当你需要修改一个值时,可以通过传递指针来达到目的。
  • 当你需要修改一个指针时,需要使用指针的指针(二级指针)。
  • 二级指针作为函数参数的作用是在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效。

二级指针代码

#include <stdio.h>

int main(int argc, const char *argv[])
{   
	int a = 10;//定义一个整形变量a;
	int *p = &a; //定义一个指针p指向a的地址;
	int **q = &p; //定义一个二级指针q,指向p的地址

	printf("a=%d,*p=%d,**q=%d\n",a,*p,**q);//打印未更改的值;
	printf("&a = %p,p = %p,&p = %p,q = %p,&q = %p\n", &a, p, &p, q, &q);//打印未更改的地址

	**q = 20;//这里将二级指针指向的值更改为20;
	printf("a=%d,*p=%d,**q=%d\n",a,*p,**q);//打印更改后的值;
	printf("&a = %p,p = %p,&p = %p,q = %p,&q = %p\n", &a, p, &p, q, &q);//打印更改后的地址
	
	return 0;
}

运行结果

指针数组

指针数组:

指针数组:指针数组就是数组,数组内部的每个成员都是指针。
指针数组存储了一组指针,每个指针可以指向不同的数据对象。

指针数组的定义格式

char *a[3];

int *b[3];

指针数组与变量之间的关系

实例:

#include <stdio.h>

int main(int argc, const char *argv[])
{
	int a=10,b=20,c=30;//定义3个整型变量;
	int *ptr[3]={&a,&b,&c};//定义一个数组指针并赋初始值为a,b,c的值;

	for(int i=0;i<3;i++){
	  printf("%d\n",*ptr[i]);
	}
	return 0;
}

运行结果

10
20
30

指针与一维数组

当定义一个一维数组时,系统会在内存中为改数组分配一个储存空间,其数组的变量名就是数组的首地址。若定义一个指针变量,并将数组的首地址赋值给指针变量,则我们说该指针指向了这个一维数组。

把 ptr 声明为一个数组,由 3个整数指针组成。ptr 中的每个元素,都是一个指向 int 值的指针。下面的实例用到了三个整数,它们将存储在一个指针数组中,如下所示:

#include <stdio.h>

int main(int argc, const char *argv[])
{
	int arr[3]={10,20,30};//定义一个整形数组;
	int *ptr[3];//定义一个指针数组;
	for(int i=0;i<3;i++){
	  ptr[i] = &arr[i];
	}

	for(int i=0;i<3;i++){
	  printf("arr[%d]=%d\n",i,*ptr[i]);
	}
	return 0;
}

运行结果

arr[0]=10
arr[1]=20
arr[2]=30

数组指针

数组指针的含义

它是一个指针,指向数组;本质是指针

数组指针的定义格式

<存储类型> <变量类型> (*变量名)[成员个数];

例:

char (*a)[3]; //指向一个数组,数组有3个元素,每个元素是一个char型;

int (*b)[3];//指向一个数组,数组有3个元素,每个元素是一个int型整数;

long (*c)[3]; //指向一个数组,数组有3个元素,每个元素是一个long型;

数组指针占用内存大小及加1问题

所有的数组指针在64位系统上都是占8字节;

指针加1移动的大小是它指向类型的大小;

char (*a)[3]; //它的类型是 char (*)[3],这个指针加1移动的大小是3*sizeof(char)

int (*b)[3]; //它的类型是 int (*)[3],这个指针加1移动的大小是3*sizeof(int)

代码实例

#include <stdio.h>

int main(int argc, const char *argv[])
{
	int arr[5]={1,2,3,4,5};//定义一个整型数组arr
	int *ptr = arr;//定义一个指针指向数组;

	printf("ptr=%ld arr=%p\n",sizeof(ptr),arr);//打印ptr所占内存大小和arr的首地址;

	for(int i=0;i<5;i++){
	   printf("arr[%d] ptr=%p\n",*ptr,ptr);//打印次数,以及数组指针加1的地址;
	   ptr = ptr+1;//数组指针加1;
	}
	return 0;
}

运算结果

ptr=8 arr=0x7ffc97b25620
arr[1] ptr=0x7ffc97b25620
arr[2] ptr=0x7ffc97b25624
arr[3] ptr=0x7ffc97b25628
arr[4] ptr=0x7ffc97b2562c
arr[5] ptr=0x7ffc97b25630

指针函数和函数指针

指针函数

指针函数实质是一个函数。
函数都有返回类型,函数的返回值类型是指针的函数就是指针函数

指针函数定义格式:类型名 *函数名(函数参数列表);

char *a(char *dest,const char *src)

int *func(void)

指针函数使用实例

#include <stdio.h>
int *p(){ //定义一个指针函数;
  static int arr[5]={11,22,33,44,55};
  return arr;

}

int main(int argc, const char *argv[])
{
	int *ptr=p();//调用指针函数获取数组地址;
	for(int i=0;i<5;i++){
	 printf("%3d",ptr[i]);//循环打印数组中的元素;
	}
	printf("\n");
	return 0;
}

运行结果
 11 22 33 44 55

函数指针

函数指针是指向函数的指针变量。

通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

函数指针定义格式:类型名 (*函数名)(函数参数列表)
int (*pfun)(int, int);

函数指针使用实例

#include <stdio.h>

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

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

int main(int argc, const char *argv[])
{
	int (*fp)(int,int);//声明一个函数指针变量;
	fp = add;//将fp指向add函数
	printf("%d+%d=%d\n",2,3,fp(2,3));//使用函数指针调用add函数;
	fp = subtract;
	printf("%d-%d=%d\n",5,3,fp(5,3));//使用函数指针调用subtract函数;
	return 0;
}

运行结果
2+3=5
5-3=2

总结:

  • 函数指针:是一个指针变量,其值为函数的地址,可以通过它来间接调用函数。
  • 指针函数:是一个函数,其返回值为指针(通常是地址),这个指针可以指向任何数据类型(数组、结构体、基本数据类型等)。
  • 函数指针主要用于实现回调函数、函数表等高级编程技巧。
  • 指针函数主要用于返回地址或引用,例如返回数组或动态分配的内存地址。

函数指针数组

它是一个数组,数组中的成员是函数指针。

函数指针数组的定义格式

<存储类型> <返回值类型> (*数组名[成员个数])(参数列表);

int (*a[2])(int ,int );

函数指针数组实例

#include <stdio.h>
int add(int a,int b){
    return (a+b);
}

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

int main(int argc, const char *argv[])
{  int (*fa[2])(int ,int );//定义函数指针数组fa;
	fa[0] = add;//将fa第1个元素指向函数add;
	fa[1] = subtract;//将fa第2个元素指向函数subtract;
	for(int i=0;i<2;i++){
	  printf("%d\n",fa[i](30,10));
	}
	
	return 0;
}

运行结果
40
20

在C语言中,函数指针数组是一种特殊类型的数组,其元素是指向函数的指针。函数指针数组允许你存储多个函数的地址,并在运行时根据需要使用这些函数。以下是函数指针数组的一些作用:

  1. 实现多态性
    通过函数指针数组,你可以根据不同的条件调用不同的函数,实现类似多态的行为。

  2. 实现回调函数机制
    函数指针数组可以作为回调函数机制的一部分。

  3. 简化代码结构
    使用函数指针数组可以将多个功能相似的函数组织在一起,通过数组索引来调用它们,从而简化代码结构,提高代码的可读性和可维护性。

  4. 实现函数表
    函数指针数组可以看作是一种函数表,其中每个元素都指向一个具有相同签名的函数。通过遍历这个数组,你可以调用不同的函数来执行不同的任务。

  5. 动态函数选择
    在某些情况下,你可能需要根据运行时的条件动态地选择调用哪个函数。

  6. 代码扩展性
    使用函数指针数组可以方便地向系统中添加新的函数,只需要将新函数的地址添加到数组中即可,而无需修改其他部分的代码。

本节完!
下一节主要为c语言部分的知识点以及C语言总结,下一节会总结c语言中的每个知识点,以及前四节未写的部分!icon-default.png?t=N7T8http://t.csdnimg.cn/wncMJ

有问题或者错误反馈请联系邮箱:z1600306511@163.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倾~~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值