C语言操作符详解和优先级问题

.C语言是底层语言,因为其操作符便能直接操作到内存中存储的二进制位,使用起来灵活方便,为此我认为有必要专门写一篇博客总结C语言的操作符然后分享给大家,没有深入了解过的朋友一定能够在此有所收获,如果喜欢点赞收藏支持一下吧!⭐

目录

1.算数操作符

2.移位操作符( <<    >>)⭐

3.位操作符(&  |  ^)⭐

 4.赋值操作符

5.单目操作符

6.关系操作符

7.逻辑操作符(&&   | | )

8.条件操作符(exp1  ?  exp2  :  exp3 )

9.逗号表达式(exp1 , exp2 , exp3 , exp n)

10.下标引用、函数调用和结构成员

11.表达式求值(隐式类型转换,优先级)⭐

12.操作符优先级⭐


 

1.算数操作符

+             -               *              /                %  ---------------    (加,减,乘,除,取模)

算数操作符也和数学上的定义是一样的,不必过多介绍,不过既然是在计算机上操作的必然有一些规定:

  • 只有%取模不能操作小数,操作符的两端只能是整数,其执行的结果是两个整数相除的余数;
  • /除想要得到小数操作符的两端必须有一端为小数,这样它就是执行的小数除法,反则执行整数除法;

