操作符详解

一、操作符的分类

1、操作符的分类

(1)算数操作符:+、-、、/、%
(2)移位操作符:<< 、>>
(3)位操作符:& 、|、^
(4)赋值操作符:=、+=、-=、
=、/=、%=、<<=、>>=、&=、|=、^=
(5)单目操作符:!、++、–、&、*、+、-、~、sizeof、(类型)
(6)关系操作符:>、>=、<、<=、==、!=
(7)逻辑操作符:&&、||
(8)条件操作符:? :
(9)逗号操作符:
(10)小标操作符:[]
(11)函数调用:()
(12)结构成员访问:. 、->

二、二进制和进制转换

在我们生活中经常都能听到有二进制、八进制、十进制和十六进制这样的说法。我们经常会产生很多疑问,这些进制都是什么东西,或者说都有什么作用?其实二进制、八进制、十进制、十六进制是数值的不同表现形式而已,本质其实就是一个数。我们可以用15这个数来举一个例子:

15的二进制:1111
15的八进制:17
15的十进制:15
15的十六进制:F
//八进制前要写:0
//十六进制前要写:0x

看似有很多什么进制啊其实本质上都是一个数,只是它的表达方式不一样。
虽说有那么多个进制,我们首要讲的还是二进制:我们在之前学校加法呀我们都知道的一件事是满十要进一位,换成二进制也是一个道理(满2进一位)由此我们可以知道像:1101这一类就是二进制的数字了,是不是非常简单理解。

二进制转十进制

二进制和十进制之间是可以相互转转化的,其实进制与进制间都是可以相互转换的。下面我们举个例子来说明二进制和十进制之间的相互转换,我们用123这个数来讲解吧!我们知道123的十进制值是一百二十三,那为什么是这个值呢?其实十进制每一位都是有权重的,十进制的数字从右向左是个位、十位、百位…,分别每位的权重是十的零次方、十的一次方和十的二次方。
在这里插入图片描述
二进制和十进制是类似的,只不过二进制的每一位权重,从右到左十二的零次方、二的一次方…
在这里插入图片描述

十进制转换二进制数字

在这里插入图片描述

十进制的125转换的二进制:1111101

二进制转换成八进制

八进制的数字每一位都是0~7之间的数,我们举一个例子就可以直接理解了。例子如下:二进制的01101011,换成八进制就是:0153,以0开头的数字,就会被当作八进制。
在这里插入图片描述

二进制转十六进制

十六进制的数字每一位是0 ~ 9、a ~ f的,其实你可以这样看a表示10,b表示11以此类推f表示16.如二进制的01101011,转换成十六进制就是:0x6b,十六进制表示的时候前面要加上0x。

三、原码、反码、补码

整数的二进制有三种表达方式,即原码、反码和补码。

有符号整型的三种表达方法均有符号位数值位两个部分组成,在二进制中最高位的1被当作符号位,而1后面的位就是数值位(剩余的全部)符号位:0表示正,1表示负。

正整数的原码、反码、补码都是相同的。
负整数的三种表达方式各不相同。

原码:直接将数值转换成二进制的数值。
反码:符号位保持不变,数值位每一位按位取反。
补码:反码 + 1
*补码想得到原码可以取反后加一

对于整型来说:数据存放内存中其实存放的是补码。

四、移位操作符

左移操作符:<<
右移操作符:>>
*移位操作符的操作数只能是整数

1、左移操作符

左移操作符的规则是:左边抛弃,右边补0。

#include <stdio.h>

int main()
{
    int num1 = 10;
    int num2 = num1<<1;
    printf("num2= %d\n",num2);
    printf("num1= %d\n",num1);
    return 0;
}

在这里插入图片描述

我们会很奇怪为啥左移一位,得到的数是20
我们要转换成二进制的形式来理解,如下:
在这里插入图片描述

2、右操作符

右操作符规则:有两种

(1)逻辑右移:左边用0填充,右边丢弃
(2)算术右移:左边用原该值的符号位填充,右边丢弃

#include <stdio.h>
int main()
{
    int num1 = 10;
    int num2 = num1>>1;
    printf("num2= %d\n",num2);
    printf("num1= %d\n",num1);
    return 0;
}

在这里插入图片描述

以上跟上面左移是一样的,要遵循右移的规则

若num1是负数的时候,方法如下:

在这里插入图片描述

  • 对于移位运算符,不要移动负数位,这个是标准未定义的。

五、位操作符

位操作符有:

1&  //按位与
2|  //按位或
3^  //按位异或
4~  //按位取反

1、按位与 &

&就是将两个二进制的数,有0为0,两个都是1才为1
来举个例子:

2&3
2 ->  00000000 00000000 00000000 00000010
3 ->  00000000 00000000 00000000 00000011
2&3 -> 00000000 00000000 00000000 00000010  
//转换成十进制结果就为2

2、按位或 |

|就是两个二进制的数,有1为1,连个数都是0才为0。
来举个例子:

2|3
2 ->  00000000 00000000 00000000 00000010
3 ->  00000000 00000000 00000000 00000011
2|3 -> 00000000 00000000 00000000 00000011
//转换成十进制结果为3

3、按位异或 ^

^就是两个二进制的数,相同为0,不同为1。
举个例子:

