[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申请的存储空间的指针变量进行赋值修改 后果:失去了原来的地址,原来的空间无法访问也无法释放,造成内存泄漏 还 ...
随机推荐
- 序列终结者(bzoj 1521)
Description 网上有许多题,就是给定一个序列,要你支持几种操作:A.B.C.D.一看另一道题,又是一个序列 要支持几种操作:D.C.B.A.尤其是我们这里的某人,出模拟试题,居然还出了一道这 ...
- [NOIP2004] 提高组 洛谷P1089 津津的储蓄计划
题目描述 津津的零花钱一直都是自己管理.每个月的月初妈妈给津津300元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同. 为了让津津学习如何储蓄,妈妈提出,津津可以随时把整百的钱存在她那里 ...
- HDU 2222 (AC自动机)
HDU 2222 Keywords search Problem : 给若干个模式串,询问目标串中出现了多少个模式串. Solution : 复习了一下AC自动机.需要注意AC自动机中的fail,和n ...
- Xterm256终端颜色的名称
hi x016_Grey0 ctermfg=16 guifg=#000000 "rgb=0,0,0 hi x017_NavyBlue ctermfg=17 guifg=#00005f &qu ...
- MapReduce WordCount Combiner程序
MapReduce WordCount Combiner程序 注意使用Combiner之后的累加情况是不同的: pom.xml <project xmlns="http://maven ...
- web 开发之js---JS变量也要注意初始化
原先以为js作为弱类型语言,变量的初始化没必要,但是: var text; text+="你好"; alert(text); 对话框弹出的内容是:"undefined你好 ...
- iOS 特定图片的button的旋转动画
近期做的东西中,要为一个有特定图片的button加入旋转动画,Demo代码例如以下: #import "ViewController.h" @interface ViewContr ...
- ionic开发android App
在win下配置ionic可以参考七月的这篇博客:http://www.cnblogs.com/shikelong/p/4480975.html. 依照七月的思路基本可以创建一个ionic项目了,下面我 ...
- javax.servlet.http.Part 文件上传
编辑jsp页面: <html> <head> <base href="<%=basePath%>"> <title>My ...
- 蓝书2.3 Trie字典树
T1 IMMEDIATE DECODABILITY poj 1056 题目大意: 一些数字串 求是否存在一个串是另一个串的前缀 思路: 对于所有串经过的点权+1 如果一个点的end被访问过或经过一个被 ...