【C总集篇】第四章 运算符,表达式和语句


第四章 运算符,表达式和语句

运算符

赋值运算符:=

​   在C语言中,=并不意味着“相等”,而是一个赋值运算符。下面的赋值表达式语句:
​                   bmw=2002;
​   把值 2002赋给变量bmw。也就是说,=号左侧是一个变量名,右侧是赋给该变量的值。符号=被称为赋值运算符。另外,上面的语句不读作“bmw 等于 2002”,而读作“把值 2002赋给变量 bmw”。赋值行为从右往左进行。

​   也许变量名和变量值的区别看上去微乎其微,但是,考虑下面这条常用的语句:
​                   i=i + 1;
​   对数学而言,这完全行不通。如果给一个有限的数加上1,它不可能“等于”原来的数。但是,在计算机赋值表达式语句中,这很合理。该语句的意思是:找出变量i的值,把该值加1,然后把新值赋值变量i。
在这里插入图片描述
在C语言中,类似这样的语句没有意义(实际上是无效的):

​                     2002 =bmw;

​   因此,在编写代码时要记住,=号左侧的项必须是一个变量名。实际上,赋值运算符左侧必须引用一个存储位置。最简单的方法就是使用变量名。

常见示例

int ex;
int why;
int zee;
int Two=2;

why =42;
zee = why;
ex=TWO *(why + zee);

说明

/*定义整形变量 ex why zee Two,其中Two赋值为2*/
int ex;
int why;
int zee;
int Two=2;

/* 
将42 赋值给 变量why
*/
why =42;

/* 
将why中的值(42) 赋值给 变量zee
*/
zee = why;

/* 
将TWO *(why + zee)中的值 2 *(42 + 42) 赋值给 变量ex
*/
ex=TWO *(why + zee);

加法运算符:+

​   加法运算符用于加法运算,使其两侧的值相加。

int i;

/* 将 35(12+23)的值赋值给变量i */
i = 12+23 

减法运算符: -

  减法运算符用于减法运算,使其左侧的数减去右侧的数。

int i;

/* 将 20(21-1)的值赋值给变量i */
i = 21 - 1 

+和-运算符可以都被称为二元运算符,即这些运算符需要两个运算对象才能完成操作。

符号运算符:-和+

  减号还可用于标明或改变一个值的代数符号

int i = -1;
int a = -23;
int s = -12312;

以这种方式使用的负号被称为一元运算符。一元运算符只需要一个运算对象

正号也可以写上去,但是默认如果不是 负号,则该数为正
在这里插入图片描述

乘法运算符:*

符号*表示乘法

/*下面的语句用2.54乘以inch,并将结果赋给cm:*/

int cm,inch = 2;

cm=2.54*inch;

除法运算符:/

  C使用符号/来表示除法。/左侧的值是被除数,右侧的值是除数。

​     整数除法和浮点数除法不同。

​     浮点数除法的结果是浮点数

​     而整数除法的结果是整数

​   整数是没有小数部分的数。这使得5除以3很让人头痛,因为实际结果有小数部分。在语言中,整数除法结果的小数部分被丢弃,这一过程被称为截断。

#include <stdio.h>
int main(void)
{
    printf("integer division:  5/4   is %d \n", 5/4);
    printf("integer division:  6/3   is %d \n", 6/3);
    printf("integer division:  7/4   is %d \n", 7/4);
    printf("floating division: 7./4. is %1.2f \n", 7./4.);
    printf("mixed division:    7./4  is %1.2f \n", 7./4);
    
    return 0;
}

则会出现以下结果

/*整数*/
integer division:  5/4   is 1
integer division:  6/3   is 2
integer division:  7/4   is 1
    
/*浮点数*/
floating division: 7./4. is 1.75
    
/*混合类型*/
mixed division:    7./4  is 1.75

注意

​   整数除法会截断计算结果的小数部分(丢弃整个小数部分),不会四舍五入结果混合整数和浮点数计算的结果是浮点数。实际上,计算机不能真正用浮点数除以整数,编译器会把两个运算对象转换成相同的类型。本例中,在进行除法运算前,整数会被转换成浮点数。

size_t类型

​   C语言规定,sizeof返回size_t类型的值。这是一个无符号整数类型,但它不是新类型。

​   size_t作为 unsigned int 或unsigned long的别名。这样,在使用sizet类型时,编译器会根据不同的系统替换标准类型。

