C和指针学习笔记四:操作符和表达式

一、操作符

包括:算术操作符,移位操作符,位操作符,赋值操作符,单目操作符,关系操作符,逻辑操作符,条件操作符,下标引用,函数调用,结构体成员调用。

1、算术操作符

加、减、乘、除、取模(%)
取模的两个操作数只能是整型值。

2、移位操作符

左移位:<< 最左边几位丢弃,最右边几位补0;
右移位:>> (1)逻辑移位:最右边几位丢弃,左边补0;
(2)算术移位:最右边几位丢弃,左边符号位为0,则全移入0,为1则全移入1。

标准规定无符号数都执行逻辑移位,而有符号数执行的是逻辑移位还是算术移位由编译器决定,若移位的位数超过操作数的位数,则具体情况由编译器决定。

所以出现有符号数移位操作的程序是不可移植的。

3、位操作符

& | ^ (与,或,异或)
通常用于位操作,修改变量指定位的值。
举例:
value的第bit_number位置1:
value |= 1 << bit_number;
value的第bit_number位置0:
value &= ~ (1 << bit_number);
测试指定位是否为1,为1则表达式非0:
value &= 1 << bit_number;

4、赋值操作符

赋值是表达式的一种,而不是某种类型的语句,所以只要允许出现表达式的地方都可以进行赋值。

举例:
a = x = y + 3;
a和x被赋的值并不是同一个值
若x的长度不够,则x被赋予的y+3值会被截短,再存储于x中,之后这个被截短的值再被赋予到a中。

复合赋值符:+=,-=,|=等等,多使用可以使代码更简洁。

5、单目操作符

!; ++; -- ; + ;-; ~ ;& ;* ;sizeof ; (类型)

  • sizeof():判断操作数的类型长度,以字节为单位。
    sizeof (int):必须加括号;
    sizeof x:可以不加括号;

举例:
16位机器中:int arry[10];
则sizeof(arry)=20
sizeof(arry[0])=2
sizeof(arry)/sizeof(arry[0])=10,即数组中元素的个数。

sizeof(a = b + 3):sizeof可以判断表达式的长度,此时不需要求表达式的值,所以a并没有被赋值;表达式返回a的大小。

  • (类型):强制类型转换(cast),具有很高的优先级,所以注意:把强制类型转换放在表达式的前面只会改变第一个项目的类型,要操作整个表达式的话,就要加括号。
    float(a):获得a对应的浮点数值。

  • 增值++和减值--操作符。
    前缀操作符在变量被使用之前改变变量的值;
    后缀操作符在变量被使用之后改变变量的值。

举例:
c = ++a; d=a++;
其中,c得到a增加之前的值,d得到a增加之后的值。

前缀和后缀形式的增值操作都复制了一份变量值的拷贝,用于周围表达式的值是这份拷贝来的值(赋值表达式);
前缀形式,在变量值增加之后复制,后缀形式,在变量值增加之前复制。
因此这些操作符的结果不是被他们修改的变量,而是变量值的拷贝。
++a = 10;
这条语句时错误的,++a的结果是变量值的拷贝,不是变量a本身,因此无法向一个值进行赋值。

6、关系操作符

>;>=;<;<=;!=;==

关系操作符的结果是一个整型值,而不是布尔值,可以将结果赋值给整型变量。(C语言中没有布尔类型,所以用整数来代替,非0位真,0为假)

7、逻辑操作符

逻辑与&&,逻辑或||,都会控制表达式的顺序执行:
从左到右依次执行,&&若左操作数为假,则后续右操作数不再求值,整个表达式为假,||逻辑或同理。这个行为被称为“短路求值”(short-circuited evaluation)。

举例:
if(a<b && c>d)
if(a<b & c>d)

if(a && b)
if(a & b)
第一组语句的结果一样,因为关系操作符的结果只能是0或1,而第二组语句结果不一样,因为若a,b非0,则第一个一定为真,而a,b按位与的值并不一定非0,所以不一定为真。

8、条件操作符

expression1 ? expression2 : expression3
会控制子表达式的求值顺序,expression2和expression3只会执行其中一个。
b = a > 5 ? 3 : -20;
a > 5则b = 3,否则b = -20。
条件操作符的作用有时类似于if,else,且比它更简洁。

9、逗号操作符

