1、算数操作符
2、移位操作符
3、位操作符
4、赋值操作符
5、单目操作符
6、关系操作符
7、逻辑操作符
8、条件操作符
9、逗号表达式
10、下标引用、函数调用和结构成员
整型除法:1/2-->0
浮点数的除法:1.0/2-->0.5
%:取模操作符两边必须时整型。弄懂负数取模
移动的是二进制补码,因此在讲移位操作符之前,先了解一下补码是什么。
什么是补码?
正反补码(计算机中正负数的表示)
正的整数的原码、反码、补码相同
负数的原码、反码、补码是需要计算的
对于数字“7”来说,他的原码、反码和补码如下
00000000000000000000000000000111 - 原码
00000000000000000000000000000111 - 反码
00000000000000000000000000000111 - 补码
对于数字“-7”来说,他的原码、反码和补码如下
10000000000000000000000000000111 - 原码
11111111111111111111111111111000 - 反码(原码符号位不变,其他位按位取反就是反码)
11111111111111111111111111111001 - 补码(反码+1就是补码)
整数在内存中存的是补码
下面开始讲移位操作符:
左边丢弃,右边补0
效果:乘2
#include
int main()
{
int a = 7;
int b = a << 1;
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
算数移位:右边丢弃,左边补原符号位(正数补0,负数补1)。
逻辑移位:右边丢弃,左边补0。
对于正数来讲是一样的,但是对于负数来说,两种逻辑不同。
vs2019编译器采用的是算数右移(一般来讲编译器都采用的是算数右移)
移位操作不要移动负数位,这是标准未定义的。基本认为是错误的
& : 按(2进制)位与
| : 按(2进制)位或
^ : 按(2进制)位异或
int main()
{
int a = 3;
int b = -5;
int c = a & b;
//00000000000000000000000000000011 - 3的补码
//11111111111111111111111111111011 - (-5)的补码
//00000000000000000000000000000011 - 3&(-5)的结果(补码也是原码)
//按位与:补码位都为1则为1,其他情况都是0,求出来的数是补码,转回原码就是输出值。
int d = a | b;
//11111111111111111111111111111011 - 3|(-5)的结果(补码)
//10000000000000000000000000000101 - 3|(-5)的结果(原码码)
//按位或:补码位只要其中一个为1则为1,都是0才为0,求出来的数是补码,转回原码就是输出值。
int k = a ^ b;
//11111111111111111111111111111000 - 3^(-5)的结果(补码)
//10000000000000000000000000001000 - 3^(-5)的结果(原码)
//按位异或:相同则为0,相异为1,求出来的是补码,要把原码换回原码。
printf("%d\n", c);
printf("%d\n", d);
printf("%d\n", k);
return 0;
}
谈谈异或(^)的性质
a^a=0
0^a=a
a^a^b=b
a^b^a=b
a^a^b=a^b^a 交换律
利用性质写了一个a、b交换的代码,中间不需要建立临时变量
int main()
{
int a = 3;
int b = -5;
a = a ^ b;
b = a ^ b;
a = a ^ b; //三次异或实现交换,不需要建立新的临时变量
printf("%d %d", a, b);
return 0;
}
练习:求一个证书存储在内存中二进制的个数
int main()
{
int a = 5; //00000000000000000000000000000101 结果应为2
int i = 0;
int count = 0; //用于计数
for (i = 0;i < 32;i++)
{
if (a & 1 == 1) //1的二进制为00000000000000000000000000000001
{
count++;
}
a = a >> 1; //a右移一位判断下一位
}
printf("%d", count);
return 0;
}
赋值操作符是一个很棒的操作符,它可以让你得到你之前不满意的值,也就是可以重新赋值。(初始化是初始化,赋值是赋值,二者之间是有区别的)
int a = 0; 这叫初始化。
a = 100; 将100赋值给a。这是赋值
a = x = y+1; 连续赋值也是可以的,但是不建议。可读性不好。
复合赋值符:+= -= *= /= %= >>= <<= &= |= ^=
这些是将赋值和前面的其他操作符符合
如:a += 5 ; 和 a = a + 5 ; 这两个一模一样
a >>= 5 ; 和 a = a >> 5 ; 这两个一模一样
a &= 5 ; 和 a = a & 5 ; 这两个一模一样
不难发现这些符合都是用同样的规则将表达式简化而已。
单目操作符包括:
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数类型的长度(以字节为单位)
~ 对一个二进制数进行取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
下面进行详细讲解:
int main()
{
int flag = 3;
if (flag) //flag为真进入此if语句
{
}
if (!flag) //flag为假进入此if语句
{
}
}
c语言中,非0表示真,0表示假。
- 负值
+ 正值
int main()
{
int a = 10;
int b = +a; //正值a
int c = -a; //负值a
printf("%d %d", b, c); //输出10 -10
}
int main()
{
int a = 0;
printf("%0Xd\n", &a); //将a的首地址输出
int* p = a; //地址需要用指针存放
return 0;
}
int main()
{
int a = 0;
int b[5] = { 0 };
int n = sizeof(a);//sizeof是一个操作符,计算a所占内存的大小,单位是字节
int k = sizeof(int);//sizeof也可以计算类型的大小(其实是类型创建的变量所占内存空间的大小,因为类型本身没有大小)。单位都是字节
int l = sizeof(b);//sizeof还可以计算数组的大小。单位是字节。
printf("n=%d k=%d l=%d\n",n,k,l);//n=4 k=4 l=20
return 0;
}
当sizeof用于测量变量大小时,后面的括号可以去掉,这点说明了sizeof并不是一个函数。
但是要对类型进行测量时,如:sizeof(int)。此时就不能去括号。
int main()
{
int a = 0;
printf("%d\n", ~a); //对a的二进制序列(补码)按位取反再输出,这里输出为-1
return 0;
}
int main()
{
int a = 1;
printf("%d\n", a--);//先进行打印,后a=a-1。这里输出为1。
a = 1;
printf("%d", --a);//先进行a=a-1,后打印.这里输出为0。
return 0;
}
main()
{
int a = 1;
printf("%d\n", a++);//先进行打印,后a=a+1。这里输出为1。
a = 1;
printf("%d", ++a);//先进行a=a+1,后打印。这里输出为2。
return 0;
}
int main()
{
int a = 10;
int* p = &a; //用&取出a的地址赋值给p(上面讲了&作用)
printf("%d", *p);//这里是将a的地址放在了p中,然后通过*对p进行访问,将p地址对应的内存输出,即将a输出。输出结果为10。
return 0;
}
int main()
{
int a = 1;
printf("%f", (float)a);//将本来int类型的a强制转化为float类型输出,输出结果为1.000000。之后的a都变成了float类型,不会变回int类型了。
printf("%d", a);//输出结果1.0000001
return 0;
}
另一个例子:
int main()
{
int a = (int)3.14;
printf("%d\n", a);//将3.14这个小数(默认是double类型),强制转化成整型,输出。这里输出为3。
int b = (int)3.6;
printf("%d\n", b);//这里输出b的值为3,说明强制取整就是将整数部分取出,并不会进行四舍五入。
return 0;
}
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 用于测试“不相等”
== 用于测试“相等”
注意:“==”和“=”的区别。前者是判断相等,后者是赋值。
在运用“==”是,注意,不能用于比较数组相不相等,如果这样写:"abc"=="abcdef"。这样不是在比较两个字符串数组的内容是否相等,而是在比较两个字符串首地址相不相等。
逻辑操作符有两种:
&& 逻辑与
|| 逻辑或
逻辑与(&&)和按位与(&)区别
1&&3 结果为 1
1&0 结果为 0
逻辑或(||)和按位或(|)区别
1||2 结果为1
1|2 结果为3
注意:
&& 左边为假,右边就不计算了
|| 左边为真,右边就不计算了
例如:
int a = 1;
int i = 0&&(a = a+3);
这样算完a的值还是1,a = a + 3这个算式不计算,因为&&左边为0,即左边为假。i的值最后为0
同理:
int b = 2;
int i = 1||(b = b-2);
这样算完b的值还是2,不计算 b = b + 2。i的值最后为1。
表达式1 ? 表达式2 :表达式3
真 √ ×
假 × √
如:
int a = 3;
int b = 0;
b = a > 5 ? 3 : -3;
这样的表达式,最后b的值就为 -3 。因为 a = 3 不满足a > 5这个式子,所以整个条件表达式 a > 5 ? 3 : -3 的值就为 : 后面的结果,即为-3。即相当于b = -3。
可以在表达式1 、 表达式2 、 表达式3 中进行运算,但不建议写得太复杂,这样不便于程序理解,可读性会降低。
用逗号隔开的多个表达式即为逗号表达式
形式: exp1, exp2, exp3, ...expn;
逗号表达式运行情况:从左向右以此执行。整个表达式的结果是最后一个表达式的结果。
如:
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//这里c的值最后为13,即b=a+1的结果。
注意,虽然逗号表达式结果为最后一个表达式的结果,但是中间的表达式不可忽略,因为中间的表达式会改变最后表达式中的参数的值。
比如上面这个例子,a在第二个表达式中被赋值为b+10也就是12,因此在最后b=a+1的值才为13。
如:
int arr[10] = { 0 };
arr[7] = 8; //arr和7是[]的操作数,他们可以调换位置,可以写成 7[arr] 是一样的,但是这样写挺怪的,不推荐。
如:
int Add(int a, int b)
{
return a + b;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b); //这里调用Add函数用的()就是函数调用操作符。因为sizeof可以省略()去计算变量大小,所以sizeof并不是函数。
printf("%d", c); //这里输出为30
return 0;
}
1 . 结构体.成员名
2 -> 结构体指针->成员名
如:
struct Stu
{
char name[20];
int age;
double score;
};
void set_stu(struct Stu* ss)
{
strcpy((*ss).name, "zhengsan"); //这里用了strcpy函数,要在程序开头调用库函数:#include 。
(*ss).age = 20;
(*ss).score = 100.0;
strcpy(ss->name, "zhengsan"); //这里和上面达到的效果是一样的。因此我们发现 ss->age 完全相当于 (*ss).age。
ss->age = 20;
ss->score = 100.0;
}
void print_stu(struct Stu ss)
{
printf("%s %d %lf", ss.name, ss.age, ss.score);
}
int main()
{
struct Stu s = { 0 };
set_stu(&s);
print_stu(s);
return 0;
}
以上就是所有操作符的讲解了。