目录
操作符分类:
1.算术操作符
+ - * / %
2.移位操作符
(1)左移操作符:
(2)右移操作符
3.位操作符
(1)按位与
(2)按位或
(3)按位异或
面试题:
4.赋值操作符
5.单目操作符
5.1 !操作符实例
5.2 _Bool类型
5.3 ++、--操作符
5.4 ()强制转换操作符
5.5 unsigned int 问题:
5.6 &、*操作符
5.7 sizeof
5.8 sizeof与数组
5.9 ~按位取反操作符:
6.关系操作符
7.逻辑操作符
&&逻辑与
|| 逻辑或
8.条件操作符
9.逗号表达式
10.下标引用、函数调用和结构成员
+ - * / %
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数(取模只能用于整数)。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
#include
int main()
{
//操作符(运算符)
int ret1 = 10 / 3;
备注:对于除法操作符来说,两边的操作数都是整数,执行的是整数除法
如果想计算出小数,除号的两端至少有一个操作数是浮点数
double ret2 = 10.0 / 3.0;
printf("%d\n", ret1);
printf("%lf\n", ret2);
printf("%.1lf\n", ret2);//%.lf表示保留小数点后一位
int ret3 = 10 % 3;//取模(取余),计算的是整除之后的余数,取模操作符的两边的操作数只能是整数
printf("%d\n", ret3);
return 0;
}
首先要提一下2进制整数的表示形式 :原码、反码、补码。
其中正数的原、反、补码相同。而负数的反码等于原码按位取反(符号位不变),补码=反码+1;
00000000000000000000000000001010 - 原码
00000000000000000000000000001010 - 反码
00000000000000000000000000001010 - 补码
int a = 10;//正数
10000000000000000000000000001010 - 原码
11111111111111111111111111110101 - 反码
11111111111111111111111111110110 - 补码int b=-10;//负数
以下是原码,反码,补码的相互变换(针对的是负数)
左边抛弃、右边补0
num<<1的结果是移位之后的效果,但num的值是不变的
分两种:
1. 逻辑移位 左边用0填充,右边丢弃
2. 算术移位 左边用原该值的符号位填充,右边丢弃
int a = -1;
原码:10000000000000000000000000000001
反码:11111111111111111111111111111110
补码:11111111111111111111111111111111
int b=a>>1;
逻辑移位左边最高位符号位都是补0,算术移位补原来该数值的符号位。
在计算机中移位是采取算术移位的
注意:
对于移位运算符,不要移动负数位,这个是标准未定义的。
int num = 10;
num>>-1;//error
以下操作符均针对于二进制位进行计算的
& //按位与
| //按位或
^ //按位异或
注:他们的操作数必须是整数
对应的二进制位有0,则为0,两个同时为1,才为1
注意都是转换成补码后才进行计算的
//& - 按2进制位与
//对应的二进制位有0,则为0,两个同时为1,才为1
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101
//11111111111111111111111111111010
//11111111111111111111111111111011 -补码
int c = a & b;
//00000000000000000000000000000011
//11111111111111111111111111111011
//00000000000000000000000000000011
printf("%d\n", c);
return 0;
}
对应的二进制位有1则为1,两个同时为0则为0
//按2进制位或
//对应的二进制位有1则为1,两个同时为0则为0
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101
//11111111111111111111111111111010
//11111111111111111111111111111011 -补码
//
int c = a | b;
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111011
//
printf("%d\n", c);
return 0;
}
对应的二进制位:相同为0,相异为1
//^ - 按2进制位异或
//对应的二进制位:相同为0,相异为1
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101
//11111111111111111111111111111010
//11111111111111111111111111111011 -补码
//
int c = a ^ b;
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111000
//11111111111111111111111111110111
//10000000000000000000000000001000
//-8
printf("%d\n", c);
return 0;
}
不能创建临时变量(第三个变量),实现两个整数的交换
两种方法:
第一种
int main()
{
int a = 3;
int b = 5;
printf("%d %d\n", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("%d %d\n", a, b);
return 0;
}
//a=a+b;
//b=a+b-b;
//a=a+b-a;
但是a和b的数值太大会溢出,超出规定范围,有一定风险。
第二种
异或操作符:
1.a^a=0
2.0^a=a
3.异或是支持交换律的
#include
int main()
{
int a = 3;
int b = 5;
printf("交换前:");
printf("%d %d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("交换后:");
printf("%d %d\n", a, b);
return 0;
}
但是异或操作符交换2个变量也有缺点:
1.可读性差
2.效率也不如使用临时变量的方法
3.异或只能对整数的交换
赋值操作符自右向左结合,也可以进行复合赋值,简化代码
int main()
{
int a = 10;
a = a + 5;
a += 5;
}
复合赋值符
+= -= *= /= %= >>= <<= &= |= ^=
连续赋值可以写成:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值
但这样写有缺点:
1.可能会让人产生困惑,到底是先把x值赋给a,再把y+1赋给x。还是先把y+1赋给x,再把x赋给a。(本质上是从右向左赋值)
2.不好调试
x = y+1;
a = x;//这样写更容易调试
单目操作符介绍
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
#include
int main()
{
//C语言中0表示假,非0表示真
int flag = 5;
if (flag)
{
printf("haha\n");
}
if (!flag)
{
printf("hehe\n");
}
printf("%d\n", !flag);
printf("%d\n",flag);return 0;
}
C语言中0表示假,非0表示真
这里提一下布尔类型,没必要赋值浮点数,负数等待,布尔类型就是为了不让胡乱赋值造的。
//布尔类型是用来表示真假的类型
#include
#include
int main()
{
_Bool flag = true;//true,false
if (flag)
{
printf("hehe\n");
}
else
{
printf("hahaha\n");
}
}
++a//先++,后使用
a++//先使用,后++
--//相同道理
int main()
{
int a= (int)3.14;//强制类型转换,只拿整数部分,小数不要了
printf("%d", a);
}
//少用
int main()
{
srand((unsigned int)time());//需要一个time函数的返回值作为参数,但time()函数的返回值是time_t,srand又需要unsigned int类型。
}
int main()
{
unsigned int num = -10;
printf("%u\n", num);
int num1 = -10;
printf("%d\n", num1);
return 0;
}
理解: unsigned int无符号位将本来数据的最高位符号位变成了有效位,那么数据就变大了。
int main()
{
int a = 10;
printf("%p\n", &a);
int* pa = &a;
char ch = 'w';
char* pc = &ch;
//printf("%p\n", &ch);char arr[10] = { 0 };
char* p2 = arr;
char* p3 = &arr[0];char* p = "abcdef";
printf("%p\n", p);
printf("%c\n", *p);//打印的是a
return 0;
}
void test1(int *arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);//*(arr+i),arr是首元素地址+i跳过i个整型
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
test1(arr, sz);
return 0;
}
从int*pa=&a来看,pa是指针变量,类型是int*,&a是取出a在内存空间中开辟的地址,*是解引用操作符,通过a的地址找到a,可以改变a的值。
而arr是首元素地址,+i跳过i个整型。
函数调用的时候,要写()但是sizeof后边的括号可以省略,说明sizeof不是函数
sizeof既是操作符,也是关键字,可以求变量(类型)所占空间的大小
#include
int main(){
int a = 10;
short s = 5;
printf("%d\n", sizeof(s = a + 3));
printf("%d\n", s);
return 0;
}
上图说明sizeof内部的表达式是不计算的。
#include
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);//ok
printf("%d\n", sizeof(int));//但是不能写出sizeof int
int arr[10] = {0};
printf("%d\n", sizeof arr);//ok
printf("%d\n", sizeof(arr));//ok
printf("%d\n", sizeof(int[10]));//ok
return 0;
}
注意:不可写成sizeof 类型 ,不加()是不行的
#include
void test1(int arr[])//数组传参,形参可以写成数组,也可以写出指针
{ //即使写成数组,本质上也是指针
printf("%zd\n", sizeof(arr));
}
void test2(char ch[])
{
printf("%zd\n", sizeof(ch));
}
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%zd\n", sizeof(arr));//计算的数组大小
printf("%zd\n", sizeof(ch));//计算的数组大小
test1(arr);
test2(ch);
return 0;
}
~按位取反
00000000000000000000000000000000
11111111111111111111111111111111 - 补码是全1
int main()
{
int a = 0;
printf("%d\n", ~a);//?
return 0;
}
#include
int main()
{
int a = 9;
//00000000000000000000000000001001
//00000000000000000000000000010000 1<<4
//00000000000000000000000000011001
//
//把a的二进制中第5位改成1
a |= (1<<4);
printf("%d\n", a);
//把a的二进制中的第5位改回来,变成0
//00000000000000000000000000011001
//11111111111111111111111111101111
//00000000000000000000000000001001
a &= (~(1 << 4));
printf("%d\n", a);//9
return 0;
}
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
&& 逻辑与
|| 逻辑或
&(按位与)和|(按位或)是针对2进制来说的,而上述两个是针对真假来说的。
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++;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
return 0;
}
&&左边为假,右边无需计算(自左向右, 只要有一个操作数为假,后面不用算了)。
int main()
{
int i = 0, a =0, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("a=%d\nb=%d\nc=%d\nd=%d\n", a, b, c, d);
return 0;
}
|| 左边为真,右边无需计算(自左向右, 只要有一个操作数为真,后面不用算了)。。
exp1 ? exp2 : exp3
真返回条件2,假返回条件3
#include
int main()
{
int a = 10;
int b = 20;
int m = 0;
m= (a > b ? a : b);
printf("%d\n", m);
b = (a > 5 ? 3 : -3);
printf("%d\n", b);
return 0;
}
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式,返回最后一个值,但前面的会影响后面的变量
printf("%d", c);
}
逗号表达式,返回最后一个值,但前面的会影响后面的变量
1. [ ] 下标引用操作符 ,操作数:一个数组名 + 一个索引值
2. ( ) 函数调用操作符 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
int main()
{
int arr[10] = { 1,2,3,4,5,6 };
printf("%d\n", arr[4]);//下标引用操作符,操作数是arr,10;
int len = strlen("abcdef");//函数调用操作符,操作数是strlen,"abcdef"
return 0;
}
3. 访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
结构体-自定义类型(聚合类型),把简单的类型集合在一起
生活中有些对象要被描述的话,不能简单的使用单个内置类型(char int short long 、 long long 、 float、 double)
例如:
书:书名,作者,出版社,定价
学生:名字,年龄,学号.....
struct Book
{
char name[20];
char author[30];
int price;
};
void print1(struct Book *b)
{
printf("《%s》%s %d\n", b->name, b->author, b->price);
printf("《%s》%s %d\n", (*b).name, (*b).author, (*b).price);
}
int main()
{
struct Book b1 = { "单人破b","niko",60 };
struct Book b2 = { "全体白给","simple",80 };
//结构体变量.成员名
printf("《%s》 %s %d\n", b1.name, b1.author, b1.price);
printf("《%s》 %s %d\n", b2.name, b2.author, b2.price);
print1(&b1);
//print1(&b1);
return 0;
}
#include
struct Stu {
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu)
{
stu.age = 18;
}
void set_age2(struct Stu *stu)
{
stu->age = 18;
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;
stu.age = 20;
set_age1(stu);
printf("%d\n", stu.age);
set_age2(&stu);
printf("%d\n", stu.age);
// set_age2(stu);
}
这是结构体指针,第一个是传值调用,第二个是传址调用。