当前位置:网站首页>C语言-入门-基础-语法-[运算符,类型转换](六)
C语言-入门-基础-语法-[运算符,类型转换](六)
2022-07-04 08:46:00 【胡安民】
运算符基本概念
和数学中的运算符一样, C语言中的运算符是告诉程序执行特定算术或逻辑操作的符号
什么是表达式: 表达式就是利用运算符链接在一起的有意义,有结果的语句; 例如: a + b; 就是一个算数表达式, 它的意义是将两个数相加, 两个数相加的结果就是表达式的结果 注意: 表达式一定要有结果
运算符分类
按照功能划分:
- 算术运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 位运算符
按照参与运算的操作数个数划分:
- 单目运算 , 只有一个操作数 如 : i++;
- 双目运算 , 有两个操作数 如 : a + b;
- 三目运算 , C语言中唯一的一个,也称为问号表达式 如: a>b ? 1 : 0;
运算符的优先级和结合性
早在小学的数学课本中,我们就学习过"从左往右,先乘除后加减,有括号的先算括号里面的", 这句话就蕴含了优先级和结合性的问题
C语言中,运算符的运算优先级共分为15 级。1 级最高,15 级最低
- 在C语言表达式中,不同优先级的运算符, 运算次序按照由高到低执行
- 在C语言表达式中,相同优先级的运算符, 运算次序按照结合性规定的方向执行
算数运算符
注意事项
- 如果参与运算的两个操作数皆为整数, 那么结果也为整数
- 如果参与运算的两个操作数其中一个是浮点数, 那么结果一定是浮点数
- 求余运算符, 本质上就是数学的商和余,中的余数
- 求余运算符, 参与运算的两个操作数必须都是整数, 不能包含浮点数
- 求余运算符, 被除数小于除数, 那么结果就是被除数
- 求余运算符, 运算结果的正负性取决于被除数,跟除数无关, 被除数是正数结果就是正数,被除数
- 是负数结果就是负数
- 求余运算符, 被除数为0, 结果为0
- 求余运算符, 除数为0, 没有意义(不要这样写)
赋值运算符
简单赋值运算符
复合赋值运算符
结合性和优先级
自增自减运算符
在程序设计中,经常遇到“i=i+1”和“i=i-1”这两种极为常用的操作。C语言为这种操作提供了两个更为简洁的运算符,即++
和--
如果出现在一个表达式中, 那么符号写在前面和后面就会有所区别
前缀表达式:
++x, --x;
其中x表示变量名,先完成变量的自增自减1运算,再用x的值作为表达式的
值;即“先变后用”,也就是变量的值先变,再用变量的值参与运算后缀表达式:
x++, x--;
先用x的当前值作为表达式的值,再进行自增自减1运算。即“先用后变”,也
就是先用变量的值参与运算,变量的值再进行自增自减变化
自增:
如果只有单个变量, 无论++写在前面还是后面都会对变量做+1操作
如果自增参与运算
自减
注意:
- 自增、自减运算只能用于单个变量,只要是标准类型的变量,不管是整型、实型,还是字符型变量
等,但不能用于表达式或常量 ,错误用法:++(a+b);
- 开发中尽量让
++ --
单独出现, 尽量不要和其它运算符混合在一起
请用如下代码替代
C语言标准没有明确的规定, 同一个表达式中同一个变量自增或自减后如何运算 , 不同编译器得到结果也不同, 在企业开发中千万不要这样写
sizeof运算符
sizeof可以用来计算一个变量或常量、数据类型所占的内存字节数
标准格式: sizeof(常量 or 变量);
sizeof()和+=、*=
一样是一个复合运算符, 由sizeof和()两个部分组成, 但是代表的是一个整体
所以sizeof不是一个函数, 是一个运算符, 该运算符的优先级是2
sizeof的几种形式:
// sizeof( 变量\常量 );
sizeof(10);
char c = 'a';
sizeof(c);
//sizeof 变量\常量;
sizeof 10;
char c = 'a';
sizeof c;
// sizeof( 数据类型);
sizeof(float); //如果是数据类型不能省略括号
逗号运算符
- 在C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把多个表达式连接起来组成一个表达式,称为逗号表达式
- 逗号运算符会从左至右依次取出每个表达式的值, 最后整个逗号表达式的值等于最后一个表达式的
值 - 格式:
表达式1,表达式2,… …,表达式n;
#include <stdio.h>
int main(){
int a=2,b=4,x,y;
y=(x=a+b,b+x); //在逗号表达式中,最后一个运算才是最终的值,然后赋值给y
printf("y=%d, x=%d \n",y,x);// y=10, x=6
return 0;
}
关系运算符
默认情况下,我们在程序中写的每一句正确代码都会被执行。但很多时候,我们想在某个条件成立的情况下才执行某一段代码, 这种情况的话可以使用条件语句来完成,但是学习条件语句之前,我们先来看一些更基础的知识:
如何判断一个条件是否成立, C语言中的真假性 , 在C语言中,条件成立称为“真”,条件不成立称为“假”,因此,判断条件是否成立,就是判断条件的“真假”
怎么判断真假呢?C语言规定,任何数值都有真假性,任何非0值都为“真”,只有0才为“假”。也就是说,108、-18、4.5、-10.5等都是“真”
,0则是“假
关系运算符的运算结果只有2种:如果条件成立,结果就为1,也就是“真”;
如果条件不成立,结果 就为0,也就是“假
优先级和结合性
注意: 无论是float还是double都有精度问题, 所以一定要避免利用==判断浮点数是否相等
逻辑运算符
优先级 | 名称 | 符号 | 说明 |
---|---|---|---|
2 | 逻辑非运算符 | ! | 单目运算符,具有右结合性 |
11 | 逻辑与运算符 | && | 双目运算符,具有左结合性 |
12 | 逻辑或运算符 | || | 双目运算符,具有左结合性 |
逻辑非
格式: ! 条件A; (真变假,假变真)
逻辑与
格式: 条件A && 条件B;
运算结果:一假则假 ,也就是都为真那么才是真
运算过程:
- 总是先判断"条件A"是否成立,如果"条件A"成立,接着再判断"条件B"是否成立, 如果"条件B"也成立,结果就为1,即“真”
- 如果"条件A"成立,"条件B"不成立,结果就为0,即“假”
- 如果"条件A"不成立,不会再去判断"条件B"是否成立, 因为逻辑与只要一个不为真结果都不为真
使用注意: 条件A"为假, "条件B"不会被执行
逻辑或
格式: 条件A || 条件B;
运算结果:一真则真
运算过程:
- 先判断"条件A"是否成立
- 如果"条件A"不成立,接着再判断"条件B"是否成立, 如果"条件B"成立,结果就为1,即“真”
- 如果"条件A"不成立,"条件B"也不成立成立, 结果就为0,即“假”
- 如果"条件A"成立, 不会再去判断"条件B"是否成立, 因为逻辑或只要一个为真结果都为真
使用注意: "条件A"为真, "条件B"不会被执行
三目运算符
三目运算符,它需要3个数据或表达式构成条件表达式
格式1: 表达式1?(结果A):(结果B)
格式2: 表达式1?表达式2:表达式3
示例: 考试及格 ? 及格 : 不及格;
求值规则: 如果"表达式1"为真,三目运算符的运算结果为(结果A),否则为(结果B)
我们还可以进行复杂点的 int res = a>b?a:(c>d?c:d);
类型转换
数据类型转换就是将数据(变量、数值、表达式的结果等)从一种类型转换为另一种类型。
自动类型转换
自动类型转换就是编译器默默地、隐式地、偷偷地进行的数据类型转换,这种转换不需要程序员干预,会自动发生。将一种类型的数据赋值给另外一种类型的变量时就会发生自动类型转换,例如:
//100 是 int 类型的数据,需要先转换为 float 类型才能赋值给变量 f。
float f = 100;
//再如:f 是 float 类型的数据,需要先转换为 int 类型才能赋值给变量 n。
int n = f;
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;所以说,自动类型转换并不一定是安全的。对于不安全的类型转换,编译器一般会给出警告。
在不同类型的混合运算中,编译器也会自动地转换数据类型,将参与运算的所有数据先转换为同一种类型,然后再进行计算。转换的规则如下:
- 转换按数据长度增加的方向进行,以保证数值不失真,或者精度不降低。例如,int 和 long 参与运算时,先把 int 类型的数据转成 long 类型后再进行运算。
- 所有的浮点运算都是以双精度进行的,即使运算中只有 float 类型,也要先转换为 double 类型,才能进行运算。
- char 和 short 参与运算时,必须先转换成 int 类型。
下图对这种转换规则进行了更加形象地描述:
unsigned 也即 unsigned int,此时可以省略 int,只写 unsigned。
自动类型转换示例:
#include<stdio.h>
int main(){
float PI = 3.14159;
int s1, r = 5;
double s2;
s1 = r * r * PI;
s2 = r * r * PI;
printf("s1=%d, s2=%f\n", s1, s2);
return 0;
}
在计算表达式r*r*PI
时,r 和 PI 都被转换成 double 类型,表达式的结果也是 double 类型。但由于 s1 为整型,所以赋值运算的结果仍为整型,舍去了小数部分,导致数据失真。
强制类型转换
自动类型转换是编译器根据代码的上下文环境自行判断的结果,有时候并不是那么“智能”,不能满足所有的需求。如果需要,程序员也可以自己在代码中明确地提出要进行类型转换,这称为强制类型转换。
自动类型转换是编译器默默地、隐式地进行的一种类型转换,不需要在代码中体现出来;强制类型转换是程序员明确提出的、需要通过特定格式的代码来指明的一种类型转换。换句话说,自动类型转换不需要程序员干预,强制类型转换必须有程序员干预。
强制类型转换的格式为: (type_name) expression
type_name为新类型名称,expression为表达式。例如:
(float) a; //将变量 a 转换为 float 类型
(int)(x+y); //把表达式 x+y 的结果转换为 int 整型
(float) 100; //将数值 100(默认为int类型)转换为 float 类型
下面是一个需要强制类型转换的经典例子:
#include <stdio.h>
int main(){
int sum = 103; //总数
int count = 7; //数目
double average; //平均数
average = (double) sum / count;
printf("Average is %lf!\n", average); //Average is 14.714286!
return 0;
}
sum 和 count 都是 int 类型,如果不进行干预,那么sum / count的运算结果也是 int 类型,小数部分将被丢弃;虽然是 average 是 double 类型,可以接收小数部分,但是心有余力不足,小数部分提前就被“阉割”了,它只能接收到整数部分,这就导致除法运算的结果严重失真。
既然 average 是 double 类型,为何不充分利用,尽量提高运算结果的精度呢?为了达到这个目标,我们只要将 sum 或者 count 其中之一转换为 double 类型即可。上面的代码中,我们将 sum 强制转换为 double 类型,这样sum / count的结果也将变成 double 类型,就可以保留小数部分了,average 接收到的值也会更加精确。
在这段代码中,有两点需要注意:
- 对于除法运算,如果除数和被除数都是整数,那么运算结果也是整数,小数部分将被直接丢弃;如果除数和被除数其中有一个是小数,那么运算结果也是小数。
- ( )的优先级高于/,对于表达式(double) sum / count,会先执行(double) sum,将 sum 转换为 double 类型,然后再进行除法运算,这样运算结果也是 double 类型,能够保留小数部分。注意不要写作(double) (sum / count),这样写运算结果将是 3.000000,仍然不能保留小数部分。
类型转换只是临时性的
无论是自动类型转换还是强制类型转换,都只是为了本次运算而进行的临时性转换,转换的结果也会保存到临时的内存空间,不会改变数据本来的类型或者值。请看下面的例子:
#include <stdio.h>
int main() {
double total = 400.8; //总价
int count = 5; //数目
double unit; //单价
int total_int = (int)total; //400
unit = total / count; // 80.160000
printf("total=%lf, total_int=%d, unit=%lf\n", total, total_int, unit); //total=400.800000, total_int=400, unit=80.160000
return 0;
}
通过上面的代码,我们能看出来,total虽然转换了类型给了total_int ,但是total 原本的数据类型没有发生变化,所以最后计算的时候还是小数 ,也就表明类型转换只是占时的只是作用在当前的计算
自动类型转换 VS 强制类型转换
在C语言中,有些类型既可以自动转换,也可以强制转换,例如 int 到 double,float 到 int 等;而有些类型只能强制转换,不能自动转换,例如以后将要学到的 void * 到 int *,int 到 char * 等。
可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。现在我们学到的数据类型,既可以自动转换,又可以强制转换,以后我们还会学到一些只能强制转换而不能自动转换的类型。
可以自动进行的类型转换一般风险较低,不会对程序带来严重的后果,例如,int 到 double 没有什么缺点,float 到 int 顶多是数值失真。只能强制进行的类型转换一般风险较高,或者行为匪夷所思,例如,char * 到 int * 就是很奇怪的一种转换,这会导致取得的值也很奇怪,再如,int 到 char * 就是风险极高的一种转换,一般会导致程序崩溃。 使用强制类型转换时,程序员自己要意识到潜在的风险。
边栏推荐
- DM8 uses different databases to archive and recover after multiple failures
- awk从入门到入土(18)gawk线上手册
- Basic operations of databases and tables ----- view data tables
- Technology sharing | MySQL parallel DDL
- Fault analysis | MySQL: unique key constraint failure
- deno debugger
- 2022 tower crane driver examination and tower crane driver examination questions and analysis
- Relationship and operation of random events
- The upper layer route cannot Ping the lower layer route
- How college students choose suitable computers
猜你喜欢
Developers really review CSDN question and answer function, and there are many improvements~
[error record] no matching function for call to 'cacheflush' cacheflush();)
snipaste 方便的截图软件,可以复制在屏幕上
What should I do if there is a problem with the graphics card screen on the computer
Sequence model
How to choose solid state hard disk and mechanical hard disk in computer
到底什么才是DaaS数据即服务?别再被其他DaaS概念给误导了
Azure ad domain service (II) configure azure file share disk sharing for machines in the domain service
4 small ways to make your Tiktok video clearer
Educational Codeforces Round 119 (Rated for Div. 2)
随机推荐
Basic discipline formula and unit conversion
老掉牙的 synchronized 锁优化,一次给你讲清楚!
到底什么才是DaaS数据即服务?别再被其他DaaS概念给误导了
C#实现一个万物皆可排序的队列
ES6 summary
Redis sentinel mechanism
上周热点回顾(6.27-7.3)
ctfshow web255 web 256 web257
2022 electrician (intermediate) examination question bank and electrician (intermediate) examination questions and analysis
Relationship and operation of random events
Codeforces Round #803 (Div. 2)(A-D)
@Role of pathvariable annotation
The second session of the question swiping and punching activity -- solving the switching problem with recursion as the background (I)
OpenFeign 服务接口调用
deno debugger
[attack and defense world | WP] cat
es6总结
NewH3C——ACL
awk从入门到入土(12)awk也可以写脚本,替代shell
Collections in Scala