指针的本质、指针变量的定义与初始化和间接寻址运算符

前言

指针是C和Cpp的重要特色,它为程序员提供了直接访问物理内存的手段。本文介绍了指针的一些基础知识,例如 指针到底是什么?如何定义一个指针变量及初始化间接寻址运算符。如有问题,欢迎看文的小伙伴提醒博主~

正文

指针的本质

在具体讲指针的本质之前,需要从内存的结构说起。
计算机访问内存的最小单位是字节(Byte),计算机为每个字节分配了一个地址,目的是实现对字节的正确读取。
举个例子:
对于一个4G内存的计算机系统,其内存地址为0 ~ (2^32) - 1(即4 * 1024 * 1024 * 1024 ,0~FFFFFFFF)。而我们知道,变量根据其数据类型会占有的一个或者多个字节,比如int型占有4个字节。而对于编译器来说,它把第一个字节的地址视为变量的地址,具体可以看下图:
在这里插入图片描述
int型变量a占有了地址为6000-6003这4个字节。因而我们可以认为,变量a的地址就是6000。
我们还可以这么认为:
6000指向了变量a对应的存储单元!这便是指针的由来。
因而,指针的本质就是内存地址。

指针变量的定义与初始化

1. CPU访问内存的两种方式

我们先介绍CPU访问内存的两种方式,进而引入指针变量的概念:
通常CPU在访问内存的时候有两种方式:直接寻址方式间接寻址方式

  • 直接寻址方式:在编程时直接给出变量的地址

例如,对变量执行scanf(“%d”, &a),通过取地址符“&”来告诉CPU int变量a的地址,然后从键盘中输入的值将被存放进地址为6000开始的四个字节中。
在这里插入图片描述

  • 间接寻址方式:在指令中不直接给出变量的内存地址,而将该变量的地址事先存放于某寄存器或者某内存单元中,在指令中给出的是存储了该变量的地址的寄存器或变量。

例如,可以先把变量a的地址6000存放在变量p中,CPU先通过读取p的内容,然后根据p的内容来间接地访问变量a。
在这里插入图片描述
其中,上文中提到的变量p便是一个指针变量。
我们可以总结一下:指针就是地址,而指针变量是存储地址的变量。有的时候会把指针变量简称为指针,具体含义需要自己去判断一下。指针变量不仅可以存储普通变量的地址,还可以存放结构体和数组的地址。

2. 指针变量的定义

// 定义一个指针变量的方法
int a, *p;
char c, *q;
// 定义多个指针变量的方法
float *p1, *p2;

上述a为int型变量,p为int型变量的指针变量(其类型为int *);c为char型变量,q为char型变量的指针变量(其类型为char *)。其中,指针类型变量指向的对象的数据类型叫做基类型,指针变量只能指向其基类型的变量,就比如int型指针变量只能保存int型变量的地址,而不能保存char型变量的地址。

3. 指针变量的初始化

与普通的auto型变量一样,未初始化和未赋值的指针变量中存储的值也是随机的。因而,在使用指针变量前,我们需要对其进行初始化为空指针或者令其指向一个变量

- 指针初始化时就指向一个确切的变量
// 可以在定义的时候就赋值
int a, *p = &a; // 把int型变量a的地址赋给p
// 可以使用格式控制符%p以十六进制无符号形式输出内存地址、指针变量的内容和指针变量的地址
printf("&a = %p, p = %p, &p = %p\n", &a, p, &p);

// 也可以先定义指针,然后再赋值
char b, *q;
q = &b; // 将char型变量b的地址赋给q

// 注意q前面不要*,这句代码的意义是将b的地址存放在q的内存区域中,
// 加上*后意义就不同了,因为 ‘*’ 是一个寻址的操作(下文的间接寻址运算符会细说),
// 则*q不是一个变量,它代表的是依据q中存储的地址来寻址。
// 或许你会有疑惑,但在上面的 “在定义时赋值” 的代码中是有*的,
// 但请注意,*p不是独立的,其实它是int *p,它是int *类型,int和*连在一起共同组成一种类型,
// 因而不能将两种定义方式混为一谈。

printf("&b = %p, q = %p, &q = %p\n", &a, q, &q);

//还可以查看指针变量所占用的字节大小
printf("sizeof(p):%d\n", sizeof(p));
printf("sizeof(q):%d\n", sizeof(q);

return 0;

在这里插入图片描述
我们可以看到不同基类型的指针变量所占用的字节都是相同的,都是4个字节。

此时,当p指向a时,scanf(“%d”, &a)和scanf(“%d”, p)的作用是一模一样的,都是将输入存入变量a。前者指将输入的数据直接存入变量a,后者指将输入的数据存入给由p指向的变量。
但是!!!
scanf(“%d”, p)和scanf(“%d”, &p)的意义是截然不同的。前者指的是将输入存入给p指向的变量,后者指的是将输入存入变量p,而指针变量的值是不能直接由输入赋值的。

- 指针初始化时置为空
int *p = NULL;

因而,如果使用这种初始化的方法,我们可以通过if( p != NULL )来判断某指针变量是否指向一个有效的变量,以确定是否要访问p指向的对象。

注意!!!
初始化时不能将除NULL以外的整数直接赋值给指针,如int * p = 100这种操作是不允许的,因为地址为100的内存单元未被操作系统分配,因而这样子定义是属于非法访问内存。

间接寻址运算符

C语言通过间接寻址运算符 ‘ * ’ 来访问指针指向的对象。其格式为 *指针

当指针p保留了某对象的地址后,*p就代表其指向的对象

    int a = 10, *p = &a;
    printf("%5d\n", a);

    a = a + 10;
    printf("%5d\n", a);

    *p = *p + 10;
    printf("%5d\n", a);
    printf("%5d\n", *p);
    printf("%5d\n", *&a);

    return 0;

在这里插入图片描述
可以看到,变量a ⇔ *p ⇔ *&a!

结语

如果有任何问题,都可以告诉我。
如果这些关于指针的内容能够帮助到你,我也会很开心的~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值