C语言中的操作符用于执行各种运算和操作,主要包括算术操作符、关系操作符、逻辑操作符、位操作符、赋值操作符等。

在计算机编程的学习中,其实我们经常能听到2进制、8进制、10进制、16进制 这样的讲法,那是什么意思呢?
其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。
比如:数值15的各种进制的表示方式
我们先重点讲解一下二进制
十进制图表

二进制图表

八进制的每一位数字都是由0~7组成,正所谓满8进1
位数 | 权重(8的幂) | 示例值 | 计算方式 |
|---|---|---|---|
第1位 | 8^0 = 1 | 1 | 1 * 8^0 |
第2位 | 8^1 = 8 | 2 | 2 * 8^1 |
第3位 | 8^2 = 64 | 3 | 3 * 8^2 |
第4位 | 8^3 = 512 | 4 | 4 * 8^3 |
第5位 | 8^4 = 4096 | 5 | 5 * 8^4 |
第6位 | 8^5 = 32768 | 6 | 6 * 8^5 |
第7位 | 8^6 = 262144 | 7 | 7 * 8^6 |
第8位 | 8^7 = 2097152 | 0 | 0 * 8^7 |
十六进制(Hexadecimal)是一种基数为16的计数系统,使用0-9的数字和A-F的字母来表示数值。
16进制位 | 权重(16^n) | 示例值 | 计算方式 |
|---|---|---|---|
0 | 16^0 = 1 | 0xF | 15 * 1 |
1 | 16^1 = 16 | 0xE | 14 * 16 |
2 | 16^2 = 256 | 0xD | 13 * 256 |
3 | 16^3 = 4096 | 0xC | 12 * 4096 |
4 | 16^4 = 65536 | 0xB | 11 * 65536 |



整数的2进制表示方法有3种,即原码、反码和补码
有符号整数的三种表示方法均有符号位和数值位两部分,2进制序列中,最⾼位的1位是被当做符号位,剩余的都是数值位。
符号位都是⽤0表示“正”,用1表示“负”。