求模运算符:%

​   求模运算符只能用于整数,不能用于浮点数。

​   求模运算符给出其左侧整数除以右侧整数的余数。

​   例如,13%5(读作“13求模5”)得3,因为13比5的两倍多 3,即 13除以5的余数是3。

负数求模如何进行?

​   C99 规定“趋零截断”之前,该问题的处理方法很多。但自从有了这条规则之后,如果第1个运算对象是负数,那么求模的结果为负数:如果第1个运算对象是正数,那么求模的结果也是正数:
在这里插入图片描述

递增运算符:++

  递增运算符执行简单的任务,将其运算对象递增1。

​     该运算符以两种方式出现。

​       第1种方式,++出现在其作用的变量前面,这是前缀模式;

​       第2种方式,++出现在其作用的变量后面,这是后缀模式。

两种模式的区别在于递增行为发生的时间不同。

#include <stdio.h>
int main(void)
{
    int a = 1, b = 1;
    int a_post, pre_b;
    
    a_post = a++;  
    pre_b = ++b;   
    printf("a  a_post   b   pre_b \n");
    printf("%1d %5d %5d %5d\n", a, a_post, b, pre_b);
    
    return 0;
}

在这里插入图片描述
  a和b都递增了 1,但是,a_post是a递增之前的值,而b pre 是b递增之后的值。这就是++的前缀形式和后缀形式的区别

在这里插入图片描述

递减运算符:–

  每种形式的递增运算符都有一个递减运算符与之对应,用–代替++即可:
    --count;//前缀形式的递减运算符
    count–;//后缀形式的递减运算符

#include <stdio.h>
int main(void)
{
    int a = 5, b = 5;
    int a_post, pre_b;
    
    a_post = a--;  
    pre_b = --b;   
    printf("a  a_post   b   pre_b \n");
    printf("%1d %5d %5d %5d\n", a, a_post, b, pre_b);
    
    return 0;
}

在这里插入图片描述
方式与递增运算符同理

移位运算符

  注意:移位操作符不能移动负数位

左移操作符:<<

  移位规则:左边抛弃、右边补零。
在这里插入图片描述

右移操作符:>>

  移位规则:右边抛弃,左边用该值的原符号位填充

​     右移操作符同理也是对操作数的二进制位向右进行移动,如果是整数,符号位为0,则左面填充0,如果为负数,符号位为1,则左面填充1

位操作符

&(按位与)

|(按位或)

^(按位异或)

这些操作符的操作数必须为整数

在这里插入图片描述

按位与:&
基础概念

​     按位与是指:参加运算的两个数据,按二进制位进行“与”运算。

​     如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。

​     这里的1可以理解为逻辑中的true,0可以理解为逻辑中的false。

​     按位与其实与逻辑上“与”的运算规则一致。逻辑上的“与”,要求运算数全真,结果才为真。

​       若,A=true,B=true,则A∩B=true

例如:3&5

​     3的二进制编码是11(2)。

​         将11(2)补足成一个字节,则是00000011(2)。

​      5的二进制编码是101(2),

​          将101(2)其补足成一个字节,则是00000101(2)

注:

​   (为了区分十进制和其他进制,本文规定,凡是非十进制的数据均在数据后面加上括号,括号中注明其进制,二进制则标记为2)

​   内存储存数据的基本单位是字节(Byte),一个字节由8个位(bit)所组成。位是用以描述电脑数据量的最小单位。二进制系统中,每个0或1就是一个位。

3&5:

00000011(3的二进制)&00000101(5的二进制)=00000001(最终与的结果)

具体操作

00000011
&&&&&&&&
00000101
========
00000001

注意:可以将&(与) 想象成数学中的乘法

C语言中数学中
0 & 1 = 00 * 1 =0
1 & 1 = 11 * 1 =1
1 & 0 = 01 * 0 =0
0 & 0 = 00 * 0 =0

作用:

清零

​   若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件:

​   原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。

​   例:原数为43,即00101011,另找一个数,设它为148,即10010100,将两者按位与运算:
​         00101011&10010100=00000000

#include <stdio.h>
int main()
{
int a=43;
int b = 148;
printf("%d\n",a&b);
}

在这里插入图片描述

清除高位/低位

​   若有一个整数a,想要取其中的低字节,只需要将a与8个1按位与即可。

a & b = c

