C语言操作符详解

操作符和表达式

操作符

分类:

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

算术操作符

  +  -  *  /  %
  1. 除了%操作符之外,其他的几个操作符可以作用于整数和浮点数。
  2. 对于/操作符如果两个操作数为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
  3. %操作符的两个操作数必须为整数。返回的是整除之后的余数。
int main()
{
   int a = 5 / 2; //商2余1
   printf("a = %d\n", a);
   return 0;
}

运行结果
在这里插入图片描述

int main()
{
   double a = 5 / 2.0; //商2余1
   printf("a = %lf\n", a);
   return 0;
}

运行结果
在这里插入图片描述

int main()
{
   int a = 5 % 2; //商2余1
   printf("a = %d\n", a);
   return 0;
}

运行结果
在这里插入图片描述

移位操作符

<<< 左移操作符
>>> 右移操作符

数据在内存中以32位二进制的形式存储,16在内存中是00000000000000000000000000010000

在内存中负数在符号位上用1表示,-1在内存中是10000000000000000000000000000001

正数符号位为0

左移操作符 移位规则:

左边抛弃、右边补0

在这里插入图片描述

int main()
{
    int a = 5;
    int b = a << 1;
    printf("%d\n", b);
    return 0;
}

运行结果
在这里插入图片描述
右移操作符 移位规则:

有两种

  1. 算术右移:右边丢弃,左边补原符号位(现在通常是算术移位)
  2. 逻辑右移:右边丢弃,左边补0(这种现在已经不用了)

在这里插入图片描述

我们不妨运行一下,下面代码结果看看-1右移后还带不带负号,如果还带负号就是算术右移

int main()
{
    int a = -1;
    int b = a >> 1; //右移操作符
    printf("%d\n" ,b);
    return 0;
}

运行结果
在这里插入图片描述
整数的二进制表示有:原码、反码、补码。存储到内存的是补码。
在这里插入图片描述
还是运行上面那段代码,我们看看在内存中真的存放的是补码吗?
在这里插入图片描述
这里是以16进制显示的,16进制ff ff ff ff转化为二进制正是11111111111111111111111111111111。内存中真的存放的是补码。

警告⚠:对于移位运算符,不要移动负位数,这个是标准未定义的。例如:

int num = 10;
num >> -1; //右移负1位

位运算符

位操作符有:

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

注:他们的操作数必须是整数。

按位与

均为1时,结果才是1

int main()
{
      //& - 按二进制与
      int a = 3;
      int b = 5;
      int c = a&b;     
      //00000000000000000000000011
      //00000000000000000000000101
      //00000000000000000000000001
      
       printf("%d\n", c);
       return 0;
}

运行结果
在这里插入图片描述

按位或

只要一个为1,结果就为1

int main()
{
    // | - 按二进制或
    int a = 3;
    int b = 5;
    int c = a | b;
    //00000000000000000000000011
    //00000000000000000000000101
    //00000000000000000000000111

    printf("%d\n", c);
    return 0;
}

运行结果
在这里插入图片描述

按位异或

两个变量取值相异,结果为1

int main()
{
    // ^ - 按二进制异或
    //相同为0,相异为1
    int a = 3;
    int b = 5;
    int c = a ^ b;
    //00000000000000000000000011
    //00000000000000000000000101
    //00000000000000000000000110

    printf("%d\n", c);
    return 0;
}

运行结果
在这里插入图片描述
题目:不能创建临时变量(第三个变量),实现两个数的交换。

int main()
{
    int a = 3;
    int b = 5;
  //int tmp = 0;
    printf("before: a=%d b=%d\n", a, b);
    
    //临时变量法
  /*tmp = a;
    a = b; 
    b = tmp;*/
     
    //加减法
    a = a + b;
    b = a - b;
    a = a - b;

    //异或的方法
    a = a^b;
    b = a^b;
    a = a^b;
    
    printf("after : a = %d b = %d\n", a,b);
    
    
    return 0;
   
}

加减法只能计算比较小的数,如果是两个特别大的数,可能会溢出。

下面介绍异或法的过程
在这里插入图片描述
题目:求一个整数存储在内存中的二进制中1的个数

错误解法

int main()
{
   int num = 0;
   int count = 0;
   scanf("%d", &num); //3 - 011
   
   while(num)
   {
      if(num % 2 == 1)
      {
         if(num % 2 == 1)
              count ++;
         num = num / 2; 
      }
   }
   printf("%d\n",count);
   return 0;
}

