C语言操作符详解

文章目录

前言

用一篇文章彻底总结C语言操作符。请耐心看完哦!!!(该博客适合复习时候,建立框架)

一、操作符的分类

算数操作符:+-*/%

移位操作符:<<>>

位操作符:&|^

赋值操作符:=+=-=*=/=%=<<=>>=&=|=^=

单目操作符;!++--&*+-~sizeof(类型)

关系操作符:>>=<<===!=

逻辑操作符:&&||

条件操作符:?:

逗号表达式:,

下标引用:[]

函数调用:()

结构成员访问:.->

二、二进制和进制转换

其实我们经常能听到2进制、8进制、10进制、16进制这样的讲法是有什么深刻的含义吗?

其实2进制、8进制、10进制、16进制是数值的不同表现形式罢了。

比如数值15的各种进制的表现形式:

  1. 152进制:1111
  2. 158进制:17
  3. 1510进制:15
  4. 1516进制:F

我们重点解释一下二进制:

首先我们还是得从10进制讲起,其实10进制是我们生活中经常使用的,我们已经形成了很多尝试;

  • 10进制中满10进1
  • 10进制的数字每一位都是由0~9的数字组成
    其实二进制也一样
  • 2进制中满2进1
  • 10进制的数字每一位都是由0~1的数字组成
    注意;这几种进制只是最常用还有其他进制的,例如星期是7进制,月份是12进制

三、原码,反码,补码

四、算数操作符

这些操作符都是双目操作符
注:操作符也被叫做运算符是不同的翻译,意思是一样的

4.1+-

+-用来完成加法和减法。
+-都是有两个操作数的,位于操作符两端的就是它们的操作数,这种操作符也叫做双目操作符。

c 复制代码
#include<stdio.h>
int main()
{
	printf("%d\n", 35 + 35);
	int a = 20;
	int b = 20;
	printf("%d\n", a + b);
	printf("%d\n", a + 33);
	printf("%d\n", a - b);
	printf("%d\n", a - 33);

	return 0;
}

4.2*

*用来完成乘法。

同上,*也是双目操作符。

c 复制代码
#include<stdio.h>
int main()
{
	printf("%d\n", 35 * 35);
	int a = 20;
	int b = 20;
	printf("%d\n", a * b);
	printf("%d\n", a * 33);
	
	return 0;
}

4.3/

/用来完成除法。
除号的两端如果都是整数,执行的是整数除法,如果要计算出小数的效果,那么除号的两端至少有一个数得是浮点数

同上,/也是双目操作符。

如果计算小数除法需要限制几位小数只需%lf在lf前加上.加上数字,就是几位小数。、

数字小数默认是double类型,如果需要float类型,可以使用强转,或者在数字后面加上f

c 复制代码
#include<stdio.h>
int main()
{
	printf("%d\n", 35 / 35);
	int a = 20;
	int b = 20;
	printf("%d\n", a / b);
	printf("%lf\n", a / 33.0);
	
	return 0;
}

4.4%

%用来完成求模运算,即返回两个整数相除的余数。这个运算符只能用于整数,不能用于浮点数。

同上,%也是双目操作符。

c 复制代码
#include<stdio.h>
int main()
{
	int a = 7 / 2;
	int b = 7 % 2;

	printf("%d\n", a);
	printf("%d\n", b);
 	
	return 0;
}

负数求模的规则是,结果的正负号由第一个运算数的正负号决定。

c 复制代码
#include<stdio.h>
int main()
{
	printf("%d\n", 11 % -5);
	printf("%d\n", -11 % -5);
	printf("%d\n", -11 % 5);

	return 0;
}

五、移位操作符

<<左移操作符
>>右移操作符

==注:==移位操作符的操作数只能是整数

5.1左移操作符

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

c 复制代码
#inlcude<stdio>

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

5.2右移操作符

移位规则:

逻辑右移:右边抛弃,左边补0

算术右移:右边抛弃,左边补符号位

c 复制代码
#inlcude<stdio>

int main()
{
	int a = 10;
	int b = a >> 1;
	printf("%d\n",a);
	printf("%d\n",b);
	return 0;
}

逻辑右移:

算术右移:

c 复制代码
#inlcude<stdio>

int main()
{
	int a = -10;
	int b = a >> 1;
	printf("%d\n",a);
	printf("%d\n",b);
	return 0;
}

逻辑右移:

算术右移:

==警告:==位移操作符不要移动负数位,这是标准未定义行为

c 复制代码
a = 10;
b = a << -1//error

六、位操作符

6.1&

该操作符是按位与,具体计算过程是将补码的每一位进行与计算

例:

c 复制代码
-3 & 5;