a0010110010101100
b0000000011111111
c0000000010101100

这样,高位就清零了,低位则保存下来了

保留指定位:

​   与一个数进行“按位与”运算,此数在该位取1.

​     例如:有一数84,即01010100,想把其中从左边算起的第3,4,5,7,8位保留下来,运算如下:

01010100
&
00111011
=
00010000
#include <stdio.h>
void main()
{
int a=84;
int b = 59;
printf("%d",a&b);
}
按位或:|

​   两个相应的二进制位中只要有一个为1,该位的结果值为1。借用逻辑学中或运算的话来说就是,一真为真,全假才假

0 | 0 = 0
0 | 1 = 1
1 | 1 = 1
1 | 1= 1

作用:

置位

​   通常我们可把按位“与”操作|作为置位(即将该位置1)的手段,例如我们想要将a数中的第0位和1位置1,而又不影响其它位的现状,可以用一个数0x03,即二进制数00000011去与a数作按位“或”运算:

十六进制二进制
0x8810001000
||
0x0300000011
==
0x8B10001011

​   注意,这个数除第0、1位为1外,其它各位均为0,操作的结果只会将a数中的第0、1位置0,而a数的其它位不受影响。也就是说,若需要某个数的第n位置1,只需要将该数与另一个数按位相“或”,另一个数除了相应的第n位为1外,其它各位都为0,以起到对其它各位的屏蔽作用

按位异或:^

​   按位“异或”运算符^的作用是对运算符两侧以二进制表达的操作数按位分别进行“异或”运算,而这一运算是以数中相同的位(bit)为单位的。异或运算操作的规则是:仅当两个操作数不同时,相应的输出结果才为1,否则为0。

10111001
^^^^^^^^
10011011
00100010

再例如:

      0x88 10001000 a数

      ^
      0x81 10000001 异或数
      =00001001

交换两个值,不用临时变量

  例如:a=3,即11(2);b=4,即100(2)。
      想将a和b的值互换,可以用以下赋值语句实现:

a=a^b;
b=b^a;
a=a^b;

a=011; b =100

/*a=a^b;*/
a = a ^ b = 011 ^ 100 = 111 = 7

此时 a = 7 b =4

b = b ^ a = 100 ^ 111 = 011 = 3

此时 a = 7 b =3

a = a ^ b = 011 ^ 111 = 100 = 4

综上,完成交换

使特定的位取反

​ a = 0x88

​   例如:我们想让a数中的最低位和最高位取反,只要用0x81,即二进制数10000001去与它作按位“异或”运算,其运算结果同上式。经过操作后,最高位的值已经由1变0,而最低位的值也已经由0变1,起到了使这两位翻转的效果。其它位的状态保持不变。
  可以看到,这个数除最低位、最高位为1外,其它各位均为0,操作的结果只会将a数中的第0、7位取反,而a数的其它位不受影响。也就是说,若需要某个数的第n位取反,只需要将该数与另一个数按位相“异或”,另一个数除了相应的第n位为1外,其它各位都为0,以起到对其它各位的屏蔽作用。上面的运算可以用a=a^ (0x81)来表示,也可以用a^=(0x81)来表达。

取反运算符:~

​   他是一元运算符,用于求整数的二进制反码,即分别将操作数各二进制位上的1变为0,0变为1。

例如:

​     10010110取反后为01101001。

将1取反为254

​     ~00000001 = 11111110

小注意事项

  • & 按位与 如果两个相应的二进制位都为1,则该位的结果值为1,否则为0
  • *| 按位或 两个相应的二进制位中只要有一个为1,该位的结果值为1
  • ^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1
  • ~ 取反 ~是一元运算符,用来对一个二进制数按位取反,即将0变1,将1变0
  • << 左移 用来将一个数的各二进制位全部左移N位,右补0
  • >> 右移 将一个数的各二进制位右移N位,移到右端的低位被舍弃,对于无符号数,高位补0

(1)对于+、-、*、/来说,如果操作符的两个操作数其中一个操作数是小数,则执行小数运算,如果都为整数,则进行整数运算。

(2)%的两个操作数必须都为整数,否则会报错,其他四个操作符则都可以。

(3)的语言/、%,0不能当除数或者模数 (这应该是个数学问题)。

