C语言——操作符

一.操作符的分类

  • 算术操作符: + - * / %
  • 移位操作符:<< >>
  • 位操作符: & | ^
  • 赋值操作符: = += -= *= /= %= >= &= |= ^=
  • 单⽬操作符: ! ++ -- & * + - ~ sizeof ( 类型 )
  • 关系操作符: > >= < <= == !=
  • 逻辑操作符:&& || ! ++ -- & * + - ~ sizeof ( 类型 )
  • 条件操作符:? :
  • 逗号表达式:,
  • 下标引用:[ ]
  • 函数调用:()
  • 结构成员访问:. - >

二.二进制和进制转换

计算机能够识别的是2进制信息,数据在内存中存出的时候,也是以2进制的形式存储的

2进制转10进制:

10进制转换为2进制

2进制转化为8进制

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进制

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

例:

三.原码,反码,补码

原码::直接将数值按照正负数的形式翻译成⼆进制得到的就是原码

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。

补码:反码+1就得到补码。补码得到原码也是可以使⽤:取反,+1的操作。

正整数的原,反,补码都相同。

负整数的三种表示方法各不相同

**整数(有符号整数,无符号整数)**的2进制表示方法有三种,即原码、反码和补码

有符号整数 的三种表示方法均有符号位和数值位两部分,2进制序列中,最高位的1位是被当做符号 位,剩余的都是数值位。

符号位都是0表示正,1表示负。

无符号整数的三种 2 进制表⽰相同,没有符号位,每⼀位都是数值位。

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

四.移位操作符

移位操作符只能对整数进行移位,移动的是二进制位(存储在内存中的补码)

<<左移操作符

移位规则:左边抛弃,右边补零

cs 复制代码
int main()
{
    int a=6;
    int b=(a<<1)
    printf("%d\n",b);
    return 0;
}

最后a不没有变,结果是b变了,以为之后为12

>>右移操作符

移位规则:首先右移运算分两种(具体哪种取决于编译器):

1.逻辑右移:左边⽤0填充,右边丢弃

2.算术右移:左边⽤原该值的符号位填充,右边丢弃

对于移位运算符,不要移动负数位!

五.位操作符:& | ^ ~

&:按位与

|:按位或

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

零和任何数异或还是任何数,异或支持交换律

~:按位取反(对应的二进制位,原来是0变成1,原来是1变成0)

以上操作符的操作数必须是整数

这里举两个例子应用一下

  • 编写代码将13二进制序列的第5位修改为1,然后再改回0
cs 复制代码
int main()
{
	int a = 13;
	a |= (1 << (5 - 1));
	printf("%d\n", a);//29
/*
13=00001101
 1=00010000
按位或00011101
*/
	a &= ~(1 << (5 - 1));
/*
29=00011101
   11101111
按位与00001101
*/
	return 0;
}
  • 不创建临时变量(第三个变量),实现两个整数的交换
cs 复制代码
int main()
{
//0与任何数按位异或都为任何数
    int a=10;
    int b=20;
    a=a^b;//a'=a^b
    b=a^b;//b=a'^b=a^b^b=a
    a=a^b;//a=a^b^a=b
    printf("a=%d b=%d\n",a,b);
    return 0;
}

六.单目操作符

! ++ -- & * + - ~ sizeof ( 类型 )

七.逗号表达式

cs 复制代码
exp1,exp2,exp3,...expN

逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果

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

1.[ ]下标引用操作符

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

cs 复制代码
int arr[10];
arr[9]=10;//使用下标引用操作符
[ ]的两个操作是arr和9

2.函数调用操作符()

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

九.结构成员访问操作符

在学习这个操作符之前,我们得先知道一个概念:结构体

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

1.结构体的声明和结构体变量的创建和初始化

cs 复制代码
struct tag//(自定义的一个名字)
{
    member -list;//里面可以有一个或多个成员
}variable-list;//(可有可无)


//例:描述一个学生
struct Stu
{
    char name[20];
    int age;
    char id[11];
    float score;
}s6,s7,s8;//声明的同时顺便创建了几个变量,可有可无,这是全局变量
struct Stu s4;//全局变量
int main()
{    
    int num=0;
    struct Stu s1;//结构体变量的创建,s1,s2,s3都是局部变量
    struct Stu s2={"zhangsan",20,"20240809",85.5f};//初始化用大括号
    struct Stu s3={.age=18,.name="wangcai",.score=95.5f,.id="20240801"};//想指定初始化那个这里就用到了结构访问操作符.去初始化
    return 0;

}

2.结构体的嵌套