-3的补码是111111015的补码是00000101然后按位与计算就是一位一位进行与操作,比如说-3的第一位是1,5的第一位是0,那么进行与操作计算结果的第一位就是0然后一直到最后一位,最后的结果是00000111这个是补码,转化为10进制就是7

6.2|

该操作符是按位或,具体计算过程可以参照&操作符的过程,就是对每一位进行或操作。

6.3^

该操作符是按位异或,具体计算过程可以参照&操作符的过程,就是对每一位进行异或操作。(相同时为1,不同时为0。)

6.4~

该操作符是按位取反,具体计算过程可以参照&操作符的过程,就是对每一位进行取反操作。

七、赋值操作符

7.1=

在变量创建的时候给一个初始值叫做初始化,在变量创建好后,再给一个值,这叫做赋值。

c 复制代码
int a = 100;//初始化
a = 200;//赋值,这里使用的就是赋值操作符

赋值操作符是一个随时可以给变量赋值的操作符。

7.1.1连续赋值

赋值操作符也可以连续赋值,如:

c 复制代码
int a = 3;
int b = 5;
int c = 0;
c = b = a+3;//连续赋值,从右向左一次赋值。

c语言虽然支持那种连续赋值,但是写出的代码不容易理解(不推荐连续赋值),建议还是拆开来写,这样方便观察代码的执行细节(方便阅读代码或者找出错误)。

7.2复合赋值

在写代码的时候,我们经常可能对一个数进行自增或自减如下代码

c 复制代码
int a = 10;
a = a + 3;
a = a - 2;

这样代码c语言给提供了更加简便的方法

c 复制代码
int a = 10;
a += 3;
a -= 2;

c语言提供了复合赋值符,方便我们编写代码,这些赋值符有:

c 复制代码
+= 
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

八、单目操作符

单目操作符 ------ 只有一个操作数
++--+(正),-(负)!(逻辑非),&(取地址操作符),*(解引用操作符),~(按位取反),sizeof(类型)就是单目操作符。

8.1++--

++是一种自增操作符,又分为前置++和后置++

8.1.1前置++
c 复制代码
int a = 10;
int b = ++a;
printf("a = %d,b = %d",a,b);

使用口诀:先+1,后使用

a原来是10,先+1,后a变成11,再使用就是赋值给b,b得到的也是11,所以最后a,b都变成11了

相当于下面的代码

c 复制代码
int a = 10;
a = a + 1;
b = a;
printf("a = %d,b = %d",a,b);
8.1.2后置++
c 复制代码
int a = 10;
int b = a++;
printf("a = %d,b = %d",a,b);

使用口诀:先使用,后+1

a原来是10,先使用就是赋值给b,b得到的是10,然后再+1就是a+1,所以最后a变成11了,

而b是10

相当于下面的代码

c 复制代码
int a = 10;
b = a;
a = a + 1;
printf("a = %d,b = %d",a,b);
8.1.3前置--
c 复制代码
int a = 10;
int b = --a;
printf("a = %d,b = %d",a,b);

使用口诀:先-1,后使用

a原来是10,先-1,后a变成9,再使用就是赋值给b,b得到的也是9,所以最后a,b都变成9了

相当于下面的代码

c 复制代码
int a = 10;
a = a - 1;
b = a;
printf("a = %d,b = %d",a,b);
8.1.4后置--
c 复制代码
int a = 10;
int b = a--;
printf("a = %d,b = %d",a,b);

使用口诀:先使用,后-1

a原来是10,先使用就是赋值给b,b得到的是10,然后再-1就是a-1,所以最后a变成9了,

而b是10

相当于下面的代码

c 复制代码
int a = 10;
b = a;
a = a - 1;
printf("a = %d,b = %d",a,b);

8.2+-

这里的+是正号,-是负号,都是单目操作符。

运算符+对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。

c 复制代码
int a = +10;
int a = 10;

上面的两个a是等价的;

运算符-用来改变一个值的正负号,负数的前面加上-就会得到正数,正数的前面加上-会得到负数。

c 复制代码
int a = 10;
int b = -a;
int c = -10;
printf("b = %d c =  %d\n",b,c);

int a = -10;
int b = -a;
printf("b = %d",b); 

8.3!

该操作符是逻辑非,

例: while(!0)

九、关系操作符

c语言用于比较的表达式,称为"关系表达式"(relational expression),里面使用的运算符就成为"关系运算符"(relational expression),主要有下面6个。

  • >大于运算符
  • <小于运算符
  • >=大于等于运算符
  • <=小于等于运算符
  • ==相等运算符
  • !=不相等运算符
    下面举一些例子
c 复制代码
a == b;
a != b;
a < b;
a > b;
a <= b;
a >= b;

关系表达式通常返回01,表示真假。

C语言中,0表示假,所有非零值表示真。比如,20 > 12返回112>20返回0

