C语言操作符详解

本文详细介绍了C语言中的各种操作符,包括算术、移位、位、赋值、单目、关系、逻辑、条件和逗号操作符。特别讨论了移位操作符(<<和>>)以及位操作符(&、|、^)的工作原理,强调了它们在处理二进制数时的注意事项。还探讨了单目操作符如逻辑非(!)、取地址符(&)、按位取反(~)以及指针解引用(*)的使用。此外,文章还讲解了条件操作符(三目操作符)和逗号表达式的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 算术操作符

 + 			-			*			/			%
  1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。

  2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

  3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

2. 移位操作符

2.1 整数二进制的表现形式

  • 有三种表现形式:原码、反码、补码
    (正整数的原码、反码、补码都是相同的)
    (负整数的原码、反码、补码是要计算的)
  • 原码:根据正负直接写出的二进制序列就是原码
    (不管正整数还是负整数都可以直接写出二进制原码)
  • 反码:原码的符号位(0为正1为负)不变,其他的按位取反,得到的就是反码
  • 补码:反码+1就是补码
    (整数在内存中存储的是补码)
    (计算机也是用补码计算的)
  • 1个整数是4个字节 = 32 bit位
正整数:10
原码:00000000000000000000000000001010
反码:00000000000000000000000000001010
补码:00000000000000000000000000001010
负整数:-10
原码:10000000000000000000000000001010
反码:11111111111111111111111111110101
补码:11111111111111111111111111110110

2.2 ‘’<<''左移操作符

<< —左移操作

  • 算术左移:
    将一个二进制数向左移动指定的位数,并在最低位补0。在算术左移中,符号位不变,因此可以保持负数的符号不变
  • 逻辑左移:
    将一个二进制数向左移动指定的位数,并在最低位补0。在逻辑左移中,符号位也会被移动,因此可能会改变负数的符号
  • 左移 << 每移一位相当于乘2
    在这里插入图片描述
    在这里插入图片描述
    警告⚠ :
    对于移位运算符,不要移动负数位,这个是标准未定义的。

2.3 ">>''右移操作符

”>>"—右移操作符

  • 算术右移:
    将一个二进制数向右移动指定的位数,并在最高位补符号位。在算术右移中,符号位不变,因此可以保持负数的符号不变
  • 逻辑右移:
    逻辑右移是指将一个二进制数向右移动指定的位数,并在最高位补0。在逻辑右移中,符号位也会被移动,因此可能会改变负数的符号
  • C语言中没有明月规定到时算术右(左)还是逻辑右(左),一般编译器上采用的是算术右(左)
  • 右移 >> 每一移位相当于正整数除2取整(负数则取整后在减一)
    在这里插入图片描述
    在这里插入图片描述
    警告⚠ :
    对于移位运算符,不要移动负数位,这个是标准未定义的。

3. 位操作符

3.1 ‘’&''按位与

& —按位与

  • 按位与:对应二进制有0则为0,俩个同时为1则为1
    在这里插入图片描述
    在这里插入图片描述
    以上俩张图只有在计算的时候,要注意操作数的正负,原码、反码、补码和正负整数之间的关系

3.2 ‘’|''按位或

| —按位或

  • 按位或:对应的二进制位有1则为1,俩个同时为0则为0
    在这里插入图片描述
    在这里插入图片描述

3.3 "^''按位异或

^ —按位异或

  • 按位异或:对应的二进制位相同为0,相异为1
  • 这里利用一个题目来促进理解:
定义俩个整型变量,并且交换俩个数的值
//这里只显示main()部分的函数内容
int main()//方法一
{
    int a = 1;
    int b = 2;
    int tem;
    //换值
    tem = a;
    a = b;
    b = a;
    printf("a=%d\n",a);
    printf("b=%d\n",b);
    return 0;
}
//这里只显示main()部分的函数内容
int main()//方法二
{
    int a = 1;
    int b = 2;
    //换值
    a = a + b;//将a+b(3)的值赋给a(3)
    b = a - b;//用a(3)的值减b(2),得到1,再赋值给b
    a = a - b;//用a(3)的值减b(1),得到2,再赋值给a
    printf("a=%d\n",a);
    printf("b=%d\n",b);
    return 0 ;
}

