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

2023-12-28 20:04

本文主要是介绍C语言KR圣经笔记 5.1指针和地址 5.2指针和函数参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第五章 指针和数组

指针是包含变量地址的变量。在 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 用什么类型作为其返回值? 

这篇关于C语言KR圣经笔记 5.1指针和地址 5.2指针和函数参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/547142

相关文章

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

webapp地址

F:\LSP\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX