操作符详解!!!讲解剖析

简介: 给大家介绍一个比较神奇的代码,它是不创建变量,然后将a,b内容交换。用的正是这里介绍的按位异或^操作符

---- 操作符分类+剖析----:


1.算术操作符


算术操作符:


0666114681cc4f03914edc0368d4e1d0.png


**1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。


1.对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。


2.% 操作符的两个操作数必须为整数。返回的是整除之后的余数。**


2.移位操作符


f74b5e8dacba4070bf312b59db55d56f.png


2.1 左移操作符


使用规则:


左边抛弃,右边补0


2.2 右移操作符


使用规则:


   逻辑移位 :  左边用0填充,右边丢弃。    


7066827c5b4442d1b52a30a1496eaa7f.png


   算术移位 : 左边用原该值的符号填充


abad3f0532ad443f89e66fe258c09d49.png


3.位操作符


位操作符有:(这里的位都是二进制位)


这里都是看二进制位喔


& //按位与     0和0还是 0 , 1和0还是0 , 必须两个都为1 才能结合出来1
| //按位或     只要有一个1就能结合出来1啦,而只有;两个0 才能结合出来0
^ //按位异或    相同为0 相异为1
注:他们的操作数必须是整数。


#include <stdio.h>
int main()
{
 int a = 1;
 int b = 2;
 a & b;
 a | b;
 a ^ b;
 return 0;
}


给大家介绍一个比较神奇的代码,它是不创建变量,然后将a,b内容交换。用的正是这里介绍的按位异或^操作符


#include <stdio.h>
int main()
{
 int a = 10;
 int b = 20;
 a = a^b;  //把a^b 看成一个钥匙,它遇到a就能打开b的然后变成b;
 b = a^b;//a相当于钥匙,它遇到b就打开b变成a,然后再赋给b;b就变成a的值了
 a = a^b;//b相当于钥匙,它遇到a就打开a变成b,然后再赋值给a,a值就变成了b值
 printf("a = %d b = %d\n", a, b);
 return 0;
}


编写代码实现: 求一个整数存储在内存中的二进制中1的个数


#include <stdio.h>
int main()
{
 int num  = 10;
 int count=  0;//计数
 while(num)
 {
 if(num%2 == 1)
 count++;
 num = num/2;//正常思想从如何分解一个数的各个位数,,%10  再/10;
 }
 printf("二进制中1的个数 = %d\n", count);
 return 0;


另一种方法:


#include <stdio.h>
int main()
{
 int num = -1;
 int i = 0;
 int count = 0;//计数
 for(i=0; i<32; i++)
 {
 if( num & (1 << i) )
 count++; 
 }
 printf("二进制中1的个数 = %d\n",count);
 return 0;
 }


还有没有更好的方法呢?


int main()
{
  int a = -1;
  int count = 0;
  //怎么更好的计算a的1 的个数呢?
  // a & (a - 1);这种方式可以每次把最右边的1给拿掉;
  while (a)
  {
    a = a&(a - 1);
    count++;
  }
  printf("1的个数:%d", count);
  return 0;
}


但这种方法实在想不到喔…


4.赋值操作符


 看好是一个 = 而不是两个==喔


/

int a=10;//可以将想要的数赋值
int b=a+10;


复合赋值符


+=

-=

*=

/=

%=

<<=

&=

|=

^=


5.单目操作符


单目操作符介绍


!           逻辑反操作
 -           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符)
(类型)       强制类型转


1. sizeof是用来计算变量所占空间的大小   ,它是一个操作符,不是库函数喔。


