C语言操作符和表达式详细讲解
Posted Jiawen_captial
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言操作符和表达式详细讲解相关的知识,希望对你有一定的参考价值。
文章目录
1、操作符分类:
1、算术操作符 + - * / %
2、移位操作符 << >>
3、位操作符 & | ^
4、赋值操作符 = += -= …
5、单目操作符 sizeof ! ++ –
6、关系操作符 > >= < <= != ==
7、逻辑操作符 && ||
8、条件操作符 ?:
9、逗号表达式 ,
10、下标引用、函数调用和结构成员 [] -> () .
2、算术操作符
加+ 减- 乘* 除/ 取余%
+、-、*都非常简单,但要注意/和%
- 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
- 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
int ret =9/2; 整数除法
double tmp=9/2.0 浮点数除法
- % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
3、移位操作符
3.1 操作符基础知识
- 移位操作符,移动的是二进制位(补码)
- 对于整数的二进制有3种表示形式:原码、反码、补码
- 正整数-原码、反码、补码相同
- 负整数
1、原码-直接按照数字的正负写出的二进制序列
2、反码-原码的符号位不变,其他位按位取反
3、补码-反码+1 - 警告⚠ : 对于移位运算符,不要移动负数位,这个是标准未定义的 。并且操作数要针对整数。
3.2 左移操作符<<:左边丢弃,右边补0
int a=5;
int b=a<<1;
//原00000000000000000000000000000101
//反00000000000000000000000000000101
//补00000000000000000000000000000101
//将a的二进制位向左移动一位,首位的0被丢弃,后面
//的空缺位置补0,如下图所示
//注意:在这个过程中a的值没变,只不过是对a进行了一种运算
3.3 右移操作符>>
1、逻辑移位:左边用0填充,右边丢弃
int a=-1;
int b=a>>1;
//11111111111111111111111111111111
//移位得到
//01111111111111111111111111111111
2、算术以为:左边用原该值的符号位填充,右边丢弃,大多数情况下编译器以此为主
int a=-1;
int b=a>>1;
//11111111111111111111111111111111
//移位得到
//11111111111111111111111111111111
4、位操作符
- 位操作符有
& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数
- &按位与,二进制位都为1为1,有一个0及以上时为0;
int main()
int a=3;
int b=-2;
int c=a&b;
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111110 -2的补码
//00000000000000000000000000000010 按位与的结果
//得到的结果是补码,要翻译成原码才是屏幕显示的
- |按位或 二进制位只要有1就为1
int main()
int a=3;
int b=-2;
int c=a&b;
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111110 -2的补码
//11111111111111111111111111111111 按位或的结果
- ^异或 二进制位相同为0,相异为1
int main()
int a=3;
int b=-2;
int c=a&b;
//00000000000000000000000000000011 3的补码
//11111111111111111111111111111110 -2的补码
//11111111111111111111111111111101 按位异或的结果
- 位操作符的应用,交换两个变量
//方法0 交换变量常用的临时变量法
int main()
int a=10;
int b=90;
int tmp=0;
tmp=a;
a=b;
b=tmp;
//方法一 整型变量有最大上限,如果数值较大,会出现溢出
int main()
int a=10;
int b=90;
a=a+b;
b=a-b;
a=a-b;
return 0;
//方法二
int main()
int a=10;
int b=90;
a=a^b;
b=a^b;
a=a^b;
尽管方法二较为简便,但是在过程中使用较少,因为可读性低,且效率低于方法0。
对方法二的理解:a和b异或得到密码,b和密码异或能翻译出a,a和密码继续异或
能翻译出原来的b。
a^a=0 0^a=a
a^a^b=b a^b^a=b 所以异或是支持交换律的
5、赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值。
赋值操作符可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值
先将y+1的值赋给x,再将x的值赋给a。可读性低
拆分后的同义句:这样的写法更加清晰而且易于调试
x=y+1;
a=x;
6、符合赋值符
简单介绍一些赋值运算符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。 比如:
int a=10;
a=a>>1;
a>>=1;//复合赋值
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁
7、单目操作符:只有一个操作数的操作符
- 一些单目操作符
! 逻辑反操作
- 负值
- 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
- 间接访问操作符(解引用操作符)
(类型) 强制类型转换
- !逻辑反操作
!逻辑反操作,把真变成假,把假变成真
int main()
int a=0;
int b=!a; //!a=1
if(a) //a为真打印Hello
printf("Hello");
if(!a) //a为假打印Hello
printf("Hello");
- *解引用操作符
int main()
int a=10;
itn *p=&a;
int b=*p;
*p=20
//对于任何变量都由所其空间和空间里的值构成
//左值用的是空间,右值用的是内容
//*p放在右边用的是指向空间里面的值,*p放在左边用的是指向的空间
- sizeof
//1、sizeof是操作符,不是函数
//2、sizeof是计算变量或者类型创建变量的内存大小,单位字节
//和内存中存放什么数据没有关系
int main()
int a;
printf("%d",sizeof(a));//4
printf("%d",sizeof a);//4
//当是变量时可以省略,求的还是a的内存大小,说明sizeof不是函数
printf("%d",sizeof(int));//4
printf("%d",sizeof int)//4 有些编译器支持这种写法
int b=5;
short s=10;
printf("%d",sizeof(s=a+2));//2
printf("%d",s);//10
在正常情况下int类型的值放入short类型中会发生截断,在sizeof中
因为是将值放到short类型的s中,所以计算的值为2。
sizeof内部的表达式是不参与运算的,所以s的值仍为10
详解sizeof内部的表达式不产与运算:在编译期间就把sizeof的功能实现了,编译器一看s的类型取决于short就得到了2这个结果,即在链接的过程中没有机会执行a+2这个动作。所以sizeof内部的表达式不参与运算。
- 详解sizeof和数组
#include <stdio.h>
void test1(int arr[])
printf("%d\\n", sizeof(arr));//(2) 4
void test2(char ch[])
printf("%d\\n", sizeof(ch));//(4) 4
int main()
int arr[10] = 0;
char ch[10] = 0;
数组名单独放在sizeof内部,
数组名表示整个数组,
计算的是整个数组的大小,单位是字节
printf("%d\\n", sizeof(arr));//(1) 40
printf("%d\\n", sizeof(ch));//(3) 10
数组传参的时候,数组名会降级传过去首元素的地址,本质上是个指针。
sizeof计算的指针的大小,在32位为4,64位为8
test1(arr);
test2(ch);
return 0;
- ~将二进制位按位取反
//可以与其他操作符配合使用,进行二进制位改变。
int a=0;
//00000000000000000000000000000000
~a;
//11111111111111111111111111111111
- ++和–
int main()
//后置++,先使用,后++
int a=10;
int b=a++;
printf("%d",a);//11
printf("%d",b);//10
int main()
//前置++,先++,后使用
int a=10;
int b=a++;
printf("%d",a);//11
printf("%d",b);//11
//-- 也分为前置--和后置--,用法和++一样。
//注意:++和--不要写出过于复杂的表达式,比如下面代码:
int main()
int a=1;
int b=(++a)+(++a)+(++a);
return 0;
- ()强制类型转换
int main()
int a=3.14//默认写出的浮点数是double
//直接这样写可能会出现丢失数据
//正确写法
int a=(int)3.14;
8、关系操作符
用在同类型的变量比较,时间和地址的比较没有意义
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
这些关系运算符比较简单,没什么可讲的,但是我们要注意一些运算符使用时候的陷阱。
警告: 在编程的过程中== 和=不小心写错,导致的错误。
9、逻辑操作符
&& 逻辑与(表并且)
int main()
int a=3;
int b=5;
//逻辑与只关注真假,a为真并且b为真,那整体就为真
//a和b有一个为假就为假
int c=a&&b;
|| 逻辑或(表或者)
int main()
int a=0;
int b=5;
//a和b中有一个为真,表达式即为真
int c=a||b;
真题
#include <stdio.h>
int main()
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;//1 2 3 4
先使用,a以0的值使用,因为前面为假,所以表达式整体为假,还没有到
++b部分,现在以0&&d++,为整体,前面为假,整体为假,d++也没有执行。
i = a++||++b||d++//1 3 3 4
a++,先使用原先a的值,a为假,继续到++b,看真或假
printf("a = %d\\n b = %d\\n c = %d\\nd = %d\\n", a, b, c, d);
return 0;
#include <stdio.h>
int main()
int i =0,a=1,b=2,c =3,d=4;
i = a++||++b||d++;
先运行到a++,先使用a的值1,为真,所以整个a++||++b为真,
得到结果1,++b被跳过,接着1||d++,1
的结果为真,整个表达式的为真,得到结果1,d++也被跳过
printf("a = %d\\n b = %d\\n c = %d\\nd = %d\\n", a, b, c, d);
//2 2 3 4
return 0;
10、条件操作符
- exp1? exp2 : exp3,也是唯一的三目操作符,如果表达式的结果为真,则表达式2的结果为整体表达式的结果,否则表达式3的结果为整体表达式的结果。可以看成if else选择语句。
转换成条件表达式
if (a > 5)
b = 3;
else
b = -3;
a>5?b=3:b=-3;
求两个数的最大值
int main()
int a=10;
int b=20;
int m=((a>b)?(a):(b));//利用括号括起来,可读性高且语法安全
11、逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
/代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
13
//代码2
if (a =b + 1, c=a / 2, d > 0)
真正起到判断作用的是d>0是否成立
//代码3
a = get_val();
count_val(a);
while (a > 0)
//业务处理
a = get_val();
count_val(a);
如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
//解决了原有的代码冗余问题
//业务处理
12、下标引用、函数调用和结构成员
1、[] 下标引用操作符
操作数:一个数组名+一个索引值
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
arr[9]->*(arr+9)->*(9+arr)->9[arr]
//语法支持9[arr]形式,但一般不写成9[arr]的形式。
[]的两个操作数是arr和9
printf("%p---%p",&arr[8],arr+8);//这俩地址一样
//arr+i就是数组arr中,下标为i的元素的地址
2、()函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include <stdio.h>
void test1()
printf("hehe\\n");
void test2(const char *str)
printf("%s\\n", str);
int main()
test1(); //实用()作为函数调用操作符。
test2("hello bit.");//实用()作为函数调用操作符。
return 0;
3、结构成员的访问
. 用法:结构体.成员名
->用法:结构体指针->成员名
//自定义数据类型
struct Book
char name[10];
float price;
char id[10];
;
void Print1(struct Book b)
printf("书名:%s\\n",b.name);
printf("价格:%f\\n",b.price);
printf("书号:%s\\n",b.id);
void Print2()
printf("书名:%s\\n",b->name);
printf("价格:%f\\n",b->price);
printf("书号:%s\\n",b->id);
int main()
struct Book b="C book","55.5f","c21415215";
b.name="数据结构";//错误写法,name是首元素的地址
strcpy(b.name,"数据结构");//利用strcpy来实现改变
Print1(b);
Print2(&b);
以上是关于C语言操作符和表达式详细讲解的主要内容,如果未能解决你的问题,请参考以下文章
C语言基础学习笔记五操作符详解(详细讲解+练习巩固+记忆总结)