当前位置: 首页 > news >正文

C语言基础—指针变量与变量指针

引入

内存地址

  • 字节:字节是内存的容量单位,英文名Byte1 Byte = 8bits
  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号(编号是唯一),称为内存地 址,简称地址。int a = 5;

基地址(首地址)

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。举例:char a = 'A'
  • 多字节数据:对于多字节数据而言,其地址是所有字节中编号最小的那个 ,称为基地址(首地 址)

取地址

  • 每个变量都是一块内存,都可以通过取址符 & 获取其地址。
  • 例如
int a = 100;
printf("整型变量a的地址是:%p\n",&a); // 0x7ffe382a8114 64位系统 是12位16进制
char c = 'x';
printf("字符变量c的地址是:%p\n",&c); // 0x7ffdf5482af3
注意:
  • 虽然不同的变量的尺寸是不同的,但是它们的地址的尺寸是一致的。
  • 不同的地址虽然形式上看起来是一样的,但由于它们代表的内存尺寸和类型都不同,因此它 们在逻辑上时严格区分的。

变量指针与指针变量

指针的概念

内存单元与地址机制

  • 内存单元划分

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

    • 每个内存单元拥有唯一编号,称为内存地址(十六进制表示:如:0x7ffdf5482af3)

  • 变量存储特性

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

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

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

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

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

变量指针与指针变量

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

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

指针的尺寸

注意: char *p = &a 中 char修饰的是a的类型,*p永远都只能存放地址值

指针的本质

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

  • 指针变量:存储门牌号的"笔记本”(int *p)

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

内存数据的存取方式

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

直接存取
  • 通过基本类型(整型、浮点型、字符型)的变量访问这个变量代表空间的数据
  • 通过数组元素的引用,访问这个引用代表的内存空间的数据
间接存取
  • 通过指针变量,间接的访问内存中的数据

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

int *p;             //声明符

printf("%d",*p);     //解引用

 

指针变量的定义

语法:

数据类型* 变量列表;

注意:指针变量的值只能是8/12位的十六进制整数。

注意:

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

②使用指针变量间接访问内存数据时,指针变量必须要明确的指向。

③如果想要借助指针变量间接访问指针变量保存的内存地址上的数据,可以使用指针变量前加*来间接返回访问。指针变量前加*,如果不带数据类型,就称之为对指针变量解引用。

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

⑤指针变量在定义时可以初始化:这一点和普通变量是一样的。

int a = 5;
int *p = &a;//定义指针变景的同时初始化
printf("%d\n",*p);

int b;
int* p1 = &b;     //指针初始化的时候不需要关注指向的变量空间中是否有值
printf("%d\n",*p1);

指针变量的使用

使用

  • 指针变量的赋值
//方式1
int a,*p
p=&a;    //先定义,后赋值

//1方式2
inta1,*p1,*q1=&a1;   //定义并初始化
p1=q1        //其实就是变景的赋值,指针变景q1的值城值给了指针变量p1,此时q1和p1同时指向a1
  • 操作指针变量的值
int a,*p,*q &a;
p=q;            //将指针变量q的值赋值给指针变量p此时p和g都指向了变量a
printf("%p",p); //方问的是指针变量p的值(也就是变量a的地址
  • 操作指针变量指向的值
inta=6,*q=&a,b=25;//一定要注意,指计变量q的变量名就是q
*q=10;           //访问q指向的变量a的空间,其实就是间接的给a赋值(a=10)
printf("%d,%d\n",*q,a); //10 10
q=&b;            //一个指针变量只能同一时刻指向一个变量,但是一个变量可以同时被多个指针变显指向

两个指针运算符的使用

  • &:取地址运算符。&a是变量a的地址,这个是变量指针。

  • *:指针运算符、解引用符,这个是指针变量

直接与间接交换数据的值

指针变量做参数函数

指针变量做函数参数往往传递的是变量的地址(基地址),借助与指针变量间接访问是可以修改实参变量数据的。

#include <stdio.h>
/**
* 定义一个函数,实现两个数的交换
*/
void swap(int *p_a, int *p_b)
{
    int *p_t;
    // 以下写法,只会改变指针指向,不会改变指向对象的值
    p_t = p_a;
    p_a = p_b;
    p_b = p_t;
    printf("交换后:%d,%d\n",*p_a,*p_b);
}
int main(int argc,char *argv[])
{
    int a = 3, b = 5;
    swap(&a,&b);
    printf("a=%d,b=%d\n",a,b);
    return 0;
}

