C语言:指针基础知识

内存地址

  • 字节:字节是内存的容量单位,英文名Byte,1Byte = 8Bit = 1Char

  • 地址:系统为了便于区分每一个字节而对他们逐一进行编号(编号唯一),称之为内存地址,简称地址。

基地址(首地址)
  • 单字节数据:对于单字节数据,其地址就是其字节编号。举例:char a = 'A';

  • 多字节数据:对于多字节数据,其地址是所有字节中编号最小的那个,称为基地址,或者首地址。

指针 == 地址

指针的目的是为了进行寻址操作,找到对应的内存。

内存
  • 虚拟内存:实际上是硬盘上的一块区域

  • 物理内存:计算机中安装的硬件内存(比如:内存条) RAM:随机存储器,用于临时存储数据,读写速度快

  • 虚拟内存 内存总大小:4G 内存单元:每个内存单元大小 节(byte) 内存单元总数:4*1024*1024*1024 内存块:地址空间连续的多个内存单元 地址:每个内存单元都有一个对应的地址,我们称之为内存地址,而地址我们通常使用十六进制表示。 寻址:通过地址找到对应的内存

取地址符
  • 每个变量都是一块内存,都可以通过取地址符&获取其地址(基地址)。

  • 例如:

    int a = 100;
     printf("整型变量a的地址:%p\n", &a); // 0x7ffd3f7d7894  64位系统下:地址是12位16进制整数,占8字节
     ​
     char c = 'x';
     printf("字符变量c的地址:%p\n", &c); // 0x7ffd3f7d7895

  • 注意:

    • 虽然不同的变量的尺寸是不同的,但是它们的地址的尺寸是一致的。

    • 不同的地址虽然形式上看起来是一样的,但由于它们代表的内存尺寸和类型都不同,因此他们在逻辑上是需要严格区分。

为什么要引用指针

  • 为函数修改实参提供支持。

  • 为动态内存管理提供支持。

  • 为动态数据结构(链表、队列、二叉搜索树)提供支持。

  • 为内存访问提供了另一种途径。

变量指针与指针变量

指针的概念
内存单元与地址机制
  • 内存单元划分

    • 系统将内存划分为连续的基本存储单元,每个单元的容量为1字节(8 Bits)

    • 每个内存单元 拥有唯一编号,称为内存单元(64位系统下,12位16进制表示:如:0x7ffe382a8114)

  • 变量的存储特性

    • 变量根据数据类型占据不同数量的内存单元:

      • char类型占1字节(1个单元)

      • int类型占4字节(4个单元)

      • double类型占8字节(8个单元)

    • 变量的基地址(首地址)是其首个内存单元的地址(首地址一般是这一组编号中最小的那个)

变量指针与指针变量
对比维度变量指针指针变量
本质地址值(指针值),变量指针,其实就是变量的 首地址。本质是地址存储地址的变量。本质是变量。
操作符&(取地址符)*(指针声明符,解引用符,如:int *p)
代码示例&a(获取变量a的地址)int* p = &a;
核心特性不可修改(地址由系统分配)可修改指向(p = &b)
指向

指针变量中存放谁的地址,就说明该指针变量指向谁。举例:

int a = 10;  // 变量a,存放常量10
 int* p = &a; // 指针变量p,存放变量a的地址,此时我们可以说 指针变量p指向对象a
 ​
 int b = 20;
 p = &b;      // 指针变量p指向对象b 
 ​
 double c = 12;
 double *p1 = &c;  
指针的尺寸
系统类型指针尺寸地址位数十六进制显示长度
32位系统4字节(long)32Bit8位(如:0x0804A000)
64位系统8字节(long)48Bit12位(如: 0x7FFDEADBEEF)

小贴士:

Linux系统中打印地址时,最多显示12个十六进制数,为什么?