2^3
2->  00000000 00000000 00000000 00000010
3->  00000000 00000000 00000000 00000011
2^3-> 00000000 00000000 00000000 00000001
//转换成十进制结果为1

4、按位取反 ~

~是指将二进制形式的数按位置取反

//例如0
0->  00000000 00000000 00000000 00000000
~0 ->  11111111 11111111 11111111 1111111

六、单目操作符

单目操作符有:!、++、–、&、*、+、-、~、sizeof、(类型)
这里应该就不用过多赘述了这些操作符应该都了解了。

七、逗号操作符

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

//代码1
int a = 1;
int b = 2;
int c = (a>b,a=b+10,a,b=a+1);

//代码2
if(a = b + 1,c=a / 2,d > 0)

八、下标访问[] ,函数调用()

1、[] 下标引用操作符

操作数:一个数组名 + 一个索引值(下标)

1int arr[10];
2、 arr[9] = 10;
//[]两个操作数是arr和9

2、函数调用操作符

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

#include <stdio.h>
voif test1()
{
     printf("haha\n");
}
void test2(const char *str)
{
     printf("%s\n",str);
}
int main()
{
    test1();
    test2("hello world");
    return 0;
}

这里test1中的()就是作为函数调用操作符
test2中的()就是函数调用操作符

九、结构成员访问操作符

1、结构体

在C语言中已经提供了很多内置类型:如int、char、float、double等。但只有这些内置的类型还是不够的,比如我要描述一个学生的姓名、身高和体重,又或者是一本书的出版社、作者和定价。这个我们仅仅是用内置类型是不行的。所以C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造合适的类型。

结构是一些值的集合,这些值称为成员变量。结构体中的每一个成员是不同的变量。如标量、数组、指针、甚至是其他的结构体。

(1)、结构体的声明

描述一个学生:

struct student
{
    char nume[20]; //学生的名字
    int age; //学生的年龄
    char sex[2];  //学生的性别
};  //注意这里有个分号

(2)、结构体变量的定义和初始化

//代码1:变量的定义
struct one
{
    int x;
    int y;
}p1;       //声明类型的同时定义变量p1
struct one p2; //定义结构体变量p2

//代码2:初始化
struct two p2 = {10,20};

struct two    //类型声明
{
    char name[20];
    int age;
};

struct two s1 = {"zhangsan",20);  //初始化
struct two s2 = {.age = 20, .name = "lisi"};  //指定顺序初始化

//代码3
struct three
{
    int data;
    struct point p;
    struct three next;
}n1 = {10,{4,5},NULL};     //结构体嵌套初始化

struct three p2 = {20,{4,5},NULL};

2、结构体成员访问操作符

(1)、结构体成员的直接访问

结构体成员的直接访问是通过点操作符(.)访问的。上面代码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;
}

综上所述使用方法是:结构体变量.成员名

(2)、结构体成员的间接访问

有时候我们得到的不是一个结构体变量,而是得到了一个指向结构体的指针。如下所示:

#include <stdio.h>
struct Point
{
    int x;
    int y;
};

int main()
{
    struct Point p = {3,4};
    struct 	Point *prt = &p;
    prt -> x = 10;
    prt -> y = 20;
    printf("x = %d y = %d\n",prt->x,prt->y);

    reurn 0;
}

综上所述使用方法为:结构体指针->成员名

十、操作符的属性:优先级、结合性

C语言的操作符有两个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。

1、优先级

优先级指的是,一个表达式包含多个运算符,那个运算符先执行。每个运算符的优先级是不一样的。

13 + 4 * 5

如上我们可以看到表达式中既有加法又有乘法,这个小学都应该知道先算乘法再算加法,所以说乘法的优先级高于加法所以就先算乘法后算加法。

2、结合性

如果两个运算符的优先级相同,优先级没办法确定先要计算哪个的时候,这个时候就看结合性了,则根据运算符的左结合,还是右结合,决定执行顺序。大部分运算符是左结合(从左到右执行),少部分运算符是右结合(从右到左执行),比如赋值运算符。
在这里插入图片描述
参考:https://zh.cppreference.com/w/c/language/operator_precedence

十一、表达式求值

1、整型提升

C语言中整型算术运算中总是至少以缺省(默认)整数类型的精度来进行的。
为了获取这个精度,表达式中的字符和短整形操作数在使用前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:

表达式的整型运算要在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;

b和c的值被提升为普通整型后,再进行加法运算。加法运算完成后,结果将被截断,然后再存储于a中。

如何进行整体提升呢?
1、有符号整数提升是按照变量的数据类型的符号位来提升的。
2、无符号整数提升,高位补0。

//负数的整型提升
char a1 = -1;
//a1的二进制位中只有8个比特位
//11111111
//因为char为有符号的char,所以整型提升的时候,高位补充符号位,即1
//提升后为:11111111111111111111111111111111

//正数的整型提升
char a2 = 1;
//变量a2的二进制位中只有8个比特位:
//00000001
//因为char为有符号的char,所以整型提升的时候,高位补充符号位,即0
//提升后为:00000000000000000000000000000001

//无符号整型提升,高位补0

2、算术转换

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

1long
2double
3float
4unsigned long int
5long int
6unsigned int
7int

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值