指针变量指向数组元素

数组元素的指针
  • 数组的指针就是数组中的第一个元素的地址,也就是数组的首地址

  • 数组元素的指针是指数组的首地址。因此,同样可以用指针变量来指向数组或数组元素。

  • 在C语言中,由于数组名代表数组的首地址,因此,数组名实际上也是指针。我们访问数组名就是访问数组首地址。

注意:虽然我们定义了一个指针变量接收了数组地址,但不能理解为指针变量指向了数组,而应该理解为指向了数组的元素(默认为第1个元素)。

指针的运算

指针运算:前提是指针变量必须要指向数组的某个元素。(指针运算只能发生在同一数组内)

如果指针变量 p 已指向数组中的一个元素,则 p+1 指向同一数组中的 下一个元素, p-1 指向
同一数组中的上一个元素。即 p+1 p-1 也表示地址。但要注意的是,虽然指针变量 p 中存放的 是地址,但p+1 并不表示该地址加 1 ,而表示在原地址的基础上加了该数据类型所占的字节数 d (d = sizeof( 数据类型 ) ) 。
如果 p 原来指向 a[0] ,执行 ++p p 的值改变了,在 p 的原值基础上加 d ,这样 p 就指向数组的
下一个元素 a[1] d 是数组元素占的字节数。
如果 p 的初值为& a[0] p+i a+i 就是数组元素 a[i] 的地址,或者说,它们指向a数组的第 i
个元素 。
*(p+i) *(a+i) p+i a+i 所指向的数组元素,即 a[i]
如果指针变量 p1 p2 都指向同一数组,如执行 p2-p1 ,结果是两个地址之差除以数组元素的 长度d
通过指针引用数组元素

引用数组元素,可以用:

①下标法:如arr[i]形式

②指针法:如*(arr+i)或者*(p+i)。其中arr是数组名,p是指向数组元素的指针变量,其初始值:p=arr;

#include <stdio.h>
void main()
{
    int arr[10];
    int *p, i;
    // 给数组元素赋值
    for(i = 0; i < 10; i++)
        scanf("%d",&arr[i]);
    // 遍历数组元素
    for(p = arr; p < (arr + 10); p++)
        printf("%-4d%",*p);
    printf("\n");
}
注意:数组一旦创建,就无法改变其值。
http://www.dtcms.com/a/81429.html

相关文章:

  • 【c++】异常处理
  • mysql 导入全量备份
  • Python Web 框架 Django、Flask 和 FastAPI 对比
  • 【负载均衡系列】LVS
  • Swagger 笔记
  • Centos 7 安装VNC服务
  • C# WinForms 中的回调:从性能到技术层面的全面解析
  • HTTP代理IP技术详解及在Web开发中的应用
  • 深入解析MySQL存储引擎:从InnoDB到MyISAM的技术全景
  • 单页响应式 图片懒加载HTML页面
  • 2025年- G23-Lc97-104.二叉树的最大深度-java版(递归调用左右子树)
  • 基于Python编程语言实现“机器学习”,用于车牌识别项目
  • 林阳域管理系统功能简介
  • Oracle 数据库安全评估(DBSAT)简明过程
  • Java 大视界 -- Java 大数据在智能医疗远程会诊与专家协作中的技术支持(146)
  • Python在数据科学中的应用:完整指南
  • 【高并发内存池】第三弹---构建Central Cache的全方位指南——从整体设计到核心实现
  • 《C++11 基于CAS无锁操作的atomic原子类型》
  • 头歌 JAVA 桥接模式实验
  • UI数据处理新隐私保护:确保用户新信息安全
  • 固定公网 IP
  • 【浙大PTA:L1系列题目】
  • NFS 安装与测试
  • 如何在SQL中高效使用聚合函数、日期函数和字符串函数:实用技巧与案例解析
  • 001 你好LabVIEW
  • 如何理解java中Stream流?
  • 对IKFOM论文中一些关键内容的理解
  • github如何为开源项目作出贡献
  • 高防ip和高防服务器的区别?
  • MSE分类时梯度消失的问题详解和交叉熵损失的梯度推导