第3章“程序的机器级表示”:理解指针

news2024/11/16 3:52:15

指针是 C 语言的一个重要特色。它们提供一种统一方式,能够远程访问数据结构。

指针基本的概念其实非常简单,下面的代码说明了许多这样的概念:

struct str { /* Example Structure */
	int t;
	char v;
};

union uni { /* Example Union */
	int t;
	char v;
} u;

int g = 15;

void fun(int *xp)
{
	void (*f)(int*) = fun; /* f is a function pointer */
	
	/* Allocate structure on stack */
	struct str s= {1, 'a'}; /* Initialize structure */

	/* Allocate union from heap */
	union uni *up = (union uni *) malloc(sizeof(union uni));
	
	/* Locally declared array */
	int *ip[2] = {xp, &g};

	up->v = s.v + 1;
	
	printf("ip = %p, *ip = %p, **ip = %d\n", 
			ip, *ip, **ip);
	printf("ip+1 = %p, ip[1] = %p, *ip[1] = %d\n", 
			ip+1, ip[1], *ip[1]);
	printf("&s.v = %p, s.v = '%c'\n", &s.v, s.v);
	prinf("&up->v = %p, up->v = '%c'\n", &up->v, up->v);
	printf("f = %p\n", f);
	if (--(*xp) > 0) 
		f(xp); /* Recursive call of fun */
}

int test()
{
	int x = 2;
	fun(&x);
	return x;
}
  • 每个指针都有一个类型。这个类型表明指针指向的对象是哪一类的。示例代码中,看到如下的一些指针类型:
    在这里插入图片描述
    注意,该表中,既指明了指针本身的类型,也指出了它所指向的对象的类型。通常,如果对象类型为 T T T,则指针的类型为 T ∗ T* T。特殊的 void * 类型代表通用指针。例如,malloc 函数返回一个通用指针,然后它再被强制类型转换成一个有类型的指针(第21行)。

  • 每个指针都有一个值。这个值是某个指定类型的对象的地址。特殊的 NULL(0) 值表示该指针没有指向任何地方。

  • 指针是用 & 运算符创建的。这个运算符可应用到任何 lvalue 类的 C 表达式上,即可以出现在赋值语句左边的表达式,这样的例子包括变量以及结构、联合和数组的元素。示例代码中,看到这个操作符应用到全局变量 g 上(第24行),应用到结构元素 s.v 上(第32行),应用到联合元素 up->v 上(第33行),以及应用到局部变量 x 上(第42行)。

  • * 操作符用于指针的间接引用。其结果是一个值,它的类型与该指针的类型相关。看到间接引用应用到 ip*ip 上(第 29 行),应用到 ip[1] 上(第31行),以及应用到 xp 上(第 35 行)。此外,表达式 up->v(第 33 行)既间接引用了指针 up,同时还选取了域 v

  • 数组与指针是紧密联系的。可以引用一个数组的名字(但是不能修改),就好像它是一个指针变量一样。数组引用(如 a[3])与指针运算和间接引用(如 *(a+3)) 有一样的效果。可以在 29 行看到这一点,打印出数组 ip 的指针值,并用 *ip 引用它的第一项(元素0)。

  • 指针也可以指向函数。这提供了一个很强大的存储(storing)和传递代码引用的功能,这些代码可以被程序的某个其他部分调用。看看变量 f (第 15 行),它被声明为一个指向函数的变量,该函数以一个 int * 作为参数,并返回 void。赋值语句使 f 指向 fun。在后面使用 f(第 36 行)时,是在进行递归调用。

函数指针

函数指针声明的语法对于程序员新手是难以理解的。对于这样一个声明:

void (*f)(int *);

要从里(从 “f” 开始)往外读。因此,看到像 “(*f)” 表明的那样,f 是一个指针。像 “(*f)(int *)” 表明的那样,它是一个指针,指向一个以一个 int * 作为参数的函数。最后,它是一个指向一个以 int * 作为参数并返回 void 的函数的指针。

*f 两边的括号是必须的,因为否则声明

void *f(int *);

就要读成

(void *) f(int *);

也就是,它会被解释成一个函数原型,声明了一个函数f,它以一个 int * 作为参数并返回一个 void *

代码中包含很多对 prinf 的调用,打印出一些指针(用指令 %p)和值。在执行时,产生下面这样的输出:

在这里插入图片描述
可以看到,这个函数执行了两次——第一次是从 test 中直接调用(第 42 行),而第二次是间接的递归调用(第 36 行)。可以看出,打印出来的指针值都对应于地址。那些从 0xbfffef 开始的指针指向栈中的位置,而其他的是全局存储的一部分(0x804965c),或是可执行代码的一部分(0x8048414) ,或是堆中的位置(0x8049760 和 0x8049770)。

数组ip 被初始化了两次——每次调用 fun 都初始化一次。第二次的值(0xbfffef68)小于第一次的值(0xbfffefa8),这是因为栈是向下增长的。不过,数组的内容两次都是一样的。数组元素 0(*ip) 是一个指向 test 栈帧中变量 x x x 的指针,元素 1 是一个指向全局变量 g g g 的指针。

可以看到,结构 s s s 也被初始化了两次,两次都是在栈中,而变量 up 指向的联合是在堆中分配的。

最后,变量 f f f 是一个指向函数 fun 的指针。在反汇编代码中,看到如下 fun 的初始化代码:

在这里插入图片描述
打印出来的指针 f f f 的值 0x8048414 就是 fun 的代码中第一条指令的地址。

向函数传递参数

其他语言(如Pascal)提供两种方式来向过程传递参数——传值(by value)和引用(by reference)。

传值是指调用者提供实际的参数值,而引用是指调用者提供一个指向该值的指针。

在 C 中,所有的参数都是传值的,但是可以通过显式地产生一个指向一个值的指针,并把该指针传递给过程,从而实现了引用参数的效果。函数 fun(&x) 中的参数 xp 就是这样的。第一次调用 fuc(&x) 时(第 42 行),给了一个函数对 test 中局部变量 x x x 的引用。每次调用 fun 时,这个变量都会减小,从而在两次调用之后,递归会停止。

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

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

相关文章

大厂C++面试基础题第1辑——虚函数七题精讲之一

> “虚函数的作用” 是面向对象的C编程最基础也最核心的知识点,如果不能无法正确回答本题,则只此一题,不管大厂还是小厂,都铁定无缘了。 概述 “虚函数” 是 C面向对象三最:最基础、最重要、最关键的知识点。我们从…

什么是Vue的Vite构建工具?如何使用Vite进行项目开发

什么是Vue的Vite构建工具?如何使用Vite进行项目开发 介绍 Vite是一个由Vue.js核心团队开发的构建工具。它的目标是提供一种快速的开发体验,同时保持生产环境的稳定性和可靠性。Vite使用了ES模块作为开发环境的原生模块格式,通过在开发服务器…

C++11中的关键字constexpr

文章目录 1、constexpr修饰普通变量2、constexpr修饰函数3、constexpr修饰类的构造函数 constexpr 关键字的功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段。C 11 标准中,constexpr 可用于修饰普通变量、函数&…

【Leetcode】DP | 序列及子数组问题

