●🧑个人主页:你帅你先说.
●📃如果文章有帮助,欢迎点赞👍关注💡收藏💖
●📖既选择了远方,便只顾风雨兼程。
●🤟欢迎大家有问题随时私信我!
●🧐版权:本文由[你帅你先说.]原创,CSDN首发,侵权必究。
目录
1.算术操作符
+ - * / %
int ret = 9/2;
double ret = 9/2;
大家可能会认为9/2算出来是4是因为int整型,实际上不是,当你以double类型打印时结果是4.0,所以说这是‘/’自身的特点,并不是int类型造成的。
那要怎么算出4.5呢?
上面第二点提到了,只要有浮点数就执行浮点数除法,所以可以改成9.0/2或者9/2.0就能算出4.5了。
2.移位操作符
<< 左移操作符>> 右移操作符
移位操作符,移动的是二进制位
对于整数的二进制有3中表示形式:原码、反码、补码
正整数 - 原码、反码、补码相同
负整数
原码 - 直接按照数字的正负写出的二进制序列
反码 - 原码的符号位不变,其他位按位取反得到的
补码 - 反码+1
二进制的第一位为符号位,负数为1,正数为0。
整数在内存中存储的是二进制的补码
那移位操作符是怎么对二进制进行移位的呢?
int a = 5;
int b = a << 1;
以32bit系统为例
算出来结果是10
注意:此时a的值还是5,b的值是10,移位操作符并不对自身进行改变。
刚刚上面是以左移操作符为例,实际上右移操作符遵循的规则和左移略有不同。
首先右移运算分两种:1. 逻辑移位左边用0填充,右边丢弃2. 算术移位左边用原符号位填充,右边丢弃
大部分编译器采用的是算术移位。
int a = -1;
int b = a >> 1;
图解
因为-1的原符号位是1,所以补1。
3.位操作符
& 按位与
| 按位或
^ 按位异或注:他们的操作数必须是整数。
3.1 &(按位与)
int a=3 b=5;
c=a&b;
a的二进制 00000000000000000000000000000011
b的二进制 00000000000000000000000000000101
结果为 00000000000000000000000000000001
即只要有0即为0,其余不变(1向左移位按位与这个数可以找二进制里有几个1)
3.2 |(按位或)
a的二进制 00000000000000000000000000000011
b的二进制 00000000000000000000000000000101
结果为 00000000000000000000000000000111
即只要有1即为1,其余不变
3.3 ^(按位异或)
a的二进制 00000000000000000000000000000011
b的二进制 00000000000000000000000000000101
结果为 00000000000000000000000000000110
相同为0,相异为1
异或是支持交换律的!
在这里有一道非常经典的变态面试题
不能创建临时变量(第三个变量),实现两个数的交换
int a = 3,b = 5;
a = a + b;
b = a - b;
a = a - b;
但这种方法有个局限,因为整型变量有上限,如果数据过大可能会导致溢出。
法二:
int a = 3;
int b = 5;
a = a^b;
b = a^b;
a = a^b;
图解
这个解法只是就题解题,平时我们在写代码时避免写出这种可读性差的代码。
4.赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值。也就是你可以给自己重新赋值。
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值
这个表达式是从右向左依次赋值的。但平时我们不建议这种写法。
最好写成以下这样
x = y + 1;
a = x;
5.复合赋值符
+= 例如a+=5就相当于a = a +5,下面的以此类推。
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
6.单目操作符
! 逻辑反操作- 负值+ 正值& 取地址sizeof 操作数的类型长度(以字节为单位)~ 对一个数的二进制按位取反-- 前置、后置 --++ 前置、后置 ++* 间接访问操作符 ( 解引用操作符 )( 类型 ) 强制类型转换
6.1!(逻辑反操作)
C语言是如何表示真假的?
0位假,非0为真,C语言规定真所对应的值为1。所以 0为假 !0 为真。
6.2sizeof
这边强调一点大家容易误解的,sizeof是操作符不是函数!
int a = 5;
short s = 10;
printf("%d\n", sizeof(s = a + 2));//结果为2
printf("%d\n", s); //结果为10
sizeof()最终算的类型是表达式最终的类型,当把int类型赋给short int 类型时会发生截断。
6.3~(按位取反)
00000000000000000000000000001111
11111111111111111111111111111110000
即0和1互换
6.4&取地址
int a = 10;
int * p = &a;
取地址时取出的是4个字节中的第一个字节,且是低地址。
6.5*解引用操作符
int a = 10;
int * p = &a;
*p = 20; //解引用操作符
*p表示通过p找到a的地址从而改变a的内容。
int b = *p;//右值
*p = 20;//左值
当*p在等号左右两边所表达的意义不同。
左值代表空间,右值代表空间的内容。
6.6 ++、-- 操作符
这里以++为例
前置++
int a = 10;
int b = a++;
//最终结果为 a为11 b为10,后置++,先赋值,后++
int a = 10;
int b = ++a;
//最终结果为 a为11 b为11,前置++,先++,后赋值
7.关系操作符
>>=<<=!= 用于测试 “ 不相等 ”== 用于测试 “ 相等 ”
8.逻辑操作符
&& 逻辑与|| 逻辑或
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
此题输出结果为1234。首先,a++,是先使用后++,a是0,0是假,所以不管后面的表达式是什么,最终结果都是假,后面的表达式没有参与运算,故结果为1234。
9.条件操作符
exp1 ? exp2 : exp3
b = a > 5 ? 3 : -3;
这段代码等价于下面这段代码
if (a > 5)
b = 3;
else
b = -3;
10.逗号表达式
exp1 , exp2 , exp3.....expn
从左向右依次计算,整个表达式的结果是最后一个表达式的结果。
int a=3,b=5,c=6;
int d=(a=a-2,b=a+c,c=a-b);//逗号表达式的结果即为c=a-b的结果
printf("%d",d);//输出结果为-6
11.下标引用、函数调用和结构成员
11.1[ ] 下标引用操作符
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
//[ ]的两个操作数是arr和9。
所以arr[10]也可以写成10[arr],因为arr和10就是两个操作数,可以交换顺序。只是平时我们习惯写arr[10]。
11.2( ) 函数调用操作符
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //使用()作为函数调用操作符。
test2("hello world.");//使用()作为函数调用操作符。
return 0;
}
12.结构成员
. 结构体.成员名
-> 结构体指针->成员名
#include <string.h>
struct Book
{
char name[20];
float price;
char id[10];
};
void print1(struct Book b)
{
printf("书名: %s\n", b.name);
printf("价格: %f\n", b.price);
printf("书号: %s\n", b.id);
}
void print2(struct Book* pb)
{
printf("书名: %s\n", pb->name);
printf("价格: %f\n", pb->price);
printf("书号: %s\n", pb->id);
}
int main()
{
struct Book b = {"C语言程序设计", 55.5f, "C20190201"};
print2(&b);
return 0;
}
在这里强调一点,如果要对结构体里的内容进行改变,不可直接改,要用strcpy函数进行更改,例如strcpy(b.name, "数据结构"),因为b.name中name是数组名,是首元素的地址,如果直接对b.name进行更改相当于是对地址进行更改,而你要改的是内容,这种做法肯定是错的。
13.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
13.1隐式类型转换
C 的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为 整型 提升 。
看完这段话,你一定是这样的
举个栗子
int main()
{ char a = 3;//a是1byte - 8bit
//发生整型提升的过程
//00000000000000000000000000000011
//00000011 - a 发生截断
char b = 127;//b是1byte - 8bit
//00000000000000000000000001111111
//01111111 - b
//a和b都是char类型,自身大小的都是1byte,所以这里计算的时候要进行整型提升
//整形提升是按照变量的数据类型的符号位来提升的,正数补0,负数补1,无符号数补0
//例如a是00000011最高位为0,所以前面补0若a是-1则11111111最高位是1,前面补1
//00000000000000000000000000000011 a
//00000000000000000000000001111111 b
//00000000000000000000000010000010 a+b
//
char c = a + b;
//10000010 - c 发生截断
printf("%d\n",c);
//11111111111111111111111110000010 最高位为1,前面补1,内存中存的是补码
//11111111111111111111111110000001 反码 补码-1就是反码
//10000000000000000000000001111110 原码 反码按位取反就是原码
//-126
return 0;
}
整型提升的具体例子
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");
return 0;
}
打印结果为c,因为c是int不用整型提升,而char和short都需要整型提升。
int main()
{
char c = 1;
printf("%u\n", sizeof(c));//1byte
printf("%u\n", sizeof(+c));//4byte
printf("%u\n", sizeof(-c));//4byte
return 0;
}
第一个因为c是char类型所以是1个字节,而第二第三个因为+-号所以c参与了运算,要进行整型提升,所以是4个字节。
13.2算术转换
long doubledoublefloatunsigned long intlong intunsigned intint
转换优先级自下而上,例如
float a = 3.7
double b = 3.14
//则a*b结果的类型是double
13.3操作符的属性
那什么是结合性呢?
举个栗子
比如a+b+c到底是先a+b 再加c还是先b+c再加a呢?表中也写了方向是从左向右,所以是先a+b再加c。
优先级的作用对象是相邻操作符。
a*b+c*d+e*f
这个式子的计算路径有两种
一是:
第二种计算路径说明了优先级作用的对象是相邻操作符。
这段代码实际上是问题代码,因为计算路径不唯一,平时写程序时一定要确保计算路径的唯一性。
看完这些,此刻的你
这样的文章你还不赶紧 点赞收藏+关注作者!