C语言KR圣经笔记 2.8自增和自减 2.9位运算 2.10赋值

2.8 自增和自减操作符

C提供了两个不同寻常的操作符,用于对变量进行自增和自减。自增操作符++对操作数加上1,而自减操作符 -- 对操作数减去1。我们已经频繁使用++ 对变量进行自增,如:

if (c == '\n')++nl;

不寻常之处在于 ++ 和 -- 既能用作前缀操作符(在变量之前,如++n),又能用作后缀(在变量之后,如n++)。两种情况下,效果都是n递增。不过表达式 ++n 是在它的值被使用之前对n进行递增,而 n++是在它的值被使用之后对n进行递增。这意味着如果不仅要用到递增的效果,还要用到表达式的值时,++n 和 n++ 是不一样的。如果 n 为 5,则

x = n++;

将x设为5,而

x = ++n;

将x设为6。两种情况下,n都会变成6。自增和自减操作符只能用于变量;像 (i+j)++ 这样的表达式是非法的。

当只需要用到递增效果,而不需要值的时候,如

if (c == '\n')nl++;

前缀和后缀是一样的。不过有些情况下会专门要求使用前缀,而有些则专门使用后缀。举个例子,看看下面这个函数 squeeze(s, c) ,将字符串s中出现的所有字符c都删除:

/* squeeze: 从s中删除所有的c */
void squeeze(char s[], int c)
{int i, j;for (i = j = 0; s[i] != '\0'; i++)if (s[i] != c)s[j++] = s[i];s[j] = '\0'; 
}

每当非c字符出现时,它就被拷贝到当前的 j 位置,而只有这时 j 才会自增,为接收下一个字符做准备。与下面的写法是完全相同的:

if (s[i] != c) {s[j] = c;j++;
}

类似结构的另一个例子来自于我们在第一章写的 getline 函数,其中的

if (c == '\n') {s[i] = c;i++;
}

可以替换成更紧凑的形式:

if (c == '\n')s[i++] = c;

第三个例子可以看看标准库函数 strcat(s, t),它将字符串 t 连接到字符串 s 的末尾。strcat 假定 s 有足够的空间来存放合并的结果。按我们下面的写法, strcat 不返回值,标准库的strcat版本返回指向结果字符串的指针。

/* strcat: 把t拼接到s的末尾;s必须足够大 */
void strcat(char s[], char t[])
{int i, j;i = j = 0;while (s[i] != '\0')    /* 找到s的结尾 */++s;while ((s[i++] = t[j++]) != '\0')    /* 拷贝t */;
}

由于每个字符都要从 t 拷贝到 s,++后缀同时用于 i 和 j,以保证它们在循环的下一轮时处于正确的位置。

练习2-4,写另一个版本的 squeeze(s1, s2),把字符串s1中出现的所有字符串 s2 都删除

练习2-5,写一个函数 any(s1, s2),返回字符串s2中任意字符在字符串s1中首次出现的位置,如果s1不包含s2的任何字符,则返回-1。(标准库函数 strpbrk 做同样的事,但返回的是位置的指针)

2.9 位运算操作符

C提供了六个位操作符;它们只能用于整型,即 char, short, int 和 long,不管有无符号均可。

&        按位与

|        按位或

^        按位异或

<<        左移

>>        右移

~        取反(一元)

按位与操作符 & 经常用于屏蔽位中的某些部分;例如

 n = n & 0177

只保留 n 的低7位,其他位都设为0。

按位或操作符 |  用于将一些位打开(设为1)

x = x | SET_ON

会将 SET_ON 中为1的位设置到 x 上对应的位。

按位异或操作符 ^ 的规则是:若两个操作数对应的位不同时,则运算结果中该位设为1,若相同则设为0。

必须把位操作符 &  |  和逻辑运算符 && || 区分开,后者隐含的是从左到右的真值计算。

例如,如果 x是1,y 是2,则 x & y 结果为0 ,而 x && y 结果是1。