优先级
优先级运算符名称或含义使用形式结合方向说明
1[]数组下标数组名[常量表达式]左到右
()圆括号(表达式)/函数名(形参表)
.成员选择(对象)对象.成员名
->成员选择(指针)对象指针->成员名
2-负号运算符-表达式右到左单目运算符
~按位取反运算符~表达式
++自增运算符++变量名/变量名++
自减运算符–变量名/变量名–
*取值运算符*指针变量
&取地址运算符&变量名
!逻辑非运算符!表达式
(类型)强制类型转换(数据类型)表达式
sizeof长度运算符sizeof(表达式)
3/表达式/表达式左到右双目运算符
*表达式*表达式
%余数(取模)整型表达式%整型表达式
4+表达式+表达式左到右双目运算符
-表达式-表达式
5<<左移变量<<表达式左到右双目运算符
>>右移变量>>表达式
6>大于表达式>表达式左到右双目运算符
>=大于等于表达式>=表达式
<小于表达式<表达式
<=小于等于表达式<=表达式
7==等于表达式==表达式左到右双目运算符
!=不等于表达式!= 表达式
8&按位与表达式&表达式左到右双目运算符
9^按位异或表达式^表达式左到右双目运算符
10|按位或表达式|表达式左到右双目运算符
11&&逻辑与表达式&&表达式左到右双目运算符
12||逻辑或表达式||表达式左到右双目运算符
13?:条件运算符表达式1?表达式2: 表达式3右到左三目运算符
14=赋值运算符变量=表达式右到左
/=除后赋值变量/=表达式
*=乘后赋值变量*=表达式
%=取模后赋值变量%=表达式
+=加后赋值变量+=表达式
-=减后赋值变量-=表达式
<<=左移后赋值变量<<=表达式
>>=右移后赋值变量>>=表达式
&=按位与后赋值变量&=表达式
^=按位异或后赋值变量^=表达式
|=按位或后赋值变量|=表达式
15逗号运算符表达式,表达式,…左到右

逻辑运算符

与或非介绍

在这里插入图片描述

& 和 &&

相同点:
  这两个逻辑运算符都表示:“与”,一个为False,全为False。

区别:
  &:表达式a & 表达式b 在运算时,表达式a和b都会被执行,进行True和False的判断。
  &&:表达式a && 表达式b 在运算时,如果表达式a为False,则表达式b不进行运算,直接返回结果,计算效率高。

| 和 ||

相同点:
  这两个逻辑运算符都表示:“或”,一个为True,全为True。

区别:
  |:表达式a | 表达式b 在运算时,表达式a和b都会被执行,进行True和False的判断。
  ||:表达式a || 表达式b 在运算时,如果表达式a为True,则表达式b不进行运算,直接返回结果,计算效率高。

!

​   逻辑非:取相反结果

三目运算符

​   三目运算符,又称条件运算符,是计算机语言(C,C++,Java等)的重要组成部分。它是唯一有 3 个操作数的运算符,所以有时又称为三元运算符,其实三目运算符和 if / else 条件判断类似。

在这里插入图片描述
三目运算符的书写:**

<表达式1> ? <表达式2> : <表达式3>;

​   返回值:先求表达式 1 的值,如果为真,则执行表达式 2,并返回表达式 2 的结果;如果表达式 1 的值为假,则执行表达式 3,并返回表达式 3 的结果。

举个例子:

// 对于条件表达式 
b ? x : y

​   q先判断条件 b 真假,如果 b 的值为 true ,那么返回表达式 x 的计算结果;否则,计算 y 的值,返回表达式 y 的计算结果。

  一个条件表达式绝不会既计算 x,又计算 y(就好比在 if / else 条件判断中,不可能既执行 if 后的代码又执行 else 之后的代码)。

例子:

#include "stdafx.h"
#include <stdio.h>


int main()
{

	int a = 1;
	int b = 2;
	int c = 0;
	//三目运算符返回整数
	c = a ? (a + b) : (a - b);//因为表达式1(a=1),条件为真,所以执行表达式2,返回a+b的结果
	printf("整数c = %d\n",c);
	//三目运算符返回字符串
	char *s = 0 ? "条件成立" : "条件不成立";//因为表达式1(0),条件为假,所以执行表达式3,返回字符串"c条件不成立"
	printf("字符串s = %s\n", s);

	printf("main函数结束!\n");
	return 0;
}
/*
输出结果:

整数c = 3
字符串s = 条件不成立
main函数结束!
请按任意键继续. . .

*/