关系表达式常用于ifwhile结构。

c 复制代码
if(x == 3)
{
	printf("x is 3.\n");
}

注意:相等运算符==与赋值运算符=是两个不一样的运算符,不要混淆。有时候,可能不小心写出下面的代码,它可以运行,但很容易出现意料之外的结果。

c 复制代码
if( x = 3);

上面示例中,原意是x == 3,但是不小心写成 x = 3,这个式子表示对变量x赋值3,它的返回值为3,所以if判断总是为真。为了防止出现这种错误,有的程序员喜欢将变量写在等号的右边。

c 复制代码
if(3 == x);

这样的话,如果把==误写成=,编译器就会报错。

c 复制代码
if(3 = x)

另一个需要避免的错误是:多个关系运算符不宜连用。

c 复制代码
i<j<k

上面示例中,连续使用两个小于运算符。这是合法表达式,不会报错,但是通常达不到想要的结果,即不是保证变量的值在ik之间。因为关系运算符是从左到右计算,所以实际执行的是下面的表达式。

c 复制代码
(i < j) < K;

上面式子中i < j返回01,所以最终是01与变量k进行比较。如果想要判断变量j的值是否在ik之间,应该使用下面的写法。

c 复制代码
i < j && j < k;

十、条件操作符

条件操作符也叫三目操作符,需要接受三个操作数的,形式如下:

c 复制代码
exp1 ? exp2 : exp3;

条件操作符的计算逻辑是:如果exp1为真,exp2计算,计算的结果是整个表达式的结果,如果exp1为假,exp3计算,计算的结果是整个表达式的结果。

十一、逻辑操作符

逻辑运算符提供逻辑判断功能,用于构建更复杂的表达式,主要有下面三个运算符。

  • !:逻辑取反运算符(改变单个表达式的真假)
  • &&:与运算符,就是并且的意思(两侧的表达式都为真,则为真,否则为假)
  • || :或运算符,就是或者的意思(两侧至少有一个真,则为真,否则为假)
    注:C语言中,非0为真,0为假

11.1逻辑取反运算符!

11.2逻辑与运算符&&


&&就是与运算符,也是并且的意思,&&是一个双目操作符,使用的方式是a&&b&&两边的表达式都是真的时候,整个表达式才为真,只要有一个是假,则整个表达式为假。

11.3逻辑或运算符||


||就是或运算符,也是一个双目操作符,使用方式是a||b,||两边的表达式只要有一个是真的,整个表达式就是真,两边的表达式都为假的时候,才为假。

十二、逗号表达式

逗号表达式,就是用逗号隔开的多个表达式。

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

c 复制代码
int main(){
int a = 1;
int b = 2;
int c = (a>b,a=b+10,a,b=a+1);
printf("%d\n",c); 
return 0;
}

十三、下表访问[],函数调用()

13.1[]

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

c 复制代码
int arr[10];
arr[9] = 10;

[]的两个操作数是arr和9。

13.2()

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

十四、结构体成员访问操作符

14.1结构体

C语言已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的。描述一个学生需要名字、年龄、学号、身高、体重等;描述一本书需要作者、出版社、定价等。C语言为了解决这个问题,增强了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型。

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

14.1.1结构的声明
c 复制代码
struct tag
{
	member-list;
}variable-list;

14.2结构体访问操作符

14.2.1 结构体成员的直接访问.

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

c 复制代码
struct Point 
{
	int x;
	int y;
}  p = {1,2};
int main()
{
	printf("x:%d\n,y:%d\n",p.x,p.y);
	return 0;
}

14.2.2 结构体成员的间接访问 ->

结构体成员的间接访问通过箭头操作符 -> 实现,主要用于结构体指针 访问成员。其语法为 结构体指针->成员名,本质上是 (*结构体指针).成员名 的简化写法。

c 复制代码
struct Point 
{
    int x;
    int y;
};

int main()
{
    struct Point p = {3, 4};
    struct Point *ptr = &p;  // 定义结构体指针并指向变量p
    
    // 通过箭头操作符修改成员
    ptr->x = 10;  // 等价于 (*ptr).x = 10
    ptr->y = 20;  // 等价于 (*ptr).y = 20
    
    printf("x: %d\n", ptr->x);  // 输出x: 10
    printf("y: %d\n", ptr->y);  // 输出y: 20
    return 0;
}

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

15.1 优先级

