C语言:指针(一)

news2024/11/15 15:37:10

目录

  • 1.内存和地址
  • 2. 指针变量和地址
    • 2.1 取地址操作符(&)
    • 2.2 指针变量和解引用操作符(*)
      • 2.2.1 指针变量
      • 2.2.2 解引用操作符(*)
    • 2.3 指针变量的大小
  • 3.指针变量的类型和意义
    • 3.1 指针的解引用
    • 3.2 指针+ -指针
    • 3.3 void*指针
  • 4.const修饰指针
    • 4.1 const修饰变量
    • 4.2 const修饰指针变量
  • 5.传值调用和传址调用

1.内存和地址

在生活中,我们住的房子一般都有门牌号,学生一般都有学号,注册一个软件账户一般都有账户编号,可见给一些事物编上号可以方便我们的生活。
计算机也是这样,计算机中处理数据的机器时CPU(中央处理器),它在处理数据的时候是在内存中读取数据,处理后将数据放回内存。在内存中将其划分为一个一个的内存单元,每个内存单元的大小取一个字节(8个bite),每个内存单元也有一个编号,就相当于门牌号一样,这样CPU就可以快速访问内存中需要的数据了。
内存单元就相当一间学生宿舍,一个字节能存放8个比特位,就相当于一间宿舍住了8个人。

1byte= 8bite
1kb = 1024byte
1mb = 1024kb
1Gb = 1024mb
1Tb = 1024Gb
1Pb = 1024Tb

生活中我们也把门牌号叫做地址,在计算机中,我们把内存单元的编号也叫做地址还可叫做指针
所以:内存单元编号 == 地址 == 指针

2. 指针变量和地址

2.1 取地址操作符(&)

取地址操作符&和按位与操作符&是同一种符号,但是功能完全不一样,而且取地址符是单目操作符,按位与操作符是双目操作符。
在C语言中创建变量其实就是向内存申请一块空间,比如

int a = 10;

这段代码就是向内存中申请4个字节(int类型占用4个字节)用于存放整数10,那如何得到这个地址呢,那么就要用到取地址操作符(&),比如:
在这里插入图片描述
这里的地址是用16进制表示
而int类型的数据会占用4个字节的大小,所以**&a取出的是所占4个字节中地址较小的字节的地址。**
在这里插入图片描述
十六进制的a表示的数就是10,当我们知道了第一个字节的地址,就可以往下顺藤摸瓜找到其他的地址

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

2.2.1 指针变量

当我们用取地址操作符拿到一个地址后就可以将这个地址存放到指针变量中,比如:

int main() {
	int a = 10;
	int* pa = &a;

	return 0;
}

指针变量是一种变量,这种变量是用来存放地址的,存放在指针变量的值都会被理解为地址
pa是指针变量,int*是指针变量的类型, *表示pa是指针变量,int表示pa指向的类型是int类型。这里的 int和星号中间加不加空格都表示指针变量,没有去区别。

2.2.2 解引用操作符(*)

指针变量用来保存地址,而解引用操作符(*)用来使用地址
在C语言中,我们拿到了指针就可以通过指针找到指针指向的对象,这里就需要*(解引用操作符)