这里附上方法二的结果显示:
在这里插入图片描述
接下来要用的就是我们刚刚学的这个"^"按位异或操作符的用法:

//这里只显示main()部分的函数内容
int main()//方法三
{
    int a = 1;
    int b = 2;
    //换值
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    printf("a=%d\n",a);
    printf("b=%d\n",b);
    return 0;
}

你可能看到一个这样的代码很蒙,别急先看代码运行结果:
在这里插入图片描述

//这里只拿换值部分的代码来解读
值:	3				1				2
代码:	a		=		a		^		b
二进制 :011		       001			   010
	   //根据''^''的使用规则,可以知道a ^ b == 001 ^ 010 == 011 == 3
————————————————————————————————————————————————————————————————————
值:	1				3				2
代码:	b		=		a		^		b
二进制 :001		       011			   010
       //根据''^''的使用规则,可以知道a ^ b == 011 ^ 010 == 001 == 1
————————————————————————————————————————————————————————————————————       
值:	2				3				1
代码:	a		=		a		^		b
二进制 :010		       011			   001
       //根据''^''的使用规则,可以知道a ^ b == 011 ^ 001 == 010 == 2

其实"^"按位异或在这个代码中就是一个规律:

								c = a ^ b
								a = c ^ b
								b = a ^ c

这样理解的话是不是就简单多了呢?不急接下来还有"^"按位异或的特性没说呢


代码:	b		==		a		^		a		^		b
二进制:	|			   001 			   001			   010
过程:	|				|_______________|               |
        |                        |						|
        |                       000  					|
        |                        |______________________|
        |                       			|
        |——————————————————————————————————010 
———————————————————————————————————————————————————————————————————
代码:   b		==		a		^		b		^		a
二进制:	|			   001			   010			   001
过程:	|				|_______________|               |
        |                        |						|
        |                       011  					|
        |                        |______________________|
        |                       			|
        |——————————————————————————————————010 
  • 根据这个流程图,应该就可以看出:
  1. "^"按位异或是支持交换律的
  2. 俩个相等的值按位异或的话得到的一定是0
  3. 任何数按位异或0得到都是那个数的本身值

4. 赋值操作符

+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

以上就是c语言中所有的赋值操作符了,具体的使用也是非常的简单:

	a += b	/	a = a + b
	a -= b	/	a = a - b
	a *= b	/	a = a * b
	a /= b	/	a = a / b
	a %= b	/	a = a % b
	a >>= b	/	a = a >> b
	a <<= b	/	a = a << b
	a &= b	/	a = a & b
	a |= b	/	a = a | b
	a ^= b	/	a = a ^ b

以上就是赋值操作符的用法!

5. 单目操作符

5.1 单目操作符介绍

  • 单目操作符就是只有一个操作数的控制的操作符

5.1.1 "!''逻辑反操作符

!—逻辑反操作符

  • 逻辑反操作符:它的作用是将一个布尔值取反,即将 true 变为 false,将 false 变为 true。(如果一下子不知道是布尔值什么意思,别急你暂时就先理解成反义词的概念,本来是"大"加个 “!” 就变成"小"的意思了,后面的小结我会解释布尔值、false、true的意思)
  • 来个代码促进理解:
#include<stdio.h>
int main()
{
    int num = 0;
    do
    {
        num++;
    } while (!num);//num=0,!num就不为0,反而当num不为0时,!num就为0
    //应为在C语言中0为假,非0为真
    printf("num = %d\n", num);
    return 0;
}

代码结果为:

num = 1

  • 一个代码不够那我们就再来一个:
#include<stdio.h>
int main()
{
    int num = 0;
    while (num != 10)//当num不等于10的时候为真就进入循环,否则退出
    {
        num++;
    }
    printf("num = %d\n", num);
    return 0;
}

代码结果为:

