C语言KR圣经笔记 5.1指针和地址 5.2指针和函数参数

news2025/4/13 4:18:21

第五章 指针和数组

指针是包含变量地址的变量。在 C 语言中,指针被大量使用,部分原因是有时只能用指针来表达某种计算,而部分原因是相比其他方式,指针通常能带来更紧凑和高效的代码。指针和数组是紧密关联的;本章也讲探讨它们的关系,并演示如何利用这个关系。

指针曾经和 goto 语句一起,被归结为用于创建“让人不可能理解”的程序的绝妙方式。如果粗心大意地使用指针,这个说法当然是对的,而且很容易创建指向不可预料位置的指针。然而,通过训练和规范约束,指针可以用来得到清晰性和简洁性。这正是我们在本书中尽量展示的方面。

ANSI C 的主要改动是把如何操纵指针的规则给明确了,实际上就是把优秀程序员已有的实践,和优秀编译器已加强的地方,做成了强制要求。另外,void * 类型(指向 void 的指针)取代了 char * ,作为通用指针的正确类型。

5.1 指针和地址

我们先从内存组织的简化图开始。一台典型的机器有一组连续编号或者叫编址的内存单元,这些内存单元可以被各自独立操作,或者是按连续分组进行操作。通常的情况是任一个字节可以是一个 char, 一对单字节的内存单元可以被当作是 short 整型,而四个连续的字节组成一个 long。指针是一组(通常是2个或4个)可以保存地址的单元。这样,如果 c 是一个char 而 p 是指向它的指针,则我们可以用下图来表示这种情况:

一元操作符 & 给出一个对象的地址,因此如下语句

p = &c;

把 c 的地址赋给 p,而 p 被称作“指向” c。操作符 & 只能用于内存中的对象:变量和数组元素。它不能用于表达式、常量 或者寄存器(register)变量。

一元操作符 * 是 间接 或者叫 解引用 操作符;当用于指针时,它访问指针指向的对象。假定 x 和 y 是整数而 ip 是指向 int 的指针。下面这段人为构造(但没有实际用途)的代码,演示了如何声明指针,以及如何使用 & 和 *:

int x = 1, y = 2, z[10];
int *ip;        /* ip是指向int的指针 */

ip = &x;        /* ip现在指向x */
y = *p;         /* y现在是1 */
*p = 0;         /* x现在是0 */
ip = &z[0];     /* ip现在指向z[0] */

x,y,z的声明不需要多说了。指针 ip 的声明为

int *ip;

这种形式旨在助记;它表示表达式 *ip 是一个 int。变量声明的语法,模仿了变量可以在其中出现的表达式的语法。这个推理也适用于函数声明。例如

double *p, atof(char *);

表示,在一个表达式中,*p 和 atof(s) 都有着 double 类型的值,而 atof 的参数是 char 的指针。

你还应当注意到有个隐含的约束:指针被限制指向一种特定的对象类型。(有一个例外,“指向 void 的指针”被用来保存任意类型的对象,但不能对它自身解引用。我们会在5.11节说明)

如果 ip 指向整数 x,则 *ip 可以出现在任何 x 可以出现的上下文中,因此

*ip = *ip + 10;

将 *ip 增加10。

一元操作符 & 和 * 比算术操作符的结合更为紧密,因此赋值表达式

y = *ip + 1

首先取出 ip 所指向的对象,将其加1,并把结果赋给 y。而

*ip += 1

会把 ip 指向的对象递增,正如

++*ip

(*ip)++

最后一个例子中的括号是必须的;如果没有括号,则表达式会对 ip 递增,而不会对它指向的内容递增,因为一元操作符 * 和 ++ 是从右至左结合。

最后,由于指针是变量,那么不解引用也可以使用它们。例如,如果 iq 是另一个 int 的指针,

iq = ip

将 ip 的内容拷贝给 iq,这样就使 iq 也指向 ip 所指的对象了。

5.2 指针和函数参数

由于C将参数传给函数时是值传递,因此没有直接的方法可以使被调函数修改调用者函数中的变量。例如,一个排序例程可能会用一个 swap 函数来交换两个无序的元素。如果写

swap(a, b);

其中 swap 函数的定义为

void swap(int x, int y)    /* 错误 */
{
    int temp;

    temp = x;
    x = y;
    y = temp;
}

但这样是不行的。因为所有调用都是值传递,swap 不能影响调用它的例程中的参数 a 和 b。上面的函数只是交换了 a 和 b 的拷贝

要得到想要的效果,方法是让调用程序传递要修改的值的指针

swap(&a, &b);

由于操作符 & 得到了变量的地址,&a 是 指向 a 的指针。在 swap 之中,参数被声明为指针,而通过它们来间接访问操作数。

void swap(int *px, int *py)    /* 交换 *px和 *py */
{
    int temp;

    temp = *px;
    *px = *py;
    *py = temp;
}

用图来表示就是:

