C~操作符知识

本文详细介绍了C语言中的操作符分类,包括算术、移位、位、赋值、单目、关系、逻辑、条件、逗号操作符等,并通过实例解析了表达式的求值顺序和类型转换。还探讨了操作符的优先级、结合性和求值顺序对复杂表达式的影响,强调了存在歧义的表达式及其潜在问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、操作符分类

1.算术操作符

在这里插入图片描述
注意:

 1. 除了`%`操作符外,其他几个操作符可以作用于整数和浮点数
 2. 对于`/`操作符如果两个操作数都为整数,执行整数除法。只要有浮点数执行的就是浮点数除法    
 3. `%`操作符的两个操作数必须为整数。返回的是整除之后的余数

2.移位操作符

移位操作符,作用在正数的内存存储二进制序列(补码形式)
正数的原码、反码、补码都是相同的;
负数的原码、反码、补码是不相同的(反码:原码除符号位外每位取反;补码:反码末尾+1)

    <<  左移操作符
    >>  右移操作符
  注:移位操作符的操作数只能是整数

2.1 左移位操作符

移位规则 : 左边抛弃,右边补0
在这里插入图片描述2.2 右移操作符

移位规则:分为两种

      1)逻辑移位: 左边用0填充,右边丢弃
      2)算术移位: 左边用原该值的符号位填充,右边丢弃

右移操作时,逻辑右移还是算术右移看使用的编译器,这里使用的VS2019是算术右移

  int  main()
  {
     int num = -1;  //-1的补码为1111 1111 1111 1111 1111 1111 1111 1111
				    // 32个1
     num >> 1;  //error,这个会出警告(“>>”:未使用表达式结果)
               //因为这一行只是做了运算,而运算结果并未赋值给任何变量或者用来做判断条件
     num >>= 1;	
     //         1111 1111 1111 1111 1111 1111 1111 1111
     //算术右移: 1111 1111 1111 1111 1111 1111 1111 1111  符号位为1,左边补1
     //逻辑右移: 0111 1111 1111 1111 1111 1111 1111 1111  左边补0
     return 0;        
  }

注:对于移位运算符,不要移动负数位,这个标准是未定义的

3.位操作符

     &   // 按位与, 都为1时,结果为1
     |   // 按位或, 一个为1时,结果为1
     ^   // 按位异或  相同为0,不同为1
    注:操作数必须是整数

位操作符还是作用在整数的内存中二进制中(补码形式)

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

#include<stdio.h>
int main()
{
	int num = 10;
	int count = 0; //计数
	while(num > 0)
	{
		num  &= (num-1);   //num和num-1每一次按位与,都会去掉num右边第一个1
		count++;
	}
	printf("%d",count);
	return 0;
}

4.赋值操作符

     =   //赋值操作符
   int weight = 20;  //重量
   //赋值操作符可以连续使用
   int a = 10;
   int b = 0;
   int c = 20;
   a = b = c + 1; //连续赋值

复合赋值符

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

5.单目操作符

//逻辑反操作,真取假(非0取0),假取真(0取1)
    ~         //对一个数的二进制位按位取反(补码取反)----例如:-1取0//负值
    +         //正值
    &         //取地址
    sizeof    //操作数的类型长度(以字节为单位),求变量(类型)所占空间大小
    --        //前置、后置--
    ++        //前置、后置++
    *         //间接访问操作符(解引用操作符)
   (类型)      //强制类型转换

对于- -和++操作符: 前置-------->先自增(减)再使用,后置------->先使用再自增(减)

练习:求a, b, c的值

#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;  
	c = ++a;  //a=5+1 , c=a=6
	b = ++c, c++, ++a, a++;  //a=6+1+1=8, b=c+1=7, c=c+1+1=8
	b += a++ + c;   //b=a+b+c=23 , c=8 , a=9
	printf("a = %d b = %d c = %d\n:", a, b, c); //a=9 , b= 23 ,c=8
	return 0;
}

6.关系操作符

     >       //大于
     >=      //大于等于
     <       //小于
     <=      //小于等于
     !=      //不等于
     ==      //等于

7.逻辑操作符

     &&    //逻辑与   从左到右,两边表达式同为真,返回1;一个为假,返回0
     ||    //逻辑或   从左到右,一个表达式为真,返回1;同时为假,返回0

练习:

#include <stdio.h>
int main()
{
    int i = 0,a=0,b=2,c =3,d=4;
    i = a++ && ++b && d++;
    //i = a++||++b||d++;
    printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); 
    return 0;   //输出的结果为1,2,3,4,因为a为0,右边不执行,然后a再自增1
}

8.条件操作符

     exp1 ? exp2 : exp3    //exp1为真时,返回exp2;为假时,返回exp3