正整数的原、反、补码都相同 负整数的三种表达方式各不相同
原码: 直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。 反码: 将原码的符号位不变,其他位依次按位取反就可以得到反码。 补码: 反码+1就得到补码。 补码得到原码也是可以使⽤:取反,+1的操作。
在计算机中,整数通常以补码存储,但理解补码需要先了解原码和反码。
C语言中的位操作符用于直接操作整数的二进制位。以下是C语言中常用的位操作符及其功能: 移位操作符的操作数只能是整数,并且是转换成二进制去运行的
<<左移操作符移位规则:左边抛弃,右边补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;
}
>>右移操作符移位规则:首先右移运算分为两种:
#include <stdio.h>
int main(){
int num = 10;
int n = num >> 1;
printf("n = %d\n",n);
printf("num = %d\n",num);
return 0;
}
对于移位运算符,不要移动负数位,这个标准是未定义的
&按位与|按位或^按位异或~按位取反 它们的操作数必须是整数
&两个位都为1时结果为1,否则为0;任何数跟 1 与,结果还是它自己;跟 0 与,结果一定是 0
unsigned char a = 5; // 0101
unsigned char b = 3; // 0011
unsigned char c = a & b; // 0001 (1)|两个位中至少有一个为1时结果为1,否则为0
unsigned char a = 5; // 0101
unsigned char b = 3; // 0011
unsigned char c = a | b; // 0111 (7)^两个位不同时结果为1,相同时为0
unsigned char a = 5; // 0101
unsigned char b = 3; // 0011
unsigned char c = a ^ b; // 0110 (6)~0变1,1变0
unsigned char a = 5; // 00000101
unsigned char b = ~a; // 11111010 (250)C语言还提供了位操作符与赋值操作符结合的复合运算符:
a &= b; // 等价于 a = a & b;a |= b; // 等价于 a = a | b;a ^= b; // 等价于 a = a ^ b;a <<= n; // 等价于 a = a << n;a >>= n; // 等价于 a = a >> n;#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d b = %d\n", a, b);
return 0;
}单目操作符(Unary Operators)是只需要一个操作数的操作符。
操作数类型:标量类型(非零值变为0,零值变为1)
操作数类型:整数类型
操作数类型:左值(可修改的对象)
操作数类型:指针类型
操作数类型:任意可转换的类型
这一节的内容跟
数组和指针的关系很大
[]操作数: 一个数组名 + 一个索引值[下标
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
[ ]的两个操作数是arr和9接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数。
#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;
}优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是 不⼀样的。
3 + 4 * 5;上⾯⽰例中,表达式 3 + 4 * 5⾥⾯既有加法运算符( +),⼜有乘法运算符(*)。由于乘法
的优先级⾼于加法,所以会先计算4 * 5,⽽不是先计算3 + 4。
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )。
5 * 6 / 2;上⾯⽰例中, * 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 ,
再计算 / 2 。
运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概
记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看下⾯表格就可以了。
由于圆括号的优先级最⾼,可以使⽤它改变其他运算符的优先级。
整型提升(integer promotion) 是指当表达式中使用比int类型小的整型(如char、short)时,这些值会被自动转换为
int或unsigned int类型后再参与运算的规则。
如果原始类型的所有值都能用int表示,则提升为int,否则提升为unsigned int
如果原始类型的所有值都能用int表示,则提升为int,否则提升为unsigned int
#include <stdio.h>
int main() {
char a = 120;
char b = 20;
char c = a + b;
printf("%d\n", c); // 输出 -116
return 0;
}为什么最后c的值是-116呢
这个问题涉及到 C语言中的整型提升、溢出和补码表示
提升后: a(120)→ int 类型的 120 b(20)→ int 类型的 20 计算 a + b 得到 140(仍然是 int 类型)。
如何计算截断后的值?
140超出char的表示范围,计算方式如下:
最高位 1 表示负数。
取反:0b10001100 → 0b01110011 加 1:0b01110011 + 1 = 0b01110100(116) 所以 0b10001100 表示 -116。
由于某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型
int i = 5;
float f = 3.14;
double d = i + f; // i先转换为float,相加后再转换为double
unsigned int u = 10;
int s = -5;
long result = u + s; // s转换为unsigned int,可能导致意外结果这里我们把操作符一些残枝末节给收拾干净,避免给以后的学习留下盲点
C 语⾔⽤于⽐较的表达式,称为 “关系表达式”,⾥⾯使⽤的运算符就称 为“关系运算符”
>⼤于运算符<⼩于运算符>=⼤于等于运算符<=⼩于等于运算符==相等运算符!=不相等运算符关系表达式通常返回 0 或 1 ,表⽰真假。
注意: 相等运算符==与赋值运算符=是两个不⼀样的运算符,不要混淆。
为了防止出现这种错误,有的程序员喜欢将变量写在等号的右边。
if (3 == x) ... 1这样的话,如果把 == 误写成 = ,编译器就会报错。
/* 报错 */
if (3 = x) ...i < j < k上⾯示例中,连续使用两个小于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果, 实际执行的是下面的表达式。
(i < j) < k 1上⾯式⼦中, i < j 返回 0 或 1 ,所以最终是 0 或 1 与变量 k 进⾏⽐较。
如果想要判断变量 j的值是否在 i 和 k 之间,应该使⽤下⾯的写法。
i < j && j < k条件操作符也叫三⽬操作符,需要接受三个操作数的,形式如下:
exp1 ? exp2 : exp3条件操作符的计算逻辑是: 如果 exp1 为真, exp2 计算,计算的结果是整个表达式的结果; 如果exp1 为假, exp3 计算,计算的结果是整个表达式的结果。
C语⾔逻辑运算符还有⼀个特点,它总是先对左侧的表达式求值,再对右边的表达式求值,这个顺序是保证的。
如果左边的表达式满⾜逻辑运算符的条件,就不再对右边的表达式求值。这种情况称为短路。
if(month >= 3 && month <= 5) 表达式中&& 的左操作数是 month >= 3 ,右操作数是 month <= 5 ,当左操作数 month >= 3 的结果是0的时候,即使不判断 month <= 5 ,整个表达式的结果也是0(不是春季)。 所以,对于&&操作符来说,左边操作数的结果是0的时候,右边操作数就不再执⾏。
if(month == 12 || month == 1 || month == 2) 1如果month == 12,则不⽤再判断month是否等于1或者2,整个表达式的结果也是1。 所以,
||操作符的左操作数的结果不为0时,就⽆需执⾏右操作数。
像这种仅仅根据左操作数的结果就能知道整个表达式的结果,不再对右操作数进⾏计算的运算称为短路求值。
由于这一章的很多内容小编已经在前面讲过了,所以有些内容会一笔带过 并且这一章的内容只是给C语言的分支与循环的程序练习作下铺垫
小编提醒 由于操作符的繁杂和多样性,所以在编程时候尽量不要写太乱的代码,以免程序发生错乱