C语言指针——最简单粗暴的方式带你手撕各种指针!!!

我们常说c/c++的独特之处就在于它的指针,同时这部分的学习也是较为困难的,本篇文章就向大家分享一些关于指针的理解方式:

  1. 指针是什么?

我们可以将指针看作一个“指路牌”,其上明确地写着指向某一“地点”(即一个内存中的元素地址)的路,我们想要找到这个“地点”(即访问内存操作)可以直接通过这个“指路牌”快速找到。

那么将上述的话用稍微专业一点的术语表示则为:指针用于存放地址,地址唯一标识一块内存空间。

指针存储的是地址,计算机对信息的存储根本上是对一串正负电的排列(因为计算机通的就是交流电),这里我们以x32(32位)机器为例:

#include<stdio.h>
int main()
{
    int a = 0;//a的存储如下:
//00000000 00000000 00000000 00000000
    return 0;
}

32位机器中即保存32个电信号(用0和1表示),一个电信号的存储空间我们称之为1位(bit),x32即有32位,每8位称为一个字节。

计算机通过存储正负电信号,在内存中记下来,通过这种特定的1/0序列存储一个特定的信息(int型、char型等),而指针的作用就是将这种序列的“位置”存下来,所以也有一种说法是:

·指针就是地址,地址就是指针。

这种说法尽管有点小瑕疵,但是也可以作为一种初期理解方式。

  1. 指针的大小

指针本身也是一种变量,只不过指针的内容存的是其他变量的地址,所以指针本身大小一般也为32位,即4个字节,x64机器中就是64位,即8个字节,所以:

·指针的大小为4个字节或者8个字节

3.指针的种类

(1)字符/整形型指针

#include<stdio.h>
int main()
{
    int a = 0;
    //创建一个整形数据a
    char arr[] = {'a','b','c'};
    //创建一个字符数组arr
    char *parr = &a[0];
    //创建一个指针变量将arr中下标为0的元素地址存起来
    int *pa = &a;
    //创建一个指针变量将a的地址存起来
    return 0;
}

如上就是一个创建简单的字符型/整型指针,存储了相应的元素地址。

可以看到,对一个指针变量的创建与创建一个基本的元素类型是极其相似的,只不过多了一个符号

“ * ”,我们知道,将一个类型的名称去掉,剩下的就是它的数据类型,所以我们将变量名parr去掉,得到的即为:”char *“,所以char *就是字符型指针;将变量名a去掉,得到的int *就是整形指针。

同理,创建其他简单指针都是如此,在原有的类型后加上“ * ”即可。

(2)数组指针/指针数组

1、数组指针:

顾名思义,数组指针本质上还是一个指针,但是指向的是一个数组,也就是说,这个指针存放的是一个数组的地址,我们用如下例程来分析:

#include<stdio.h>
int main()
{
    int arr[5] = {1,2,3,4,5};//创建一个整型数组arr
    int (*parr)[5] = &arr;
    //创建parr指针指向数组arr
    return 0;
}

首先那我们得到了一个数组指针int (*parr)[5],那么这个指针是怎么得来的呢?

上文提到过,创建一个简单的指针时,在原有的类型加上“ * ”即可,且将一个类型的名称去掉,剩下的就是它的数据类型,那么arr数组的数据类型即为:

int [5]

那么创建该数据类型的指针加上“ * ”即可:

int(*) [5](加上括号是因为”[ ]“的结合优先级更高,但是我们这里需要名称与”*“结合)

之后再将指针的名字parr写进去即可:

int (*parr)[5]

这样就正确创建了一个“数组指针”,可以存放数组arr的地址。

2、指针数组:

指针数组,根据名称可以知道,首先是一个“数组”,其次是一个存放“指针”的数组,所以指针数组的数据类型是数组,那么就有如下代码:

#include<stdio.h>
int main()
{
    int a[5] = {1,2,3,4,5};
    int b[5] = {1,2,3,4,5};
    int c[5] = {1,2,3,4,5};//创建整型数组
    int *p[] = {a,b,c};
    //创建指针数组存放a、b、c三个数组的地址
    return 0;
}

因为指针数组的本质还是数组,所以这里我们以数组的视角来分析:

创建整型数组a的方式是int a[5],去掉名称a,数组的类型就是:

int [5]

含义为:数组内有五个元素,每个元素类型都是int,所以称为整形数组。

那么一个指针数组的含义就是:数组内的元素每个都是指针类型,那么我们就可以写出:

int *[5]

即为一个数组有五个元素,每个元素类型都是int *类型,那么再加上名称p,即为:

int *p[5]

(上文说过,”[ ]“的结合优先级更高,所以这里的p会跟后方的[]直接结合,形成一个数组 p[],一个整型数组)

这样就创建好了一个整形指针数组。

(3)函数指针

最后就是函数指针了,函数指针就是指向函数的指针,存放的是函数的地址,一般在我们编写“回调函数”等的时候会遇到(回调函数在文末介绍)。我们根据如下例程来分析:

#include<stdio.h>
int Add(int x, int y)
{
    int nt z = x + y;
    return z;
}//书写一个简单的相加函数
int main()
{
    int (*p)(int , int) = Add;
    //存放函数Add的地址
    return 0;
}

如上我们可以看到“int (*p)(int , int)”就是一个函数指针,接下来刨析这样的指针是怎样书写出来的:

int Add(int x, int y)是Add函数,我们去函数名称Add,形参变量名称x、y,就能得到它的数据类型:

int(int , int );

接下来要写一个这样类型的指针,与上文一样,在类型中加上“ * ”即可:

int (*)(int , int );

这样我们就写出了Add函数的指针类型,之后再加上指针名称p:

int (*p)(int , int );

这样,我们就写出了一个正确的函数指针。

以此类推,其他函数指针也只需声明清楚所要指向的函数类型即可。

(4)多级指针

一般情况下只会经常遇见二级指针,更高级的指针少见,所以我们以二级指针展开。

前边说过,指针也是一个变量,所以指针在内存中也有它自己的地址,而指针本身又是存储地址的,所以理论上来讲,我们可以用一个指针来存放另一个指针的地址,像这样的指针就称为二级指针

#include<stdio.h>
int main()
{
    int a = 0;
    int *pa = &a;//一级指针存储a的地址
    int **f = &pa;//二级指针,存储pa的地址
    return 0;
}

如上,有几级指针就可以用几次“ * ”来声明,三级指针就是

int ***fp = &f;

这就是高级指针的相关概念。

4.汇总

综上,在编写指针相关概念的时候,我们要秉持以下原则:

指针是一种数据类型,用于存放地址。

无论指针指向何种数据类型,一定要声明清楚指针所指向的类型。

创建指针时一定要有指向的具体类型(如暂定,则用NULL置空),野指针是非常危险的情况。

小tip:

回调函数相关概念:

将一个函数A传给另一个函数B,在B中用A的地址去调用A,就称A函数为回调函数。

~~~~~~点赞加关注,学c不迷路~~~~~~·

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值