Linux64位操作系统中,一个内存地址占8个字节,一个字节8bit位,所以一个地址8*8=64bit位, 每4个bit可以表示1个十六进制数; 64个bit位用十六进制表示最多有16个数值位;

系统为了寻址方便,默认当前计算机系统没必要寻址64bit位,只寻址了48个bit位,所以用12个十六进制数表示一个地址。

二进制: 0100 1010 十六进制: 0x4A 4*16+10 = 74

注意: 在Linux64位操作系统中,指针类型的变量占8个字节的内存空间 在Linux32位操作系统中,指针类型的变量占4个字节的内存空间

指针的本质
  • 变量指针:数据的“门牌号”(&a)

  • 指针变量:存储门牌号的“笔记本”(int *p; 指针变量存储的地址所指向的对象的类型是int类型,不能说指针变量是int类型)

  • 指向操作:通过门牌号访问数据(*p)

 int a = 10;
 printf("%d\n", a); // 直接访问对象a的值
 ​
 int *p = &a;
 printf("%p\n", p); // 直接访问指针变量p的值,返回的是a的地址    p;访问指针变量,拿到的是地址值
 printf("%d\n", *p);// 间接访问对象a的值,*p,解引用,访问p指向对象的数据  *p:访问指针变量指向的对象,拿到的是对象的值
内存数据的存取方式

在C语言中对内存数据(变量、数组元素等)的存取有两种方式:

直接访问
  • 通过基本类型(整型、浮点型、字符型)的变量,访问这个变量代表的内存空间的数据。

  • 通过数组元素的引用(下标),访问这个引用代表的内存空间的数据

     // 基本类型的变量
     int a = 10 ;       // 存
     printf("%d\n", a); // 取
     ​
     // 数组元素
     int arr[] = {11,22,33}; // 存
     arr[0] = 66;            // 存
     printf("%d\n", arr[0]); // 取

间接访问
  • 通过指针变量,间接的访问内存中的数据。

    • *:读作指针声明符或者 解引用符。如果*前面有数据类型,读取声明指针;如果*前面没有数据类型,读作解引用。

      案例:

      /*************************************************************************
         > File Name:    demo03.c
         > Author:       三道渊
         > Description:  
         > Created Time: 2025年07月24日 星期四 14时24分54秒
        ************************************************************************/
       ​
       #include <stdio.h>
       ​
       int main(int argc,char *argv[])
       {
           // 定义一个普通变量
           int a = 3;
       ​
           // 定义一个指针变量,并赋值(指针变量本身还是一个变量,只不过只能存放地址值)
           int *p = &a; // 这里的地址值一定要有其在内存中对应的空间
       ​
           // 访问变量a,直接访问
           printf("直接访问-%d\n", a); // 3
       ​
           // 访问变量a,通过指针变量p访问,间接访问
           printf("间接访问-%d\n", *p); // 3 *p 解引用 通过指针变量访问其指向的对象
       ​
           // 访问p,*p, &p
           printf("p-指针变量的值:%p\n*p-指针变量的值指向的对象:%d\n&p-获取指针变量的地址:%p\n", p, *p, &p);// a的地址,a的值,p的地址
       ​
       ​
           return 0;
       }
       ​

      运行结果:

指针变量的定义

语法:

 // 写法1
 数据类型 *变量列表;
 ​
 // 写法2
 数据类型* 变量;

举例:

 /*************************************************************************
   > File Name:    demo04.c
   > Author:       三道渊
   > Description:  
   > Created Time: 2025年07月24日 星期四 14时49分30秒
  ************************************************************************/
 ​
 #include <stdio.h>
 ​
 int main(int argc,char *argv[])
 {
 ​
     // 普通变量
     int a;  
 ​
     // 指针变量
     int *p1;        // p1是指针变量
     int *p2,*p3;    // p2,p3都是指针变量
     int* p5,p6;     // p5是指针变量,p6是普通变量
     
     int* p4;        // p4是指针变量
     int* p5,p6;     // p5是指针变量,p6是普通变量
     int *p7,p8;     // p7是指针变量,p8是普通变量
     
     return 0;
 }

