【C语言】指针

一、指针简介

1.什么是指针

指针是一个概念,是计算机内存地址的代名词之一。指针变量本身就是变量,存放内存的地址。在大多数情况下,指针变量简称为指针。

2.指针变量的定义

定义指针变量的一般形式为:类型名 * 指针变量名
①指针声明符*表明声明的变量是指针
②类型名表示所指对象的类型

例:
int *pi
定义了一个指针变量pi,指向整型变量。
char *cp
定义了一个指针变量cp,指向字符型变量。

注意:
①无论何种类型的指针变量,他们都是用来存放地址的,因此指针变量自身所占内存的大小它所指向的变量数据类型无关,尽管不同类型的变量所占内存空间不同,但是不同类型指针变量所占内存空间大小相同。
②指针声明符*不是指针的组成部分,如:int *p;说明p是指针变量,*p不是。
③指针的类型和它所指向变量的类型必须相同

3.指针变量的初始化

指针变量需要先赋值再使用,看下面代码:

int i,*p;

p = &i;
p = 0;
p = NULL;
p = (int*)1732;

①对于赋值的第一条语句:&把 i 的地址取出,赋给指针变量p,这是很常用的赋值方法。
②对于赋值的第二、三条语句:NULL在stdio.h的文件中有定义,其值为0,这两条语句把0赋给指针,代表该指针为空指针,不指向任何单元。
③对于赋值的第四条语句:使用强制类型转换(int*)来避免编译错误,表示p指向的地址为1372的int型变量。但是我们不建议把绝对地址赋值给指针,NULL除外。

注意:
①在指针变量定义或者初始化时变量名前面的*,只表示该变量是一个指针变量,它不是间接访问符。

②不能用数值作为指针变量的值。例: int *p = 100 ;(错误)

③可以用初始化了的指针变量给另一个指针变量作初始值。

④把一个变量的地址作为初始化值赋给指针变量时,该变量必须在此之前已经定义。因为变量只有在定义后才被分配单元,它的地址才能赋给指针变量。

4.指针类型的意义

(1)指针的类型决定了指针向前或者向后走一步有多大。(距离)
地址是按字节编址的,在C语言中,指针加1指的是增加一个储存单元。

(2)指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

5.指针的大小

(1)在32位机器上,地址是32 个 0 或者 1 组成二进制序列,那地址就得用 4 个字节的空间来存储,所以一个指针变量的大小就应该是 4个字节
(2)在64位机器上,一个指针变量的大小是8个字节

6.指针的运算

(1)指针+ -整数:
在指针类型的意义那里我们已经见过啦

(2)指针 - 指针: 结果的绝对值表示它们之间相隔的数组元素的数目,前提:两个指针指向同一块区域。

7.野指针

(1)野指针就是指针指向的位置是不可知的。
(2)野指针的成因:

①指针未初始化
②指针越界访问
③指针指向的空间释放

(3)规避野指针的做法:

①指针初始化
②小心指针越界
③指针指向空间释放即使置NULL
④避免返回局部变量的地址
⑤指针使用之前检查有效性

8.二级指针

(1)二级指针:指向指针的指针(指针变量也是变量,是变量就有地址,指针变量的地址存放在指针里面)

(2)一般定义为:类型名** 变量名
例:int** pa;
pa是一个指针,pa指向的变量是一个int*的变量

(3)二级指针的初始化

int a = 10;
int* pa = &a;//a的地址存在pa中,pa是一级指针
int** ppa = &pa;//pa的地址存在ppa中,ppa是二级指针

在这里插入图片描述

9.指针表示法和数组表示法

(1)一维数组
①arr[i] 等价于 *(arr+i),可以认为*(arr+i)的意思是“到内存的arr位置,然后移动i个单位,检索存储在那里的值”。

②arr + i 等价于 &arr[i],前面我们说过数组名是数组首元素的地址,所以arr是该数组首元素的地址,首元素的地址+i得到的是第i个元素的地址,即&arr[i]

注意:不要混淆*(arr+2)和*arr+2;前者的意思是arr第2个元素的值,后者的意思是arr第0个元素的值+2

(2)二维数组
假设有定义:int a[3][2];