运行结果
在这里插入图片描述
看似可以但我们输入负数呢,输入-1

运行结果
在这里插入图片描述
这里老师演示的是输入-1输出0,0是错误的答案,所以这个方法不对。而我的电脑不输出任何结果。哎,反正就是不对。

正确解法

int main()
{
   int num = 0;
   int count = 0;
   scanf("%d", &num); //3 - 011
   
   int i = 0;
   for (i = 0; i <32; i++)
   {
        if(1==((num >> i) & 1))
          count++;
   }
   printf("%d\n",count);
   return 0;
}

赋值操作符

int weight = 120; //体重
weight = 89; //不满意就赋值
double salary = 10000.0;
salary = 20000.0; //使用赋值操作符赋值

//赋值操作可以连续使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y + 1;//连续赋值,不推荐

x = y + 1;
a = x;

复合赋值符

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

这些运算符都可以写成复合的效果。比如:

int x = 10;
x = x + 10;
x += 10;//复合赋值

单目操作符

单目操作符有哪些呢?

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

a+b中“ + ”是双目操作符,因为有两个操作数。而单目运算符只有一个操作数。

int main()
{
   int a = 0;
   if (a) //a为真打印呵呵
   {
      printf("呵呵\n");
   }
   if (!a) //a为假打印呵呵
   {
      printf("呵呵\n");
   }
   return 0 ;
}
取地址和指针
int main()
{
  int a = 10;
  int *p = &a; //取地址操作符
  *p = 20;//解引用操作符
  return 0 ;
}
sizeof的用法
int main()
{
    int a = 10;
    char c = 'r';
    char *p = &c;
    int arr[10]= {0};
    //sizeof计算的变量所占内存空间的大小,单位是字节
    printf("%d\n",sizeof(a));//4
    printf("%d\n",sizeof(int));
    
    printf("%d\n",sizeof(c));//1
    printf("%d\n",sizeof(char));
    
    printf("%d\n",sizeof(p));//4
    printf("%d\n",sizeof(char*));
    
    printf("%d\n",sizeof(arr));//40
    printf("%d\n",sizeof(int [10]));
    return 0;
}

运行结果
在这里插入图片描述
下面这串代码运行结果是什么呢?

int main()
{
	short s = 0;
	int a = 10;
	printf("%d\n",sizeof(s= a + 5));
	printf("%d\n",s);

	return 0;
}

运行结果
在这里插入图片描述
s是短整型变量,s=a+5意思是a+5的结果放到s里面。所以sizeof(s=a+5)s的类型说了算。且sizeof里面的表达式不会进行运算。

那么会不会是第一行s=a+5虽然进行运算,但是第二行输出的是定义的s=0。

int main()
{
	short s = 0;
	int a = 10;
	printf("%d\n",s= a+5);
	printf("%d\n",s);

	return 0;
}

运行结果
在这里插入图片描述
可见s=a+5是直接改变内存中的值。

~

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

因为数字在内存中是以补码的形式存储的。按位取反是对补码进行符号位一起取反。

比如说对0按位取反。0的补码、反码、原码都是00000000000000000000000000000000。
按位取反是对存在内存中0的补码进行取反,即11111111111111111111111111111111。

电脑要输出的结果为原码,我们得到的是补码11111111111111111111111111111111,所以应该往回推,补码11111111111111111111111111111111的反码是11111111111111111111111111111110,在往回推原码是10000000000000000000000000000001 ,即输出的结果是-1。

int main()
{
	int a = 0;
	printf("%d\n", ~a);

	return 0;
}

运行结果
在这里插入图片描述
题目:11的二进制形式第三位变为1,其他位不变

int main()
{
    int a = 11;
    a = a | (1 << 2);
    printf("%d\n", a);
    //00000000000000000000000000001011
    //第三位为1,其他位不变
    //00000000000000000000000000001011
    //00000000000000000000000000000100
    //1<<2;
    //00000000000000000000000000001111

     a = a & (~(1<<2));
     //00000000000000000000000000001111
     //11111111111111111111111111111011
     //00000000000000000000000000000100
     //00000000000000000000000000001011
}

++
++a 前置++,先加1后使用
a++ 后置++,先使用后加1

