C语言从基础 -> 精通 -> 底层 (4) 运算符与类型转换

一、算术运算符

算术运算符分为单目和双目操作:

  • 单目操作是指对一个操作数进行操作。例如: -a是对a进行一目负操作。
  • 双目操作(或多目操作)是指两个操作数(或多个操作数)进行操作。

1.双目运算符

  • : 加法运算
  • : 减法运算
  • : 乘法运算
    / : 除法运算
    % : 求余运算(又叫模运算)

重点说一下后两个运算符:

  • 除法运算符左右两边的数据类型决定了运算结果的类型。两边都是整数结果为整数,有任一方是小数,结果为小数。如果两个整数相除有余数,舍弃余数。运算符右边的数不能为0
  • 整除运算符左右两边的数据必须都是整数,结果是这两个数相除的余数值。如果能整除,结果为0
int a = 3;
int b = 2;

int sum, diff, product, res, mod;

// +
sum = a + b;
printf("sum = %d\n", sum);//5

// -
diff = a - b;
printf("diff = %d\n", diff);//1

// *
product = a * b;
printf("product = %d\n",  product);//6

// /
res = a / b;
printf("res = %d\n", res);//1

// %
mod = a % b;
printf("mod = %d\n", mod);//1

接下来我们来看它的本质,在相应的ide应该有对象的汇编代码
(去掉打印后的)
idivl 除数存储在ax寄存器 余数在dx寄存器