int main() {
	int a = 10;
	int* pa = &a;
	*pa = 0;

	return 0;

*pa的意思就是通过指针变量pa找到pa指向的对象a,然后改变它的值,上述代码就是将a的值由10改为0.

*pa = 0;的效果和a = 0;的效果一样,都是将a的值置为0,但是使用指针提供了一种新的修改途径。

2.3 指针变量的大小

在32位计算机中有32根地址总线(相关知识可以自行学习),每根地址总线都可以使用电信号来表示0和1,这样就由32根地址总线产生的二进制序列就可以当作地址,那么一个地址就是32个bite位,需要4个字节来存储。
所以:任何指针变量的大小都是4个字节(64位机是8个字节)

int main() {
	printf("%zd\n", sizeof(char*));
	printf("%zd\n", sizeof(int*));
	printf("%zd\n", sizeof(double*));
	printf("%zd\n", sizeof(short*));
	printf("%zd\n", sizeof(long int*));

	return 0;
}

在这里插入图片描述

3.指针变量的类型和意义

既然在相同平台下所有的指针类型的大小都是一样的,那么为什么还要设置这么多的指针变量呢。

3.1 指针的解引用

对比以下代码:

int main() {
	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;

	return 0;
}
int main() {
	int a = 0x11223344;
	char* pa = &a;
	*pa = 0;

	return 0;
}

通过调试中的内存监控可知,代码1会将0x11223344中的4个字节全部置为0,而代码2只会将第一个字节44置为0,其他不变。
在这里插入图片描述
结论:指针的类型决定了对指针解引用的时候有多大的权限(一次能操作几个字节)
比如,int类型的指针变量可以解引用4个字节,而char类型的指针变量只能访问1个字节

3.2 指针+ -指针

int main() {
	int n = 10;

	int* pi = &n;
	char* pc = &n;

	printf("%p\n", &n);
	printf("%p\n", pi);
	printf("%p\n", pc);
	printf("%p\n", pi + 1);
	printf("%p\n", pc + 1);

	return 0;
}

运行结果如下:
在这里插入图片描述
这里的pi和pc都指向变量n,而pi+1加了4个字节,因为pi是int*类型的指针变量,而pc+1只加

了1个字节,因为pc是char* 的指针变量。
结论:指针的类型决定了指针想前或者其向后走一步有多少距离。

3.3 void*指针

在指针类型中有一种特殊的指针类型是void*类型,它是无具体类型的指针(泛型指针),它可以用来接收任何类型的地址,但也有局限性:不能进行+和-和解引用的操作

int main() {
	int a = 0x11223344;
	void* pa = &a;
	*pa = 0;//错误操作

	return 0;
}

void*指针可以接受不同类型的地址,但是无法直接进行指针运算

4.const修饰指针

变量是可以修改的,而在变量前面加上const修饰那么该变量就不能被修改了

4.1 const修饰变量

int main() {
	const int a = 10;
	a = 29;

	return 0;
}

在这里插入图片描述
可以看到,变量a被const修饰过后,那么改值就不能修改了,而这个限制只是在语法层面加上限制,我们可以用指针来越过这个限制,比如:
在这里插入图片描述
上面用指针变量pa来读取a的地址,而用解引用操作符操作指针变量a,使其修改值为20,那么有什么办法能让指针也不能改变其值呢?

4.2 const修饰指针变量

int main() {
	const int a = 10;
	const int* pa = &a;
	*pa = 20;//错误
	printf("%d\n", a);


	return 0;
}

在这里插入图片描述
可以看出,在指针变量pa前面加上const,那么该指针变量就无法进行去引用操作并赋值的操作
注意:const int* pa = &a;的效果和 int const * pa = &a;的效果一致

那const放在a的右边呢

int main() {
	const int a = 10;
	int* const pa = &a;
	*pa = 20;
	printf("%d\n", a);

	return 0;
}

这样就对pa的值没有限制作用了,加在右边的作用是限制指针变量的内容不能修改。
这里必须要弄懂三个概念的含义:

1.pa存放的是a的地址 2.pa是指针变量,也是一种变量,他有自己的地址 3.*pa是pa指向的空间即a的值

上述代码就是限制了变量pa里面存放的值不能改变

int main() {
	const int a = 10;
	int* const pa = &a;
	*pa = 20;
	printf("%d\n", a);
	int b = 0;
	pa = &b;//错误

	return 0;
}

在这里插入图片描述
结论:

  • const放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针改变但是指针变量本身的内容可以改变
  • const放在*的右边,修饰的是指针本身的内容,保证指针变量本身的内容不可以改变但是但是指针指向的内容可以改变

5.传值调用和传址调用

传值调用就是调用函数,参数是值。而传址调用就是传的地址
比如:

用函数实现交换两个数

void Swap(int x, int y) {
	int type = x;
	x = y;
	y = type;
}
int main() {
	int a = 10;
	int b = 20;
	printf("交换前:%d %d\n", a, b);
	Swap(a, b);
	printf("交换后:%d %d\n", a, b);

	return 0;
}


这里可以看到交换值不成功,这是因为实参传递给形参时,形参会单独创建一分临时空间来接受实参,对形参的修改不影响实参
可以使用指针来实现两个数的交换

void Swap(int *x, int *y) {
	int type = 0;
	type = *x;
	*x = *y;
	*y = type;
}
int main() {
	int a = 10;
	int b = 20;
	printf("交换前:%d %d\n", a, b);
	Swap(&a, &b);
	printf("交换后:%d %d\n", a, b);

	return 0;
}

在这里插入图片描述
传址调用可以让函数和主调函数建立真正的联系,在函数内部可以修改主调函数的变量;所以只是需要主调函数中的值进行计算,那么就可以使用传值调用;如果函数内部要修改主调函数中变量的值,那么就要传址调用。

/考研势在必行/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

桥接模式:解耦抽象与实现,实现灵活多变的扩展结构

文章目录 一、引言二、应用场景与技术背景三、模式定义与实现四、实例详解五、优缺点分析总结: 一、引言 ​ 桥接模式是一种结构型设计模式,它将抽象部分与它的实现部分分离,使它们可以独立变化。这种模式通过创建一个抽象层和实现层的结构&…

【前端素材】推荐优质后台管理系统Be admin平台模板(附源码)

一、需求分析 后台管理系统(或称作管理后台、管理系统、后台管理平台)是一种专门用于管理网站、应用程序或系统后台运营的软件系统。它通常由一系列功能模块组成,为管理员提供了管理、监控和控制网站或应用程序的各个方面的工具和界面。以下…

ThreeJS 几何体顶点position、法向量normal及uv坐标 | UV映射 - 法向量 - 包围盒

文章目录 几何体的顶点position、法向量normal及uv坐标UV映射UV坐标系UV坐标与顶点坐标设置UV坐标案例1:使用PlaneGeometry创建平面缓存几何体案例2:使用BufferGeometry创建平面缓存几何体 法向量 - 顶点法向量光照计算案例1:不设置顶点法向量…

探索设计模式的魅力:状态模式揭秘-如何优雅地处理复杂状态转换

​🌈 个人主页:danci_ 🔥 系列专栏:《设计模式》 💪🏻 制定明确可量化的目标,并且坚持默默的做事。 探索设计模式的魅力:状态模式揭秘-如何优雅地处理复杂状态转换 文章目录 一、案例…

使用 ES|QL 优化可观察性:简化 Kubernetes 和 OTel 的 SRE 操作和问题解决

作者:Bahubali Shetti 作为一名运营工程师(SRE、IT 运营、DevOps),管理技术和数据蔓延是一项持续的挑战。 简单地管理大量高维和高基数数据是令人难以承受的。 作为单一平台,Elastic 帮助 SRE 将无限的遥测数据&#…

亿道丨三防平板丨加固平板丨工业平板丨选购三大要素?

在当今的市场中,企业在复杂多变的大环境中面临着许多挑战和机遇。其中包括尽管消费者对产品的需求不断增长,但劳动力短缺仍在继续的问题。大部分企业正在采用更具成本效益的方法,并希望利用他们获得的硬件和软件做更多的事情,因此…

Stable Diffusion 3的到来巩固了 AI 图像对抗 Sora 和 Gemini 的早期领先优势

Stability AI 将其更改为 Stable Diffusion 3。VentureBeat 报道称,Stability AI 的下一代旗舰 AI 图像生成模型将使用类似于 OpenAI 的 Sora 的扩散变压器框架。其当前模型仅依赖于扩散架构。虽然尚未发布,但您可以在等候名单中注册。 官方网址链接&am…

28V、115V、270V坦克装甲车启动电源:为现代战争注入新能量

28V、115V、270V坦克装甲车启动电源:为现代战争注入新能量 世界新格局的诞生后,现代战争已经从传统的陆地、海洋、空中扩展到了网络空间和外太空。在这种背景下,各种先进的武器装备不断涌现,为国家安全提供有力保障。28V、115V、2…

微信小程序01: springboot获取accessToken方式 ,配合redis缓存使用

全文目录,一步到位 1.前言简介1.1 专栏传送门1.1.1 上文小总结1.1.2 上文传送门 2. springboot获取accessToken2.0 accessToken简介2.1 准备工作2.2 具体代码使用与注释如下2.2.1 代码解释(一)[无需复制]2.2.2 代码解释(二)[无需复制] 2.3 最后一步 获取accessToken2.3.1 两行代…

kubernetes负载均衡部署

目录 1.新master节点的搭建 对master02进行初始化配置(192.168.88.31) 将master01的配置移植到master02 修改master02配置文件 2.负载均衡的部署 两台负载均衡器配置nginx 部署keepalived服务 所有node节点操作 总结 实验准备: k8s…

【区块链】智能交易模式下的数据安全流通模型

【区块链】智能交易模式下的数据安全流通模型 写在最前面**区块链智能交易模式概述****数据安全流通的挑战****数据安全流通模型的核心要素****实现数据安全流通的区块链技术****区块链智能交易模式下数据安全流通模型的设计原则****数据安全流通模型的应用案例分析****面临的挑…

海外媒体推广通过5个发稿平台开拓国际市场-华媒舍

随着全球化的进程,国际市场对于企业的吸引力日益增加。进入国际市场并获得成功并非易事。海外媒体推广发稿平台成为了一种重要的营销手段,能够帮助企业在国际市场中建立品牌形象、传递信息和吸引目标受众。本文介绍了五个海外媒体推广发稿平台&#xff0…

什么是去中心化云计算?

去中心化云计算是一种新型的云计算方式,它与传统的中心化云计算不同,将数据和计算任务分布到多个节点上,而不是将数据集中存储在中心服务器上。这种云计算方式具有许多优势,包括提高数据安全性、降低运营成本、增强可扩展性和灵活…

2_怎么看原理图之协议类接口之UART笔记

通信双方先约定通信速率,如波特率115200 一开始时,2440这边维持高电平 1> 开始发送时,由2440将(RxD0)高电平拉低,并持续一个T的时间(为了让PC机可以反应过来),T1/波…

无公网IP情况下如何远程查看本地群晖NAS存储的文件资源

文章目录 前言本教程解决的问题是:按照本教程方法操作后,达到的效果是前排提醒: 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机:1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

提示工程(Prompt Engineering)、微调(Fine-tuning) 和 嵌入(Embedding)

主要参考资料: 还没搞懂嵌入(Embedding)、微调(Fine-tuning)和提示工程(Prompt Engineering)?: https://blog.csdn.net/DynmicResource/article/details/133638079 B站Up主Nenly同学…

XUbuntu22.04之解决:systemd-journald占用cpu过高问题(二百一十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒…

2024如何恢复旧版的Chrome的主题样式

起因 chrome 更新版本之后的主题样式变成了浅紫色的页签卡样式,感觉很不习惯,也很不喜欢 如何换回旧版主题 通过主题商店,安装旧版本的主题 主题商店搜索下面,或着直接访问下面的地址 Chrome Original White Theme https://…

【原理图专题】OrCAD Capture原理图属性过滤器

在使用Orcad Capture CIS进行原理图设计时,在拷贝了其他器件或网络时,往往连同属性一起带入到自己的设计中。 如下所示其中很多属性是没有用到的。比如我们在导BOM或平时看的时候,根本不关注器件是哪个作者(author)画的。还有一些如Graphic等等。 原理图中属性太多,而想…

【力扣hot100】刷题笔记Day11

前言 科研不顺啊......又不想搞了,随便弄弄吧,多花点时间刷题,今天开启二叉树! 94. 二叉树的中序遍历 - 力扣(LeetCode) 递归 # 最简单递归 class Solution:def inorderTraversal(self, root: TreeNode) …