int main()
{
   int a = 10;
   printf("%d\n",a++);
   
   return 0;
}
(类型)强制类型转换
int main()
{
  int a = (int)3.14;
  printf("%d\n", a);
  return 0;
}

题目:输出结果是?

void test1(int arr[])
{
    printf("%d\n",sizeof(arr));
}

void test2(char ch[])
{
    printf("%d\n", sizeof(ch));
}
int main()
{
    int arr[10] = { 0 };
    char ch[10] = { 0 };
    printf("%d\n",sizeof(arr));
    printf("%d\n",sizeof(ch));
    test1(arr);
    test2(ch);
    return 0;
}

运行结果
在这里插入图片描述
数组传参传的是首元素的地址,地址就要由指针来接收。所以void test2(char ch[])中ch[]是个指针。指针大小4个字节。

关系操作符

>
<
<=
!=  不等于
==  等于

**警告:**在编程的过程中==和不小心写错,导致的错误。

逻辑操作符

逻辑操作符有哪些:

&& 逻辑与
|| 逻辑或

区分逻辑与按位与区分逻辑或按位或

1&2----->0
1&&2---->1

1|2----->3
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\n d=%d\n",a ,b ,c ,d);

	return 0;
}

运行结果
在这里插入图片描述
逻辑与左边只要为假(0)就不进行运算了。i = a++ && ++b && d++中。a++先使用后加加,a=0逻辑或运算后因为左边为0所以右边就不运行了。

int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
  
	printf(" a=%d\n b=%d\n c=%d\n d=%d\n",a ,b ,c ,d);

	return 0;
}

运行结果
在这里插入图片描述

int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
	i = a++ || ++b || d++;

	printf(" a=%d\n b=%d\n c=%d\n d=%d\n", a, b, c, d);

	return 0;
}

运行结果
在这里插入图片描述
总结

如果是“||”或,左边为真右边就不要算了

如果是“&&”并,左边为真右边就不要算了

条件操作符

exp1? exp2 : exp3 

表达式1的结果如果为真,表达式2的结果是整个表达式的结果。表达式1的结果如果为真,表达式3的结果是整个表达式的结果。

int main()
{
	int a = 10;
	int b = 20;
	int max = 0;
	if (a > 5)
		max = 3;
	else
		max = -3;

	max = (a > 5 ? a : b);

	printf("%d",max);
	return 0;
}

逗号表达式

exp1, exp2 ,exp3,...expN

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

题目:c是多少?

//代码1
int a = 1;
int b = 2;
int c = (a>b,a=a+b,a,b=a+1);//逗号表达式
printf("%d",c);

运行结果
在这里插入图片描述

//代码2
if (a =b+1, c=a/2, d>0) //真正起到作用的是d>0
a = get_val();
count_val(a);
while(a>0)
{
    a = get_val();
    count_val(a);
}

如果使用逗号表达式,改写:

while (a = get_val(),count_val(a), a>0)
{
    //业务处理
}

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

[ ]下标引用操作符

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

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

int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符
[]的两个操作数是arr和9
()函数调用操作符

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

int get_max(int x, int y)
{
    return x > y ? x : y;
}
int main()
{
	int a = 10;
	int b = 20;
    //调用函数的时候的()就是函数调用操作符
	int max = get_max(a, b);
	printf("max = %d\n", max);
	return 0;
}
访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
//学生
struct Stu
{
	char name[20];
	int age;
	char id[];
};

int main()
{
	int a = 10;
	//使用struct Stu这个类型创建了一个学生对象叫s1,并初始化
	struct Stu s1 = {"张三", 20, "2019010205"};
	printf("%s\n", s1.name);
	printf("%d\n", s1.age);
	printf("%s\n", s1.id);//结构体变量.成员名
	return 0;
}

使用指针打印

struct Stu
{
	char name[20];
	int age;
	char id[];
};

int main()
{
	int a = 10;
	//使用struct Stu这个类型创建了一个学生对象叫s1,并初始化
	struct Stu s1 = { "张三", 20, "2019010205" };
	struct Stu* ps = &s1;

	printf("%s\n", ps->name);
	//printf("%s\",(*ps).name)
	printf("%d\n", ps->age);
	printf("%s\n", ps->id);

	return 0;
}

运行结果
在这里插入图片描述

隐式类型转换

c的整型类型算术运算总是至少以缺省整型类型的精度来进行的。

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值