注意:指针变量的值只能是8(32位系统)| 12(64位系统)位的十六进制整数。

注意:

① 虽然定义指针变量*a,是在变量名前加上*,但是实际变量名依然为a,而不是*a

② 使用指针变量间接访问内存数据时,指针变量必须要明确指向。(指向:指针变量存放谁的地址,就指向谁)

③ 如果想要借助指针变量间接访问指针变量保存的内存地址上的数据,可以使用指针变量前加*来间接返回访问。

 
/*************************************************************************
   > File Name:    demo01.c
   > Author:       三道渊
   > Description:  
   > Created Time: 2025年05月19日 星期三 09时39分08秒
  ************************************************************************/
 ​
 #include <stdio.h>
 ​
 int main(int argc,char *argv[])
 {
     int i = 5, *p;
     p = &i;  // 将i的地址赋值给指针变量p
     printf("%lx,%p,%p\n",p,p,&p);  // %p-p:访问的是p的值,也就是i的地址;%p-&p:访问的是p自身的地址
     printf("%d\n",*p);// 5
     *p = 10; // 间接的给p对应的地址上的数据空间赋值,也就是给变量i赋值  
     printf("%d,%d\n",*p,i);// 10,10
     return 0;
 }

④ 指针变量只能指向同类型变量,借助指针变量访问内存,一次访问的内存大小是取决于指针变量的类型。

 int a = 10;     // 普通变量
 double b = 10;  // 普通变量
 ​
 int *pa = &a;   // 指针的类型 要和指向的对象的 类型一致
 double *pb = &b; // 这里的double不是指针的类型,是指针指向的对象的类型

⑤ 指针变量在定义时可以初始化,这一点和普通变量一致。(指针变量本质上还是变量,只不过存放的是地址)

 
 int a = 5; 
 int *p = &a;        // 定义变量的同时初始化
 printf("%d\n", *p); // 5
 ​
 int b;
 int *p1 = &b;
 printf("%d\n", *p1);// 随机值
指针变量的使用
使用
  • 指针变量的赋值

     
     // 方式1
     int a, *p;
     p = &a;         // 先定义,后赋值
     ​
     // 方式2
     int a1, *p1, *q1 = &a1; // 定义并初始化
     p1 = q1;        // 变量的赋值,将q1的值赋值给p1,此时q1和p1都指向了a1

  • 操作指针变量的值

     
    int a, *p , *q = &a;
     p = q;   // 指针变量的赋值,将q的值赋值给了p,此时p,q都指向了a
     ​
     printf("%p\n", p); // 访问p的值(也就是a的地址)

  • 操作指针变量指向的值

     
     int a = 6, *q = &a, b = 25;
     ​
     *q = 10;  // 赋值操作,修改q指向的a的值,此时 a = 10
     printf("%d,%d\n", *q, a); // 10,10
     ​
     q = &b;  // 赋值操作,给指针变量q重新赋值,此时q指向b
     printf("%d,%d\n", *q, a); // 25,10

指针运算符的使用
  • &:取地址运算符。&a是获取变量a的地址值。这个是变量指针。

  • *:指针运算符、间接访问运算符,*p是指针变量p指向的对象的值。这个是指针变量。

案例
案例1
  • 需求:通过指针变量访问整型变量

  • 分析:

  • 代码:

     #include <stdio.h>
     ​
     int main()
     {
         int a = 3, b = 4, *p1 = &a, *p2 = &b;
         
         printf("a=%d,b=%d\n", *p1, *p2); // a=3,b=4
         
         return 0;
     }