void test1(int arr[])
{
printf("%d\n",sizoeof(arr));
}
void test2(char ch[])
{
printf("%d\n",sizeof(ch[]);
}
int main()
{
  int a = 10;
  char s = 'a';
  int arr[10] = { 0 };
  char ch[10] = { 0 };
  printf("%d\n", sizeof(a)); //一个整形4个字节
  printf("%d\n", sizeof(s));//1一个字符1个字节
  printf("%d\n", sizeof(arr));//数组10个整形40个字节
  printf("%d\n", sizeof(ch));//数组10个字符10个字节
  test1(arr);//问题1:这个是多少呢?
  test2(ch);//问题2:这个又是多少呢?
  return 0;
}


问题1和问题2其实本质一样,这里把arr和ch传给函数test1和test2,函数传参,这里数组传过去的是首元素地址,也就是相当于指针,而sizeof计算地址大小这个就是由编译器是X64还是X32决定的了,如果是64位的那么大小应该是8,如果是32位的大小应该是4.


3e77b16d2d0a48f19447835f08f6ec35.png


  3 .前置++和后置++


前置++: 先++后使用。

后置++:先使用后++。


int main()
{
  int a = 10;
  int x = ++a;
  //前置++,先将a自增变成11,再使用a,将a赋值给x,x=11;
  printf("a=%d x=%d\n", a, x);
  return 0;
int main()
{
  int a = 10;
  int y = --a;
  //前置--,先将a自减成9,再使用a,将a赋值给x ,x=9
  printf("a=%d y=%d\n", a, y);
}
int main()
{
  int a = 10;
  int z = a++;
      //后置++,先将a赋值给z,z=10,再给a自增+1,a就变成11;
    printf("a=%d z=%d\n", a, z);
}
int main()
{
  int a = 10;
  int v = a--;
    //后置--,先将a赋值给y,y=10,再给a自建-1,a就变成9了;
    printf("a=%d v=%d\n", a, v);
}


6.关系操作符


 >
 >=
 <
 <=
 !=
 == 注意和一个=的区别


7.逻辑操作符


 &&                    逻辑与
 ||                    逻辑或


区别:


逻辑与 -------按位与

逻辑或 -------按位或


1&&2 ---->1(表示真)
1&2   ---->0(二进制进行计算)
1||2   ---->1(表示真)
1|2    ---->3(二进制进行计算)


逻辑与和逻辑或有什么特点呢?


短路求值!


比如逻辑与&&: 表达式1&&表达式2 表达式1为假则右边不再进行。


逻辑或|| : 表达式1||表达式2 表达式1为真则右边不再进行。


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;
}
因为 i = a++ && ++b && d++;   a++是后置++,先使用后++,所以a=0,先使用与&&进行配对,然后是假,
所以后面++b, d++都不再进行,但a++,这个还是进行的,所以a用完后还要给a+1,
所以a=1,b=2,c=3,d=4;


/

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;
}
这里a++(a=0,先使用与||配对为假,再+1,++b,b自增为3与||配对为真,后面的d++不再进行了所以
a=1,b=3,c=3,d=4;


8.条件操作符


表达式1  ? 表达式2 :表达式3               表达式1成立--->最终结果为表达式2,否则为表达式3.


一般条件表达式(if else )可以用条件操作符代替


求两个数中较大值:


int main()
{
  int a = 10;
  int b = 20;
  int max = (a > b ? a : b);//a表达式1 a>b如果成立那么max=a,否则max = b;
  printf("%d", max);
  return 0;
}


9.逗号表达式


 表达式1,表达式2,……表达式n


逗号表达式,就是用逗号隔开的多个表达式。


逗号表达式,从左向右依次执行。整个表达式的结果是最后一 个表达式的结果


int main()
{
  int a = 0;
  int b = 1; 
  int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
  printf("%d", c);
  //c是多少?
  return 0;
}
虽然结果是最后一个但是前面的逗号表达式还是要进行的,第一个a>b假的,不用管,第二个a=b+10,a=11了,第三个b=a+1,那b就等于12


  a = b + 1;
  c = a / 2;
  if (d > 0)
  {
    ……
  }
  写成逗号表达式if (a = b + 1, c = a / 2, d > 0)


在这里插入代码片


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


1. [ ]下标引用操作符


操作数:一个数组名+一个索引值


int main()
{
  int arr[5] = { 0,1,2,3,4};//创建数组,初始化
   printf("%d",arr[4]);//arr[4]是等于4
   arr[4]=10; //使用下标引用改变arr[4]成10
    []操作数是arr和 5
  return 0;
}


2.( )函数调用操作符


接收一个或多个操作数,第一个操作数是函数名,剩余的操作数就是传递给函数的参数。


void test()
{
  printf("hello\n");
}
void test1(char * ch)
{
  printf("%s", ch);
}
int main()
{
  test();
  test1("hello");
  return 0;
}


3. . 访问一个结构的成员


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


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.表达式求值


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


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


11.1.隐式类型转化


C语言算术运算总是至少以缺省型类型的精度来进行的。


为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转化为普通整形,这种转化称为整形提升。


整形提升的意义:


表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的 字节长度一般 就是int的字节长度,
同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为 CPU 内整型操作
数的标准长度。通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有
这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才
能送入CPU去执行运算


int main()
{
  char a, b, c;
  a = b + c;//b+c要进行整形提升,b和c的值被提升到普通整形,然后再进行加法运算
  //加法运算完成之后,结果将被截断,然后再存贮到a中。
  return 0;
}


怎么进行整形提升的呢?


  整形提升是按照变量的数据类型的符号位来提升的


负数的整形提升


> 
int main()
{
  char ch = -1;//字符一个字节---8个比特位
  变量ch的二进制位(补码)中只有8个比特位;
  11111111
  因为char为有符号的char
  所以整形提升的时候,高位补充符号位,即为1
  提升之后的结果是:
  11111111111111111111111111111111
  return 0;
}


正数整形提升


int main()
{
char ch = 1;
//变量ch的二进制位(补码中)只有8个比特位
00000001;
因为char 为有符号的char
所以整形提升的时候,高位补充符号位即为0;
提升之后的结果就是:
00000000000000000000000000000001
无符号整形提升,高位补0;
return 0;
}


整形提升的例子1:


int main()
{
  char a = 0xb6;
  short b = 0xb600;
  int c = 0xb6000000;
  if (a == 0xb6)
  {
    printf("a");
  }
  if (b == 0xb600)
  {
    printf("b");
  }
  if (c = 0xb600000)
  {
    printf("c");
  }
  return 0;
}


结果是什么呢?


20e3ecad9565424b8afcd584c9f827c4.png


这里是因为a和b都要先进行整形提升,而c不需要进行整形提升。


a,b整形提升之后变成了负数,所以表达式,a==0xb6,b= =0xb600为假,c的结果为正,所以只打印c


实例2:


int main()
{
  char a = 1;
  printf("%d\n", sizeof(a));
  printf("%d\n", sizeof(+a));
  printf("%d\n", sizeof(-a));
  return 0;
}


嘿嘿结果又是多少呢?


fa6f6d7eae5b41e095922b4f874727f9.png


神奇嘛?


实例2中的,a只要参与表达式运算的,就会发生整形提升,表达式+a,就会发生整形提升,所以sizeof(+a)就是4个字节。

表达式-a也会发生整形提升,所以sizeof(-a)也是4个字节,但sizeof(a)没有发生整形提升所以是1个字节。


11.2.算术转化


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


long double
double
float
unsigned long int
long int
unsigned int
int


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


但要注意算术转化要合理,要不然就会存在一些潜在问题

float a=3.14;

int num =a; 在会发生隐式转化,精度丢失


11.3.操作符的属性


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


1.操作符的优先级

2.操作符的结合性

3.是否控制求值顺序。


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


  操作符优先级:


下面给出详细的操作符的优先级表格,从上至下优先级依次递减(越靠近上面,操作符的优先级越高)


3726bcd8f8774d2a98e975d4875c9880.png


![在这


e52e07ceddc1443ea9b1b64be3c926a2.png

001cc9e23827488393b539341a4f53c4.png

2ecb1ac069bc4f09bf38edc2c539eb17.png

相关文章
|
11月前
|
编译器 C语言 索引
操作符详解下(非常详细)
操作符详解下(非常详细)
42 0
|
1月前
|
编译器
|
4月前
|
存储 编译器
操作符详解1(一)
本文介绍了编程中的操作符分类和位操作,包括算术操作符(如 +, -, *, /, %)、移位操作符(<<, >>)、位操作符(&, |, ^)、赋值操作符、单目操作符、关系操作符、逻辑操作符和条件操作符。还讨论了原码、反码和补码的概念,以及左移(<<)和右移(>>)操作符的工作原理,包括逻辑右移和算术右移的区别。最后提到了位操作符按位与(&)、按位或(|)、按位异或(^)和按位取反(~)的运算规则。
34 1
|
3月前
|
编译器 Linux C语言
操作符详解(2)
操作符详解(2)
36 0
|
4月前
|
编译器 C语言 C++
操作符详解2(二)
本文介绍了编程中的操作符转换和表达式解析规则。当操作数类型不同时,会进行寻常算术转换,按照long double、double、float等类型顺序进行向上转换。表达式求值时,虽然操作符有优先级,但不能决定操作数的求值顺序,例如`a*b + c*d + e+f`中乘法先于加法,但具体计算顺序不确定,可能导致不同结果。同样,如`c++ + c--`这样的表达式也是有歧义的,因为++和--的左右结合性不能确定操作数的获取顺序。文章强调,复杂的表达式可能因编译器差异产生不同结果,应避免使用可能导致非唯一计算路径的表达式以减少潜在风险。
35 0
|
4月前
|
存储 程序员 C语言
操作符详解1(二)
该内容是一个关于C语言操作符和结构体的教程摘要。首先提到了加法操作符`+`的使用,并给出了一种不使用临时变量交换两个数的方法。接着讨论了如何计算整数的二进制表示中1的个数,通过位操作符实现。然后介绍了逗号表达式和函数调用操作符`()`,以及结构体成员访问操作符`.`和`-&gt;`,用于访问和修改结构体内的成员变量。文章以讲解结构体的声明、初始化和通过指针访问结构体成员为重点,展示了如何直接和间接地操作结构体数据。
39 0
|
编译器
详解操作符(下)
详解操作符(下)
|
Linux C++ 索引
操作符详解(二)
操作符详解(二)
79 0
操作符详解(一)
操作符详解(一)
79 0
|
存储 编译器 C语言
7.27C语言知识点之操作符
7.27C语言知识点之操作符
51 0