【C语言】——认识指针变量和地址,以及指针变量类型的意义

🎥 岁月失语唯石能言的个人主页        

🔥个人栏专:秒懂C语言

若在许我少年时,一两黄金一两风    

目录

前言

一、指针变量和地址

1.1 取地址操作符(&)

1.2 指针变量和解引用操作符(*)

1.2.1 指针变量

1.2.2 如何拆解指针类型

1.2.3 解引用操作符

1.3 指针变量的大小

二、指针变量类型的意义

2.1指针的解引用

2.2 指针+-整数

2.3 void* 指针

总结


前言

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

一、指针变量和地址

1.1 取地址操作符(&)

在C语言中创建变量其实就是向内存申请空间,比如:
# include <stdio.h>
int main ()
{
        int a = 10 ;
        return 0 ;
}

比如,上述的代码就是创建了整型变量a,内存中
申请4个字节,用于存放整数10,其中每个字节都
有地址,上图中4个字节的地址分别是:
1. 0x006FFD70
2. 0x006FFD71
3. 0x006FFD72
4. 0x006FFD73

#include <stdio.h>
int main()
{
    int a = 10;
    &a;//取出a的地址
    printf("%p\n", &a);
    return 0;
}

&a取出的是a所占4个字节中地址较小的字节的地址。
虽然整型变量占用4个字节,我们只要知道了第一个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。

1.2 指针变量和解引用操作符(*)

1.2.1 指针变量

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

ex:

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;//取出a的地址并存储到指针变量pa中

	return 0;
}
指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。

1.2.2 如何拆解指针类型

我们看到pa的类型是 int* ,我们该如何理解指针的类型呢?
int a = 10 ;
int * pa = &a;
这里pa左边写的是 int* * 是在说明pa是指针变量,而前面的 int 是在说明pa指向的是整型(int)
类型的对象。
那如果有⼀个char类型的变量ch,ch的地址,要放在什么类型的指针变量中呢?
char ch = 'w' ;
char * pc = &ch;

1.2.3 解引用操作符

我们将地址保存起来,未来是要使用的,那怎么使用呢?
在现实生活中,我们使用地址要找到⼀个房间,在房间里可以拿去或者存放物品。
C语言中其实也是一样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这里必须学习一个操作符叫解引用操作符(*)。
#include <stdio.h>

int main()
{
	int a = 100;
	int* pa = &a;
	*pa = 0;
	return 0;
}
上面代码中第7行就使用了解引用操作符, *pa 的意思就是通过pa中存放的地址,找到指向的空间,*pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0.
有同学肯定在想,这里如果目的就是把a改成0的话,写成 a = 0; 不就完了,为啥非要使用指针呢?
其实这里是把a的修改交给了pa来操作,这样对a的修改,就多了一种的途径,写代码就会更加灵活。

1.3 指针变量的大小

前面的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产生的2进制序列当做⼀个地址,那么一个地址就是32个bit位,需要4 个字节才能存储。
如果指针变量是用来存放地址的,那么指针变的大小就得是4个字节的空间才可以。
同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变的大小就是8个字节。
#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
	printf("%zd\n", sizeof(char*));
	printf("%zd\n", sizeof(short*));
	printf("%zd\n", sizeof(int*));
	printf("%zd\n", sizeof(double*));
	return 0;
}

                  X86环境输出结果                                                        X64环境输出结果
结论:
32位平台下地址是32个bit位,指针变量大小是4个字节
64位平台下地址是64个bit位,指针变量大小是8个字节
注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。


二、指针变量类型的意义

指针变量的大小和类型无关,只要是指针变量,在同⼀个平台下,大小都是⼀样的,为什么还要有各种各样的指针类型呢?
其实指针类型是有特殊意义的,我们接下来继续学习。

2.1指针的解引用

对比,下面2段代码,主要在调试时观察内存的变化。
//代码1 #include <stdio.h>
int main()
{
	int n = 0x11223344;
	int* pi = &n;
	*pi = 0;
	return 0;
}
//代码2 #include <stdio.h>
int main()
{
 int n = 0x11223344;
 char *pc = (char *)&n;
 *pc = 0;
 return 0; }
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为0。
结论:指针的类型决定了,对指针解引用的时候有多⼤的权限(⼀次能操作几个字节)。
比如: char* 的指针解引用就只能访问⼀个字节,而  int* 的指针的解引用就能访问四个字节。