案例2
  • 需求:声明a,b两个变量,使用间接存取的方式实现数据的交换。

  • 分析:

  • 实现:指针指向改变,指向对象的值不变。

    代码:

     /*************************************************************************
       > File Name:    demo05.c
       > Author:       三道渊
       > Description:  
       > Created Time: 2025年07月24日 星期四 15时52分09秒
      ************************************************************************/
     ​
     #include <stdio.h>
     ​
     int main(int argc,char *argv[])
     {
         int a = 3, b = 4, *p_a = &a, *p_b = &b;
     ​
         printf("交换前:a=%d-%d,b=%d-%d\n", a, *p_a, b, *p_b);// a=3-3,b=4-4
     ​
         // 指针的值交换,临时变量也是指针
         int *p_t = p_a;
         p_a = p_b; // p_a指向b
         p_b = p_t; // p_b指向a
     ​
         printf("交换后:a=%d-%d,b=%d-%d\n", a, *p_a, b, *p_b);// a=3-4,b=4-3
     ​
         return 0;
     }
    
     ​

    总结:此时改变的只是指针的指向,原始变量a,b中数据并没有发生改变。

  • 指针指向不变,指向对象的数据改变

    代码:

     /*************************************************************************
       > File Name:    demo06.c
       > Author:       三道渊
       > Description:  
       > Created Time: 2025年07月24日 星期四 16时00分28秒
      ************************************************************************/
     ​
     #include <stdio.h>
     ​
     int main(int argc,char *argv[])
     {
         int a = 3, b = 4, *p_a = &a, *p_b = &b;
         printf("交换前:a=%d-%d,b=%d-%d\n", a, *p_a, b, *p_b);// a=3-3,b=4-4
     ​
         // 交换:指向不变,指向对象的值改变
         int temp = *p_a;
         *p_a = *p_b;  // 将p_b指向对象b的数据赋值给p_a指向的对象a
         *p_b = temp;  
     ​
         printf("交换后:a=%d-%d,b=%d-%d\n", a, *p_a, b, *p_b);// a=4-3,b=3-4
     ​
         return 0;
     }
    
     ​

    总结:此时改变的是原始变量,原始变量a,b中数据发生了改变。

案例3
  • 需求:输入a,b两个整数,按先大后小的顺序输出a和b。

  • 分析:

  • 实现:指针指向改变,指向对象的值不变

    代码:

     /*************************************************************************
       > File Name:    demo07.c
       > Author:       三道渊
       > Description:  
       > Created Time: 2025年07月24日 星期四 16时14分17秒
      ************************************************************************/
     ​
     #include <stdio.h>
     ​
     int main(int argc,char *argv[])
     {
         int a, b, *p_a = &a, *p_b = &b, *p_t;
         printf("请输入两个整数:\n");
         scanf("%d%d", &a, &b);
     ​
         if (a < b)
         {
             p_t = p_a;
             p_a = p_b;
             p_b = p_t;
         }
     ​
         printf("按从大到小的顺序输出a,b的值:%d > %d\n", *p_a, *p_b);
     ​
         return 0;
     }
    
     ​
  • 实现:指针指向不变,指向对象的值改变

    案例:

     /*************************************************************************
       > File Name:    demo07.c
       > Author:       三道渊
       > Description:  
       > Created Time: 2025年07月24日 星期四 16时14分17秒
      ************************************************************************/
     ​
     #include <stdio.h>
     ​
     int main(int argc,char *argv[])
     {
         int a, b, *p_a = &a, *p_b = &b, temp;
         printf("请输入两个整数:\n");
         scanf("%d%d", &a, &b);
     ​
         if (a < b)
         {
             temp = *p_a;
             *p_a = *p_b;
             *p_b = temp;
         }
     ​
         printf("按从大到小的顺序输出a,b的值:%d > %d\n", *p_a, *p_b);
     ​
         return 0;
     }
    
     ​
上一篇: C语言:函数的作用域和生命周期
下一篇: C语言:变量指针、指针变量与数组指针、指针数组
三道渊
博客等级 码龄3年 118粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值