write in front :

个人主页 : @啊森要自信的主页

✨ 作者寄语 : 小菜鸟的力量不在于它的体型,而在于它内心的勇气和无限的潜能,只要你有决心,就没有什么事情是不可能的。

欢迎大家关注点赞收藏⭐️留言>希望看完我的文章对你有小小的帮助,如有错误,可以指出,让我们一起探讨学习交流,一起加油鸭。

文章目录

  • 开端
  • 一、内存和地址
    • 1.1 内存
    • 1.2 怎么理解编址呢?
  • 二、 指针变量和地址
    • 2.1 取地址操作符(&)
    • 2.2指针变量和解引⽤操作符(*)
      • 2.2.1 如何拆解指针类型
      • 2.2.2 解引⽤操作符
    • 2.3 指针变量的⼤⼩
  • 三、 指针变量类型的意义
    • 3.1 指针的解引⽤
    • 3.2 指针+ – 整数
    • 3.3 void* 指针

开端

C语言中的指针是一种特殊的变量,它存储了一个内存地址,该地址指向另一个变量的位置。指针允许程序直接访问和操作内存中的数据,而不需要将数据复制到另一个位置

指针在C语言中具有重要的作用,它可以用于动态内存分配、数组和字符串操作、函数传递参数等方面。通过指针,程序可以更灵活地处理内存中的数据,提高了程序的效率和性能。

看到这里,你可能会想到指针竟然有这么多的用处,但是我都不会呀?接下来博主带你一起解开指针的面纱,体会不一样的指针!


一、内存和地址

1.1 内存

在学习内存和地址之前,我们想想这个:

当我们早八要去教室上课时,我们冲到教学楼,我们怎么找到我们上课的教室呢” />

有了这个教室门牌号,你就可以立马跑上去,快速找到,并签到,不会迟到。

接下来,我们把上面的案例对比我们的计算中,又会怎么样呢?

CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,买电脑的,电脑上的内存8G/16G/32G等,这些内存空间是怎么高效的管理我们的数据的呢?

在内存中,内存划分为一个一个内存单元,每个内存单元的大小取1个字节一个字节有多大呢?
计算机中常⻅的单位(补充):
⼀个⽐特位(bit)可以存储⼀个2进制的位1或者0

bit - ⽐特位byte - 字节KB - 千字节MB -兆字节GB - 千兆字节TB - 千千兆字节PB - 拍字节
  1. 1byte = 8bit
  2. 1KB = 1024byte
  3. 1MB = 1024KB
  4. 1GB = 1024MB
  5. 1TB = 1024GB
  6. 1PB = 1024TB

每个内存单元也都有⼀个编号(这个编号就相当于教室的⻔牌号),有了这个内存单元的编号,CPU就可以快速找到这个内存空间。

生活中我们把门牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫:指针。

所以我们可以理解为:
内存单元的编号 == 地址 == 指针

1.2 怎么理解编址呢?

CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节很多,所以需要给内存进行编址(就如同教室很多,需要给教室编号一样)。

小知识来了:计算机中的编址,并不是把每个字节的地址记录下来,⽽是通过硬件设计完成的。也就是说计算中的编址,不是把整个地址编号记录下来,而是制造商已经在硬件层⾯上设计好了。

计算机内部有许多硬件单元,这些单元需要相互协作。协作的意思是它们至少要能够进行数据传输。但是,硬件单元之间是相互独立的,那么它们如何进行通信呢?

答案很简单,通过连接线进行通信。

CPU想读取内存中某一个数据中时,控制总线发出一个信号,CPU通过地址总线把内存中的这个位置找到,然后再通过数据总线传给CPU.

CPU和内存之间也需要进行大量的数据交互,因此它们必须通过连接线进行连接。然而,今天我们要关注的是一组连接线,称为地址总线。

学到这里,同学们可能有个疑问,地址总线有多少根,怎么构建联系的呢?32位机器有32根地址总线,64位机器有64根地址总线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么⼀根线,就能表⽰2种含义,2根线就能表⽰4种含义,依次类推。32根地址线,就能表⽰2^32种含义,每⼀种含义都代表⼀个地址。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传⼊CPU内寄存器。

二、 指针变量和地址

2.1 取地址操作符(&)

当我们理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实有两种含义:

#include int main(){ int a = 66;内层含义 return 0;}

&a取出的是类型为a所占4个字节中地址较小的地址。我们知道a的地址,就能推出他剩下的三个地址了,就可以访问到他4个字节的数据。

16进制转换为2进制:

怎么观测到a的地址呢?
按F10调试起来,打开窗口找到内存窗口,4个点击哪一个都可以。

输入&取地址操作符(&a)就可以找到对应的地址,当然也可以在监视看,以下在内存中观察:

2.2指针变量和解引⽤操作符(*)