2.2 指针+-整数

先看一段代码,调试观察地址的变化。
#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return 0;
}
代码运行的结果如下:
我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。
结论:指针的类型决定了指针向前或者向后走一步有多大(距离)。

2.3 void* 指针

在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为无具体类型的指针(或者叫泛型指 针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。
ex:
#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;
	return 0;
}
在上面的代码中,将⼀个int类型的变量的地址赋值给⼀个char*类型的指针变量。编译器给出了⼀个警告(如下图),是因为类型不兼容。而使用void*类型就不会有这样的问题。
                                                              VS2022编译的结果
使用void*类型的指针接收地址:
#include <stdio.h>
int main()
{
	int a = 10;
	void* pa = &a;
	void* pc = &a;

	*pa = 10;
	*pc = 0;
	return 0;
}
VS编译代码的结果:
这里我们可以看到, void* 类型的指针可以接收不同类型的地址,但是无法直接进行指针运算。
⼀般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以
实现泛型编程的效果。使得⼀个函数来处理多种类型的数据,这我会在后面博客提到。


总结

指针变量的大小和类型无关,只要是指针变量,在同⼀个平台下,大小都是⼀样的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/251627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【vmware】虚拟机固定ip和网络配置

废话不多说&#xff0c;直接干货 桥接模式不多说&#xff0c;动态ip&#xff0c;一般一键下一步就可 本文主要讲 NAT模式下 静态IP设置及公网问题 创建虚拟机 查看ip ip a 或者 ifconfig 设置静态ip 1.设置虚拟机网络 点击上图中NAT设置&#xff0c;配置网关IP&#xff08;vmv…

【改进YOLOv8】生猪胖瘦评价分级系统:可重参化EfficientRepBiPAN优化Neck

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义&#xff1a; 随着计算机视觉和深度学习的快速发展&#xff0c;目标检测成为了计算机视觉领域的一个重要研究方向。目标检测的目标是在图像或视频中准确地识别和定…

DL Homework 11

由于好多同学问我要代码&#xff0c;但这两天光顾着考四六级了&#xff0c;所以只能今天熬夜先给赶出来&#xff0c;第一题先搁置&#xff0c;晚点补上&#xff0c;先写第二题 习题6-4 推导LSTM网络中参数的梯度&#xff0c; 并分析其避免梯度消失的效果 习题6-3P 编程实现…

pat 乙级 1018 锤子剪刀布

解题思路&#xff0c;这题你硬比也可以&#xff0c;用技巧减少比较也可以&#xff0c;总共有九种情况 去掉 都相同的 还有6种&#xff0c;我的想法是这样 把 B C J 转换成了0 1 2&#xff0c;这样我们只要看他们大小就行&#xff0c;如果前面的比后面的小并且只差1,那就是前面的…

Java 并发编程(六)-Fork/Join异步回调

一、并发编程 1、Fork/Join分支合并框架 Fork/Join它可以将一个大的任务拆分成多个子任务进行并行处理&#xff0c;最后将子任务结果合并成最后的计算结果&#xff0c;并进行输出。Fork/Join框架要完成两件事情&#xff1a; Fork&#xff1a;把一个复杂任务进行分拆&#xff0…

一文搞懂Android和嵌入式Linux开发差异点

前言 因业务需要&#xff0c;过去一年从熟悉的Android开发开始涉及嵌入式Linux开发&#xff0c;编程语言也从Java/Kotlin变成难上手的C&#xff0c;这里面其实有很多差异点&#xff0c;特此整理本文来详细对比这两者开发的异同&#xff0c;便于对嵌入式Linux开发感兴趣的同学一…

Java 多线程学习(三)

目录 一、多线程 1、深入synchronized 1.1、synchronized的优化 1.2、synchronized实现原理 1.3、synchronized的锁升级 1.4、重量锁底层ObjectMonitor 一、多线程 1、深入synchronized 1.1、synchronized的优化 在JDK1.5的时候&#xff0c;Doug Lea推出了ReentrantLoc…

Redis核心知识小结

基础 redis为什么快呢&#xff1f; 单线程基于io多路复用底层C语言对数据结构做了优化完全内存的操作 Redis6.0使用多线程是怎么回事? Redis不是说用单线程的吗&#xff1f;怎么6.0成了多线程的&#xff1f; Redis6.0的多线程是用多线程来处理数据的读写和协议解析&#x…