移位操作符 << 和 >> 分别对它们左边的操作数进行左移或者右移,移动的位数由右边的操作数(必须为非负数)指定。这样  x << 2 会将 x 的值左移两位,空出的位补0;这就等于乘以 4。对 unsigned 值进行右移,空位总是补0。对有符号的值进行右移,在有些机器上会填充符号位(算术移位),而有些机器上填充0(逻辑移位)。

一元操作符 ~ 得出整数的反码,也就是说,把每个1都转成0,每个0都转成1。例如

x = x & ~077

把 x 的低6位设为0。注意 x & ~077 不依赖于字长,这种写法比假定字长的写法好,比如 x & 0177700 假定 x 是 16位的值。可移植的写法不涉及额外的开销,因为 ~077 是常量,可以在编译期间求值。

函数 getbits(x, p, n) 可用来演示一些位操作符的用法,该函数返回 x 从位置 p 算起的 n 个位(右对齐)。我们假定第0位是最右边一位,并且 n 和 p 都是合适的正数。例如, getbits(x, 4, 3) 返回第 4,第3 和 第2 位的三个比特,右对齐。

/* getbits:返回从位置 p 开始的 n 个比特 */
unsigned getbits(int x, int p, int n)
{return x >> (p+1-n) & ~(~0 << n);
}

表达式 x >> (p+1-n) 把所需的比特位段移到字的最右边。~0 是所有位均为1;用 ~0 << n 把它左移 n 位,会把最右边的 n 个位变为 0;再对它取反,就得到一个最右边 n 位 都是 1 的掩码。

练习2-6、写个函数 setbits(x,p,n,y),返回值是 x 从位置 p 开始 的 n 个位 被 y 的最右边 n 个位替换后得到的值,x 其他位都不变。

练习2-7、写个函数 invert(x,p,n) ,返回值是 x 从位置 p 开始的 n 个位被翻转(即0变1,1变0)后的结果,其他位都不变。

练习2-8、 写个函数 rightrot(x,n),返回值是整数 x 向右旋转了 n 个位。

2.10 赋值操作符和表达式

 i = i + 2

像这种左侧的变量在右边马上重复出现的表达式,可以写成紧凑的形式:

i += 2

其中操作符 += 被称为 赋值操作符

大部分的二元操作符(像 + 这样左右两边各有一个操作数的操作符)都有一个对应的赋值操作符 op=, 其中 op 是下列操作符之一

 + - * / % << >> & ^ |

设有表达式 expr1 和 expr2,则

expr1 op= expr2

等价于

expr1 = (expr1) op (expr2)

唯一区别是在前面一种形式中, expr1 只会被计算一次。注意 expr2 两边的括号:

x *= y + 1

意思是

x = x * (y+1)

而不是

x = x * y + 1

看看下面这个例子,函数 bitcounts 统计其整数参数中为1的比特位数量。

/* bitcounts: 计算x中为1的比特位数量 */
int bitcounts(unsigned x)
{int b;for (b = 0; x != 0; x >> 1)if (x & 01)b++;return b;
}

将参数 x 声明为 unsigned 可以保证,对 x 做右移时,左边填充的总是0而不是符号位,不管这个程序在什么样的机器上运行。

除了简洁之外,赋值操作符的优势在于它们与人们思考的方式更为一致。我们会说“把 i 加上 2 ”或者“ i 自增 2 ”,而不是“拿到 i 的值,加上 2, 再把结果放回 i ”。因此 表达式  i += 2 比 i = i + 2 更好。另外,对于复杂的表达式如

yyval[yypv[p3+p4] + yypv[p1]] += 2

赋值操作符使代码容易理解,因为读者不必费力地检查两个长表达式是否相等,或者疑惑它们为什么不相等。而且赋值操作符甚至能帮助编译器生成高效的代码。

我们已经知道赋值语句是有值的,可以出现在表达式中;最常见的例子是

while ((c = getchar()) != EOF)...

其他赋值操作符( +=   -= 等)也能出现在表达式中,不过出现频率会低些。