expression1,expression2,expression3,......,expressionN
将多个表达式用逗号分开,并从左到右依次执行,整个表达式的值为最后那个表达式的值。

while(a = get_value(), count_value( a ), a>0)
{
  expression;
}

因为依次执行,所以用在循环语句的测试表达式中时,获得下一个测试值语句只出现一次,修改时只需要在一个地方修改,方便程序的维护。

二、表达式求值

  • 表达式的求值顺序由所包含操作符的优先级和结合性决定;
  • 求值过程中的类型转换。

1、隐式类型转换(implicit conversion)

  • 发生的情况:

    • 算术表达式或逻辑表达式的操作数类型不相同时;(执行常用算术转换,即usual arithmetic conversion)
    • 操作符两边的变量类型不相同时;
    • 函数调用时,实参与形参不匹配时;
    • return语句中表达式类型和函数返回值类型不匹配时。
  • 隐式转换规则
    long double
    double
    float
    unsigned long int
    long int
    unsigned int
    int
    排名较低的操作数首先转换为另一个操作数的类型,即低精度数像高精度数转换。
    但是,在32位机器上,int类型和long字长相同,这时unsigned int的精度就比long精度高。

  • 整型运算符的精度至少是缺省整型类型,则运算过程中,字符型和短整型在使用之前被转换成普通整型,这种转换过程被称为整型提升(integral promotion)。
    提升精度往往无害,但降低精度可能会导致问题,低精度类型可能不够大,不能容纳高精度的完整数据。

(1)算术转换

举例:

char a,b,c;
  statement;
  a = b + c;

首先,b和c的值被提升为整型,然后,执行加法运算,最后,将结果截短后赋值并存储于a中。

当然,也可以使用强制类型转换执行显示转换(explicit conversion):(int)a = b + c;

(2)显示转换

举例:

int a = 5000;
int b = 25;
long c = a * b;
  • 在32位机器上,int和long int都是32位,这段代码运行起来没有问题;但在16位机器上,int是16位,long是32位,a*b的值应该是int型,但由于数据类型不够大(max = 2^16 - 1 = 65535),所以发生溢出,c会被赋一个无意义的值,因此需要进行强制类型转换:
    long c = (long)a*b;

  • 注意:long c = (long)(a*b);是错误的,因为溢出在强制转换之前就已经发生了,所以这样做不会有任何改变。

  • 数据溢出:

    • 有符号数的数据溢出是未定义的;
    • 无符号数的数据溢出:溢出后的数以2^(8*sizeof(type))作模运算。比如用unsigned char型变量存储258,其实存进去的是258-2^8=2。

(3)操作符两边表达式的转换

举例:

unsigned int a = 6;
int b = -20;
int c = (a + b) > 6 ? 1 : 2;

在加法运算中a和b类型不一致,会发生隐式转换,将int型转换为unsigned int型,b=-20转换为无符号数会变成一个很大的整数,因为无符号负数转换为的正数是用负数的补码所表示的,正数的源码,反码,和补码相同。所以,程序输出1,而不是2。

举例:

if(strlen(arry) < 10)
if(strlen(arry) - 10 < 0)

这两条语句不等价,因为strlen函数返回值是unsigned int型,两个无符号数运算所得的结果仍为无符号数,而无符号数肯定大于0。所以,要尽量避免使用第二个表达式。

2、操作符的优先级

表达式的运算规则:
两个相邻操作符的执行顺序由他们的优先级决定,若优先级相同,则由他们的结合性决定。
结合性:当多个相同优先级的运算符出现在表达式中时,先执行左边的叫具有左结合性,先执行右边的叫具有右结合性。

  • 举例:

a*b + c*d + e*f

相邻的加法和乘法运算符中,乘法运算符先执行;两个加法运算,根据加法的左结合性,是左边的加法运算先执行。但对于哪个乘法运算先执行,以及是否在所有乘法执行完后再执行加法运算,这些都由编译器决定,所以表达式就会有多种执行顺序。

c + --c

相邻的+和--操作符是--先执行,但对于表达式c和--c并不知道哪个先执行,又因为--c具有副作用,所以这两个表达式的执行顺序会对结果有影响。

  • 编译器只要不违背优先级和结合性规则,就可以任意决定复杂表达式的取值顺序。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容