目录
1.操作符分类
- 算术操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下表引用、函数调用和结构成员
2.算数操作符
+ - * / %
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
下面举个例子:
那么m、n、k打印的答案是什么呢?
那么为什么
int m = 7 / 2; //因为int是整型打印的也只会是整型
double n = 7 / 2;
double k = 7.0 / 2;
3.移位操作符
<< 左移位符
>> 右移位符
注:移位符的操作数只能是整数
3.1左移位符
移位规则:
左边抛弃,右边补0
移位符移动的是二进制的位
说起移位符号,我们不得不补充一点关于源码、反码、补码的知识了
整数的二进制表示形式有3种,源码、反码、补码 (正数来说源码、反码、补码相等)
原码:按照数值直接写出的二进制序列
反码:对于正数来说,原码=反码、补码
对于负数来说,反码等于原码符号位 不变,其他位置取反
补码:对于负数来说,补码是反码的二进制+1
一个整数是4个字节,32位
一个整数写出2进制序列时,就是32位
有符号的整数来说,最高位的1位是符号位
有符号的整数来说,最高位的1是表示负数
有符号的整数来说,最高位的0是表示正数
无符号的整数来说,没有符号位,所有位都是有效位
无符号位例子:unsigned int num=10;
随机数
在计算机中,用补码计算打印出来的是原码
下面我们看一下这个例子:
我们先看一下正数:
int main()
{
int n = 7 << 1;
printf("%d", n);
return 0;
}
不是源码应该是原码 对不起 这里打错字啦
接下来是 负数:
3.2右移操作符
移位规则:
1.逻辑移位
左边用0填充,右边丢弃
2.算术移位
左边用原该值的符号位填充,右边丢弃
先举个正数的例子:
接下来看一下负数的:
(算数移位)
(逻辑移位)
当然这样的方法太暴力,不可能实现从-10到+5
注意:对于移位运算符,不要移动负数为,这个标准未定义的
//例子
int num=-10;
num<<-1;
4.位操作符
& 按位与
| 按位或
^ 按位异或
&遇0得0
| 见1得1
^相同为0,不同为1
练习一下:
num1& num2
一波变态的面试题:
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
a = a ^ b;
a = a ^ b;
pritnf("%d %d", a, b);
return 0;
}
5.赋值操作符
赋值操作符简而言之就是得到一个之前不满意的值,你可以重新赋值
int main()
{
int a=0; //初始化
a=10; //赋值
}
赋值操作符也可以连续使用
int a=10;
int x=0;
int y=20;
a=x=y+1;
a=x=y+1;
={
x=y+1;
a=x;
}
复合赋值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些操作符可以实现符合效果
例如:
int x=10;
x=x+10;
x+=10;
注意:=是赋值操作符
==是判断相等
6.单目操作符
!=
-
+
&
sizeof
~
--
++
*
(类型)
6.1.!=逻辑反操作符
int main()
{
int flag = 0;
if (! flag)
printf("111111111");
return 0;
}
6.2 - +
int main()
{
int a = 10;
printf("%d\n", +a);
printf("%d\n", -a);
return 0;
}
6.3 & 取地址
6.4 *解引用操作符
对p进行解引用,*p是通过p中存放的地址找到p指向的对象,*p就是a
6.5 sizeof操作数的类型长度
下面看一下这例子:
int main()
{
int arr[10] = { 0 };
printf("%d\n", sizeof(arr));
return 0;
}
这个打印出来将会是什么呢?
为什么是40呢??
因为sizeof(int)=4 arr[40] 4*10=40
我们举个例子在有序数列里面用二分法找一个数
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10};
int left = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int right = sz - 1;
int k = 0;
scanf("%d", &k);
int flag = 0;
while (left <= right)
{
int mid = (left + right) / 2;
if (k > arr[mid])
{
left = mid + 1;
}
else if (k < arr[mid])
{
right = mid - 1;
}
else
{
printf("找到啦,下标是%d\n", mid);
flag = 1;
break;
}
}
if (flag == 0)
{
printf("没找到");
}
return 0;
}
sizeof和数组
void test1(int arr[])
{
printf("%d\n", sizeof(arr)); //2
}
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)); //1
printf("%d\n", sizeof(ch)); //3
test1(arr);
test2(ch);
return 0;
}
1 2 3 4
都会输出什么?
为什么2 4位置会输出 8 8呢
因为我的电脑是64位
如果32位将会输出4
其实int arr[]本质是指针
6.6 ~对一个数的二进制按位取反
~10为什么会是-11呢?
6.7 ++ --
前置++ -- 先加一(减一)后使用 ++a
后置++ -- 使用后加一(减一) a++
前置++ --
后置-- ++
7.关系操作符
>
<
>=
<=
!=
==
注意:==是判断等于
=是赋值
8.逻辑操作符
&& 逻辑与
|| 逻辑或
举个例子:老师让小红和小明来搬书(小明小红一个人完成不了) 1表示来了 0表示没来
小明 | 小红 | 完成否 | |
小明&&小红 | 1 | 1 | 完成 |
小明&&小红 | 1 | 0 | 未完成 |
小明&&小红 | 0 | 1 | 未完成 |
小明&&小红 | 0 | 0 | 未完成 |
举个例子:老师让小红和小明来搬书(小明小红来一个就可以完成) 1表示来了 0表示没来
小明 | 小红 | 完成否 | |
小明||小红 | 1 | 1 | 完成 |
小明||小红 | 1 | 0 | 完成 |
小明||小红 | 0 | 1 | 完成 |
小明||小红 | 0 | 0 | 未完成 |
区分逻辑操作符与和按位与
1&2-------->0
00000000000000000000000000000001
& 00000000000000000000000000000010
00000000000000000000000000000000
1&&2------>1
1真 2真--------->真
区分逻辑操作符或和按位或
1|2---------->3
00000000000000000000000000000001
| 00000000000000000000000000000010
00000000000000000000000000000011
1||2
1真 ||2真 --->真
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a=%d,b=%d,c=%d,d=%d\n", a, b, c, d);
printf("%d\n", i);
return 0;
}
9.条件操作符
exp1?exp2:exp3
int main()
{
int a = 0;
int b = 0;
scanf("%d", &a);
if (a > 5)
printf("3");
else
printf("-3");
return 0;
}
这个代码我们要是用条件操作符应该怎么写呢
a>5?3:-3
10.逗号表达式
exp1,exp2,exp3,.........expn
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);
printf("%d\n", c);
return 0;
}
11.下标引用、函数调用和结构成员
11.1.[]下标引用操作符
操作数:一个数组+一个索引值
int arr[10]; //创建数组
arr[9]=10; //实用下标引用操作符
[]的两个操作数是arr和9
11.2.()函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数
void test1()
{
printf("111111\n");
}
void test2(const char*str)
{
printf("%s\n",str);
}
int main()
{
test1();
test2("hello hwh");
return 0;
}
11.3.访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
struct Book
{
char name[20];
int price;
};
void Print(struct Book* pb)
{
printf("%s %d\n", (*pb).name, (*pb).price);
printf("%s %d\n", pb->name, pb->price);
}
int main()
{
struct Book b = {"C语言", 55};
printf("%s %d\n", b.name, b.price);
Print(&b);
return 0;
}
12.表达式求值
12.1隐式类型转换
表达式的整型运算要在 CPU 的相应运算器件内执行, CPU 内整型运算器 (ALU) 的操作数的字节长度一般就是 int 的字节长度,同时也是 CPU 的通用寄存器的长度。因此,即使两个 char 类型的相加,在 CPU 执行时实际上也要先转换为 CPU 内整型操作数的标准长度。通用 CPU ( general-purpose CPU )是难以直接实现两个 8 比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于 int 长度的整型值,都必须先转换为 int 或 unsigned int ,然后才能送入 CPU 去执行运算。
//实例1char a,b,c;...a=b+c;
整型提升是按照变量的数据类型的符号位来提升的
int main()
{
char a = 10;
//00000000000000000000000000001010
//00001010
char b = 126;
//00000000000000000000000001111110
//01111110
//00000000000000000000000000001010->a
//00000000000000000000000001111110->b
//00000000000000000000000010001000->a+b
//10001000
//11111111111111111111111110001000
//10000000000000000000000001111000 //-120
char c = a + b;
printf("%d\n",c);
return 0;
}
12.2算术转换
12.3操作符属性
1. 操作符的优先级2. 操作符的结合性3. 是否控制求值顺序