在所有这样的表达式中,赋值表达式的类型就是它左边操作数的类型,而赋值表达式的值就是赋值之后的值。

练习2-9、在2的补码系统中,x &= (x-1) 删除x最右边的一个比特位。解释为什么。并使用这个发现来写一个更快的 bitcount 版本。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32-创建项目流程

一、基于STM官网得库进行开发 准备工作&#xff1a;下载STM库文件 1、创建项目文件夹 2、在keil 中new uVision project,然后选择刚刚创建得文件夹&#xff0c;在文件夹里面创建一个文件&#xff0c;用来存放这个项目&#xff0c;然后在文件夹里面&#xff0c;写个文件名&am…

掌握微信批量添加好友技巧,让你的社交更高效

微信作为当今的热门通讯工具&#xff0c;在企业营销中扮演着越来越重要的角色。然而&#xff0c;微信并没有提供自动批量添加好友的功能&#xff0c;给运营者带来了不小的挑战。一个个手动添加不仅耗时&#xff0c;而且频繁操作还容易导致账号被封。本文将介绍几种手动批量添加…

Python 应用 之 转换音频格式

目录 一、python音频转换 1、pydub 音频包安装 2、 ffmpeg安装 1&#xff09;、解压后&#xff0c;添加到环境变量中 2&#xff09;、可以直接放在python安装目录下 3、python程序 1&#xff09;、引入相关包 2&#xff09;、重命名 3&#xff09;、to Mp3 4&#xf…

el-table中的el-input标签修改值,但界面未更新,解决方法

el-table中的el-input标签修改值&#xff0c;界面未更新 在el-table中的el-input里面写的change事件根本不触发&#xff0c;都不打印&#xff0c;试了网络上各种方法都没用 然后换成input事件&#xff0c;input事件会触发&#xff0c;但界面也未更新。我在触发事件的时候&…

【设计模式】第23节:行为型模式之“策略模式”

一、简介 策略模式&#xff1a;定义一族算法类&#xff0c;将每个算法分别封装起来&#xff0c;让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端&#xff08;这里的客户端代指使用算法的代码&#xff09;。 二、适用场景 动态选择算法多种类似的行为 …

idea中启动多例项目配置

多实例启动 日常本地开发微服务项目时&#xff0c;博主想要验证一下网关的负载均衡以及感知服务上下线能力时&#xff0c;需要用到多实例启动。 那么什么是多实例启动嘞&#xff1f;简单说就是能在本地同时启动多个同一服务。打个比方项目中有一个 MobileApplication 服务&…

Redis 应用问题

1-缓存穿透 1.1-问题描述 Key 对应的数据在数据源并不存在&#xff0c;每次针对此 Key 的请求从缓存获取不到&#xff0c;请求都会压到数据源&#xff0c;从而可能压垮数据源。 比如&#xff1a;用一个不存在的用户ID 获取用户信息&#xff0c;不论缓存还是数据库都没有&…

项目实战之安装依赖npm install

文章目录 nvmdeasync包和node-gyp报错deasync包node-gyp报错 前言&#xff1a;有些人看着还活着其实已经凉了好一会儿了。 初拿到项目 初拿到项目肯定是先看配置 package.json的啦&#xff0c;看看都需要安装什么依赖&#xff0c;然后 npm install,OK结束 皆大欢喜。 ————…

第十五章 EM期望极大算法及其推广

文章目录 导读符号说明混合模型伯努利混合模型(三硬币模型)问题描述三硬币模型的EM算法1.初值2.E步3.M步初值影响p,q 含义 EM算法另外视角Q 函数BMM的EM算法目标函数LEM算法导出 高斯混合模型GMM的EM算法1. 明确隐变量, 初值2. E步,确定Q函数3. M步4. 停止条件 如何应用GMM在聚…

html获取网络数据,列表展示 一

html获取网络数据&#xff0c;列表展示 js遍历json数组中的json对象 image.png || - 判断数据是否为空&#xff0c;为空就显示 - <!DOCTYPE html> <html><head><meta charset"utf-8"><title>网页列表</title></head><b…

