目录
1.5单目操作符 ! - + & sizeof ~ -- ++ *
本章重点
1.各种操作符的介绍
2.表达式求值
一、各种操作符的介绍
操作符的分类 | 举例 |
---|---|
算术操作符 | + - * / % |
位移操作符 | << >> |
位操作符 | & | ^ |
赋值操作符 | = += -= >>= <<= %= /= |
单目操作符 | ! -(负号) +(正号) & sizeof ~ -- ++ *(解引用) |
关系操作符 | < >= > <= != == |
逻辑操作符 | && || |
条件操作符(三目操作符) | exp1 ? exp2 : exp3 |
逗号操作符 | exp1,exp2,exp3,.... |
下标引用、函数调用和结构成员操作符 | [ ] ( ) . -> |
1.1算术操作符 + - * / %
:> 加减乘除还有取余,都是比较基础的操作符。
但其中值得注意的是 "/",如果A/B ,其中A,B都是整形,那么结果也为整形。如果想要得到浮点数的结果,那么至少有一边得是浮点数形式。
eg.
#include<stdio.h>
int main()
{
int a = 3 / 5;
printf("%d\n", a);//0
float b = 3.0 / 5;//两边至少有一个浮点数
printf("%f\n", b);//0.6
return 0;
}
#include<stdio.h>
int main()
{
int a = 7 % 3;//必须两端都是整数
printf("%d\n", a);
return 0;
}
1.2位移操作符 << >>
:> 位移操作符是针对二进制位的运算
左移操作符:
int main()
{
int a = 2;
//把a的二进制位向左移动一位
//00000000000000000000000000000010 ---- 2
//00000000000000000000000000000100 ---- 4
int b = a << 1;//左移操作符,左边丢弃,右边补0
printf("b = %d\n", b);//4
return 0;
}
右移操作符:
1.算术右移
右边丢弃,左边补原符号位
2.逻辑右移
右边丢弃,左边补0
int main()
{
int a = 10;
//把a的二进制位向右移动一位
//00000000000000000000000000001010 ---- 10
//00000000000000000000000000000101 ---- 5
int b = a >> 1;
//右移操作符
//1.算术右移
//右边丢弃,左边补原符号位
//2.逻辑右移
//右边丢弃,左边补0
printf("b = %d\n", b);//5
printf("a = %d\n", a);//10,所以不会改变原本的a
return 0;
}
测试当前编译器是算术位移还是逻辑的方法:
int main()
{
int a = -1;
//负数:-1
//存放在内存中,内存中存的是二进制的补码
//整数的二进制表示形式:3种
//原码:直接根据数值写出的二进制序列就是原码
//反码:原码符号位不变,其他位按位取反就是反码
//补码:反码+1,就是补码
//以上算法针对负数,对于正整数原码反码补码都一样
//原码:100000000000000000000001 --- -1
//反码:111111111111111111111110
//补码:111111111111111111111111(内存中是补码)
//把a的二进制位向右移动一位
int b = a >> 1;
printf("b = %d\n", b);//-1
//当前的右移操作符是算数操作符!
printf("a = %d\n", a);
return 0;
}
1.3位操作符 & | ^
注:他们的操作数必须是整数。
按位与:
int main()
{
int a = 3;
int b = 5;
//& - 按(2进制位)位与,都是1,才为1
int c = a & b;
//00000000000000000000000000000011 --- 3
//00000000000000000000000000000101 --- 5
// 每一位都按位与
//00000000000000000000000000000001 --- 1
printf("%d\n", c);
return 0;
}
按位或:
int main()
{
int a = 3;
int b = 5;
//| - 按(2进制位)位或,有一个是1就是1
int c = a | b;
//00000000000000000000000000000011 --- 3
//00000000000000000000000000000101 --- 5
// 每一位都按位与
//00000000000000000000000000000111 --- 7
printf("%d\n", c);
return 0;
}
按位异或:
int main()
{
int a = 3;
int b = 5;
//^ - 按(2进制位)位异或
//规则:相同为0,相异为1
int c = a ^ b;
//00000000000000000000000000000011 --- 3
//00000000000000000000000000000101 --- 5
// 每一位都按位异或
//00000000000000000000000000000110 --- 6
printf("%d\n", c);
return 0;
}
思考:在不使用第三个变量的情况下交换a和b的值
int main()
{
int a = 3;
int b = 5;
//法一:缺陷—数值太大会溢出
a = a + b;
b = a - b;
a = a - b;
//法二:异或一下
a = a ^ b;
b = a ^ b;
a = a ^ b;
return 0;
}
练习:编写代码实现:求一个整数储存在内存中的二进制中1的个数
提示:可以用该数和1进行按位与
int main()
{
int a = 13;
//00000000000000000000000000001101
//00000000000000000000000000000001
//00000000000000000000000000000001
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
int b = 1;
int c = 0;
c = a & b;
a = a >> 1;
if (c == 1)
{
count++;
}
}
printf("count = %d\n", count);
return 0;
}
1.4赋值操作符 = += -= >>= <<= %=
:>比较简单,就不过多赘述了^a^
1.5单目操作符 ! - + & sizeof ~ -- ++ *
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
运用举例:
int main()
{
short s = 5;
int a = 10;
printf("%d\n", sizeof(s = a + s));//2
//sizeof 括号中放的表达式不参与运算的!
printf("%d\n", s);//5
return 0;
}
强制类型转换
int main()
{
int a = (int)3.14;
return 0;
}
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//4
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));//4
}
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%d\n", sizeof(arr));//40
printf("%d\n", sizeof(ch));//10
test1(arr);
test2(ch);
return 0;
}
1.6关系操作符 < >= > <= != ==
tip:
1.在编程的过程中==和=不小心写错,导致的错误
2.比较两个字符串不能用==,要用strcmp
1.7逻辑操作符 && ||(判断真假的)
tip:区分逻辑与和按位与 区分逻辑或和按位或
//1&2 -----> 0
//1&&2-----> 1
//1|2------> 3
//1||2-----> 1
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
//i = a++ && ++b && d++;//&&时候,只要发现a = 0,右边不用算了已经
i = a++||++b||d++;//||的时候,只要发现a为真,后面也不用算了
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
return 0;
}
1.8条件操作符(三目操作符)
举例:
int main()
{
int a = 3;
int b = 0;
if (a > 5)
b = 1;
else
b = -1;
//三目操作符exp1 ? exp2 : exp3
b = a > 5 ? 1 : -1;
return 0;
}
1.9逗号表达式
- 从左向右依次计算,但是整个表达式结果是最后一个表达式的结果
int main()
{
int a = 3;
int b = 5;
int c = 0;
int d = (c = 5, a = c + 3, b = a - 4, c += b);
printf("d = %d\n", d);
return 0;
}
1.10下标引用、函数调用和结构成员
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", arr[4]);//[]-就是下标引用操作符
//下标引用操作符的操作数是2个:arr和4
return 0;
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b);//() - 函数调用操作符
printf("%d\n", ret);
return 0;
}
1.11结构成员访问操作符 . ->
结构体
创建了一个自定义类型
struct Book
{
char name[20];
char id[20];
int price;
};
int main()
{
//初始化
struct Book b = { "C语言","C2023482",55 };
//结构体变量名.成员名
//printf("书名:%s\n", b.name);//(.为成员访问操作符)
//printf("书号:%s\n", b.id);
//printf("价格:%d\n", b.price);
struct Book* pb = &b;
//printf("书名:%s\n", (*pb).name);//(.为成员访问操作符)
//printf("书号:%s\n", (*pb).id);
//printf("价格:%d\n", (*pb).price);
//结构体指针->成员名
printf("书名:%s\n", pb->name);
printf("书号:%s\n", pb->id);
printf("价格:%d\n", pb->price);
return 0;
}
二、表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称之为整型提升。
实例1
char a,b,c;
...
a = b + c;
b 和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之和,结果将被截断,然后再储存于a中
整形提升
1、整型提升的定义:
C的整型算术运算总是至少已缺省整型类型的精度来进行。为了获得这种精度,表达式中的字符和短整型操作数在使用前辈转换为普通整型,这种转换被称为整型提升。
我的理解:再进行整型算数运算时,要把字符和短整型的二进制位数提升到整型的位数(32位)。字符原来为8位,要提升到32位,在前补24位。短整型原来为16为,在前补16位。
2、为什么要整型提升
整型算术运算要在计算机cpu内的整型运算器中进行。而整型运算器的操作数字节长度是整形int的字节长度。所以在整型计算时,要将所有数据提升至整形int的字节长度32位,即4字节。
3、如何进行整型提升
按照变量的数据类型的符号位来补足。
若为负数(符号位为1),整型提升时补1;
若为0和正数(符号位为0),整型提升时补0。
!!注意:
1、整型提升针对的是补码。
2、整型提升完成计算后,若变量定义为字符或短整形,要根据变量类型进行截断。
例如:char a = 1
整型提升前(补码):00000001
整型提升后(补码):00000000 00000000 00000000 00000001
char b = -1
整型提升前(补码):11111111
整型提升后(补码):11111111 11111111 11111111 11111111
int main()
{
char a = 3;
//00000000000000000000000000000011 - 3
//00000011 - a
char b = 127;
//00000000000000000000000001111111 - 127
//01111111 - b
char c = a + b;
//提升👇
//00000000000000000000000000000011
//00000000000000000000000001111111
//00000000000000000000000010000010
//10000010 - c
//11111111111111111111111110000010(补码)
//11111111111111111111111110000001(反码)
//10000000000000000000000001111110(原码)
//发现a和b都是char类型,都没有达到一个int的大小
//这里就会发生整形提升
//如何进行整型提升
//按照变量的数据类型的符号位来补足。
//若为负数(符号位为1),整型提升时补1;
//若为0和正数(符号位为0),整型提升时补0。
//!!注意:
//1、整型提升针对的是补码。
//2、整型提升完成计算后,若变量定义为字符或短整形,要根据变量类型进行截断。
printf("%d\n", c);
return 0;
}
整形提升
实例1:
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)
printf("c");//只打印c
return 0;
}
实例2:
int main()
{
char c = 1;
//%u无符号十进制
printf("%u\n", sizeof(c));//1
printf("%u\n", sizeof(+c));//4
printf("%u\n", sizeof(-c));//4
//c只要参与表达式运算,就会发生整形提升
return 0;
}
算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个
操作数的类型,否则就无法进行。下面的层次体系称为寻常算术转换
//long double
//double
//float
//unsighted long int
//long int
//unsighted int
//int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另一个操作数的类型后执行运算
警告:但是算术转换要合理,不然会有一些潜在问题。
int main()
{
int a = 0;
float f = 4.5f;
a + f;//计算时a要转为float类型
return 0;
}
操作符的属性
复杂表达式的求值有三个影响因素
1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性
操作符优先级
int main()
{
int a = 4;
int b = 5;
int c = a + b * 7;//优先级决定计算顺序
int c = a + b + c;//结合性决定计算顺序
return 0;
}
tip:sizeof 和 strlen区别
主要区别如下:
1、sizeof是运算符,strlen是C语言标准库函数。
2、 strlen 测量的是字符串的实际长度,以’\0’ 结束,返回结果不包括’\0’ 。
3、而sizeof 测量的是字符的分配大小,它的参数可以是数组、指针、类型、对象、函数等。
具体而言,当参数分别如下时,sizeof返回的值含义如下:
数组 - 编译时分配的数组空间大小;
指针 - 存储该指针所用的空间大小;
类型 - 该类型所占的空间的大小;
对象 - 对象的实际占用空间大小;
函数 - 函数返回类型所占空间的大小;
int main()
{
char* str1 = "absde";
char str2[] ="absde";
char str3[8] = { 'a',};
int str4[8] = { 'a',};
char ss[] = "0123456789";
//输出:
sizeof(str1); // 4,计算的是指针内存的大小,包括’\0’
sizeof(str2); // 6 ,计算的是字符串的内存大小,包括’\0’
sizeof(str3); // 8 ,计算的是char型数组的内存大小
sizeof(str4); // 32 ,计算的是int型数组的内存大小
sizeof(ss); // 11 ,计算的是字符串的大小,包括'\0’
strlen(str1); // 5 ,计算的是字符串长度,不包括‘\0’
strlen(str2); // 5 ,计算的是字符串长度,不包括‘\0’
strlen(str3); // ? ,因为字符串需要找到’\0’才可结束,要在’a’之后找到’\0’,所以是个随机值
strlen(str4); // ? ,因为字符串需要找到’\0’才可结束,要在’a’之后找到’\0’,所以是个随机值
strlen(ss); // 10 ,计算的是字符串长度,不包括‘\0’
return 0;
}