前言
本节内容主要讲述的是c语言中一些操作符的作用以及使用案例
一、操作符是什么?
指令系统的每一条指令都有一个操作符,它表示该指令应进行什么性质的操作。不同的指令用操作符这个字段的不同编码来表示,每一种编码代表一种指令。
操作符详解:
(1)单目操作符:只有一个操作数的操作符
除法:/
得到小数:除数与被除数中至少有一个是小数得到整数:除数与被除数必须都是整数
(2)移位操作符:
左移:<<
把a的二进制位向左移动一位: 左边移出去的丢弃,右边补0
int a = 2; 00000000 00000000 00000000 00000010
00000000 00000000 00000000 00000100
int b = a << 1; 得到b = 4
右移 <<
把a的二进制位向右移动一位
右移分为两种:算术右移:右边丢弃,左边补原符号位 正数补0 负数补1
逻辑右移:右移丢弃,左边补0 都补0
int a = 10;
00000000 00000000 0000000 00001010
00000000 00000000 0000000 00000101 - 5
int b = a >> 1; 得到b = 5
整数的二进制表示形式:
原码:直接根据数值写出的二进制序列就是原码
反码:原码的符号位不变,其他位按位取反就是反码
补码:反码+1就是补码
注意:反码(补码-1)= 原码
-1
原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110(除了符号位其他位全部取反)
补码:11111111 11111111 11111111 11111111(在内存中存储的形式就是补码)
补1不变 补0变正数
真实的值:由补码推到原码就是
int a = -1;
int b = a >> 1; 得到a = -1
不要移动负数位代码
(3)位操作符:操作数必须都是整数
按位与: & (按二进制位进行与运算) 同0得0 同1得1 1和0得0 串
int a = 3; 00000000 00000000 00000000 00000011
int b = 5; 00000000 00000000 00000000 00000101
int c = a & b; 00000000 00000000 00000000 00000001
//得到c = 1
按位或: | 有1得1 否则为0 并
int a = 3; 00000000 00000000 00000000 00000011
int b = 5; 00000000 00000000 00000000 00000101
int c = a & b; 00000000 00000000 00000000 00000111
//得到c = 7
按位异或:^ 对应的二进制位进行异或 规则:相同为0 相异为1
int a = 3; 00000000 00000000 00000000 00000011
int b = 5; 00000000 00000000 00000000 00000101
int c = a & b; 00000000 00000000 00000000 00000110
//得到c = 6
(4)赋值操作符:
= += -= /= >>= <<= %= &= |= ^=
(5)单目操作符:
!(逻辑反操作:if(flag) if(!flag)就是取反的作用) - + & sizeof ~ ++ -- * (类型)
sizeof() 括号中的表达式不参与运算 表达式的类型是看左值的类型
~ 对一个数的二进制按位取反
-1:
原码10000000 00000000 00000000 00000001
反码11111111 11111111 11111111 11111110
补码11111111 11111111 11111111 11111111 反码+1 在内存中存储的是补码
~(-1)按位取反是 0
int a = 13
把a的二进制中的第五位置为1
a = a | (1 << 4)
把a的二进制中的第五位置为0
a = a & ~(1 << 4)
++ --:
int a = 10;
b = a++; 后增:先使用再加加 a = 11 b = 10
b = ++a; 先增:先加加后使用 a = 11 b = 11
判断先增后增的方法:加号在前面是先增,加号在后面是后增
*:
间接访问操作符(解引用操作符)
&:
取地址操作符 打印地址的方式 %p
int a = 10;
int * pa = &a; pa是用来存放地址的 pa就是一个指针变量也叫指针 这颗*告诉我们pa这是指针变量
*pa 里的*就叫做解引用操作符 或者 间接访问操作符
():
强制类型转换 int(c)
==:
相等 比较两个字符串不能使用等号
(6)逻辑操作符:
&& 逻辑与 || 逻辑或 != 逻辑非
1个是按位 与 2个是逻辑与
(7)条件操作符:也称为三元操作符
exp1 ? exp2 : exp3
if(a > 5) b = 1; else b = -1;
b = (a > 5) ? 1 : -1 max = (a>b? a:b)
逗号表达式:由逗号隔开的一串表达式
要从左向右计算
int d = (c = 5, a = c + 3, b = a - 4, c = c + 5); d = 10
整个表达式的结果只取决于最后一个表达的结果
例子:
a = getval();
count_val(a);
while(a>0)
{
a = get_val();
count_val(a);
...
}
改用逗号表达式后:
while(a = get_val(), count_val(a), a > 0) 1 2 判断作用的只有3
while
{
...
}
[]下标引用操作符:
int arr[10] = {1,2,3,4,5,6,7,8,9,10}
arr[5] 这个[]就是下标引用操作符 操作数是arr, 4
()函数调用操作符:
int a = 10;
int b = 20;
int ret = add(a, b); 这个()就是函数调用操作符
. ->结构成员访问操作符:
int float char double short
书:书名,书号,出版社,定价,作者
人:姓名,性别,年龄
struct book 定义结构体类型
{
char name[20];
char id[20];
int price;
}; 封号不能少
用类型创建了一本书
struct book b = {"c语言","c20220226", "66"};
printf("书名:%s\n书号:%s\n价格:%d\n", b.name, b.id, b.price);
struct book * pb = &b;
printf("书名:%s\n书号:%s\n价格:%d\n", b->name, b->id, b->price);
printf("书名:%s\n书号:%s\n价格:%d\n", (*pb).name, (*pb).id, (*pb).price);
//*pb就是对pb解引用,刚好就是b
表达式求值:
表达式求值的顺序一部分是由操作符的优先级和结合性决定
隐式类型转换:整型提升是按照变量的数据类型的符号位来提升的
内存中存储的是补码,而数值的大小是以原码来看 反码+1=原码
整型提升(p74-操作符详解 是难点)
值属性 类型属性
只要参与表达式运算就会发生整型提升
%u unsigned 打印无符号整数 sizeof返回的是一个无符号整数
%d 打印有符号整数
算术转换:
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行
int a = 4;
float f = 4.5f;
a + f; 需要int向float转换
操作符的属性:
操作符的优先级 操作符的结合性 是否控制结合顺序
static int count = 1;static的作用:隐藏 保持内容的持久 默认初始化为0
总结:我们写出的表达式不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。
sizeof:操作符计算变量类型所占内存大小,单位是字节 包括\0
strlen:函数,求字符串长度,找\0之前出现的字符个数
二、使用案例
1.交换两个int变量的值不能使用第三个变量
代码如下(示例):
#include <stdio.h>
int main(void)
{
/*
a = a + b;
b = a - b;
a = a - b; 遇到超过int能表示的最大值会溢出 -32678——32678
*/
int a = 3; //00000000 00000000 00000000 00000011
int b = 5; //00000000 00000000 00000000 00000101
printf("交换前:a = %d b = %d\n", a, b);
a = a ^ b; //00000000 00000000 00000000 000000110
b = a ^ b; //00000000 00000000 00000000 000000011 == 3
a = a ^ b; //00000000 00000000 00000000 000000101 == 5
printf("交换后:a = %d b = %d\n", a, b);
//异或没有产生进位,不会溢出 相同得0 不同得1
// a^b^b = a
return 0;
}
2.输出整数的二进制数中1的个数,其中负数用它的补码表示
代码如下(示例):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int number1(unsigned int n)
{
int count = 0;
while (n)
{
if (n % 2 == 1);
{
count++;
}
n /= 2;
}
return count;
}
int main(void)
{
/*
计算参数n的二进制补码中有几个1:
123得到10进制的每一位模10除10
123%10=3
123/10=12
12%10=2
12/10=1
1%10=1
1/10=0
15---00001111
15%2=1
15/2=7---00000111
7%2=1---00000001
7/2=3---00000011
3%2=1---00000001
1%2=1---00000001
1/2=0---00000000
*/
int n = -1;
int val = number1(n);
printf("val = %d\n", val);
return 0;
}
总结
整数的二进制表示形式:
。
原码:直接根据数值写出的二进制序列就是原码
反码:原码的符号位不变,其他位按位取反就是反码
补码:反码+1就是补码
-1
原码:10000000 00000000 00000000 00000001
反码:11111111 11111111 11111111 11111110
补码:11111111 11111111 11111111 11111111(在内存中存储的形式就是补码)
对于正整数来说原码 补码 反码相同