Vite 的基本原理,和 webpack 在开发阶段的比较

目录 1&#xff0c;webpack 的流程2&#xff0c;Vite 的流程简单编译 3&#xff0c;总结 主要对比开发阶段。 1&#xff0c;webpack 的流程 开发阶段大致流程&#xff1a;指定一个入口文件&#xff0c;对相关的模块&#xff08;js css img 等&#xff09;先进行打包&#xff0…

(五)库存超卖案例实战——使用zookeeper分布式锁解决“超卖”问题

前言 本节内容使用zookeeper实现分布式锁&#xff0c;完成并发访问“超卖”问题的解决。相对于redis分布式锁&#xff0c;zookeeper能够保证足够的安全性。关于zookeeper的安装内容这里不做介绍&#xff0c;开始本节内容之前先自行安装好zookeeper中间键服务。这里我们利用创建…

CIM与MES

CIM系统&#xff0c;全称计算机集成制造系统&#xff08;Computer-Integrated Manufacturing&#xff09;&#xff0c;是一种集成了计算机技术、网络通讯技术和软件系统的制造自动化框架。CIM的主要目标是整合制造过程中的所有活动&#xff0c;包括生产管理、设备管理和品质管理…

《巴渝小将》少儿电视综艺走进江小白金色黄庄拍摄圆满成功!

巴渝小将&#xff0c;乘风破浪&#xff01; 张扬巴渝魅力&#xff0c;展示少年风采&#xff0c;本期拍摄我们来到了位于江津的江小白金色黄庄。 江小白金色黄庄位于永兴镇黄庄村&#xff0c;是一座充满诗意又不乏童趣的农文旅综合体&#xff0c;基于当地良好的酿酒高粱产业基础…

SpringBoot系列之自定义Jackson对象映射器格式日期数据

开发环境 JDK 1.8SpringBoot2.2.1Maven 3.2Mysql5.7.36开发工具 IntelliJ IDEAsmartGit 背景 在我之前的博客中&#xff0c;有对Springboot2.0集成Mybatis Plus做了比较详细的描述&#xff0c;现在这篇博客介绍&#xff0c;基于开源的jackson api来自定义ObjectMapping&…

生产环境使用boost::fiber

简介 boost::fiber是一类用户级线程&#xff0c;也就是纤程。其提供的例子与实际生产环境相距较远&#xff0c;本文将对其进行一定的改造&#xff0c;将其能够投入到生产环境。 同时由于纤程是具有传染性的&#xff0c;使用纤程的代码里也全部要用纤程封装&#xff0c;本文将对…

DBeaver 23.2.3发布,带来多项增强和修复

数据库管理工具DBeaver最新版本23.2.3已经发布。这个版本带来了一系列的增强和修复&#xff0c;提升了用户的使用体验和工作效率。 以下是DBeaver 23.2.3版本的一些亮点功能&#xff1a; 数据编辑器方面的改进&#xff1a; Excel (XLSX) 导出现在支持列自动拟合&#xff0c;…

Perl爬虫程序

以下是一个使用Perl爬虫程序&#xff0c;用于爬取图像。每行代码的中文解释如下&#xff1a; #!/usr/bin/perl ​ use strict; use warnings; use Mojo::UserAgent; use JSON; ​ # 创建一个Mojo::UserAgent实例 my $ua Mojo::UserAgent->new; ​ # 使用获取代理 my $prox…

Centos7快速重置root密码

1、重新启动Centos7&#xff0c;5秒内按向下方向键&#xff0c;使其停留在开机界面&#xff0c;如下图。 2、按’e’键&#xff0c;进入如下界面&#xff0c;移动向下方向键至“linux16”开头的行。然后按向右的方向键移动,找到“ro”并将其修改为“rw init/sysroot/bin/bash…

竞赛选题 深度学习实现行人重识别 - python opencv yolo Reid

文章目录 0 前言1 课题背景2 效果展示3 行人检测4 行人重识别5 其他工具6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的行人重识别算法研究与实现 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c…