指针参数使一个函数能够访问并改变调用它的函数中的对象。举个例子,考虑实现一个函数 getint ,该函数执行自由格式的输入转换:将一个字符流拆分成多个整数值,每次调用得到一个整数。getint 必须返回它所找到的值,也必须在没有更多输入时指示文件结束。这两个值必须通过不同的路径返回,因为不管EOF使用什么值,都有可能是一个输入整数的值。

一种解决方案是让 getint 把文件结束状态作为函数返回值,而同时使用一个指针参数来保存转换后的整数,传递回给它的调用者函数。这也是 scanf 使用的模式,详见 7.4节。

下面的循环通过调用 getint 来填充整型数组:

int n, array[SIZE], getint(int *);

for (n = 0; n < SIZE && getint(&array[n]) != EOF; n++)
    ;

每次调用都将 array[n] 设置为从输入中找到的下一个整数,并对 n 递增。注意,必须将 array[n] 的地址传给 getint 。否则,getint 没有办法将转换后的整数传给调用者。

我们这个版本的 getint 在文件结尾时返回 EOF,在下一个输入不是数字时返回0,而在输入包含合法的数字时返回一个正值:

#include <ctype.h>

int getch(void);
void ungetch(int);

/* getint: 将下一个整数从输入存入 *pn */
int getint(int *pn)
{
    int c, sign;

    while (isspace(c = getch()))    /* 跳过空白字符 */
        ;
    if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
        ungetch(c);        /* 非数字 */
        return 0;
    }
    sign = (c == '-') ? -1 : 1;
    if (c == '+' || c =='-')
        c = getch();
    for (*pn = 0; isdigit(c); c = getch())
        *pn = 10 * *pn + (c - '0');
    *pn *= sign;
    if (c != EOF)
        ungetch(c);
    return c;
}

在 getint 内, *pn 自始至终 都被当作普通的 int 变量来使用。我们还使用了 getch 和 ungetch(见4.3节),因此多读但又不得不读的一个字符,就能被推回给输入。

练习5-1、在我们的getint版本中,后面没有跟数字的单独的 + 和 - 号,也被当作是合法的数字,其值为 0。修改这个问题,将这种字符推回给输入。

练习5-2、模仿 getint,写一个浮点数版本的 getfloat 函数。 getfloat 用什么类型作为其返回值? 

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

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

相关文章

抓包工具Charles安装及使用

Charles 介绍 Charles 是在 Mac 下常用的网络封包截取工具&#xff0c;在做 移动开发时&#xff0c;我们为了调试与服务器端的网络通讯协议&#xff0c;常常需要截取网络封包来分析。 Charles 通过将自己设置成系统的网络访问代理服务器&#xff0c;使得所有的网络访问请求都…

VScode——下载、安装、配置C/C++环境(windows)

一.快速下载 还在因为vscode官方下载慢而头疼嘛&#xff0c;按这个步骤来直接起飞兄弟萌 首先进入vscode官方网站然后选择对应版本下载然后进入浏览器下载页面复制下载链接粘贴到地址栏 将地址中的/stable前换成vscode.cdn.azure.cn 即可实现超速下载 下面是一个国内镜像的下…

删除数据后, redis 内存占用还是很高怎么办?

现象&#xff1a; reids 做了数据删除&#xff0c;数据量不大&#xff0c;使用 top 命令看&#xff0c;发现还是占用大量内存 原因&#xff1a; 1.redis 底层内存根据内存分配器分配&#xff0c;不会立刻释放 2.redis 释放的内存空间不是连续的&#xff0c;存在碎片 内存碎…

DP进阶之路——整数拆分

343. 整数拆分 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输入: n 10 输出: 36 解释…

YOLOv8训练自定义数据集和运行参数解读

1、YOLOv8深度学习环境搭建及安装 1.1. Yolov8介绍 设置操作类型 YOLOv8模型可用于各种任务&#xff0c;包括检测、分割和分类。这些任务的不同之处在于它们产生的输出类型和它们要解决的特定问题。 **检测:**检测任务涉及识别和定位图像或视频中感兴趣的对象或区域。YOLO模…

任务调度-hangfire

目录 一、Hangfire是什么&#xff1f; 二、配置服务 1.配置Hangfire服务 2.添加中间件 3.权限控制 三、配置后台任务 1.在后台中调用方法 2.调用延时方法 3.执行周期性任务 四、在客户端上配置任务 1.在AddHangfire添加UseHangfireHttpJob方法 2.创建周期任务 3.创建只读面板 总…

【《设计模式之美》】如何取舍继承与组合

文章目录 什么情况下不推荐使用继承&#xff1f;组合相比继承有哪些优势&#xff1f;使用组合、继承的时机 本文主要想了解&#xff1a; 为什么组合优于继承&#xff0c;多用组合少用继承。如何使用组合来替代继承哪些情况适用继承、组合。有哪些设计模式使用到了继承、组合。 …