cs 复制代码
struct Peo
{
    char name[30];
    int age;
    char tele[12];
};
struct Ebook//实现一个通讯录,这个结构体就包含了另一个结构体
{
    struct Peo data[100];
    int count;
};
int main()
{
    struct Peo p1={"zhangsan",20,"12372324"};
    struct Ebook eb={{{"wangwu",19,"19378634"},{"cuihua",18,"28646375"}},0};
    printf("%s\n",p1.name);//访问结构体变量打印姓名(.结构成员访问操作符)
    printf("%s\n",p1.age);    
    printf("%s\n",eb.data[1].name);//打印翠花的名字
    return 0;
}

3,结构体成员的直接访问操作符.

使用方式:结构体变量.成员名

具体使用示例见上面代码

4.结构体成员的间接访问操作符->

使用方式:结构体指针->成员名

具体使用实例待后面学完指针详细说明,结构体也会在后面详细学习

十,操作符的优先级和结合性

1.优先级:

如果一个表达式包含很多操作符,我们需要知道那个操作符优先,优先级高的先进行计算

2.结合性:

如果两个运算符优先级相同,这时没法确定先算哪个就看结合性。根据运算是左结合还是右结合,决定执行顺序。

具体可参考下表

十一,表达式求值

1.整型提升

cs 复制代码
int main()
{
    int a=100;
    long c=100l;//编译器会默认100是整型,除非你在后面加个l,它会转变为long型
    long long b=100ll;//同理
    float f=3.14f;//这里规定会被默认为double类型,所以我们再后面加个f就会转换为float类型
    return 0;
}

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

如何整型提升?

(1). 有符号整数提升是按照变量的数据类型的符号位来提升的

(2).无符号整数提升,高位补0

cpp 复制代码
int main()
{
//char是有符号的,signed char
    char a=20;//截断后存进了a,因为a一个字符只占了八个字节,存不下那么多所以要截断
    //000000000000000000010100
    //只有00010100存进了a
    char b=120;//发生截断
    //000000000000000001111000
    //01111000存进了b
    char c=a+b;
//整型提升
    //a-00010100-提升000000000000000000010100
    //b-01111000-提升000000000000000001111000
    // 相加所得的是补码0000000000000000010001100
    //存进c里的为100001100
    printf("%d",c);
    //c提升后为111111111111111110001100-补码
    //打印的是原码-100000000000000001110100-转化为十进制就是-116
    return 0;
}

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

打印结果就为-116

2.算术转换

如果某个操作符的各个操作数属于不同的类型,则除非其中⼀个操作数的转换为另⼀个操作数的类 型,否则操作就不能进行。这个我们不需要过多操心,编译器会自动转化

3.问题运算代码举例

(1)

cs 复制代码
c + --c

这个代码运算的问题是,前一个c的开始取值不知道,因为后一个--c先进行计算,不确定前一个c是初定义的值还是减减后的值

(2)

cs 复制代码
a*b + c*d + e*f

不确定是以下哪种顺序,因为以下都符合正确的优先级和结合性

(3)

cs 复制代码
int main()
{
    int answer;
    answer=fun()-fun()*fun();
    printf("%d\n",answer);
    return 0;
}

这个我们只能通过优先级知道先算乘法,再算减法,而上面三个fun函数调用先后顺序无法通过操作符的优先级确定,不一样的先后顺序就导致了结果的不同

所以我们尽量不要写那样运算符冗杂混乱的代码,不确定优先级的操作符可以加以括号进行辅助。

相关推荐
青出于兰6 分钟前
C语言|函数的递归调用
c语言·开发语言
whoarethenext11 分钟前
数据结构堆的c/c++的实现
c语言·数据结构·c++·
2401_8582861116 分钟前
CD36.【C++ Dev】STL库的string的使用 (下)
开发语言·c++·类和对象·string
Codeking__32 分钟前
滑动窗口——无重复字符最长的字串
算法·哈希算法
sanx1838 分钟前
从零搭建体育比分网站完整步骤
java·开发语言
若水晴空初如梦41 分钟前
QT聊天项目DAY09
开发语言·qt·bootstrap
leo__5201 小时前
matlab 产生载频和宽度的脉冲串,并叠加高斯白噪声
开发语言·人工智能·matlab
关岭风尘1 小时前
Matlab/Simulink - BLDC直流无刷电机仿真基础教程(七) - 波形解析专题P2
开发语言·matlab·bldc电机·相线接错·电机缺相
Mi Manchi261 小时前
力扣热题100之反转链表
算法·leetcode·链表
n33(NK)1 小时前
【算法基础】选择排序算法 - JAVA
数据结构·算法·排序算法