Cpluse`main:
    0x100000f50 <+0>:  pushq  %rbp
    0x100000f51 <+1>:  movq   %rsp, %rbp
    0x100000f54 <+4>:  xorl   %eax, %eax
    0x100000f56 <+6>:  movl   $0x0, -0x4(%rbp)
    0x100000f5d <+13>: movl   %edi, -0x8(%rbp)
    0x100000f60 <+16>: movq   %rsi, -0x10(%rbp)
    0x100000f64 <+20>: movl   $0x3, -0x14(%rbp)  //局部变量a
    0x100000f6b <+27>: movl   $0x2, -0x18(%rbp) //局部变量b
    0x100000f72 <+34>: movl   -0x14(%rbp), %ecx 
    0x100000f75 <+37>: addl   -0x18(%rbp), %ecx // a+b的值放到寄存器ecx
    0x100000f78 <+40>: movl   %ecx, -0x1c(%rbp) //寄存器ecx 的值放到局部变量sum中存储
    0x100000f7b <+43>: movl   -0x14(%rbp), %ecx
    0x100000f7e <+46>: subl   -0x18(%rbp), %ecx //a-b的值放到寄存器cx前4个字节
    0x100000f81 <+49>: movl   %ecx, -0x20(%rbp) //寄存器ecx 的值放到局部变量diff中存储
    0x100000f84 <+52>: movl   -0x14(%rbp), %ecx 
    0x100000f87 <+55>: imull  -0x18(%rbp), %ecx //a*b的值放到寄存器ecx前4个字节
    0x100000f8b <+59>: movl   %ecx, -0x24(%rbp) //寄存器ecx 的值放到局部变量product中存储
    0x100000f8e <+62>: movl   -0x14(%rbp), %ecx
    0x100000f91 <+65>: movl   %eax, -0x30(%rbp)
    0x100000f94 <+68>: movl   %ecx, %eax
    0x100000f96 <+70>: cltd   
    0x100000f97 <+71>: idivl  -0x18(%rbp) //a/b的值放到寄存器eax前4个字节
    0x100000f9a <+74>: movl   %eax, -0x28(%rbp) //寄存器ecx 的值放到局部变量res中存储
->  0x100000f9d <+77>: movl   -0x14(%rbp), %eax
    0x100000fa0 <+80>: cltd   
    0x100000fa1 <+81>: idivl  -0x18(%rbp) //a%b的余数放到寄存器edx前4个字节
    0x100000fa4 <+84>: movl   %edx, -0x2c(%rbp) //寄存器ecx 的值放到局部变量mod中存储
    0x100000fa7 <+87>: movl   -0x30(%rbp), %ecx
    0x100000faa <+90>: movl   %ecx, %eax
    0x100000fac <+92>: popq   %rbp
    0x100000fad <+93>: retq   

2.单目运算符

  • ++ 操作数加1
  • -- 操作数减1
    这几句最终达到的效果相同,在混编中执行的逻辑也完全相同
x = x + 1;
x++;
++x;

但是进行赋值操作就会导致差异

x = m++; // 表示将m的值赋给x后, m加1。
x = ++m; // 表示m先加1后, 再将新值赋给x。

二、赋值语句中的数据类型转换

1. 赋值运算符

int a = 0;
a = a + 5;
a += 5;
a = a - 3;
a -= 3;

第二行和第三行意思相同,第四行和第五行意思相同,可以互相替换。这是一种C语言中的简便写法。

2. 类型转换

类型转换是指不同类型的变量混用时的类型改变。

2.1 隐式类型转换

基本原则:

  • 在赋值语句中, 等号右边的值转换为等号左边变量所属的类型
  • 不同类型混合计算时,结果类型为数据类型级别较高的
  • 所有的浮点预算都是以double进行的

数据类型级别顺序:

char, short < int < float < double
    int a, b = 3;
    float f = 1.5;

    a = f * b; // 整数和浮点类型运算,结果为浮点类型,因为它类型级别高
    printf("a = %d\n", a);

    a = f; // 把一个浮点类型的变量赋值给整数类型,小数部分会被自动舍去
    printf("a = %d\n", a);

这里有个重点问题需要强调,浮点类型的和整数类型计算时,结果为浮点类型。我们看一个常见问题。

    float f;
    int a = 5;
    f = a / 2;

    printf("f = %f\n",  f);

这段程序的运行结果是:

f = 2.0000

原因在于,a是个整数,2也是个整数,它们的计算结果也是整数。此时,就已经舍弃了计算结果中的小数部分。因此,赋值时就自然没有小数部分。

如果我们需要得到f = 2.5怎么办呢?可以这样写:

    float f;
    int a = 5;
    f = a / 2.0;

    printf("f = %f\n",  f);

因为整数a和浮点数2.0计算的结果是浮点型(double),因此保留了小数部分。之后再把=右边的double类型转换成左边的float类型。仔细想想,能理解吧。

2.2 强制类型转换

在计算中,我们常常需要主动要求计算机改变变量的类型。这是可以这样做。

(数据类型)(表达式或变量)

按照这种格式写,右边部分的类型就会被强制转换成左边括号中的类型。看看具体代码:

    float f;

    f = 6.6 / 3;
    printf("f = %f\n",  f);

    f = (int)6.6 / 3;
    printf("f = %f\n",  f);

    f = (int)(6.6 / 3);
    printf("f = %f\n",  f);
运行结果

三、关系运算符

1.关系运算符

关系运算符的作用是比较符号两边的元素

> 大于
>= 大于等于
< 小于
<= 小于等于
== 等于
!= 不等于

关系运算符都是双目运算符,其结合性均为左结合。关系运算符的优先级低于算术运算符,高于赋值运算符。也就是说,在一个没有括号的关系运算表达式中,<、<=、>、>=要先于==和!=发挥作用。相同级别的预算符从左向右计算。

2.关系表达式

关系表达式的一般形式为:

例:

a + b > c - d
x > 3 / 2
‘a’ + 1 < c
-i - 5 * j == k + 1

也允许出现嵌套的情况。例:

a > (b > c)
a != (c == d)

关系表达式的值是“真”和“假”,用“1”和“0”表示。如:
5 > 0的值为“真”,即为1;
(a = 3) > (b = 5)由于不成立,故其值为假,即为0。

例:

    char c = 'k';
    int i = 1, j = 2, k = 3;
    float x = 3e + 5, y = 0.85;

    printf("%d, %d\n", 'a' + 5 < c, -i - 2 * j >= k + 1);
    printf("%d, %d\n", 1 < j < 5, x - 5.25 <= x + y);
    printf("%d, %d\n", i + j + k == -2 * j, k == j == i + 5);
  • 字符变量以它对应的ASCII码参与运算
  • 对于含多个关系运算符的表达式,如k == j == i + 5,根据运算符的左结合性,先计算k == j,该式不成立,其值为0,再计算0 == i + 5,也不成立,故表达式值为0。
表达式为真,值为1。表达式为假,值为0

需要注意的是,C语言中经常用0代表假,非零代表真。

四、逻辑运算符

逻辑运算符相当于数学中的(且、或、非),我们叫做“逻辑与”、“逻辑或”和“逻辑非”。

&& 逻辑与
|| 逻辑或
! 逻辑非

前两个是双目运算符,最后一个是单目预算符。

逻辑与:两边都为真时返回真,否则返回假;
逻辑或:只要任意一个为真就返回真,否则返回假;
逻辑非:符号右边是真,则返回假;符号右边是假,则返回真。

三、按位运算符

C语言和其它高级语言不同的是它完全支持按位运算符。这与汇编语言的位操作有些相似。

按位运算符有:

& 位逻辑与  // 参加运算的两个数,换算为二进制(0、1)后,进行与运算。只有当相应位上的数都是1时,该位才取1,否则该为为0。
| 位逻辑或 //参加运算的两个数,换算为二进制(0、1)后,进行或运算。只要相应位上存在1,那么该位就取1,均不为1,即为0。
^ 位逻辑异或 //参加运算的两个数,换算为二进制(0、1)后,进行异或运算。只有当相应位上的数字不相同时,该为才取1,若相同,即为0。
~ 位逻辑反
>> 右移
<< 左移
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345