①a:数组名是数组首元素的地址。我们可以把二维数组a看成是由a[0],a[1],a[2]组成的一维数组,而a[0],a[1],a[2]各自又是一个一维数组。因此数组名a是a[0](一个内含两个int值的数组)的首元素地址, a[0]是该数组首元素(a[0][0])的地址。所以我们可以知道两个等价关系:a 等价于 &a[0]; a[0] 等价于 &a[0][0]

②a[0]是该数组首元素(a[0][0])的地址,所以,*(a[0])等价于a[0][0]的值;与此类似,*a代表该数组首元素(a[0])的值,而a[0]本身又是一个int类型的地址,所以该值的地址为&a[0][0],换言之,a就是&a[0][0]。*a等价于&(a[0][0]),因为a就是首行首元素的地址,所以再对其进行解引用才能找到首元素的值。我们来捋一下:a即&a[0], a[0]即&a[0][0], 因此,我们可以得出&a[0] 等价于 &&a[0][0]。其实二维数组名相当于一个二级指针,而a[0]相当于一级指针。注意:二级指针和二维数组名是两码事

③由于a[i] 等价于 *(a+i),我们也可以得出a[i][j] 等价于 *( (a+i) + j)或者(a[i] + j)。


二、指针数组和数组指针

1.指针数组

其实C语言中数组可以是任何类型的,如果数组的各个元素都是指针类型,用于存放内存地址,那么这个数组就是指针数组。

(1)一维指针数组定义的一般格式为:类型名* 数组名[数组长度]

例:int* arr[5];
arr是一个数组,有五个元素,每个元素是一个整形指针。该数组的类型是int* [ ]。(去掉变量名就是类型)

(2)指针数组的初始化:

指针数组的各个元素是指针,用于存放地址,因此,我们可以用指针(地址)作为初始化内容,如:

//(1)
int arr[] = {1,2,3};
int* parr[]={arr};
 
//(2)
char* color[5] = {"red", "blue", "yellow", "green", "black"};
//字符串常量实质上是一个指向该字符串首字符的指针常量

2.数组指针

(1)数组指针:可以指向数组的指针

数组指针的一般形式为:类型名 (*指针变量名)[数组长度]。类型名指的是指针指向的数组的元素的类型。

如:int (*p)[10];
p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为10的数组,该数组的每个元素是一个整型。所以p是一个指针,指向一个数组,叫数组指针,该数组指针的类型为int(*)[10]。
在这里插入图片描述
(2)&数组名和数组名
在这里插入图片描述
①第一组的+4的解释:arr是数组首元素的地址,因为arr的类型是int*,所以arr+1跳过4个字节

②第二组的+4的解释:&arr[0]是数组首元素的地址,同样的,&arr[0]的类型也是int*,所以&arr[0]+1也是跳过4个字节

③第三组的+40的解释:&arr是数组的地址,它的类型是int(*)[10],一个指向大小为10的整型数组的指针,所以它+1,应该跳过一个数组的大小10*4=40。

(3)数组指针的使用

数组指针里面存放的是数组的地址。我们来看2个代码:
在这里插入图片描述
p指向的是arr整个数组,p[i]等价于*(p+i),当i = 1时,p+1跳过的是整个数组,所以*(p+i)访问的是随机值。

三、函数指针

1.函数指针定义的一般形式为:(类型名)(*变量名)(参数类型表)

类型名:指定函数的返回类型;变量名:指向函数的指针变量的名称

例:
int(*funp)(int,int);
定义了一个函数指针funp,它可以指向有两个整型参数且返回值类型为int的函数,该函数指针的类型为int(*)(int,int).

2.通过函数指针调用函数

(1)在使用函数指针之前,要先对它赋值。赋值时,将一个函数名赋给函数指针,但是该函数必须已经定义或声明,且函数返回值的类型要和函数指针的类型一样。(函数名和&函数名意义相同,都代表函数的地址)(函数指针用来存放函数的地址)

(2)函数调用的一般格式:

(*函数指针名)(参数表)

例:假设fun(x, y)已经有定义,现在要调用fun函数

int(*funp)(int,int) = fun;
 
fun = (3,5);
(*funp)(3,5);
//两者完全等价

3.函数指针作为函数参数:

C语言的调用中,函数名或已赋值的函数指针也能作为实参,此时,形参就是函数指针,它指向实参所代表函数的入口地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值