num = 10

这样俩个例子我觉得应该对"!"反逻辑操作符应该有个较为具体的认识了

5.1.2 ‘’-‘’、‘’+''负值、正值

  	-							+

就和数学中的一样,加减的的操作符

5.1.3 ‘’&''取地址符

& -----取地址符

  • 取地址符:配合scanf函数、指针、数组传参…的使用,向计算机获取变量的内存地址
  • 配合scanf函数的使用:
#include<stdio.h>
int main()
{
    int a;
    scanf("%d", &a);
    printf("a = %d\n", a);
    return 0;
}

代码结果为:

5
a = 5

  • 指针的使用:
#include<stdio.h>
int main()
{
    int a;
    int* p;//这是定义指针变量的,指针变量是专门存放地址的
    scanf_s("%d", &a);
    p = &a;//将a的地址取出来,存放在指针变量p中
    printf("a = %d\n", a);
    printf("&a = %p\n", p);
    printf("p = %p\n", p);
    return 0;
}

代码结果为:

5
a = 5
&a = 00000049984FF934
p = 00000049984FF934

  • 数组传参:
#include<stdio.h>
int add(int arr[], int sz)//自定义函数
{
    int i;
    for (i = 0; i < sz; i++)
    {
        arr[i] = sz;
    }
}
int main()
{
    int i;
    int arr[] = { '1','2','3','4','5','6','7','8','8','10' };
    int sz = sizeof(arr) / sizeof(arr[0]);//sizeo是一个操作符,计算数组的内存大小
    add(arr, sz);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

代码结果为:

10 10 10 10 10 10 10 10 10 10 10

5.1.4 ‘’~''对一个数的二进制(补码)按位取反

~ —对一个数的二进制(补码)按位取反

看代码:

#include<stdio.h>
int main()
{
    int a = 0;
    printf("%d", ~a);
    return 0;
}

代码结果为:

-1

解析:

a = 0
补码:00000000000000000000000000000000
按位取反
补码:11111111111111111111111111111111
反码:11111111111111111111111111111110
原码:10000000000000000000000000000001
所以 ~a = -1

5.1.5 ‘’–‘‘前置、后置"–"、’’++''前置、后置"++"

--a 前置-- 
a-- 后置--
++a 前置++
a++ 后置++
  • 前置与后置的区别:一个是先计算后–/++,一个是先–/++后计算
  • 前置++看代码:
#include<stdio.h>
int main()
{
    int a = 0;
    int b = 0;
    b = ++a;//前置++
    printf("a=%d\n", a);
    printf("b=%d\n", b);
    return 0;
}

代码结果为:

a=1
b=1

  • 后置++看代码:
#include<stdio.h>
int main()
{
    int a = 0;
    int b = 0;
    b = a++;//后置++
    printf("a=%d\n", a);
    printf("b=%d\n", b);
    return 0;
}

代码结果为:

a=1
b=0

  • 根据上面俩个代码应该就可以看出前置与后置的区别了

5.1.6"*''间接访问操作符(解引用操作符)

 *  解引用操作符
  • 解引用操作符:搭配指针变量,创建指针变量时、访问指针变量中存储的内存地址所对应的值
    *看代码:
#include<stdio.h>
int main()
{
    int a = 9;
    int* p;
    p = &a;
    printf("p=%p\n", p);
    printf("a=%d\n", a);
    printf("*p=%d\n", *p);
    return 0;
}

代码结果为:

p=00000010DDAFFCF4
a=9
*p=9

  • int*——>定义一个指针类型的变量
  • int** ——>定义一个二级指针类型的变量
    int* p——>创建(一级)指针变量p
    **p——>"*"可以访问p中存储的地址,并且取出该地址中存的数据

5.1.7 "(类型)''强制类型转换

(类型)强制转换类型

看代码:

#include<stdio.h>
int main()
{
    double a = 3.14;
    printf("a=%d", (int)a);
    return 0;
}

代码结果为:

a=3

  • 要注意的是强制转换类型的使用格式是:
    (变量名的前方)

(int)a
(char)a
(float)a

都是在变量前面加括号并且放入你要强制转换的类型名称,但是你要注意的时候,这种强制类型会使数据发生截断:

#include<stdio.h>
int main()
{
    double a = 3.14;
    printf("double a=%fl",a);
    printf("a=%f", (int)a);
    return 0;
}

代码结果为:

double a=3.140000l
a=3

可以看到a的数据在转换成int类型以后,就丢失了小数点后面的数值,这就是截断。
所有,非必要不使用强制转化类型

5.2 sizeof 和 数组

sizeof 操作符
数组 == 数组名 + [ ]

  • sizeof 操作符:计算的是变量或类型所占用的内存字节数(包含’\0’)
    (函数strlen():计算的是字符串中字符的个数(不包含’\0’))
  • 数组:数组名 + [ ]

6. 关系操作符

>
>=
<
<=
!=   用于测试“不相等”

==   用于测试“相等”

7. 逻辑操作符

7.1 "&&''逻辑与

&& 逻辑与

  • 逻辑与:一假为假,两真为真

表达式1 && 表达式2

只要表达式1和表达式2中有一个为假,那么(表达式1 && 表达式2) 返还一个0(假)

  • 看代码:
#include<stdio.h>
int main()
{
    int a = 0;
    int b = 1;
    printf("%d", a && b);
    return 0;
}

代码结果为:

0

在计算机中0表示假,非0表示真

  • 但在真正的用途里面大多是这样的:
#include<stdio.h>
int main()
{
    int a = 1;
    int b = 10;
    if (a && b)
    {
        printf("Hello !\n");
    }
    a--;
    if (a && b)
    {
        printf("Hello Hi!\n");
    }
    return 0;
}

代码结果为:

Hello !

如果还不懂你还可以再看个代码:

#include<stdio.h>
int main()
{
    int a = 1;
    int b = 2;
    int c = 3;
    int d = 4;
    if (a > b && c > d)//假 && 假
    {
        printf("Hello !\n(假 && 假)");
    }
    if (a < b && c < d)//真 && 真
    {
        printf("Hello !(真 && 真)\n");
    }
    if (a < b && c > b)//真 && 假
    {
        printf("Hello !(真 && 假)\n");
    }
    return 0;
}

代码结果为:

Hello !(真 && 真)

&& 逻辑与操作符俩边但凡有一个是假的,那么最终的返还值就是0,必须俩边都是真的才能返回一个1

7.2 ‘’||''逻辑或

|| 逻辑或

  • 逻辑或:一真为真,两假为假

表达式1 || 表达式2

只要表达式1和表达式2中有一个为真,那么(表达式1 || 表达式2)返回一个1(真)

  • 看代码:
#include<stdio.h>
int main()
{
    int a = 0;
    int b = 1;
    printf("%d", a && b);
    return 0;
}

代码结果为:

1

  • 但在真正的用途里面大多是这样的:
#inlcude<stdio.h>
int main()
{
	int a = 1;
	int b = 0;
	if (a || b)
	{
		printf("Hello !\n");
	}
	b++;// a == 1、b == 1
	if (a || b)
	{
		printf("Hello !!\n");
	}
	a--;// a == 0
	b -= 1;//b == 0
	if (a || b) 
	{
		printf("Hello !!!\n");
	}
	return 0;
}

代码结果为:

Hello !
Hello !!

7.3 区分’‘&&’'与"&“、”||“与”|"的作用

  • 其实就是
  • "&&"俩边表达式有一个为假的,那么最后整个返回值就是0
  • "||"俩边表达式有一个为真的,那么最后一个返回值就是1

8. 条件操作符(三目操作符)

exp1? exp2 : exp3

  • 这个操作符什么意思呢?
  • 就是说,如果表达式1为真,那么就输出表达式2,如果为假那就输出表示式3
  • 我们就拿一个比大小的代码来看:
#include<stdio.h>
int main()
{
	int a = 1;
	int b = 0;
	int c = a > b ? a : b;//用一个变量来接受该操作符的返回值
	printf("c = %d", c);
	return 0;
}

代码结果为:

a = 1

9. 逗号表达式

exp1, exp2, exp3, exp4

  • 逗号表达式:
  1. 就是用逗号隔开的多个表达式。
  2. 从左向右依次执行。
  3. 整个表达式的结果是最后一个表达式的结果。
  • 看代码:
#inlcude <stdio.h>
int main()
{
	int add[] = { 1, 2, 3, 4, 5, 6,(7, 8), 9 };
	int i;
	int sz = sizeof(add) / sizeof(add[0]);
	for (i = 0; i < sz; i++)
	{
		printf("第%d个元素为:%d\n", i + 1, add[i]);
	}
	return 0;
}

代码结果为:

第1个元素为:1
第2个元素为:2
第3个元素为:3
第4个元素为:4
第5个元素为:5
第6个元素为:6
第7个元素为:8
第8个元素为:9

  • 我们可以看到的是,该数组中下标为6的元素在一个括号中有俩个数,最后代码也是显示下标为6的元素是8,这也说明了逗号表达式的一个运算规则就是,“整个表达式的结果是最后一个表达式的结果”

10. 下标引用、函数调用和结构成员

  1. 下标引用操作符

[ ]

  • 下标引用操作符就是调用数组元素的操作符

操作数:数组名 +一个索引值

#include<stdio.h>
int main()
{
	int add[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	printf("%d\n", add[3]);//数组名(add) + 索引值(3)
	return 0;
}
  1. 函数调用操作符

( )

  • 顾名思义就是配合调用函数的操作符
  • 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
  • 这里拿printf函数举例子:
#include<stdio.h>
int main()
{
	int add[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	printf("%d\n", add[3]);
	return 0;
}

代码结果为:

4

  • 在"printf()"函数调用操作符中,不光只有一个操作数,缺一个都不能完成最后的效果
  1. 访问一个结构的成员

. 结构体.成员名
-> 结构体指针->成员名

这里就要讲一个结构体函数struct了,看代码:

#include<stdio.h>
struct book
{
	char name[20];//书名
	char author[20];//作者名字
	int price;//价格
};
int main()
{
	struct book s;
	scanf("%s", s.name);
	scanf("%s", &s.author);
	scanf("%d", &s.price);
	printf("书名:%s\n", s.name);
	printf("作者名:%s\n", s.author);
	printf("价格:%d\n", s.price);
	return 0;
}

输入值为:

大明 小明 66

代码结果为:

书名:大明
作者名:小明
价格:66

  • struct函数就是自定义一个结构体类型(类似 int char short float doudle,但是他更偏向于复合型的类型结构,和C语言自带的那些结构类型的单一性不全一样),然后在里面创建结构,将一些具体的有关联的数据联系在一起
    这" . "的用法
  • 接下来介绍"->"的用法:
#include<stdio.h>
struct book
{
	char name[20];//书名
	char author[20];//作者名字
	int price;//价格
};
void get_s(struct book* p)
{
	scanf("%s%s%d", p->name, p->author, &p->price);
	//这里的name和author是数组名,所以不需要加取地址符号,数组名本身就是地址
}
int main()
{
	struct book s;//
	struct book* p = &s;
	get_s(p);
	printf("%s %s %d", p->name, p->author, p->price);
	return 0;
}

代码结果为:

大明 小明 55

这个的访问方式就是倚靠地址的来访问,俩种方式都可以,自是使用的场景和方式不一样

11. 小结

10.1 移位操作符 和 位操作符使用小技巧:

1.如果我们要求一个二进制数的最后一位,我们应该怎么做?
首先我们知道我们要的是二进制数的最后一位,二进制数的最后一位无非要么是0要么是1,那怎么才能把它提取出来呢?我们要保证最后一位是0就反馈0,是1 就反馈1,我们刚刚有学过"&“、”|“、”^“运算符的用法和他们的使用规则,那么”&“的规则就是“有0则为0,俩个同时为1则是1”,当然我们还要排除其他的二进制有效位的干扰,那么我们给"那个数& 1"是不是就可以了,而且也只有”& 1"可以 ,接下来看代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不论是正还是负,都不会影响求值方式的改变

  1. 如果我们要求一个数a的二进制数的任意一位,我们应该怎么做?
    首先,我刚刚前面说到的那个计算方法不变,但是如果我现在要求的是a的二进制数的第5位(从右往左数),那我应该怎么办?我现在的方法只能求最后一位,或者说我一下子也想不出这样的一个数b的二进制是刚好在我要的第5位为1,其他都为0
a = 32
00000000000000000000000000100000
b = 32
00000000000000000000000000100000
//这个简单整个二进制就一个1
a = 25
00000000000000000000000000011001
b = ?... b = 16
//一下子看不出来吧...如果求的是第10位、第20位呢?
//而且你看的还是二进制,如果还没有二进制呢,那是不是太浪费时间了
00000000000000000000000000010000

所以,那有没有一种好方法。可以让我我要的求的那一位直接移到最后一位来,这不">>"右移操作符不就派上用场啦,我们可以让a的二进制右移四位,这样原本的第五位就到了最后一位,是不是就可以直接用我们之前的那个方法了
在这里插入图片描述
是不是很简单~

  1. “<<”、">>"这俩个操作符会不会改变操作数原本的值?
    接下来给你看一个代码:
//这里只给一个main()函数的部分
int main()
{
    int a = 16;//00000000000000000000000000010000
    int b = a >> 4;//00000000000000000000000000000001
    printf("a=%d\n",a);
    printf("b=%d\n",b);
    return 0;
}

OK,看执行结果:
在这里插入图片描述
从这里可以看出,">>“和”<<"这俩操作符只在计算的过程会临时改变a的二进制序列,当把改变后的二进制序列给另一个变量的时候,原先备份修改的a二进制序列的那块内存就会销毁,但因为他只是一个备份,所以不会影响到a的二进制序列

10.2 —>布尔值、false、true

10.3 关于操作符"&&“和”||"的短路现象

  • "&&“和”||"的使用规则可以看上面的介绍
  • '‘短路’'顾名思义就是一个事物在某件事情上进行到一半终止,未完成全部过程
  • 那么"&&“和”||"是怎么短路的呢
  • 看代码:
#include<stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	int c = 3;
	int sum = 0;
	sum = a-- && ++b && c++;
	printf("1  a=%d b=%d c=%d sum=%d\n", a, b, c, sum);
	a = 1;
	b = 2;
	c = 3;
	sum = 0;
	sum = --a && ++b && c++;
	printf("2  a=%d b=%d c=%d sum=%d\n", a, b, c, sum);
	a = 1;
	b = 2;
	c = 3;
	sum = 0;
	sum = a-- || ++b || c++;
	printf("3  a=%d b=%d c=%d sum=%d\n", a, b, c, sum);
	a = 1;
	b = 2;
	c = 3;
	sum = 0;
	sum = --a || ++b || c++;
	printf("4  a=%d b=%d c=%d sum=%d\n", a, b, c, sum);
	return 0;
}

代码结果为:

1 a=0 b=3 c=4 sum=1
2 a=0 b=2 c=3 sum=0
3 a=0 b=2 c=3 sum=1
4 a=0 b=3 c=3 sum=1

  • 我可以看到 1 和 2 的代码唯一区别就是"a–“和”–a",我们知道前置’‘–a"是先减减在计算的,而’‘a–’‘是先计算在减减的,所以这样也导致了一个一开始就是0一个一开始就是2,那么一开始为0的那个也就是2的代码,我们看到计算机在检测到’‘–a’‘为0时,后面的也就不会再运算了,应为’‘&&’'的运算规制就是一假为假
  • 接下来我们主要看 4 的代码,首先我们知道"||"的运算规则就是一真为真,所以一旦第一个开头元素为真的话,那么后面计算机也不会再浪费算力,在进行计算
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值