那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:0x006ffae0,这个数值有时候也是需要存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。
⽐如:

#include int main(){ int a = 10; int* pa = &a;//取出a的地址并存储到指针变量pa中 return 0}

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

2.2.1 如何拆解指针类型

我们看到pa的类型是 int* ,我们该如何理解指针的类型呢?

nt a = 10;int * pa = &a;

这⾥pa左边写的是 int** 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)类型的对象。

那如果有⼀个char类型的变量chch的地址,要放在什么类型的指针变量中呢?

char ch = 'R';pc = &ch;//pc 的类型怎么写呢?答案是:char *pc = &ch;

2.2.2 解引⽤操作符

解引用运算符( * ) 将指针变量所指向的对象的值赋给左值变量。当使用指针变量时,使用解引用运算符来访问指针变量所指向的对象。

#include int main(){int a = 100;int* pa = &a;*pa = 0;printf("%d", a);return 0;}

*pa 的意思就是通过pa中存放的地址,找到指向的空间,
*pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0

#include int main(){int num1 = 10;int *ptr1 = &num1;printf("The value of num1 is %d\n", num1);printf("The address of num1 is %d\n", &num1);printf("The value of ptr1 is %d\n", ptr1);printf("The value of *ptr1 is %d\n", *ptr1);return 0;}

变量 num1 的值为 10,因为它被赋值为 10。
变量 num1 的地址为 13629256,因为它在内存中的位置是 13629256。
变量 ptr1 的值为 13629256,因为它被赋值为 num1 的地址。
变量 *ptr1 的值为 10,因为它是 ptr1 所指向的对象的值。

2.3 指针变量的⼤⼩

指针变量的⼤⼩取决于地址的⼤⼩

• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。

#include int main(){int* a = 10;short* b = 10;char* c = 10;double* d = 10;printf("%d\n", sizeof(a));printf("%d\n", sizeof(b));printf("%d\n", sizeof(c));printf("%d\n", sizeof(d));return 0;}

debug X86也就是调试状态下的32位环境下:

debug X64也就是调试状态下的64位环境下:

三、 指针变量类型的意义

当你看到这里,你发现指针变量的⼤⼩和类型⽆关,只要是指针变量,在同⼀个平台下,⼤⼩都是⼀样的, 那还要那么多的指针类型干嘛呢?统一归为一种指针类型不就好了,就没那么麻烦了,bug或许可能就没有那么多了!同学带着你的疑问,让我们一起走下去。看看为什么这么设计的” />3.1 指针的解引⽤

首先,我们观察两组代码的变化环境为debug x86

  1. 代码1:
#include int main(){int n = 0x66778899;int* pk = &n;*pk = 0;return 0;}

  1. 代码2:
#include int main(){int n = 0x66778899;char* pk = (char*)&n;*pk = 0;return 0;}


调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。

结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。

⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节

3.2 指针+ – 整数

指针加减整数的语法如下:

ptr + nptr - n

其中,ptr 是指针变量,n 是整数。

当我们向一个指针加减整数时,我们实际上是在向指针所指向的内存地址加减整数。这意味着,如果我们向一个指针加 1,则指针会指向内存中下一个字节的位置。如果我们向一个指针减 1,则指针会指向内存中上一个字节的位置。

以下是不同类型的指针加减整数的示例:

整数指针:

int *p = 0;p++; // 指向内存中下一个字节的位置p--; // 指向内存中上一个字节的位置

指针数组:

int arr[] = {1, 2, 3, 4, 5};int *p = arr;//int*类型加4p++; // 指向内存中下一个元素的位置p--; // 指向内存中上一个元素的位置

以下是指针加减 1 的示例:

#include int main(){int n = 10;char* pc = (char*)&n;int* pi = &n;int* arr[] = { 1,2,3,4,5 };int* pk = arr;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc + 1);printf("%p\n", pi);printf("%p\n", pi + 1);printf("%p\n", pk);printf("%p\n", pk + 1);return 0;}

运行结果:

可以看到,
char类型的指针变量+1跳过1个字节,int类型的指针变量+1跳过了4个字节。这表明指针变量的类型差异会导致步长的变化。
结论:指针的类型决定了指针向前或向后移动一步的距离。

3.3 void* 指针

void 指针是 C 语言中一种特殊的指针,它可以指向任何类型的数据。void 指针的类型是 void,它不指向任何特定的数据类型。

void* 类型的指针不能直接进⾏指针的±整数和解引⽤的运算。

void 指针也可以用来解引用,但必须在解引用之前使用类型转换。void` 指针可以用来存储指向任何类型数据的指针。

#include int main(){int a = 88;void* p=&a;int* q = (int*)p;//类型转换printf("n = %d\n", *q);return 0;}

void* 类型的指针不能直接进⾏指针的±整数和解引⽤的运算。