操作符优先级决定表达式中运算的执行顺序。优先级高的操作符先于优先级低的操作符进行计算。常见优先级等级如下(从高到低):

  1. 括号与成员访问
    ()函数调用、[]数组下标、.结构体成员、->指针成员访问

    c 复制代码
    struct Point p = {3,4};
    int val = (p.x + 5) * 2;  // 括号优先计算
  2. 单目操作符
    ++ --(前缀)、!逻辑非、~按位取反、*解引用、&取地址

    c 复制代码
    int a = 10;
    int *ptr = &a;
    *ptr += 5;  // 先解引用再赋值
  3. 乘除取模
    *乘法、/除法、%取模

    c 复制代码
    int result = 5 + 3 * 2;  // 3*2先计算,结果为11
  4. 加减与位移
    + -(二元)、<<左移、>>右移

    c 复制代码
    int val = 10 << 2 + 1;  // 先计算2+1,再左移3位(10<<3=80)
  5. 关系与位运算
    < >比较、&按位与、^按位异或、|按位或

  6. 逻辑运算符
    &&逻辑与、||逻辑或

  7. 赋值操作符
    = += -=等复合赋值符(优先级最低)


15.2 结合性

当多个同级操作符相邻时,结合性决定运算方向。主要分为两类:

  1. 左结合(从左到右计算)

    • 算术运算符:+ - * /
    • 关系运算符:< > ==
    c 复制代码
    int a = 5 * 3 / 2;  // 先计算5*3=15,再15/2=7(整数除法)
  2. 右结合(从右到左计算)

    • 赋值操作符:= +=
    • 单目运算符:++ --(后缀)
    c 复制代码
    int a, b;
    a = b = 10;  // 先b=10,再a=b

优先级与结合性冲突案例

c 复制代码
struct Stu *p = &s;
(*p).age = 20;  // 必须加括号:*的优先级低于.

此处若写成*p.age会被解析为*(p.age)导致错误。


十六、表达式求值

16.1 求值规则

表达式求值受三个因素影响:

  1. 操作符优先级

    c 复制代码
    int x = 5 + 3 * 2;  // 3*2先计算,结果为11
  2. 操作符结合性

    c 复制代码
    int y = 10 - 5 - 3;  // 左结合:10-5=5,再5-3=2
  3. 求值顺序的不确定性

    某些情况下,操作数的求值顺序未定义(如函数参数):

    c 复制代码
    printf("%d %d", ++i, i++);  // 结果可能因编译器而异

16.2 短路求值特性

逻辑运算符&&||具有短路特性:

c 复制代码
int a = 0, b = 5;
if (a != 0 && b / a > 2) {  // a=0时直接跳过b/a的运算
    // 避免除零错误
}

16.3 顺序点(Sequence Point)

顺序点是程序中确保副作用(如变量修改)已完成执行的节点:

  • 分号 ;
  • 逻辑运算符 && ||
  • 逗号操作符 ,
  • 函数调用(参数计算完成后)
c 复制代码
int i = 0;
int j = i++ + i++;  // 未定义行为:两个i++之间无顺序点

16.4 表达式求值注意事项

  1. 避免复杂表达式

    拆分多步骤运算以提高可读性:

    c 复制代码
    // 难读的表达式
    int result = (a << 3) | (b & 0xFF) + c * 2;
    // 拆解后
    int temp1 = a << 3;
    int temp2 = b & 0xFF;
    int result = temp1 | (temp2 + c * 2);
  2. 显式使用括号

    即使优先级明确,也可用括号增强可维护性:

    c 复制代码
    int val = (a > b) && (c != 0);  // 比a > b && c !=0更清晰
  3. 警惕未定义行为

    如修改同一变量多次的表达式:

    c 复制代码
    int i = 5;
    int x = i++ + i++;  // 未定义行为

结语

以上为本人总结,如果有错误,请私信我,我会及时纠正。

相关推荐
CYRUS_STUDIO5 分钟前
安卓实现魔改版 CRC32 算法
android·算法·安全
zhu_superman42 分钟前
c语言中的未定义行为
c语言·开发语言
阿楠小波1 小时前
蓝桥杯嵌入式组第七届省赛题目解析+STM32G431RBT6实现源码
c语言·stm32·单片机·学习·蓝桥杯
四念处茫茫1 小时前
【C语言系列】字符函数和字符串函数
c语言·开发语言·visual studio
加减法原则1 小时前
LeetCode刷题 | 合并区间问题
算法
奋进的小暄2 小时前
贪心算法(5)(java)k次取反后最大化的数组和
java·算法·贪心算法
qq_332539452 小时前
Akamai 与 AWS 风控分析与绕过技术探讨
爬虫·算法·安全·云计算·网络爬虫·aws
Doopny@2 小时前
含k个3的数(信息学奥赛一本通-1090)
数据结构·算法
绵绵细雨中的乡音2 小时前
动态规划-第2篇
c++·算法·动态规划
念故思旧3 小时前
【最长递增子序列】【LeetCode算法】【c++】【动态规划】
c++·算法·leetcode·动态规划