这里写目录标题
前言
1.操作符的分类
- 算术操作符:+ 、- 、* 、/、 %
- 移位操作符:<< 、 >>
- 位操作符:&、 | 、^
- 赋值操作符:= 、+=、 -=、 *=、 /=、 %= 、<<= 、>>= 、&= 、|= 、^=
- 单⽬操作符: !、++、–、&、*、+、-、~ 、sizeof、(类型)
- 关系操作符:> 、>= 、< 、<= 、 == 、 !=
- 逻辑操作符: && 、||
- 条件操作符: ? :
- 逗号表达式: ,
- 下标引用:[]
- 函数调用:()
- 结构成员访问:. 、->
今天我们介绍其中的一部分
2. 二进制和进制转换
在日常生活中,我们一般使用的都是十进制,比如说我今年88岁了,或者说这本书30元,等等都使用的是十进制。我们可以从中得知:
-
十进制的数字是由0~9组成的
-
满十进一
于是我们便能得出二进制: -
二进制中数字都是由0~1组成的
-
满2进1
我们怎么将二进制数字转换成十进制呢?以1101举例
十进制怎么转换成二进制呢?以125举例
二进制转八进制和十六进制
八进制的每一位数字是0~7,对于0-7的每一个数字写成二进制,最多需要用三位二进制数字表示,比如7的二进制数字就是111
例子
同理,十六进制的每一位数字都是由0-9,a-f组成的,最多需要用4位二进制表示,比如f的二进制数字就是1111
3.原码,反码和补码
我们知道在计算机内部只能处理二进制数据。所以我们需要深入理解二进制。
整数的二进制表示方法一共有三种,即原码,反码,补码。
有符号整数的三种表示方法均由符号位和数值位两部分组成。二进制序列中,最高的一位是被当成符号位,剩余的都是数值位。
正整数的原码,反码,补码都是一样的。
负整数的三种表示方法各不相同。
符号位0表示正,1表示负。
原码:直接将正负数的形式翻译成二进制得到的九十原码。
反码:除了原码的符号位不变以外,其他位都按位取反得到的就是反码。
补码:反码+1得到的就是补码。
补码得到原码也可以:取反,+1
对于整形来说:在内存中是以补码的形式存放的。
原因如下:
4.移位操作符
1.<<左移操作符
2.>>右移操作符
注意:移位操作符的对象只能是整数。
4.1左移操作符
移位规则:左边抛弃,右边补0。
4.2右移操作符
移位规则:1.逻辑右移:左边用0填充,右边丢弃。2.算术右移:左边用原该值的符号位填充,右边抛弃。
那么右移到底是使用哪种方法呢?答案是取决于编译器的。
而大部分的编译器都是采用算术右移的
逻辑右移:
算术右移:
5.位操作符:&,|,^,~
&:按位与:都是1则为1,
|:按位或:有一个是1即为1
^:按位异或:不同为1,相同为0
~:按位取反:1变0,0变1
它们的操作数必须是整数。
^的一些性质:a ^ a=0。0&x=x;两个相同的数异或结果是0;0和任何数异或结果是任何数。
练习:不创建第三个变量,交换两个变量的值。
#include<stdio.h>
int main()
{ int a=10;
int b=20;
a=a^b;
b=a^b;
a=a^b;
return 0;
}
练习:输出一个-1的二进制中有几个1;
#include<stdio.h>
int main()
{ int num=-1;
int i=0;
int cnt=0;
while(num){
cnt++;
num=num&(num-1)
}
printf("%d",cnt);
return 0;
}
6.单目操作符
!、++、–、&、*、+、-、~ 、sizeof。我们在前面已经基本介绍过了。
7.逗号表达式
就是用逗号隔开的多个表达式。
从左向右依次执行,整个表达式的结果是最后一个表达式的结果
int a=1;
int b=2;
int c=(a>b,a=b+10,b=a+1);
c的值是13。
8.下标访问[],函数调用()
[]:操作数:一个数组名+一个索引值(下标)。
int arr[10];
arr[9]=10;
():接受一个或者多个操作数:第一个操作数是函数名,剩余的是传递给函数的参数。
#include<stdio.h>
int Add(int x,int y)
{
return x+y;
}
int main()
{
int a=10;
int b=10;
int c=Add(a,b);
return 0;
}
9.结构成员访问操作符
C语言提供了诸如int,float,char等等这些内置类型。但是只有这些是不够的,假如我想描述一个学生,那他需要有姓名,班级,学号等等信息。这时一个对象就需要多个变量的描述,而c语言就增加了结构体这种自定义的数据类型,让程序员可以自己创造合适的类型。
今天我们只介绍操作符,关于结构体的内容我会放在后面详细介绍。
结构成员访问操作符
结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个参数。如下所示:
#include <stdio.h>
struct Point
{
int x;
int y;
}p = {1,2};
int main()
{
printf("x: %d y: %d\n", p.x, p.y);
return 0;
}
使用方式:结构体变量.成员名
结构体成员的间接访问
有时候我们会得到一个指向结构体的指针。
#include <stdio.h>
struct Point
{
int x;
int y;
};
int main()
{
struct Point p = {3, 4};
struct Point *ptr = &p;
ptr->x = 10;
ptr->y = 20;
printf("x = %d y = %d\n", ptr->x, ptr->y);
return 0;
}
使用方式:结构体指针->成员名
10.操作符的属性:优先级,结合性
11.整型提升
C语言中整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整形,这种转换被称为整型提升
大家先思考一下这串代码。
int main()
{
char a=20;
char b=120;
char c=a+b;
printf("%d\n",c);
return 0;
}
整型提升的意义
表达式的整形运算要在cpu的相应运算器件内执行,cpu内整形运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是cpu的通用寄存器的长度。
因此,即使两个字符型相加,在cpu内部执行时实际上也要先转换为cpu内整形操作数的标准长度。
通用cpu是难以直接实现两个8比特(1字节)长度直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入cpu去执行运算。
如何进行整数提升呢?
1.有符号整数提升是按照变量的数据类型的符号位来提升的。
2.无符号整数提升,高位补0.
所以:
int main()
{
char a=20;
//00000000000000000000000000010100,会截断存储到a中,即00010100。
char b=120;
//00000000000000000000000001111000,会截断存储到b中,即01111000
char c=a+b;
//a,因为符号位是0,所以前面补0,变成00000000000000000000000000010100进行运算,
//b,因为符号位是0,所以前面补0,变成00000000000000000000000001111000进行运算,
//相加得到
//00000000000000000000000010001100,截断后存储到c中,即10001100
printf("%d\n",c);
//%d,以十进制形式打印一个有符号的整型(int)
//这里操作对象都是补码,但输出是按照原码进行输出的,所以
//10001100进行整数提升变成11111111111111111111111110001100-这是c的补码
// 10000000000000000000000001110011-这是c的反码
// 10000000000000000000000010001100-这是c的原码,即-116
return 0;
所以输出结果是-116
12.算术转换
如果某个操作符的操作数不是同一个类型,那么就需要转换类型
排名较后的类型会转换为前面的类型。
如有错误,恳请指正。