操作符的分类
进制转换
二进制与十进制
在生活中我们最常用的是十进制(十进制满10进1,十进制的每一位都是0~9的数字组成),其实二进制也同样如此(二进制满2进1,二进制的每一位都是0~1的数字组成的)。10进制的数字从右向左是个位、⼗位、百位....,分别每⼀位的权重是 10^0 , 10^1 , 10^2 ...如下:
而2进制和10进制是类似的,只要把10换成2就行了。
如果你想要在二进制和十进制之间进行转换:
十进制转二进制:只需要将十进制数依次除二,直到余数为0.由下往上依次所得的余数就是10进制转换出的2进制。
二进制转十进制:将每一位的值乘上权重值就是十进制数了。
二进制与八进制
二进制与十六进制
原码、反码、补码
正整数的原、反、补码都相同。负整数的三种表⽰⽅法各不相同。原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。补码:反码+1就得到补码。反码得到原码也是可以使⽤:取反,+1的操作。
移位操作符
包括: << 左移操作符和 >> 右移操作符
注意:移位操作符的操作数只能是整数。
左移操作符
移位规则:左边抛弃,右边补0.
如下:
#include <stdio.h>
int main()
{int num = 10;int n = num<<1;printf("n= %d\n", n);printf("num= %d\n", num);return 0;
}
右移操作符
移位规则:先右移后,分逻辑右移(左边用0填充,右边丢弃)和算数右移(左边的原该值的符号位填充,右边丢弃),大多数情况下都是算数右移。
int num = 10 ;num>> -1 ; //error
位操作符(&、| 、^ 、~)
& 按位与 有0则0,全1才1
| 按位或 有1则1,全0才0
^ 按位异或 相同为0,相异为1
~ 按位取反注: 他们的操作数必须是整数。 因为计算使用的是它们的二进制形式。
下面举几个例子:
#include <stdio.h>
int main()
{int num1 = -3;int num2 = 5;printf("%d\n", num1 & num2);printf("%d\n", num1 | num2);printf("%d\n", num1 ^ num2);printf("%d\n", ~0);return 0;
}
#include <stdio.h>
int main()
{int num1 = -3;//10000000000000000000000000000011 -3的原码//11111111111111111111111111111100 -3的反码//11111111111111111111111111111101 -3的补码 int num2 = 5;//00000000000000000000000000000101 5的原反补码printf("%d\n", num1 & num2);//5//11111111111111111111111111111101 -3的补码 //00000000000000000000000000000101 5的原反补码//00000000000000000000000000000101 & 有0则0,全1才1printf("%d\n", num1 | num2);//-3//11111111111111111111111111111101 -3的补码 //00000000000000000000000000000101 5的原反补码//11111111111111111111111111111101 | 有1则1,全0才0 补码//10000000000000000000000000000011 原码printf("%d\n", num1 ^ num2);//-8//11111111111111111111111111111101 -3的补码 //00000000000000000000000000000101 5的原反补码//11111111111111111111111111111000 ^ 相同为0,相异为1 补码//10000000000000000000000000001000 原码printf("%d\n", ~0);//-1//00000000000000000000000000000000 0的原反补码//11111111111111111111111111111111 ~ 按位取反 补码//10000000000000000000000000000001 原码return 0;
逗号表达式
逗号表达式,就是⽤逗号隔开的多个表达式。
如(exp1, exp2, exp3, …expN)
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
答案是13,从左向右依次计算,a = b + 10 = 12, b = a + 1 = 13.
if (a =b + 1, c=a / 2, d > 0)
这同样是逗号表达式,前两步也可以提出来放在if语句的上面。
a = get_val();
count_val(a);
while (a > 0)
{//业务处理a = get_val();count_val(a);
}//如果使⽤逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{//业务处理
}
这就是逗号表达式的几种方式。
下标访问[]、函数调⽤()
1、下标引用操作符
操作数:一个数组名 + 一个索引值
int arr[ 10 ]; // 创建数组arr[ 9 ] = 10 ; // 实⽤下标引⽤操作符。[ ] 的两个操作数是 arr 和 9 。
2、函数调用操作符
接收一个或者多个操作符:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include <stdio.h>
void test1()
{printf("hehe\n");
}
void test2(const char *str)
{printf("%s\n", str);
}
int main()
{test1(); //这⾥的()就是作为函数调⽤操作符。test2("hello bit.");//这⾥的()就是函数调⽤操作符。return 0;
}
结构成员访问操作符
1、结构体
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚⾄是其他结构体。
C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类 型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等;描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型。
结构的声明
struct tag{member- list ;}variable- list ;
struct Stu{char name[ 20 ]; // 名字int age; // 年龄char sex[ 5 ]; // 性别char id[ 20 ]; // 学号}; // 分号不能丢
结构体变量的定义和初始化
//代码1:变量的定义
struct Point
{int x;int y;
}p1; //声明类型的同时定义变量p1struct Point p2; //定义结构体变量p2//代码2:初始化。
struct Point p3 = {10, 20};struct Stu //类型声明
{char name[15];//名字int age; //年龄
};struct Stu s1 = {"zhangsan", 20};//初始化
struct Stu s2 = {.age=20, .name="lisi"};//指定顺序初始化//代码3
struct Node
{int data;struct Point p;struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
2、结构成员访问操作符
结构体成员的直接访问
#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;
}
操作符的优先级与结合性
两个属性决定了表达式求值的计算顺序
优先级
结合性
如果两个运算符的优先级相同,那就要看结合性了,大部分的运算符是从左向右结合,少数运算符从右向左执行,如赋值运算符。
结合性参考:https://zh.cppreference.com/w/c/language/operator_precedence
表达式求值
整型提升
整型提升的意义:表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓度。通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算
char a,b,c;
...
a = b + c;
// 负数的整形提升char c1 = -1 ;变量 c1 的⼆进制位 ( 补码 ) 中只有 8 个⽐特位:1111111因为 char 为有符号的 char所以整形提升的时候,⾼位补充符号位,即为 1提升之后的结果是:11111111111111111111111111111111// 正数的整形提升char c2 = 1 ;变量 c2 的⼆进制位 ( 补码 ) 中只有 8 个⽐特位:00000001因为 char 为有符号的 char所以整形提升的时候,⾼位补充符号位,即为 0提升之后的结果是:00000000000000000000000000000001// ⽆符号整形提升,⾼位补 0
算术转换
long doubledoublefloatunsigned long intlong intunsigned intint