2.移位操作符( <<    >>)⭐

  • << 左移操作符:将操作对象的二进制位向左移动n位(5<
  • >>右移操作符:将操作对象的二进制位向右移动n位(5>>n);

 <<左移操作符移动规则:左边抛弃,右边补零;

小知识点: 

  •   可以验证得知a<
  • 移动后只是得到一个新的数值,a的值在没有被赋值的情况下它原来的值并没有改变;

  

 >>右移操作符移动规则分为两种:

1.逻辑右移:

左边用0填充,右边丢掉;

2.算术右移:

左边补原符号位,右边丢掉;

C语言操作符详解和优先级问题_第1张图片

虽然有两种右移方法,但是现在编译器普遍都是按照算术右移执行程序的,逻辑右移在编程中还是不太现实,如果被操作数是负数,那么逻辑右移后它会变成非常大的一个值,用途不大;

小知识点:

1.可以验证得知a>>n其实有个规律,其结果为n次a/2的结果;

2.同样移动并不会改变原来变量的值,除非给变量赋值;

3.<<  >> 并不能操作浮点数,只能操作整数;

奇怪的写法:

int a = 1;

a>>-1;

这样的写法是很奇怪的,属于标准未定义,写代码我们应该保持规范,避免错误的写法

3.位操作符(&  |  ^)⭐

  •    &      //按位与
  •    |      //按位或
  •    ^      //按位异或

注:他们的操作对象同样只能是整数

&按位与:两个整数的二进制位对比都是1对应的二进制位才是1,否则为0;

C语言操作符详解和优先级问题_第2张图片

 

 |按位或:两个整数的二进制位对比有一个是1对应的二进制位就是1,都为0则为0;

C语言操作符详解和优先级问题_第3张图片

 

^按位异或:二进制位相同为0,不同则为1;

C语言操作符详解和优先级问题_第4张图片

 小知识点:

1.任何二进制位^0其结果都会等于本身(n ^ 0 = n),任何二进制位^本身结果都为0;

2.^支持交换律,即a ^ b = b ^ a;

3.^支持结合律(a^b) ^ c = a ^ (b^c);

4^自反性,a ^ b ^ b = a;---->( a ^ (b^b) =a ^ 0=a);

问不创建任何临时变量(第三个变量),如何交换int a和int b 的值?

方法不止一种,这里使用的^方法

#include 
int main()
{
 int a = 10;
 int b = 20;
 a = a^b;  //  a=a^b
 b = a^b;  //  b=a^b^b,所以b=a;
 a = a^b;   //  a=a^b^a=(a^a)^b=b;

 printf("a = %d b = %d\n", a, b);
 return 0;
}

移位操作符和位操作符练题------------------二进制中1的个数__牛客网 (nowcoder.com)

 4.赋值操作符

赋值操作符可以让我们随意的修改变量数值

 int weight = 120;//体重

weight = 89;//不满意就赋值

double salary = 10000.0;

salary = 20000.0;//使用赋值操作符赋值

我们在赋值的时候要注意代码风格,不要随心所欲,保持代码简洁明了

int a = 1;
int b = 2;
a = b = b + a ;//连续赋值让代码可读性差
 
b = b + a;
a =  b; //这样写代码就清楚许多

复合赋值符:

+=        -=       *=        /=        %=        >>=         <<=         &=          |=          ^=

如果想要快捷那么复合赋值符是不错的选择

a = a + b;--------复合赋值符----------> a + = b;

5.单目操作符(sizeof)

!                   逻辑反操作

-                   负值

+                  正值

&                 取地址

sizeof          操作数的类型长度(以字节为单位)

~                 对一个数的二进制按位取反

--                 前置、后置--

++               前置、后置++

*                 间接访问操作符(解引用操作符)

(类型)         强制类型转换

单目操作符的操作对象只有一个,很多时候符号一样但并不是意义就是一样的(a-b和-a意义不一样,一个有两个操作对象,一个只有一个操作对象)

前置++,-- 后置++,--:

前置++,-- 是先++或者--  后使用

int a = 1;

printf("%d",++a);//先+1后打印的结果

后置++,--是先使用,后++或--

int a = 1;

printf("%d",a++);// 先使用a,然后再++

sizeof与数组:

sizeof的计量单位是一个字节,计算数组大小的时候是计算数组总共占多少个字节;

int  arr  [10] = { 0 }; //一个int类型大小为4字节

char  ch [10] = { 0 }; //一个char类型大小为1字节

sizeof (arr) = 40 (字节)

sizeof (ch) = 10 (字节)

如果将它们传参给函数是什么结果?

void test1(int arr[])
{
 printf("%d\n", sizeof(arr));//打印多少?
}
void test2(char ch[])
{
 printf("%d\n", sizeof(ch));//打印多少?
}

(40,10 )?(4,1)?

都不是,数组这样传参过去我们应该把它看成一个指针,传过去的并不是整个数组而是数组首元素的地址,通常来说在32位机器上一个地址占4个字节,在64位机器上一个地址占8个字节,所以32位机器结果为(4,4)64位机器结果为(8,8);

为什么结果不是数组单个元素所占的字节大小,我们要知道sizeof(ch)并不是在计算数组一个元素的大小,ch是一个地址,这里计算的其实是一个地址的大小,sizeof(ch[0])才是计算一个元素的大小;

6.关系操作符

>           大于

>=         大于等于

<           小于

<=         小于等于

!=          用于测试“不相等”

==         用于测试“相等”

同样与数学上的逻辑和含义是一样的,没有什么能够介绍的;

注意:==是测试是否相等,=是用来赋值的,写代码的时候要注意不要写错了;

7.逻辑操作符(&&   | | )

&&                   逻辑与

| |                     逻辑或

区分&按位与和|按位或的区别:

&按位与和|按位或是按二进制位来计算的,而&&和||只关心类型的值是否符合,不会去操作二进制位;

a && b     //除非a和b都为真其表达式才为真,否则表达式为假;

a | | b    //a和b有一个为真表达式就为真,都为假表达式才为假;

int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;  //表达式1
    //i = a++||++b||d++;   //表达式2
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
    return 0;
}

两种表达式的打印结果分别是什么?

表达式1:(1,2,3,4)

因为是后置++所以先使用后++,当a=0的时候表达式不成立后面就不会去计算了,但是++仍然会执行;

表达式2:(1,3,3,4)

a=0的时候表达式就会往下计算,++b=3表达式成立后面同样不会去计算了;

8.条件操作符(exp1  ?  exp2  :  exp3 )

exp1  ?  exp2  :  exp3  //   表达式为真 ?结果1:结果2

 其实这就是一个if else语句判断的缩写

if (a > 5)
        b = 3;
else
        b = -3;


//和这个表达式是一样的意思  a > 5 ? b = 3 : b=-3

当表达式exp1为真就执行exp2的结果,为假就执行exp3的结果;

9.逗号表达式(exp1 , exp2 , exp3 , exp n)

exp1 , exp2 , exp3 , exp4 , exp5 , exp n

逗号表达式就是用逗号隔开的多个表达式,逗号表达式从左向右计算,整个逗号表达式的结果就是最后一个表达式的结果;

int main()
{
    int a = 1;
    int b = 2;
    int c = (a = a - b, b = b + a,  b = b + a + 1);
    printf("%d", c);
    return 0;
}

