入门学习计算机第十二天—操作符详解

入门学习计算机第十二天—操作符详解

编译器:Microsoft Visual Studio 2010

前言
记录第十一天学习C语言的博客。

1、 算术操作符

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

2、移位操作符

<< 左移操作符
` >>右移操作符
右操作符
1、算术右移:(基本都是算术右移)
右边丢弃,左边补原符号位
2、逻辑右移:
右边丢弃,左边补0

int main()
{
  int a = 16;
  int b = a>>1;
  //>> -- 右移操作符,移动的是二进制位
  //00000000000000000000000000010000
  // 00000000000000000000000000010000
  //最高位补原符号位0
  return 0;
}

结果输出的是8,右移一位有除以2的效果。

当负数进行右移时:

int main()
{
  int a = -1;
  int b = a>>1;
  //>> -- 右移操作符,移动的是二进制位
  //
  //
  //最高位补原符号位0
  return 0;
}

输出的结果是-1

整数的二进制表示有:原码,反码,补码
存储在内存中的是补码
-1的原码是10000000000000000000000000000001,最高位为符号位
(原码转化为反码,符号位不变,其他位按位取反)
-1的反码是11111111111111111111111111111110
(反码转化位补码,反码加一)
-1的补码是11111111111111111111111111111111
右移一位之后还是11111111111111111111111111111111(此时还是补码,需要转化为原码输出),所以输出的结果还是-1;

左移操作符
左边丢弃,右边补0

int main()
{
  int a = 10;
  int b = a << 1;
  //<<-- 左移操作符,移动的是二进制位

  return 0;
}

输出的结果是20。左移一位有乘以2的效果。

警告!!!!!:对于移位操作符,不要移动负数位,这个是标准未定义;

3、位操作符

位操作符:
&:按位与
|:按位或
^:按位异或
他们的操作数都是整数

&按位与: 有0则0

int main()
{
  int a = 3;//011
  int b = 5;//101
 int c = a&b//001
  return 0;
}

输出的结果是1

|按位或: 有1则1

int main()
{
  int a = 3;//011
  int b = 5;//101
  int c = a|b;//111
  return 0;
}

输出的结果是7

^按位异或: 对应的二进制位相同为0,相异为1

int main()
{
  int a = 3;//011
  int b = 5;//101
  int c = a^b;//110
  return 0;
}

输出的结果是6

一道变态的面试题:
不能创建临时变量 (即第三个变量),实现两个数的交换

第一种方法:加减法

int main()
{
   int a = 5;
   int b = 3;
   a= a+b;//a=8,b=3
   b= a-b;//a=8,b=5
   a = a-b;//a=3,b=5
   return 0;

}

这种方法有缺陷,当a和b的值非常大的时候,两个数相加有可能会溢出。

第二种方法:使用异或操作符

int main ()
{
   int a = 5;
   int b = 3;
   a = a^b;//a为110,b为011。a = a^b为101
   b = a^b;//a为101,b为011。b = a^b为110
   a = a^b;//a为101,b为110。a = a^b为011

}

题目二:
统计一个整数的二进制有多少个1

第一种解法:

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

但此方法有个缺陷是,当值为负数时,会错误。
第二种解法:

int main()
{
  int num = 0;
  int count = 0;
  scanf("%d", &num);
  int i = 0;
  for(i=0; i<32; i++)
  {
     if(1 ==((num>>i)&1))
       count++;
  }
  return 0;
}

这种方法可以运用于负数

第三种:

int main()
{
  int num = 0;
  int count = 0;
  scanf("%d", &num);
  while(num)
  {
    count++;
    num = (num&(num-1));
  }
   pirntf("%d\n",count);
}

4、赋值操作符

赋值操作符可以连续使用,比如

int a = 10;
int x = 0;
int y = 20;
a = x= y+1;//不推荐,建议分开写
x =y+1;
a = x;

复合赋值操作符

+=-=*=/=>>=;<<=;

5、单目操作符

!:逻辑反操作
真变成假,假变成真

int main()
{
 int a = 0;
 if(!a)
 {
   printf("hehe\n")
 }
  return 0;
}

± : 负值\正值

&:取地址操作符

int main()
{
  int a = 10;
  int* p = &a;
  //*p解引用操作符
}

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

int main()
{
  int a =10;
  char c = 'r';
  char* p = &c;
  int arr[10]= {0};
  printf("%d\n",sizeof(a));//4
   printf("%d\n",sizeof(c));//1
    printf("%d\n",sizeof(p));//4
     printf("%d\n",sizeof(arr));//40
   return 0;
}
int main()
{
  short s = 0;
  int a = 10;
  printf("%d\n", sizeof(s= a+5));//2 
  printf("%d\n",s);// 0  s的值并没有改变
  return 0;
}

~:按(二进制)位取反

int main()
{
  int a = 0;
  //00000000000000000000000000000000
  //11111111111111111111111111111111 -按位取反/补码
  //11111111111111111111111111111110 -反码
  //10000000000000000000000000000001 - 原码
  printf("%d\n", ~a);
  return 0;
}

输出的值就是-1

案例:

int main()
{
 int a = 15;
 //00000000000000000000000000001111 让1111变成1011
 //11111111111111111111111111111011 按位与 但是这个数如何获得
 //00000000000000000000000000000001 1<<2再取反即可
 a = a & (~(1<<2));
 printf("%d\n",a);
 return 0;
}

– ++ :前置 /后置

int main()
{
 int a = 10;
 printf("%d\n",++a);//先++,后使用 打印结果是11
 printf("%d\n",a++);//先使用,后++  打印结果是10
 return 0;
}

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

(类型):强制转化类型操作符

int mian()
{
 int a = (int)3.14;
 return 0;
}

6、关系操作符

<= >= != ==
赋值 = 和判断相等== 容易弄混

7、逻辑操作符

&&逻辑与 :
| |逻辑或
:关注数本身

区分逻辑与和按位与:
1&&2 ----->1
1&2-------->0

区分逻辑或与按位或:
1||2---------->1
1|2----------->3

360面试题:

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

输出的结果是
在这里插入图片描述
a++&&++b,当左边是a++,先使用a,a=0,&&的结果就是0,为假,&&右边的值不管是什么表达式都不会再计算了。

int main()
{
 int i =0,a=0,b = 2 ,c = 3 ,d =4;
 i = a++ || ++b || d++;
 printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
  
 return 0;
}

输出的结果为
在这里插入图片描述

a++||++b,当左边是a++,先使用a,a=0,||的结果就是0,为假,继续计算++b, 先++,后使用,b等于3,为真,||右边的值不管是什么表达式都不会再计算了。

8、条件操作符

exp1 ? exp2 : exp3
表达式1如果为真,执行表达式2,表达式2的结果为整个表达式的结果。如果为假,执行表达式3,表达式3的结果为整个表达式的结果

int main()
{
  int a = 0;
  int b = 0;
  if(a > 5)
     b = 3;
  else 
     b  = -3;
   //可以写成 b = (a > 5 ? 3 : -3);

}

9、逗号表达式

exp1,exp2,exp3, … ,expN
从左向右依次执行,整个表达式的结果是最后一个表达式的结果。

int main()
{
 int a = 1;
 int b = 2;
 int c = (a>b, a=b+10, a, b=a+1);//a>b不执行
   print("%d\n",c)
  return 0; 
}

输出的结果是13

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

下标引用操作符: 操作数:一个数组名+一个索引值

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);
}

访问一个结构的成员

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

struct Stu
{  //成员变量
  char name[20];
  int age;
  char id[20];//创建一个结构体类型-struct Stu
};
 
int main()
{   //使用struct Stu 这个类型创建了一个学生对象s1,并初始化
   struct Stu s1 ={"张三"20"2018024"};
  // struct Stu* ps= &s1;
   printf("%s\n", s1.name);
   //printf("%s\n", ps->name);
   printf("%d\n", s1.age);
   printf("%s\n", s1.id);
  return 0;
}

在这里插入图片描述

表达式求值

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

隐式类型转换:
C的整型算术运算总是至少以缺省整型类型的精度进行的。
为了获得这个精度,表达式中的字符和短整型操作符在使用之前转换为普通整型,这种转换类型称为整型提升
如何进行整型提升?
整型提升是按照变量的数据类型的符号位来提升的,无符号类型,补0来提升

int main()
{
   char a =3;
   //00000000000000000000000000000011 因为a是char类型,char类型是1个字节
   //00000011 - a
   char b = 127;
   //00000000000000000000000001111111 同理
   //01111111 -b
   //a和b如何相加,整型提升,char是有符号
   //a-00000000000000000000000000000011 
   //b-00000000000000000000000001111111
   //c-00000000000000000000000010000010 
   //进行截断c-10000010
   //进行整型提升
   //11111111111111111111111110000010--补码
   //11111111111111111111111110000001--反码
   //10000000000000000000000001111110--原码
   char c = a+b;
   printf("%d\n",c);
   return 0;

}

输出的结果是-126

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

只输出了c。
因为 char short 在进行判断(也算表达式运算)时,需要整型提升,一旦提升,值就不会再相等了。

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

输出的结果为
1
4
1
只要参与表达式运算,就会发生整型提升,表达式+c,就会发生提升,所以sizeof(+c)是4个字节

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

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

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

操作符的属性

复杂表达式的求值有三个影响的因素:
1、操作符的优先级
2、操作符的结合性
3、是否控制求值顺序
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

优先级一览表


计算机知识/代码知识(零碎)

在Window系统下,win+r输入calc可以打开计算器。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值