操作符
操作符的种类:
- 算术操作符 : + - * /
- 移位操作符 : >> <<
- 位操作符 : & | ^
- 赋值操作符 : = += -= *= /= &= ^= |= >>= <<=
- 单目操作符 : ! - + & sizeof ~ -- ++ * (类型)
- 逻辑操作符 : && ||
- 条件操作符 : exp1 ? exp2 : exp3
- 逗号表达式 : exp1 , exp2 , exp3
- 下表引用、函数调用和结构成员:
(1)算术运算符
如果我想算9除以2,看看得到的是4还是4.5。
int main(){
int a = 9/2;
printf("%d\n",a);
return 0;
}
控制台打印:
4
4.5000000
从上面的式子可以看到:用整型来接收,得到的值也是整型,向下取整。而我想得到4.5的话只能尝试着用浮点型来计算。
int mian(){
float a = 9/2;
printf("%f\n",a);
return 0;
}
const:
4.0000000
而通过float可以看到,即使是用float来接收所算的值,也得到的是4.0而不是想要的4.5。所以我继续尝试使用小数来计算。
int main(){
float a = 9/2.0;
printf("%f\n",a);
return 0;
}
Console:
4.5000000
从上面的代码可以看到用小数来计算可以实现精准的答案,所以我们可以得到一个结论:
若式子两边都是整数,则上面输出的数只会是整数4,即使你用浮点型来表示,输出的也是4.0,而不是想要的精确值4.5。若想要得到精确值4.5,则像c中有一边为小数,就可以得到,输出结果就会为4.5。
取模(取余)
如果我想对9取模的话,得到的值又是多少?
int mian(){
int a = 9 % 2; //取模
printf("%d\n",a);
return 0;
}
Console:
1
(2)移位运算符:
左移操作符 :移动的是二进制位
例如:我想对2使用左移操作符,那得到的是什么结果?
int main(){
int a=2;
int b =a << 1;
printf("%d\n",b);
return 0;
}
Console:
4
由上图可以看到,对2使用左移操作符,得到的是4,为什么?
因为左移运算符的作用是移动二进制位,我定义的a是int型,而在C语言中,int型是4个字节,而一个字节又是8个比特位,所以在内存图中,应该是32个比特位,向前移动一个比特位,如下:
//2的内存图
00000000 00000000 00000000 00000010
//向左移动
00000000 00000000 00000000 00000100
而在移动过的内存图中,1代表的权重为2的2次方,所以为4
int main()
{
int a = 10;
//把a的二进制位向右移动1位
int b = a >> 1;
printf("b = %d\n",b);
return 0'
}
Console:
右移操作符的内存移动:
00000000 00000000 00000000 00001010
00000000 00000000 00000000 00000101
右移操作符前补位有两种情况:
- 算术右移
右边丢弃,左边补原符号位 - 逻辑右移
右移丢弃,左边补0
(3)位操作符
& 按位与
| 按位或
^ 按位异或
注意:他们的操作数必须是整数
按位异或: 两个相同的数按位异或等于0,0按位异或任何数都等于它本身
int mian()
{
// a ^ a = 0;
// 0 ^ a = a;
return 0;
}
按位与:都为1,才为1
int mian()
{
int a =3;
int b =5;
// & - 按(2进制) 位与(都为1才为1)
//00000000000000000000000000000011
//00000000000000000000000000000101
//00000000000000000000000000000001
int c = a & b;
printf("c = %d\n",c);
}
Console:
1
按位或:若一个为1,则为1
int mian()
{
int a =3;
int b =5;
// | - 按(2进制) 位或(有一个为1则为1)
//00000000000000000000000000000011
//00000000000000000000000000000101
//00000000000000000000000000000111
int c = a | b;
printf("c = %d\n",c);
}
Console:
7
按位异或:相同为0,相异为1
int mian()
{
int a =3;
int b =5;
// | - 按(2进制) 位或(有一个为1则为1)
//00000000000000000000000000000011
//00000000000000000000000000000101
//00000000000000000000000000000110
int c = a | b;
printf("c = %d\n",c);
}
Consol:
6
练习:
交换两个int变量的值,不能使用第三个变量。即 a = 3,b = 5,交换之后,a = 5,b = 3。
int mian()
{
int a = 3;
int b = 5;
printf("a = %d b = %d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d b = %d\n", a, b);
}
Console:
a=3 b=5
a=5 b=3
这道题涉及的底层实现:
这里二进制32位简写,写有效部分:
a = 3 b = 5
011 101
第一步: a = a ^ b; 011 ^ 101 ——> a = 110 b = 101
第二步: b = a ^ b; 110 ^ 101 ——> a = 110 b = 011
第三步: a = a ^ b; 101 ^ 011 ——> a = 101 b = 011
(4)赋值操作符
赋值操作符可以让你自己重新赋值你之前不满意的值。
赋值操作符:=
int main(){
int weight = 120; //体重
weight = 90; //不满意就赋值
double salary = 10000.0
salary = 20000.0; //使用赋值操作符赋值。
return 0;
}
注意:
一个等号是赋值,两个等号为判断
= ——> 赋值
== ——> 判断
复合赋值符
+= 、 -= 、 *= 、 /= 、 %= 、 >>= 、 <<= 、 &= 、 |=
(5)单目运算符
单目操作符:只有一个操作数
! - + & sizeof ~ ++ -- * (类型)
!: 逻辑反操作
如果 a 为真,!a就为假,反之 a 为假,!a 就为真
int mian()
{
int a = 0;
//a为真,打印Yes
if(a)
{
printf("Yes\n");
}
//a为假,打印hNo
if(!a)
{
printf("No\n");
}
return 0;
}
- : 负值
+ : 正值
int mian()
{
int a = 10;
a = -a;
printf("%d\n",a);
a = +a;
printf("%d\n",a);
return 0;
}
Console:
-10
10
sizeof : 操作数的类型长度(以字节为单位)
- sizeof是一个操作符,不是函数!!
- sizeof的作用是计算类型或者变量的大小的
- sizeof括号中的表达式是不参与其他运算的
- 注: sizeof计算变量是可以省略括号不写,计算类型时必须带有括号。
int mian()
{
int a = 10;
char arr[10] = {0};
printf("%d\n",sizeof(a)); //计算a所占空间的大小,单位是字节
printf("%d'n",sizeof(arr)); //计算char类型数组10个空间所占大小
printf("%d\n",sizeof(int)); //计算int类型的大小
short s = 5;
int b = 10;
printf("%d\n",sizeof(s = b+2)); //s是short类型,不会因为加了int类型的值而改变自身类型大小
printf("%d\n",b); //sizeof括号中的表达式是不参与运算的
return 0;
}
Console:
4
4
10
2
5
sizeof在计算变量大小可以省略括号,但是在计算变量时不能省略括号
int mian()
{
int a = 10;
char arr[10] = {0};
printf("%d\n",sizeof a);
printf("%d\n",sizeof int); //err
return 0;
}
使用sizeof计算数组
int main()
{
int arr[10] ={0};
printf("%d\n",sizeof(arr)); //计算的是数组的总大小,单位是字节
printf("%d\n",sizeof(arr[0])); //计算第0个元素的大小(这里定义的是整型)
int sz = sizeof(arr)/sizeof(arr[0]); //计算数组元素个数
printf("%d\n",sz);
return 0;
}
Console:
40 //数组总大小
4 //第0个元素的大小
10 //元素个数
~ : 按位取反
按(二进制位)位取反:把所有二进制位中数字,1变成0,0变成1
例如整数a=0
二进制就为:
//a=0
00000000 00000000 00000000 00000000
//~a
11111111 11111111 11111111 11111111
对0取反:
int main()
{
int a =0;
printf("%\n",~a);
return 0;
}
Console:
-1
前置++
先++,后使用
int main(){
int a =10;
int b = ++a; //前置++
printf("%d\n",b);
ptintf("%d\n",a);
return 0;
}
Console:
11
11
后置++
先使用,再++
int mian()
{
int a =10;
int b =a++; //后置++
printf("%d\n",a);
printf("%d\n",b);
return 0;
}
Console:
11
10
后置 - -
先使用,后- -
int mian()
{
int a =10;
int b =a--; //后置--
printf("%d\n",a);
printf("%d\n",b);
return 0;
}
Console:
9
10
前置 - -
先使用,后- -
int mian()
{
int a =10;
int b =--a; //前置--
printf("%d\n",a);
printf("%d\n",b);
return 0;
}
Console:
9
9
& :取地址操作符
&:取地址操作符
int mian()
{
int a = 10;
printf("%p\n",&a);
return 0;
}
Console:
00CFF9D4
’ * ': 星号,解引用操作符 (间接访问操作符)
int mian()
{
int a = 10;
printf("%p\n",&a); //&取地址
int* pa = &a; //pa是用来存放地址的——>pa就是一个指针变量
*pa = 20; //解引用操作符 ——>间接访问操作符
printf("%d\n",a);
return 0;
}
Console:
20
(类型) 强制类型转换
不建议使用,有的转换会丢失精度。类比java类型转换
int mian()
{
//强转
int a = (int)3.14;
printf("%d\n",a);
return 0;
}
(6)关系操作符
关系运算符在平时最常用到,也比较简单,在使用的过程中要注意的是一些运算的陷阱。
int a = 1;
int b = 2;
//if(a == b)
//if(a != b)
//if(a >= b)
//if(a <= b)
//if(a > b)
//if(a < b)
//{
//}
注意:
- 在编程过程中切勿弄错 == 与 =,两个运算结果完全不同。
- 比较两个字符串相等与否,不能使用 ==
(7)逻辑运算符
&& :两个都为真才为真
|| :只要一个为真,就为真
区分逻辑与和按位与 、逻辑或和按位或
1 & 2 ----->0
1 && 2 ----->1
1 | 2 ------>3
1 || 2 ----->1
逻辑与、逻辑或的使用
int mian()
{
int a =0;
int b =5;
int c = a && b;//&&:两个都为真才为真
int d = a || b;//||:只要一个为真,就为真
printf("%d\n",c);
printf("%d\n",d);
return 0;
}
Console:
0
1
练习题
求下列代码输出的值为多少?
int mian()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b &&d++;
//i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
假如有3个数,a,b,c
int c = a && b。
在 && 操作符运算下,只要一边为假,就为假,所以如果这个式子中,a为假,则这个表达式得到的值就为假,所以不用去计算b的值,计算完a就直接终止,返回假给c,同理:
上面这道题a=0,所以不去考虑后面bd的值,直接返回假给i,所以真正进行运算的只有a,而a进行了一个后置++,所以答案为:
Console:
1 2 3 4
如果把这题改为下面的代码,求输出的值为多少
int mian()
{
int i = 0, a = 1, b = 2, c = 3, d = 4;
//i = a++ && ++b &&d++;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
同样的,当a=1时,a为真,而 || 操作符只要有一边为真,则为真,后面的b、d都不需要计算了,直接返回真。
所以这题答案为:
Console:
2 2 3 4
(8)条件操作符(三目操作符)
?:
java中的三元运算符。
三目操作符语法
exp1 ? exp2 : exp3
| | |
表达式1 表达式2 表达式3
exp1 成立,exp2计算,整个表达式的结构是 : exp2的结果
exp1 不成立,exp3计算,整个表达式的结构是 : exp3的结果
int mian()
{
int a=0;
int b =3;
int max =0;
//if(a>b)
// max = a;
//else
// max = b;
//用三目操作符表示
max = a > b ? a : b;
return 0;
}
console:
3
上面例子的条件表达式中:
a>b就等于exp1,问号后的a为exp2,b为exp3。
如果a>b成立,则表达式的值为a,否则为b
(9)逗号表达式
逗号表达式 : exp1 , exp2 , exp3
逗号表达式要从左向右依次计算,但是整个表达式的结果是最后一个表达式的结果
int main()
{
//(2, 4 + 5, 6);
int a = 3;
int b = 5;
int c = 0;
//c=10 a=8 b=4
int d = (c = 5, a = c + 3, b = a - 4, c += 5);
//逗号表达式,是从左向右依次计算的
//整个表达式的结果是最后一个表达式的结果
printf("%d\n",d);
return 0;
}
Console:
10
(10)下表引用、函数调用和结构成员
1.下标引用操作符 [ ]
操作数:一个数组名 + 一个索引值
下标引用操作符语法:
int arr[10]; //创建数组
arr[9] = 10;//使用下标引用操作符
[]的两个操作数是arr和9
常在数组使用
int mian()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",arr[5]); //[]为下标引用操作符
return 0;
}
2.函数调用操作符:( )
int main()
{
//调用函数的时候,函数名后边的()就是函数调用操作符
printf("hello!\n"); //传一个参数
printf("%d",100); //传俩参数
return 0;
}
()函数调用操作符 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
3.结构成员访问操作符
访问一个结构的成员
. 结构体.成员名
-> 结构体指针 -> 成员名
. 操作符
#include <stdio.h>
struct Book
{
//结构体的成员(变量)
char name[20];
char id[20];
int price;
};
int mian()
{
//结构体变量名.成员名
struct Book b = {"C程序设计","C59989",50};
printf("书名: %s\n",b.name);
printf("书号: %s\n",b.id);
printf("定价: %d\n",b.price);
return 0;
}
Console:
书名:C程序设计
书号:C59989
定价:50
-> 操作符
#include <stdio.h>
struct Book
{
//结构体的成员(变量)
char name[20];
char id[20];
int price;
};
int mian()
{
//结构体指针->成员名
struct Book b = {"C程序设计","C59989",50};
struct Book * pb = &b;
printf("书名: %s\n",pb->name);
printf("书号: %s\n",pb->id);
printf("定价: %d\n",pb->price);
return 0;
}
Console:
书名:C程序设计
书号:C59989
定价:50