在这里插入图片描述

类型转换**

​   通常,在语句和表达式中应当使用的是类型相同的变量和常量。但是如果是混合类型的,C会采用一套自动类型转化

类型转化规则如下:

1:当类型转换出现在表达式时,无论了unsigned还是signedcharshort都会自动转化成intfloat会被自动转化成double。由于都是从小类型转化大类型,所以这些欣慰叫做/*升级*/

2:涉及两种类型的运算,两个值会被分别转化成两种类型的更高级别。

3:类型的级别类型是(由高到低)
	long doubledoublefloatfloatunsigned long longlong longunsigned longlongunsigned intint
特殊情况:longint的大小相同时,unsigned intlong级别高

4:在赋值表达式语句中,计算的最终结果都会被转换成被赋值变量的类型,这个过程可能导致类型升级或者降级,所谓/*降级*/、是指把一种类型转化成更低的类别

类型的升级一般都不会有什么问题,但是降级会导致真正的麻烦

​   原因:较低类型可能放不下高类型的值

  如果待赋值得值与目标类型不匹配得话得规则:

1:目标类型是无符号得整形,且待赋值得值是整数得时,额外的位将会被忽略
	例如:如果目标是8位unsigned char,待赋值是原始值求模256
	
2:如果目标类型是一个有符号的整数,且待赋值的值是整数,结果因实现而异。

3:如果目标类型是一个整形,且待赋值的值是浮点数,这种行为未定义

4:如果把一个浮点值转化位整形,原来的浮点值将被截断

例程:

#include <stdio.h>
int main(void)
{
    char ch;
    int i;
    float fl;
    
    fl = i = ch = 'C';                                  /* line 9  */
    printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); /* line 10 */
    ch = ch + 1;                                        /* line 11 */
    i = fl + 2 * ch;                                    /* line 12 */
    fl = 2.0 * ch + i;                                  /* line 13 */
    printf("ch = %c, i = %d, fl = %2.2f\n", ch, i, fl); /* line 14 */
    ch = 1107;                                          /* line 15 */
    printf("Now ch = %c\n", ch);                        /* line 16 */
    ch = 80.89;                                         /* line 17 */
    printf("Now ch = %c\n", ch);                        /* line 18 */
    
    return 0;
}

强制转化

​   通常,应该避免自动类型转换,尤其是类型降级。但是如果能小心使用,类型转换也很方便。我们前面讨论的类型转换都是自动完成的。然而,有时需要进行精确的类型转换,或者在程序中表明类型转换的意图。这种情况下要用到强制类型转换,即在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。圆括号和它括起来的类型名构成了强制类型转换运算符(castoperalor),其通用形式是:

//强制转化成 int
mice =(int)1.6(int)1.7;

//强制转化成 float
mice =(float)1.61111111(float)1.7111111;

//强制转化成 char
mice =(char)1.6(char)1.7;

​   一般而言,不应该混合使用类型(因此有些语言直接不允许这样做),但是偶尔这样做也是有用的。C语言的原则是避免给程序员设置障碍,但是程序员必须承担使用的风险和责任。

带参数的函数

#include <stdio.h>
void pound(int n);   // ANSI function prototype declaration
int main(void)
{
    int times = 5;
    char ch = '!';   // ASCII code is 33
    float f = 6.0f;
    
    pound(times);    // int argument
    pound(ch);       // same as pound((int)ch);
    pound(f);        // same as pound((int)f);
    
    return 0;
}

void pound(int n)    // ANSI-style function header
{                    // says takes one int argument
    while (n-- > 0)
        printf("#");
    printf("\n");
}

​   如果函数不接受任何参数,函数头的圆括号中应该写上关键字 void。

​   由于该函数接受一个int 类型的参数,所以圆括号中包含一个int类型变量n的声明。参数名应遵循C语言的命名规则

声明参数就创建了被称为形式参数的变量。

​   该例中形式参数是int 类型的变量n。像pound(10)这样的函数调用会把10赋给n。在该程序中,调用pound(times)就是把 times 的值(5)赋给 n。我们称函数调用传递的值为实际参数,简称实参。所以,函数调用 pound(10)把实际参数10传递给函数,然后该函数把10赋给形式参数(变量 n)。也就是说,main()中的变量 times 的值被拷贝给 pound()中的新变量n。
在这里插入图片描述
在这里插入图片描述

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值