【C语言】初阶指针详解


前言

   本文总结了初学C语言指针整理出的详细知识点和一些在学习过程当中遇到的问题。

一、指针是什么?

        要了解指针,我们先要知道内存的概念,计算机中数据就是存储在内存当中,而内存又被为了一块一块的,方便存储不同的数据,举个例子,将数据看作客人,那内存就是客人的房间,而房间上的号码牌就是内存的地址,我们的内存就这样被分为了一间间的房间,来让不同的数据存入。

        当我们想要使用数据的时候,就能通过房间号来访问到数据,那么这个房间号,也就是数据对应的地址,我们就用一个创建一个指针变量来存储它,可以理解为,指针变量就是用来存放数据的地址

二、指针类型

        既然指针是一个变量,那么每种变量都有其对应的数据类型,而指针变量的类型是可变的,它取决于指针所指向的数据的类型。正如刚才所说,酒店房间入住了不同的客人,即内存中存放的可以是char、short、int、float、double等等类型的数据,那么指针的类型也要在一定程度与其指向的数据类型相关联,才能让我们正确访问到数据。

        所以我们C语言语法规定,指针变量的类型只需要在其对应的数据类型变量名后面加上*,就是该指针的类型。如: 

 (1)char:指针的类型是char*
 (2)int:指针的类型是int*
 (3)float:指针的类型是float*
 (4)double:指针的类型是double*

        于是,我们只要用取地址符&取到对应变量的地址,存放到指针里面去,就完成了一个指针变量的创建

         取地址运算符&:单目运算符&是用来取操作对象的地址。例:&i 为取变量 i 的地址。对于常量表达式、寄存器变量不能取地址(因为它们存储在存储器中,没有地址)

        (1)char*类型指针:char ch='a';    char* pch=&ch;//pch即是一个指向char类型数据的指针

        (2)int*类型指针:int i=1;    int* pi=&ch;//pi即是一个指向int类型数据的指针

        (3)float*类型指针:float f=1.0;    float* pf=&f;//pf即是一个指向float类型数据的指针

        (4)double*类型指针:double d='a';    double* pd=&d;//pd即是一个指向double类型数据的指针

        而当我们想要访问到这个指针所指向的数据时,我们就可以用解引用符来使用数据。


        指针运算符*:与&为逆运算,作用是通过操作对象的地址,获取存储的内容。例:*pi=i=1、

*pf=f=1.0。

三.指针自身的内存

        当指针变量创建成功后,那么我们也需要在内存当中开辟一块空间来存放这一指针变量,可以将指针也看作酒店的一位客人,他也是需要入住一个房间的,那么问题就来了,那这个指针变量的指针怎么表示呢?

四.一级指针和二级指针

        其实,我们最开始创建的int*、char*类型的指针,这些指针都被叫做一级指针,这类指针是用来存放除了指针变量以外的变量的地址,而二级指针就是专门用来存放指针的地址。二级指针变量的类型只需要在一级指针的基础上,多加一个*号在原来*后面。如:

        (1)char** ppch=&pch        ppch是一个指向char*类型数据的二级指针

        (2)int** ppi=&pi        ppi是一个指向int*类型数据的二级指针

        (3)float** ppf=&pf        ppf是一个指向float*类型数据的二级指针

        (4)double** ppd=&pd        ppd是一个指向double*类型数据的二级指针

        同理,我们也可以用解引用操作符(*)来对二级指针进行解引用操作,访问到之前的一级指针。而指针还可以为三级、四级指针,创建方法与二级指针的创建方法一样,但一般应用场景很少用到更高级的指针。

5.指针的运算

1.赋值

        指针里面存放的地址,既可以通过取地址符来取得,还可以直接赋予,或者是两个指针之前互相传递。

int *px, *py, *pz, x = 10;
//赋予某个变量的地址
px = &x;
//相互赋值
py = px;
//赋值具体的地址
pz = 100;

        2.指针加减整数

             指针加减一个整数,表示指针向前或向后移动整数个单位,而这个单位取决于指针指向的数据的大小。如:int *类型指针指向的是int类型的变量,而int类型的变量大小是四个字节,那么指针加1,就是向前移动四个字节,相当于跳过了一个int类型的数据。char* 类型指针指向的是char类型变量的指针,那么指针加1,就是向前移动一个字节,相当于跳过了一个char类型的数据。、

        通过指针的加减操作,我们可以用指针来遍历一个数组,我们可以先让指针指向数组的首元素,然后对指针进行解引用操作,就可以访问到首元素,再让指针加1,指向下一个元素,再解引用,访问到下一个元素,重复这个操作就能一直访问到最后一个元素。


int arr[10]={0};//创建存放十个int类型变量的数组
int *ptr=arr;//arr数组名是数组首元素的地址
for(i=0;i<10;i++)
{
    (*ptr)++;//让数组中的元素每次自增1,最后会得到1,2,3,4,5,6,7,8,9,10
    ptr++;
}

        注意:数组名可以看作是一个指针,因为它其实存放的就是数组首元素的地址,而数组名只有两种特殊情况下表示的含义不是数组首元素的地址。

        1.用取地址符取一个数组的地址,即&arr,此时取的是整个数组的地址,这个地址的数值与数组首元素的地址是相同的,但如果将arr当做一个指针,那么这个arr指针能操作的只是数组首元素,而如果是将&arr赋给一个指针,那么这个指针能操作的是整个数组,再具体一点,arr对应的指针+1只能越过一个数组元素,但&arr对应的指针+1却能越过整个数组,只是因为arr和&arr的起始地址都是数组首元素的地址,所以他们的值才会相同,但表示的意义却不同。

        2.用sizeof(arr)函数计算数组大小时,此时的arr表示的也是整个数组,而不是数组首元素的地址,sizeof(arr)计算出来的是整个数组的大小,而不是首元素的大小。

        6.指针的安全问题

        指针越界访问

        其实,从上面举的遍历数组的例子就能发现一个问题,那就是我只开辟了一个能存放十个int类型变量的数组,那如果这个指针一直加1,直到超过了十个整形,到了数组外面怎么办,此时就会发生越界访问的情况,如果数组后面四个字节存放了很重要的信息,此时通过解引用操作将其改变了,程序就会崩溃,所以要避免指针越界访问。

        再举个例子:

char s='a';
int *ptr;//创建一个int*类型的指针,解引用能访问4个字节
ptr=(int *)&s;//将s的地址赋给ptr指针,ptr是int*类型指针,所以要先强制类型转化
*ptr=100;//将100赋给s

        当创建了变量s,因为它是char类型的,那么内存中只会开辟一个字节的空间来存储s,而当我们创建一个int*类型的指针并存放s的地址,那么此时ptr是指向s的一个int*指针,但当ptr解引用时,访问是一个int变量的大小,也就是4个字节,此时再将100赋给这四个字节,那么原来不属于s的后面三个字节也被随之改变了,同理,这三个字节有可能存放很重要的信息却被我们非法篡改了,这可能会造成系统崩溃。


总结

        1.指针是一个专门用来存放地址的变量,可以通过取地址符(&)取到变量的地址赋给指针,也可以通过解引用符(*)对指针进行解引用操作,访问到原变量。

        2.指针的类型要与指向的数据类型相对应,一级指针在数据类型名后面加上一个*,二级指针加上两个*,更高级的指针,以此内推。

        3.指针加减整数,就是指针向前向后移动整数个单位,单位大小取决于指针指向的数据类型大小。

        4.在使用指针的过程要避免指针越界访问的安全问题,避免系统错误。

评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值