300 最长递增子序列 求数组最长严格递增子序列的长度。 D [ i ] D[i] D[i]代表以 n u m s [ i ] nums[i] nums[i]结尾的最长递增子序列的长度。 D [ i ] max ⁡ j < i , n u m s [ i ] > n u m s [ j ] ( D [ j ] 1 ) D[i] \max_{j < i,\ nums[i]>nums[j]}(D[…

什么是Vue的UI框架?

什么是Vue的UI框架&#xff1f; Vue.js 是一款流行的 JavaScript 框架&#xff0c;用来构建现代的单页面应用程序&#xff08;SPA&#xff09;。Vue.js 提供了丰富的功能和 API&#xff0c;但是在构建应用程序时&#xff0c;我们还需要使用一些 UI 组件来实现复杂的交互和界面…

【分布式能源选址与定容】光伏、储能双层优化配置接入配电网研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

02- 输入、输出及运算符(C语言)

一 输入、输出 1.1 输出函数 printf 函数是一个可变参数函数&#xff0c;参数的个数不定&#xff1a;int printf(const char *format, ...) printf("%d\n", x); printf("%d %d\n", x, y); 1.2 输入函数 1.2.1 scanf函数&#xff1a;int scanf(const …

利用AI点亮副业变现:5个变现实操案例的启示

AI变现副业实操案例 宝宝起名服务AI科技热点号头像壁纸职业头像收徒&#xff1a;萌娃头像定制头像平台挂载 小说推广号流量营销号百家号AI共创计划公众号流量主 知识付费知识星球小报童&#xff1a; 整体思维导图&#xff1a; 在这里先分享五个实操案例: 宝宝起名服务AI科技热…

[MySQL]一文带你学明白数据库控制语言——DCL

前言 嗨咯&#xff0c;小伙伴大家好呀&#xff01;好几天没见了&#xff0c;周末过得怎么样啊&#xff01;之前学过的SQL语句不会都忘了吧。如果忘了的话大家可以看一下前几期的文章。本期要学习的是SQL语句中的数据库控制语句——DCL&#xff0c;学习完毕之后MySQL中的SQL语句…

探索ll-hls低延迟直播协议

HLS全称为HTTP Live Streaming&#xff0c;其中m3u8作为描述协议&#xff0c;指向一系列切片文件。支持多码流与自适应码率&#xff0c;支持广告无缝播放&#xff0c;支持CMAF协议的低延时直播&#xff0c;也支持CDN动态选择。 我们先看下HLS整体架构&#xff0c;由三部分构成…

存储技术3 数据保护: RAID

Why RAID 性能限制了磁盘驱动单独的驱动存在预期的使用寿命 MTBF测量若一个驱动器的MTBF是750 000小时&#xff0c; 阵列中有100个驱动&#xff0c; 阵列的MTBF会变成 750000 / 100 7500小时 RAID用于减缓这个问题RAID特点 增大容量高可用性增强的性能 RAID implementation…

仿微信我的列表功能菜单按钮 我的个人中心页面功能菜单

前端vue自定义仿微信我的列表功能菜单按钮 我的个人中心页面功能菜单, 下载完整代码请访问https://ext.dcloud.net.cn/plugin?id12990 效果图如下: #### 使用方法 使用方法 <!-- leftTitle:标题 icon&#xff1a;左边图标 click&#xff1a;点击事件 --> <ccMe…

【java】IO流

IO流 原理 分类 字节流与字符流 节点流与包装流 Java IO详解&#xff08;五)------包装流 - YSOcean - 博客园 (cnblogs.com)JAVA I/O流 字符流和字节流、节点流和处理流(包装流、过滤流)、缓冲流_过滤流和缓冲流,字节流的关系_X-Dragon烟雨任平生的博客-CSDN博客 字符流 i…

算法模板(4):动态规划(2)

8.树形DP 没有上司的舞会 树上最大独立集问题 Ural 大学有 N N N 名职员&#xff0c;编号为 1 ∼ N 1 \sim N 1∼N。他们的关系就像一棵以校长为根的树&#xff0c;父节点就是子节点的直接上司。每个职员有一个快乐指数&#xff0c;用整数 H i H_i Hi​ 给出&#xff0c;…

顺序查找和折半查找

顺序查找和折半查找 顺序查找 一、算法思想 顺序查找&#xff0c;又叫“线性查找”&#xff0c;通常用于线性表。 算法思想&#xff1a;从头到尾挨个找&#xff08;或者反过来也OK&#xff09; 二、算法实现 结构体定义 typedef struct{ElemType *elem;int TableLen; }SS…

SpringBoot + Vue 的留守儿童系统的研究与实现

文章目录 1.研究背景2. 技术栈3.系统分析4系统设计5系统的详细设计与实现5.1系统功能模块5.2管理员功能模块 1.研究背景 以往的留守儿童爱心的管理&#xff0c;一般都是纸质文件来管理留守儿童爱心信息&#xff0c;传统的管理方式已经无法满足现代人们的需求&#xff1b;使用留…

变压器差动保护的影响因素和相应的措施

由于变压器一、二次电流、电压大小不同&#xff0c;相位不同&#xff0c;电流互感器特性差异&#xff0c;电源侧有励磁电流&#xff0c;都将造成不平衡电流流过继电器&#xff0c;必须采用相应措施消除不平衡电流的影响。 主要措施 &#xff08;1&#xff09;减小稳态情况下的…

SpringCloud学习笔记(四)RabbitMQ

一、同步通讯的优缺点 时效性较强&#xff0c;可以立即得到结果但是耦合度高&#xff0c;性能和吞吐能力下降有额外的资源消耗 二、异步通讯的优缺点 耦合度低&#xff0c;吞吐量提升故障隔离&#xff0c;流量削峰依赖于Broker的可靠性、安全性和吞吐能力 三、什么是MQ MQ…

物联网Lora模块从入门到精通(七)串口通讯

一、前言 在Lora模块的程序设计中&#xff0c;串口通信一定是一个极其重要且常用的通信方式&#xff0c;借助串口通信&#xff0c;我们不但可以向外传输我们获取的数据&#xff0c;还可以根据外部指令做出相应。 同样的&#xff0c;在例程中&#xff0c;为我们提供了一个名为us…

SSM简易项目实战(基本CRUD操作):速速来学+项目完整源码

前言&#xff1a;又是一年暑期将至&#xff0c;找实习&#xff1f;手里没几个项目恐怕不行&#xff0c;今天给各位带来一个较为简易的Web项目&#xff0c;快来学吧&#xff01; 一、项目简介 1、云借阅图书管理系统 技术栈&#xff1a;&#xff08;SpringSpringMVCMybatisMyS…