【算法笔记】动态规划,使用最小花费爬楼梯,详细刨析。

1.题目描述 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 示…

linux 开机启动流程

1.打开电源 2.BIOS 有时间和启动方式 3.启动Systemd 其pid为1 4.挂载引导分区 /boot 5.启动各种服务 如rc.local

STM32 EC200 物联网项目实操 第2篇 FTP OTA升级

背景&#xff1a; 做了个物联网项目&#xff0c;需要做个OTA升级&#xff0c;程序分为两部分&#xff0c;一部分是BOOT引导程序&#xff0c;一部是主程序&#xff0c;在BOOT引导程序里面实现了和EC200 4G模块通讯&#xff0c;和FTP服务器通讯&#xff0c;获取OTA升级BIN文件。主…

【前端八股】系列之性能指标与评估工具

【前端八股】系列之性能指标与评估工具 前言性能指标的定义和特性性能指标的分类实验室指标真实指标用户感知指标评估工具 前言 这里是以前自己关于性能指标与评估工具的相关笔记&#xff0c;下面主要是关于性能指标的定义与特性以及相关的评估工具&#xff0c;并没有记录深入…

人工智能_机器学习065_SVM支持向量机KKT条件_深度理解KKT条件下的损失函数求解过程_公式详细推导---人工智能工作笔记0105

之前我们已经说了KKT条件,其实就是用来解决 如何实现对,不等式条件下的,目标函数的求解问题,之前我们说的拉格朗日乘数法,是用来对 等式条件下的目标函数进行求解. KKT条件是这样做的,添加了一个阿尔法平方对吧,这个阿尔法平方肯定是大于0的,那么 可以结合下面的文章去看,也…

mysql8支持远程访问

上面的localhost要改为%号就打开了远程访问 ALTER USER root% IDENTIFIED WITH mysql_native_password BY fengzi2141;

vite原理

一、依赖预构建 1、为什么需要依赖预构建 CommonJS和UMD兼容性 在开发阶段中&#xff0c;vite的开发服务器将所有的代码视为原生ES模块。因此&#xff0c;vite必须先将作为CommonJS或者UMD发布的依赖项转换为ESM。 这是vite的一个特色&#xff0c;也是为什么会相对于webpack比…

Android动画(一)——逐帧动画

目录 介绍 Android动画类型分类 逐帧动画 逐帧动画的使用 效果图 介绍 Android动画是一种用于创建视觉效果和交互体验的技术&#xff0c;可以增强用户界面的吸引力和响应性。Android提供了丰富的动画框架和API&#xff0c;使开发者可以轻松地添加动画效果到他们的应用程序中…

n维随机变量、n维随机变量的分布函数

设随机试验E的样本空间是&#xff0c;其中表示样本点。 设是定义在上的随机变量&#xff0c;由它们构成一个n维向量&#xff0c;叫做n维随机向量&#xff0c;也叫n维随机变量。 对于任意n个实数&#xff0c;n元函数 称为n维随机变量的分布函数&#xff0c;也叫联合分布函数。

springCloud项目打包如何把jar放到指定目录下

springCloud项目打包如何把jar发放到指定目录下 maven-antrun-plugin springCloud微服务打包jar&#xff0c;模块过多&#xff1b;我的项目模块结构如下&#xff1a; 我把实体类相关的单独抽离一个模块在service-api下服务单独写在service某块下&#xff0c; 每个模块的jar都…

【C++】 C++11 新特性探索:decltype 和 auto

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ decltype推导变量类型推导函数返回类型 2️⃣ auto自动推导变量类型迭代器和范围循环 3️⃣ decltype 和 auto 同时使用&#x1f6ec; 结论&#x1f4d6; 参考资料 &#x1f6eb; 问题 描述 C11 引入了一些强大的新特性&#xff…

【python】Debian安装miniconda、spyder、tushare

1. miniconda 安装 — 动手学深度学习 2.0.0 documentation中有安装Miniconda的一些说明。 Miniconda — miniconda documentation是Miniconda网站&#xff0c;里面也有安装说明。 Debian安装按照linux安装即可&#xff1a; mkdir -p ~/miniconda3 wget https://repo.anaco…