从左往右依次计算结果为1,c的值为1;

10.下标引用、函数调用和结构成员

1.下标引用操作符[ ] 操作数: 一个数组名+一个索引值;

通常我们创建一个数组

int arr [10] = { 0 };   //但是这里的[ ]并不是下标引用操作符,而是创建数组空间的大小

arr[9] = 10;   //这里才是下标引用操作符,通过[ ]我们能够找到相应下标的元素

2.函数调用操作符( )

( )接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

void empty()
{
	printf("*******你好********\n");
}
void have(int*a)
{
	*a = 20;
}
int main()
{
	int a = 10;
	empty();       //实用()作为函数调用操作符
	have(&a);     //实用()作为函数调用操作符
	printf("%d", a);
	return 0;
}

3.结构成员访问

        结构体成员 . 成员名

->        结构体指针 -> 成员名

#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* pStu)
{
 pStu->age = 18;//结构成员访问
}
int main()
{
 struct Stu stu;
 struct Stu* pStu = &stu;//结构成员访问
 
 stu.age = 20;//结构成员访问
 set_age1(stu);
 
 pStu->age = 20;//结构成员访问
 set_age2(pStu);
 return 0;
}

11.表达式求值(隐式类型转换)⭐

表达式求值的顺序一部分是由操作符的优先级和结合性决定。

同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

1.隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型 提升

为什么要整型提升?

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。 通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运(虽然机器指令 中可能有这种字节相加指令)。

所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

整型提升过程:

因为char为有符号位的char,所以我们在整型提升的时候是按照原符号位来进行整型提升的(无符号位的整型提升最高位补0)

C语言操作符详解和优先级问题_第5张图片

 C语言操作符详解和优先级问题_第6张图片

 

 截断:

char a = 131;

131二进制位是0000000000000000000000001000 0011

但是char类型空间只有一个字节根本存不下4个字节内容,所以这个时候就发生了截断,我不管你有多大我只从最低位开始存我的一个字节内容,其他二进制位丢掉,所以a里面二进制位存放的其实是10000011

 C语言操作符详解和优先级问题_第7张图片

 

举个栗子:

char a = 5;
char b = 126;
char c = a + b; //a和b发生整型提升,值返回给c的时候c进行截断
printf("%d",c);

c的结果是多少?

C语言操作符详解和优先级问题_第8张图片

 

当打印c的值时c进行整型提升

C语言操作符详解和优先级问题_第9张图片

 

所以c的结果为-125;

栗子2:

int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));//1
 printf("%u\n", sizeof(+c));//2
 printf("%u\n", sizeof(-c));//3
 return 0;
}

第一条语句不会整型提升,因为c没有参加运算,但是第2,3条语句都进行了整型提升,sizeof计算的是int类型的大小,所以打印结果为1,4,4

2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double

double

float

unsigned long int

long int

unsigned int

int

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算。

12.操作符优先级⭐

复杂表达式的求值有三个影响的因素。

1. 操作符的优先级

2. 操作符的结合性

3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

C语言操作符详解和优先级问题_第10张图片

 原图文地址----------------------C语言运算符优先级和结合性一览表 (biancheng.net)

问题表达式:

搞清楚了优先级问题并不能保证表达式就没有问题,在各种编译器中表达式的运算可能各有各的道理,例如:

表达式 a * b + c * d + e * f的结果?

1.   a * b  ---  c*d  ---  e*f  ----   a * b + c * d + e * f

2. a * b  ---  c*d ---  a*b+c*d  ----  e*f  -----a * b + c * d + e * f

这都是有可能的,表达式计算的顺序不一样其结果也很可能被影响,因此我们在写表达式的时候尽量避免这样的问题表达式,将自己的代码思路写明了

经典问题表达式:

#include 
int main()
{
 int i = 1;
 int ret = (++i) + (++i) + (++i);
 printf("%d\n", ret);
 printf("%d\n", i);
 return 0;
}

ret和i的结果分别是多少?

没有明确答案,完全看编译器是按照什么样的顺序计算的,每种编译器的计算顺序都不一样,这样的题目根本就没有探讨的必要,现在都是以反面教材拿出来警示;

 喜欢的伙伴三连支持一下吧,这是对博主最大的鼓励,感谢!

你可能感兴趣的:(C语言,c语言,开发语言)