[C和指针] 4-语句、5-操作符和表达式
第4章 语句
4.1 表达式语句
C并不存在专门的“赋值语句”,赋值就是一种操作,就像加法和减法一样,所以赋值就在表达式内进行。
你只要在表达式后面加上一个分号,就可以把表达式转变成语句。所以下面这两个表达式实际上是表达式语句,而不是赋值语句。
x = y + 3;
ch = getchar();
警告:理解这点非常重要,因为像下面这样的语句也是完全合法的:
y + 3;
4.2 代码块
代码块就是位于一对花括号之内的可选的声明和语句列表。
{
declarations;
statements;
}
4.3 goto语句
goto error;
error:
printf("error!\n");
第5章 操作符和表达式
C提供了所有你希望编程语言应该拥有的操作符。它甚至提供了一些你意想不到的操作符。事实上,C被许多人诟病的一个缺点就是它品种繁多的操作符。C的这个特点使它很难精通。
另一方面。C的许多操作符具有其他语言的操作符无可抗衡的价值,这也是C适用于开发范围极广的应用程序的原因之一。
5.1 操作符
5.1.1 算数操作符
C提供了所有常用的算数操作符: + - * / %
5.1.2 移位操作符
移位操作只是简单地把一个值的位向左移动或者向右移动,在左移位中,值最左边的几位被丢弃,右边多出来的几个空位则有0补齐,右移同理。
左移位操作符为<<,右移位操作符为>>。移位的操作数必须都必须是整数类型。
下图是一个左移位的例子,它在一个8位的值上进行左移3位的操作,以二进制形式显示。
警告:
注意类似这种形式的移位:
a << -5;
左移-5位表示什么呢?是表示右移5位呢?还是根本不移位?在某台机器上,这个表达式实际上执行左移27位的操作。。。
5.1.3 位操作符
位操作符有这3种: &(与)、|(或)、^(异或)。
这些操作符以图表的形式总结如下:
位的操纵
可以使用移位操作符和位操作符来操纵一个整型值的单个位。
设定value为一整型值,这个例子把制定的位设置为1。
value = value | (1<<bit_number);
下面的例子将指定的位清零。
value = value & ~(1<<bit_number);
5.1.4 赋值操作符
赋值表达式的值就是左操作数的新值,如下面的语句所示:
a = x = y + 3;
赋值操作符的结合性(求值的顺序)是从右到左,所以这个表达式相当于:
a = (x = y + 3);
上面语句相当于
x= y+3;
a = x;
复合赋值符
还有一种复合赋值的形式:
+= -= *= /= %=
<<= >>= &= ^= |=
5.1.5 单目操作符
C具有一些单目操作符,就是只接受一个操作数的操作符。它们是
! ++ - & sizeof
~ -- + + (类型)
- !操作符对它的操作数执行逻辑反操作;如果操作数为真,其结果为假,如果操作数为假,其结果为真。和关系操作符一样,这个操作符实际上产生一个整型结果,0或1。
- ~操作符对整型类型的操作数进行求补操作,操作数中原先为1的位变为0,所有原先为0的位变为1,注意与!操作符的区别。
- -操作符产生操作符的负值。
- +操作符产生操作值的值;换句话说,它什么也不干。之所以提供这个操作符,是为了与-操作符组成对称的一对。
- &操作符产生它的操作数的地址,被称为取地址符。
- *是间接访问操作符,它与指针一起使用,用于访问指针所指向的值。
C语言中,sizeof是操作符,又是C关键字,不是函数,它用来计算它的操作数的类型长度,以字节为单位表示,它的结合性是从右到左。
sizeof的操作数可以不加括号,也是合法的:sizeof a //合法
sizeof操作符可以是表达式,判断表达式的长度并不需要对表达式进行求值,所以sizeof(a = b + 1)并没有向a赋任何值。
int a=0;
int b=1;
cout << sizeof a << endl;
cout << sizeof(a = b+1) << endl;
cout << a << endl;
(类型)操作符被称为强制类型转换(cast),它用于显式地把表达式的值转换为另外的类型。例如,为了获得整型变量a对应的浮点数值,你可以这样写:
(float)a;
- 最后我们讨论增值操作符++和减值操作符--。这两个操作符都有两个变型,分别为前缀形式和后缀形式。前缀形式的++操作符出现在操作符的前面,操作数的值被增加,而表达式的值就是操作数增加后的值。
而后缀形式的++操作符出现在操作符的后面,操作数的值仍被增加,但是表达式的值为操作数增加前的值。demo例子如下:
int a,b,c,d;
a = b = 10;
c = ++a; //a增加到11,c得到的值为11
d = b++; //b增加到11,但d得到的值仍为10
5.1.6 关系操作符
C的关系操作符如下:
= < <= != ==
注意关系操作符产生的结果都是一个整型值,而不是布尔值。如果两端的操作数符合操作符指定的关系,表达式的结果为1,否则为0。
5.1.7 逻辑操作符
逻辑操作符有&&和||。
expression1 && expression2
&&操作符的左操作数的值为假,那么右操作值便不再进行求值,因为整个表达式的值肯定是假的。||操作符也一样,当左操作数为真,则右操作数不再求值。
5.1.8 条件操作符
条件操作符是C语言中唯一一个三目操作符,接收三个操作数,用法如下:
expression1 ? expression2 : expression3
首先计算expression1的值,如果为真,那么整个表达式的值为expression2,expression3不再执行;如果为假,那么整个表达式的值为expression3,expression3也不会执行。
5.1.9 逗号操作符
逗号操作符将两个或多个表达式分隔开来,这些表达式自左向右逐个进行求值,整个逗号表达式的值就是最后那个表达式的值。例如:
if( b + 1,c / 2,d > 0 )
如果d的值大于0,那么整个表达式的值为真,否则为假。当然最好不要这么编写,前2个表达式的求值毫无意义,它们的值只是被简单地丢弃。
a = get_value();
cout_value(a);
while(a > 0){ ; }
如果使用逗号操作符,可以将上面的程序改写为:
while(a = get_value(), count_value(a), a > 0) { ; }
5.1.10 下标引用、函数调用和结构成员
下标引用操作符是一对方括号[],下标引用操作符是双目操作符,接收两个操作数,一个数组名和一个索引值。事实上,下标引用并不局限于数组名,也可以用于指针。
C的下标值总是从零开始,并且不会对下标值进行有效性检查。除了优先级不同,下标引用和间接访问表达式是等价的,这里是它们的等价关系:array[下标]
*( array + (下标) )
下标引用实际上是以后面这张形式实现的。函数调用操作符接收一个或多个操作数。它的第一个操作数就是函数名,剩余的操作数就是传递给函数的参数。把函数调用以操作符的方式实现,实际意味着“表达式”可以代替“常量”作为函数名,
事实也确实如此。在第7章详细讨论函数调用操作符如果s是个结构变量,那么s.a就访问s中名为a的成员。当是一个结构体指针,就要使用->操作符来访问成员。
5.2 布尔值
C并不具备显式的布尔类型,所以使用整数来代替。其规则是: 零是假,任何非零值皆为真。
C语言中常用#define来定义布尔值:
#define FALSE 0
#define TRUE 1
5.3 左值和右值
为了理解有些操作符存在的限制,你必须理解左值(L-value)和右值(R-value)之间的区别。左值就是那些能够出现在赋值符号左边的东西,右值就是那些可以出现在赋值符号右边的东西。
这里有个例子:
a = b + 25;
a是个左值,因为它标识了一个可以存储结果值的地点,b+25是个右值,因为它指定了一个值。
它们不可以互换
b + 25 = a; //error!
因为b+25并未标识一个特定的位置,所以这条赋值语句是非法的。
5.4 表达式求值
表达式的操作数在求值过程中可能需要转换为其他类型。
5.4.1 隐式类型转换
C的整型算数运算总是至少以缺省整数类型的精度来进行的。为了获得这个精度,表达式中的字符型和短整型操作数在使用之前被转换为普通整型。这种转换称为整型提升。
例如,在下面表达式的求值中,
char a,b,c;
a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算。加法运算的结果将被截断,然后再存储在a中。
5.4.2 算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作无法进行。下面的层次体系称为寻常算数转换。(越上面的,排名越高)
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么它将转换为另一个操作数的类型然后执行操作。
- 当整型值转换为float型,也有可能损失精度。float型仅要求6位数字的精度。如果将一个超过6位数字的整型值赋给一个float变量时,其结果可能只是该整型值的近似值。
5.4.3 操作符的属性
复杂表达式的求值顺序是由3个因素决定的:操作符的优先级、操作符的结合性以及操作符是否控制执行的顺序。两个相邻的操作符哪个先执行取决于它们的优先级,
如果两者的优先级相同,那么决定顺序由它们的结合性决定。简单地说,结合性就是一串操作符是从左到右还是从右到左依次执行。最后,有4个操作符,它们可以对整个表达式的求值顺序进行控制。
每个操作符的所有属性都列在表5.1所示的优先级表中。
表5.1 操作符优先级
5.4.4 优先级和求值的顺序
两个相邻操作符的执行顺序由它们的优先级决定,如果它们的优先级相同,它们的执行顺序由它们的结合性决定。除次之外,编译器可以自由决定使用任何顺序对表达式进行求值,只要它不违背逗号、&&、||和?操作符所施加的限制。
这里有一个例子:
a + b * c
在上面的表达式中,由于的优先级较高,所以先执行bc,再执行a+(bc)。
下面是一个更为更为有趣的表达式:
a b + c * d + e * f
如果仅由优先级决定这个表达式的求值顺序,那么所有3个乘法运算将在所有加法运算之前进行。事实上,这个顺序并不是必须的。实际上只要保证每个乘法运算在它相邻的加法运算之前执行即可。
例如,这个表达式可能会以下面的顺序执行:
a * b
c * d
(a*b) + (c*d)
e * f
(a*b) + (c*d) + (e*f)
注意第1个加法运算在最后一个乘法运算之前执行。如果这个表达式按以下的顺序执行,其结果是一样的( + 的结合性为从左到右<加法运算情况>· ):
a * b
c * d
e * f
(a*b) + (c*d)
(a*b) + (c*d) + (e*f)
+ , - 在做一元运算符时具有右结合性,如:+1,-1(表示正负1)
+ , - 在做二元运算符时是从左向右结合,如1+2-1(表示加减)
第5章 编程练习
- 编写一个程序,从标准输入读取字符,并把它们写到标准输出中。所有非字母字符都完全按照它的输入形式输出,字母字符在输出前进行加密。
加密方法很简单:每个字母被修改为在字母表上距其13个位置(前或后)的字母。例如,A被修改为N,B被修改为O,Z被修改为M,以此类推。注意大小写字母都应该被转换。
char ch;
while( (ch = getchar()) != '\0' )
{
if( (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') )
{
if( (ch<='Z'&&(ch+13)>'Z') || (ch+13)>'z' ) //因为'a'+13大于'Z',导致进入if分支
ch -= 13;
else
ch += 13;
}
putchar(ch);
}
2.请编写函数 unsigned int reverse_bits(unsigned int value);
这个函数的返回值是把value的二进制数从左到右变换一下后的值。32位机器上,25这个值包含下列各个位:00000000 000000000 00000000 00011001
函数的返回值应该是2,550,136,832,它的二进制是:10011000 00000000 000000000 00000000
unsigned int reverse_bits(unsigned int value)
{
unsigned int answer = 0;
unsigned int i;
for(i = 1;i != 0;i << -1)
{
answer <<= 1;
if(value &= 1)
{
answer |= 1;
}
value >>= 1;
}
return answer;
}
- 请编写函数,使用数组存储一个整型数的二进制位。
void setBit(char bitArr[], unsigned int num)
{
unsigned int i;
for(i=0;i<32;i++)
{
if(num&1)
bitArr[32-i] = '1';
else
bitArr[32-i] = '0';
num >>= 1;
}
}
[C和指针] 4-语句、5-操作符和表达式的更多相关文章
- C和指针 (pointers on C)——第五章:操作符和表达式
第五章 操作符和表达式 这一章假设没做过玩过单片机.汇编的话,读起来可能比較吃力,尤其是在移位运算符.位运算符应用上.另外多注意一下左值和右值的理解. 总结: 算术操作符.赋值操作符.关系操作符.条件 ...
- 《C和指针》读书笔记——第五章 操作符和表达式
1.当/操作符的两个操作数都是整数时,它执行整除运算:其他都是执行浮点数除法. 2.逻辑移位:左边移入的位用0填充: 算数移位:左边移入的位用符号位填充: 3.位置1 :value |= 1<& ...
- LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树
序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...
- 《Visual C# 从入门到精通》第一章使用变量、操作符和表达式——读书笔记
前言: 这个笔记是我个人总结,主要是熟练自己查看<Visual C# 从入门到精通>(第8版)这本书时,懵然起总结的想法,只是总结一些知识点,在工作项目会用得上,但是对毫无C#语言基础的, ...
- Java入门基础(变量、操作符与表达式)
Java入门基础 1. 第一个程序 2.变量(命名.运算.整数/小数/字符串.布尔类型) 3.操作符与表达式(算术/逻辑/关系/赋值/自增/类型转换操作符) HelloWorld! public cl ...
- C语言-操作符与表达式
C语言入门之操作符与表达式 前言 本篇文章主要包括各种操作符的介绍与表达式求值,欢迎各位小伙伴与我一起学习. 一.操作符 分类 算术操作符 移位操作符 位操作符 赋值操作符 单目运算符 关系操作符 逻 ...
- Java中的switch语句后面的控制表达式的数据类型
Java中的switch语句后面的控制表达式的数据类型 1.byte 2.char 3.short 4.int 5.枚举类型 6.Java 7允许java.lang.String类型
- mybatis 的动态sql语句是基于OGNL表达式的。
mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:1. if 语句 (简单的条件判断)2. c ...
- 无法将具有语句体的lambda表达式转换为表达式树
很早就碰到了这个问题,当时也没有深入的研究,趁着空闲,遂把这个问题研究清楚. (一)普通案例 下面从一个普通的案例入手,下面准备两个List集合,都是放在内存里面的(需要模拟到远端执行的时候,我们是通 ...
- C++ 指针悬挂和赋值操作符的重载,拷贝构造函数实现
指针悬挂: 问题:使用new申请的内存内存空间无法访问,也无法释放. 原因:直接对指向new申请的存储空间的指针变量进行赋值修改 后果:失去了原来的地址,原来的空间无法访问也无法释放,造成内存泄漏 还 ...
随机推荐
- Reactor Cooling(无源汇有上下界网络流)
194. Reactor Cooling time limit per test: 0.5 sec. memory limit per test: 65536 KB input: standard o ...
- htmlspecialschars与htmlentities的区别
根据php手册,htmlentities与htmlspecialchars功能几乎是一模一样.唯一的差别就是,对于无效的代码单元序列(通俗讲就是不认识的编码)是否进行编码.htmlentities会进 ...
- codevs3164 质因数分解
题目描述 Description (多数据)给出t个数,求出它的质因子个数. 数据没坑,难度降低. 输入描述 Input Description 第一行 t 之后t行 数据 输出描述 Output D ...
- string数据类型操作【四】
keys * 用于查找所有的key值 exists mykey #判断该键是否存在,存在返回1,否则返回0. del mykey 删除键(存在就删除返回1,不存在返回为0) ...
- [bzoj3160]万径人踪灭_FFT_Manacher
万径人踪灭 bzoj-3160 题目大意:给定一个ab串.求所有的子序列满足:位置和字符都关于某条对称轴对称而且不连续. 注释:$1\le n\le 10^5$. 想法: 看了大爷的题解,OrzOrz ...
- SpringBoot学习day01
SpringBoot目的在于创建和启动新的基于Spring框架的项目.SpringBoot会选择最合适的Spring子项目和第三方开源库进行整合.大部分SpringBoot应用只需要非常少量的配置就可 ...
- MongoDB小结27 - 聚合管道【$project】
我们有这样的数据 { "_id" : 1, title: "abcdef", isbn: "6969696969", author: { l ...
- Thread.run方法是同步方法
Thread.run方法是同步方法, 线程: package com.stono.thread.page005; public class MyThread extends Thread { @Ove ...
- C#中Stack<T>类的使用及部分成员函数的源代码分析
Stack<T>类 Stack<T> 作为数组来实现. Stack<T> 的容量是 Stack<T> 能够包括的元素数. 当向 Stack<T&g ...
- Android入门级编译错误汇总
1 描写叙述: 项目常常须要引用别人的libraryproject,在选项中add进来后,点击应用或者确定.关闭页面. 回到代码中却发现无法链接,又一次打开properties查看,发现导入的pr ...