9.逗号操作符

     exp1, exp2, exp3, ....expN

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

练习

    int a = 1;
    int b = 2;
    int c =(a > b,a = b + 10, a, b = a + 1);// c=13
    //也可以这样使用
    while(a = get_val(),count_val(a),a>0)
    {
         //函数体
    }
    //上述代码可以替代
    a = get_val();
    count_val(a);
    while(a > 0)
    {
        a = get_val();
        count_val(a);
    }

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

    []          //下标引用操作符
    ()          //函数调用操作符
    .           //结构体.成员名
    ->          //结构体指针->成员名

二、表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定的
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型

1.隐式类型转换

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

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长
度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为intunsigned int,然后才能送入CPU去执行运算
eg:
  char a,b,c;
  ...
  a = b + c;    //b和c的值被提升为普通整型,然后再执行加法运算
                //加法运算完成之后,结果被截断再存储再a中

如何进行整型提升呢?

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

   //负数的整形提升
   char c1 = -1;
   //变量c1的二进制位(补码)中只有8个比特位:
   //1111111 
   //因为 char 为有符号的 char
   //所以整形提升的时候,高位补充符号位,即为1
   //提升之后的结果是:    
   //11111111111111111111111111111111
 
  //正数的整形提升
  //char c2 = 1;
  //变量c2的二进制位(补码)中只有8个比特位:
  //00000001
  //因为 char 为有符号的 char
  //所以整形提升的时候,高位补充符号位,即为0
  //提升之后的结果是:
  //00000000000000000000000000000001
//无符号整形提升,高位补0
int main()
{
 char c = 1;
 printf("%u\n", sizeof(c));   // 1
 printf("%u\n", sizeof(+c));  // 4
 printf("%u\n", sizeof(-c));  // 4
 return 0;
}

c只要参与了表达式运算,就会发生整型提升,表达式+c,就会发生提升,所以sizeof(+c)就是4个字节,表达式-c也会发生整型提升,所以sizeof(-c)是4个字节

练习:

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

结果为c,因为a和b参与了表达式,发生整型提升是个负数,不执行,所以执行c

2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则就无法进行。下面的层次体系称为寻常算术转换
在这里插入图片描述
如果某个操作数的类型上面这个列表中排名较低,那么首先要转换为另一个操作数的类型后执行运算。

注:但是算术转换要合理,不然会有一些潜在的问题。

   float a = 3.14 ;
   int num = f;  //隐式转换,会有精度丢失

三、操作符的属性

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

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序

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

1.操作符的优先级、结合性和求值顺序

从上到下,优先级递减
在这里插入图片描述

2.问题表达式

 //表达式 1
 a*b + c*d + e*f

表达式1在计算的时候,由于*+的优先级高,只能保证,*的计算比+早,但是优先级并不能决定第三个*比第一个早执行

所以表达式的计算机顺序就可能是:

  a*b
  c*d
  a*b + c*d
  e*f
  a*b + c*d + e*f
  或者:
  a*b
  c*d
  e*f
  a*b + c*d
  a*b + c*d + e*f
//表达式2
 c + --c;

同上,操作符的优先级只能决定自减--的运算在+运算的前面,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的

//表达式3
int  main()
{
	int i = 10;
	i = i-- - --i*(i = -3)*i++ + ++i;
	printf("i = %d\n",i);
	return 0;
}

表达式3在不同的编译器中测试结果:

编译器
-128Tandy 6000 Xenix 3.2
-95Think C 5.02(Macintosh)
-86IBM PowerPC AIX 3.2.5
-85Sun Sparc cc(K&C编译器)
-63gcc,HP_UX 9.0,Power C 2.0.0
4Sun Sparc acc(K&C编译器)
21Turbo C/C++ 4.5
22FreeBSD 2.1R
30Dec Alpha OSF1 2.0
36Dec VAX/VMS
42Microsoft C 5.1
//表达式4
int  fun()
{
	static int count = 1;
	return ++count;
}
int  main()
{
	int answer;
	answer = fun() - fun()*fun();
	printf("%d\n",answer);
	return 0;
}

这个代码有问题!
虽然在大多数的编译器上求得结果都是相同的
但是上述代码answer = fun() - fun()*fun();中我们只能通过操作符的优先级得知:先算乘法,再算减法,函数的调用先后顺序无法通过操作符的优先级确定

//表达式5
int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n",ret);
	printf("%d\n",i);
	return 0;
}

在这里插入图片描述VS2013环境的结果:
在这里插入图片描述

同样的代码产生了不同的结果,这是为什么?
这段代码中的第一个+在执行的时候,第三个++是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个+和第三个前置++的先后顺序

总结: 写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值