第十讲:操作符详解

本文详细介绍了C语言中的操作符分类,包括算术、移位、位、单目、关系、逻辑、条件、逗号表达式、下标引用、函数调用和结构成员访问等。此外,还涵盖了二进制与不同进制转换、原码、反码和补码的概念,以及整型提升和算数转换在表达式求值中的重要性。
摘要由CSDN通过智能技术生成

1.操作符的分类

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

2.二进制和进制转换

2,8,10,16进制都是数值的不同表示形式而已

16进制的数值之前写:0x

8进制的数值之前写:0

十进制满十进一,二进制满二进一

2进制转10进制

十进制的每一位都有权重,10进制的数字从右向左是个位、⼗位、百位....,分别每⼀位的权重是 10 , 10 , 10 ...

2进制的每⼀位的权重,从右向左是: 2 , 2 , 2 ...

10进制转2进制

2进制转8进制和16进制
        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
        

3.原码,反码,补码

整数的二进制表示方法有三种,即原码,反码和补码

有符号整数的三种表⽰⽅法均有符号位和数值位两部分,2进制序列中,最⾼位的1位是被当做符号
位,剩余的都是数值位
符号位都是⽤0表⽰“正”,⽤1表⽰“负”
正整数的原、反、补码都相同
负整数的三种表⽰⽅法各不相同
        原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
        
        反码:将原码的符号位不变,其他位依次按位取反就可以得到反码。
        补码:反码+1就得到补码。
对于整型来说:数据存放内存中的其实是补码
        
在计算机系统中,数值⼀律⽤补码来表⽰和存储。原因在于,使⽤补码,可以将符号位和数值域统⼀ 处理;同时,加法和减法也可以统⼀处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。

4.移位操作符

移动的是二进制位,且操作数只能是整数

       4.1 <<左移操作符
#include<stdio.h>
int main()
{
	int a = 6;
	int b = (a << 1);
	printf("%d\n", b);
	printf("%d\n", a);
	return 0;
}

                

左移补0

左移一位有乘二的效果

       4.2 >>右移操作符

右移规则取决于编译器的实现,常见编译器都是算数右移

移位不能移动负数位,这个是标准未定义的

右移一位有除以2的效果(大部分情况,-1就不行)

移位规则:分两种

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

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

5.位操作符:&,|,^,~

操作的是二进制的位,操作数必须是整数

按位与:

算出补码,有0为0,都是1为1

按位或:

算出补码,有1为1,都是0为0

打印的是原码

按位异或:

相同为0,相异为1

按位取反:

1变0,0变1

练习:不创建第三个变量,交换a和b

法一:更优

法二:

a^a=0,a^0=a

练习:编写代码实现,求一个整数存储在内存中的二进制中1的个数

法一:

法二:

练习:编写代码,将13二进制序列的第五位修改为1,然后再改回0

6.单目操作符

单⽬操作符有这些:!、++ -- & * + - ~ sizeof ( 类型 )
单⽬操作符的特点是只有⼀个操作数

7.逗号表达式

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

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

[ ]下标引用操作符

        数组使用

函数调用操作符

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

9.结构成员访问操作符

9.1结构体
C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类
型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要 名字、年龄、学号、⾝⾼、体重等;描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型。
结构体的关键字:struct
结构体是用来描述一个复杂对象的
它里面可以包含多个属性
        9.1.1结构的声明
最后花括号后的分号不能少

        9.1.2结构体变量

创建变量时也可以初始化

//代码1:变量的定义
struct Point
{
 int x;
 int y;
}p1; //声明类型的同时定义变量p1
struct 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};//结构体嵌套初始化

结构体包含结构体

9.2结构成员访问操作符
        9.2.1结构体成员的直接访问

结构体成员的直接访问是通过操作符(.)访问的,点操作符接受两个操作数

#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;
}

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

        9.2.2结构体成员的间接访问

有时候我们得到的不是一个结构体变量,而是得到了一个指向结构体的指针

#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;
}

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

综合举例:

#include <stdio.h>
#include <string.h>
struct Stu
{
 char name[15];//名字
 int age; //年龄
};
void print_stu(struct Stu s)
{
 printf("%s %d\n", s.name, s.age);
}
void set_stu(struct Stu* ps)
{
 strcpy(ps->name, "李四");
 ps->age = 28;
}
int main()
{
 struct Stu s = { "张三", 20 };
 print_stu(s);
 set_stu(&s);
 print_stu(s);
 return 0;
}

10.操作符的属性:优先级,结核性

这两个属性决定了表达式的求值计算顺序

10.1优先级

包含多个运算符时,哪个应该优先执行

如果表达式优先级相同,看结合性

10.2结合性 

优先级相同时,要看结合性,有的从左向右,有的从又向左

11.表达式求值

        11.1整型提升

 C语言中的整型算数运算总是,至少以缺省整型类型的精度来进行的

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升

#include<stdio.h>
int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;

	printf("%d\n", c);

	return 0;
}

a和b计算时,先要被转换为普通整型int,称为整型提升,转换后的计算精度会更高

整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。
因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓
度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中 可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为 int或unsigned int,然后才能送⼊CPU去执⾏运算。
如何进行提升?
1.有符号整数提升是按照变量数据类型的符号位来提升
2.无符号整数提升,高位补0
char类型为一个字节,占8个比特位
%d是以十进制的形式打印有符号整数
计算时使用补码,打印时使用原码
//负数的整形提升
char c1 = -1;
变量c1的⼆进制位(补码)中只有8个⽐特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111

//正数的整形提升
char c2 = 1;
变量c2的⼆进制位(补码)中只有8个⽐特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//⽆符号整形提升,⾼位补0

char是signed char还是unsigned char,取决于编译器,常见编译器是signed char

        11.2算数转换

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

long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。

字节不同就无法计算

        11.3 问题表达式解析
         表达式1
//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f

计算顺序有两种可能

a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f
a*b
c*d
e*f

a*b + c*d
a*b + c*d + e*f

解决方法:加括号,分开写

        表达式2
//表达式2
c + --c;

操作符的优先级只能决定自减--的运算在+的运算前面,但是没有办法得知,+操作符的左操作数c的获取是在右操作数运算之前还是之后求值,所以结果是不可预测的

        表达式3
//表达式3
int main()
{
 int i = 10;
 i = i-- - --i * ( i = -3 ) * i++ + ++i;
 printf("i = %d\n", i);
 return 0;
}

表达式3在不同编译器的结果都不同

        表达式4
#include <sdtio.h>
int fun()
{
 static int count = 1;
 return ++count;
}
int main()
{
 int answer;
 answer = fun() - fun() * fun();
 printf( "%d\n", answer);//输出多少?
 return 0;
}

输出结果为-10 

count每次加都             

建议不要写复杂表达式                                                           一

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值