腾讯云轻量应用服务器和云服务器有什么不同?

腾讯云轻量服务器和云服务器CVM该怎么选&#xff1f;不差钱选云服务器CVM&#xff0c;追求性价比选择轻量应用服务器&#xff0c;轻量真优惠呀&#xff0c;活动 https://curl.qcloud.com/oRMoSucP 轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年&#xff0c;540元三…

linux ext3/ext4文件系统(part1格式化)

ext4文件系统结构 ext3的代码已经在v4.3被删除掉了&#xff08;ARM: tegra: Rebuild default configuration on v4.3-rc1 torvalds/linux241e077 GitHub&#xff09; ext4格式化的代码可以参考e2fsprogs的实现&#xff1a;mke2fs.c 格式化后的文件系统结构如下图&#xf…

AI绘图软件,科技之旅绘画

科技与艺术的碰撞总能产生令人惊叹的火花&#xff0c;现在小编要给大家介绍一款引领未来艺术潮流的AI绘图软件——首助编辑高手。这是一款将人工智能与创意绘画完美结合的软件&#xff0c;它将为你打开一扇全新的创意之门。 所需工具&#xff1a; 一个【首助编辑高手】软件 …

mysql原理---InnoDB统计数据是如何收集的

以下聚焦于 InnoDB 存储引擎的统计数据收集策略。 1.两种不同的统计数据存储方式 InnoDB 提供了两种存储统计数据的方式&#xff1a; (1). 永久性的统计数据 这种统计数据存储在磁盘上&#xff0c;也就是服务器重启之后这些统计数据还在。 (2). 非永久性的统计数据 这种统计数…

❀My学习小记录之算法❀

目录 算法:) 一、定义 二、特征 三、基本要素 常用设计模式 常用实现方法 四、形式化算法 五、复杂度 时间复杂度 空间复杂度 六、非确定性多项式时间&#xff08;NP&#xff09; 七、实现 八、示例 求最大值算法 求最大公约数算法 九、分类 算法:) 一、定义 …

Smartbi获工信部旗下赛迪网“2023行业信息技术应用创新产品”奖

近日&#xff0c;由工信部旗下的赛迪网、《数字经济》杂志共同主办的2023行业信息技术应用创新大会上&#xff0c;“信息技术应用创新成果名单”重磅揭晓&#xff0c;思迈特软件凭借“Smartbi 自然语言分析引擎”斩获“2023行业信息技术应用创新产品”大奖。 据了解&#xff0c…

codellama模型部署(待补充)

codellama介绍 Code Llama 是一个基于Llama 2的大型代码语言模型系列&#xff0c;在开放模型、填充功能、对大输入上下文的支持以及编程任务的零样本指令跟踪能力中提供最先进的性能。我们提供多种风格来覆盖广泛的应用程序&#xff1a;基础模型 (Code Llama)、Python 专业化 …

【String str = new String(“AAA“) 创建了几个对象?】

✅典型解析 创建的对象数应该是1个或者2个。 首先要清楚什么是对象? Java是一种面向对象的语言&#xff0c;而Java对象在JVM中的存储也是有一定的结构的&#xff0c;在HotSpot虚机中&#xff0c;存储的形式就是oop-klass model&#xff0c;即ava对象模型。我们在Java代码中&am…

Vue框架引入Axios

首先已经创建好了 Vue 框架&#xff0c;安装好了 node.js。 没有完成的可按照此博客搭建&#xff1a;搭建Vue项目 之后打开终端&#xff0c;使用命令。 1、命令安装 axios 和 vue-axios npm install axios --save npm install vue-axios --save2、package.json 查看版本 在 p…

Binder系列-service_manager.c

1. service_manager的任务 open 驱动告诉驱动&#xff0c;它是“servicemanager”在一个循环里 从驱动读取数据 解析数据 调用&#xff0c;根据code执行注册服务或者获取服务 &#xff08;图来自韦老师的视频&#xff09; 2.代码流程 2.1.open驱动 //framework…

elasticsearch列一:索引模板的使用

概述 近期一直在负责es这块&#xff0c;就想着和大家分享一些使用经验&#xff0c;我们从存储、查询、优化、备份、运维等几个方面来做分享。今天咱们先看下如何更加合理的存储数据。 初见索引模板 记得刚接触es还是18年那会&#xff0c;项目上线后因一些原因导致日志这部分的…

深入理解JVM虚拟机第三十二篇:详解JVM当中本地方法栈

😉😉 欢迎加入我们的学习交流群呀! ✅✅1:这是孙哥suns给大家的福利! ✨✨2:我们免费分享Netty、Dubbo、k8s、Mybatis、Spring等等很多应用和源码级别的高质量视频和笔记资料,你想学的我们这里都有! 🥭🥭3:QQ群:583783824 📚📚 工作VX:BigTreeJava 拉你…

059:vue中使用 AJAX来读取来自XML文件的信息

第059个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…