在编写代码的时候需要各种操作符配合使用才能够达到目的,下面分享一下C语言的操作符。
一、操作符分类
算数操作符:+、-、*、/、%
移位操作符:>>、<<
位操作符:&、|、^
赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>\、&=、|=、^=
单目操作符:!、++、--、&、*、+、-、~、sizeof、(类型)
关系操作符:>、>=、<、<=、==、!=
逻辑操作符:&&、||
条件操作符:? :
逗号表达式:,
下标引用:[ ]
函数调用:( )
结构成员访问:. 、->
二、二进制和进制转换
由于部分操作符与二进制有关,所以先来了解一下二进制和其他进制的转换。常见进制有二进制、十进制、八进制、十六进制,与十进制作比较,十进制是满十进一,那么二进制就是满二进一,八进制就是满八进一,十六进制就是满十六进一,另外十六进制超过9的数用字母表示,一到十五就是1 2 3 4 5 6 7 8 9 A B C D E F,并且在C语言中16进制的数值之前要写0x,8进制的数值之前写0。二进制每一位都是由0和1组成的比如:10110.
1、二进制和十进制的转换
(1)二进制转十进制
其实10进制的123表⽰的值是⼀百⼆⼗三,为什么是这个值呢?其实10进制的每⼀位是有权重的,10 进制的数字从右向左是个位、⼗位、百位....,分别每⼀位的权重是10的0次方、10的1次方、10的2次方。
2进制和10进制是类似的,只不过2进制的每⼀位的权重,从右向左是2的0次方、2的1次方、2的2次方。
那么二进制转换为10进制,比如1101,就等于1*1+0*2+1*4+1*8=13。
(2)十进制转二进制
将十进制数转换为二进制数可以用短除法,比如数字125,将125除以2得商62余1,然后再将62除以2商31余1,再31除以2商15余1,再15除以2商7余1,再7除以2商3余1,再3除以2商1余1,再1除以2商0余1,结束除法,在将所有余数按照逆序写出就是二进制的125,即1111101。
2、二进制转八进制和十六进制
(1)二进制转八进制
8进制的数字每⼀位是 0~7 的,0~7 的数字,各⾃写成2进制,最多有3个2进制位就足够了,比如7的二进制是111,所以在2进制转8进制数的时候,从2进制序列中右边低位开始向左每3个2进制位会换算⼀ 个8进制位,剩余不够3个2进制位的直接换算。 如:2进制的 01101011,换成8进制:0153,0开头的数字,会被当做8进制。
(2)二进制转十六进制
16进制的数字每⼀位是0~9, a~f 的,0~9, a~f的数字,各⾃写成2进制,最多有4个2进制位就⾜够了, 比如 f 的二进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进 制位会换算⼀个16进制位,剩余不够4个⼆进制位的直接换算。 如:2进制的01101011,换成16进制:0x6b,16进制表示的时候前面加0x。
三、原码、反码、补码
整数的2进制表示方法有三种,即原码、反码和补码 有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号 位,剩余的都是数值位。 符号位都是用0表示“正”,用1表示“负”。
注意:正整数的原、反、补码都相同。 负整数的三种表示方法各不相同。
原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
补码:反码+1就得到补码。
补码得到原码也是可以使用:取反,+1的操作。
无符号整数的三种 2 进制表示相同,没有符号位,每⼀位都是数值位,并且对于整形来说:数据存放内存中其实存放的是补码。
四、移位操作符
移位操作符分为左移操作符和右移操作符,并且移位操作符的操作数只能是整数。
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;
}
将数字n左移一位,是对n的二进制数补码进行操作的,因为整数在内存中是以补码形式存储的:
//int n=10
//二进制的n
//00000000 00000000 00000000 00001010
//n<<1,将最左边的0丢弃,再在最右边补一个0
//00000000 00000000 00000000 00010100
2、右移操作符
右移操作符分为逻辑右移和算数右移:
(1)逻辑右移:左边用0填充,右边丢弃。
(2)算数右移:左边⽤原该值的符号位填充,右边丢弃。
#include <stdio.h>
int main()
{
int num = -1;
int n = num >> 1;
printf("n = %d\n", n);
printf("num = %d\n", num);
return 0;
}
如果是逻辑右移:
//int n=-1
//二进制的n的补码
//11111111 11111111 11111111 11111111
//n>>1,将最右边的1丢弃,再在最左边补一个0
//01111111 11111111 11111111 11111111
如果是算数右移:
//int n=-1
//二进制的n的补码
//11111111 11111111 11111111 11111111
//n>>1,将最右边的1丢弃,再在最左边补一个1,
//同样如果右边丢弃的是0,就在左边补0
//1111111 11111111 11111111 11111111
注意:对于移位运算符,不要移动负数位,这个是标准未定义的。比如:
int num = 10;
num >> -1;//error
五、位操作符
位操作符有:
&:按位与
|:按位或
^:按位异或
~:按位取反
位操作符的操作数也必须是整数。
看如下代码:
#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;
}
//int num1=-3 int num2=5
//-3
//原码 10000000 00000000 00000000 00000011
//反码 11111111 11111111 11111111 11111100
//补码 11111111 11111111 11111111 11111101
//5的原反补都一样
//补码 00000000 00000000 00000000 00000101
//将两者的补码每一位进行与操作
//最后的答案也是补码
// 00000000 00000000 00000000 00000101 ->5
则num1 & num2 就是5。
//int num1=-3 int num2=5
//-3
//原码 10000000 00000000 00000000 00000011
//反码 11111111 11111111 11111111 11111100
//补码 11111111 11111111 11111111 11111101
//5的原反补都一样
//补码 00000000 00000000 00000000 00000101
//将两者的补码每一位进行或操作
//最后的答案也是补码
// 11111111 11111111 11111111 11111101 ->-3
则num1 | num2 就是-3。
异或的操作是相同为0,相异为1。
//int num1=-3 int num2=5
//-3
//原码 10000000 00000000 00000000 00000011
//反码 11111111 11111111 11111111 11111100
//补码 11111111 11111111 11111111 11111101
//5的原反补都一样
//补码 00000000 00000000 00000000 00000101
//将两者的补码每一位进行异或操作
//最后的答案也是补码
// 11111111 11111111 11111111 11111000 ->-8
则num1 ^ num2 就是-8。对于~0取反就是0变为1,1变为0。
那么如何运用位操作符,并且不创建第三个变量来实现两个整数的交换。这里可以使用异或操作符,根据异或的交换律和相同为0相异为1的特点,那么a^a=0,0^a=a,有:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;//b=a^b=a^b^b=a^0=a
a = a ^ b;//a=a^b=a^a^b=0^b=b
printf("a = %d b = %d\n", a, b);//实现交换
return 0;
}
六、单目操作符
单目操作符有:!、++、--、&、*、+、-、~ 、sizeof、(类型)。单目操作符的特点是只有⼀个操作数。
七、逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。 逗号表达式,从左向右依次执行。整个表达式的结果是最后⼀个表达式的结果。
//代码1
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
//c就是13
//代码2
if (a = b + 1, c = a / 2, d > 0)
//代码3
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、结构体
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚⾄是其他结构体。
(1)结构体声明
struct tag
{
member-list;
}variable-list;
如果要用结构体描述一个学生:
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //可以在这里创建一个变量也可以不创建
(2)结构体变量的定义和初始化
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
int score;//成绩
}s3;//可以在结构体末尾创建一个全局变量
struct Stu s1 = {"zhangsan", 20,88};//初始化
int main()
{
struct Stu s2 = {"lisi", 18,86};
return 0;
}
(3)结构体成员访问操作符
结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数,在访问这个成员进行打印或者指定顺序初始化的时候会用到。如下所示:
#include <stdio.h>
struct Point
{
int x;
int y;
}p1 = {1,2};
int main()
{
struct Point p2=(.y=20,.x=9);//指定顺序初始化
printf("x: %d y: %d\n", p1.x, p1.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;
}