C语言指针详解

目录

一、什么是指针?

二、指针类型和指针大小

三、指针运算

四、特殊指针

1.野指针

2.字符指针

 3.指针数组与数组指针

4.二级指针

5.函数指针与函数指针数组

6.指向函数指针数组的指针

五、回调函数


一、什么是指针?

指针是C语言的重要特点,正是由于指针的存在,C语言才能完成各种对内存的操作。那么,什么是指针呢?

内存被划分成一个个内存单元,每个内存单元的大小是1字节,为了实现对内存的操作,每一个内存单元都有一个编号,就想楼栋的门牌号一样,称为地址。指针就是一种储存地址的变量。如例1:

例1

 通过&取出变量的地址赋给指针变量;通过解引用操作符(*)找到指针所指向的内存。

二、指针类型和指针大小

在学习C语言时,我们了解了不同的数据类型:整数型(int)、浮点型(float)、字符型(char)等等,现在如果问例1中指针p的类型,我们根据已有知识,就会猜测指针p的类型是int* ,事实确实如此。

知道了指针类型,我们进而联想指针大小,用sizeof可以计算数据的大小。如例2:

例2

 看到输出结果是4,会不会就有人得出结论:指针大小和指针类型有关(X)。先不说这个结论对不对,我们再看几个例子:

例3

 例3的结果我们可以看到,全是4,好了这就可以推翻之前的猜测:指针的大小和指针类型无关!所以指针大小究竟怎么算呢?

指针大小和硬件平台有关,在32位平台下,指针大小是4个字节;64位平台下,指针大小是8个字节。由此可知,例3中使用的平台是32位的,大家也可以自己敲敲代码,看看自己的平台是32位还是64位。

指针类型的意义:

  1. 指针类型决定了解引用操作的权限,如例3中由于pn类型是int*,解引用操作时只能对pn指向的内存向后数4个字节解引用。
  2. 指针类型决定了指针+/-整数时跳过的步长。

三、指针运算

提到运算,我们自然想到加减乘除,不过指针的运算只有三种:指针+/-整数,指针-指针,指针的关系运算。

1.指针+/-整数,如例4:

例4

 例4中指针pa一开始指向的是数组首元素的地址,由于指针pa属于int*,(pa+1)就是pa向后跳了4个字节,指向arr[1]的地址,指针类型决定了指针+/-整数时跳过的步长,依托循环,pa可以遍历数组。

2.指针-指针

指针-指针计算的是两个指针之间的元素个数,如例5:

例5

 3.指针的关系运算

指针变量之间的关系运算,指的是指向相同数据类型的指针之间进行的关系运算,不同类型的指针之间,或者指针与非0整数之间的比较是没有意义的。0就是空指针(NULL),指针可以和0比较,也可以被初始化成NULL。

指向数组元素的指针可以和指向数组最后一个元素后面的指针比较,但是不能和指向数组首元素前的内存位置的指针比较。

 指针的运算发生在同类型的指针之间,多见于数组。

四、特殊指针

1.野指针

野指针就是指针指向的位置是不可知的、不正确的、随机的。如例6:

例6

 例6中的指针没有初始化,指向的位置是未知的,属于野指针。

野指针产生的原因:

从野指针的定义我们可以得出以下:

  1. 未初始化的指针;
  2. 访问数组时越界的指针;
  3. 指针指向的空间释放,没有把对应指针置NULL。

知道野指针产生原因,我们同样可以从这几方面防止野指针,并在使用指针前查看指针有效性。

2.字符指针

指针类型中有一种叫字符指针,例如:

char c = 'a';
char* p = &c;

除了指向字符,字符指针还能指向字符串,此时字符指针存储的是字符串首元素的地址

char *p1 = "Hello world";
char arr[20] = "Hello world";
char* p2 = arr;

上面的代码中,p1和p2是不同的,p1指向的是常量字符串;p2指向的是被常量字符串初始化的变量。

 3.指针数组与数组指针

指针数组,顾名思义,它首先是一个数组,不过这个数组存储的元素是指针类型的,例如下:

int* p1[10];  //p1是一个存储了10个int* 类型元素的数组

数组指针就是指向数组的指针,如下:

int (*p2)[10];  
//p2首先是一个指针,所以用括号括起来
//p2是一个指向有10个int元素的数组的指针

数组名和&数组名:

int arr[10] = { 0 };

对于arr,我们知道它既是数组名也是数组首元素地址,通过它,我们可以访问这个数组。那么,&arr呢?

&arr就是整个数组的地址,也是数组指针的赋值。&arr和arr的值是相同的,它们的意义却不同。

4.二级指针

指针变量也是变量,也存在地址,指针变量的地址就存储在二级指针中。通过对二级指针的解引用,我们可以实现对指针的改变。

int num = 10;
int* p = #
int** pp = &p;  //pp是指向指针p的二级指针

在函数参数的传递过程中,有时我们需要对参数的地址做出改变,这时就需要用到二级指针,就想我们在写一个交换两个数的函数,把指针作为参数一样。

5.函数指针与函数指针数组

函数指针就是指向函数的指针。如下:

int (*p3)(int ,int);
//p3是一个指向参数是(int,int),返回值也是int的函数的指针

函数名就是函数地址,也是函数指针。

函数指针数组是存储函数指针的数组。可以通过访问函数指针数组的元素,进而使用元素指向的函数。

int (*p4[10])(int, int);
//p4是一个函数指针数组,这个数组有10个元素,
//每个元素都是一个指向参数是(int, int),返回值是int的函数指针

6.指向函数指针数组的指针

可以先写出函数指针数组,再写指向它的指针。

int (*(*p5)[10])(int, int);
//p5是一个指向函数数组的指针
//p5的类型是int (*(*)[10])(int, int)

五、回调函数

有一个指向函数A的指针,当我们把这个指针作为函数B的参数去调用函数A时,函数A就称为回调函数。

#include<stdio.h>

void F1(char x, char y)
{
	//……
}

//F2的参数是函数指针
void F2(void (*p)(char,char))
{
	//……
}

int main()
{
	void (*pf)(char, char) = F1;//pf是指向F1的指针
	
	//main函数通过参数将F1的指针传给F2,F2调用F1,F1被称为回调函数
	F2(pf);

	return 0;
}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值