C语言各种操作符详细解析

大家好,本文主要内容是C语言操作符的介绍,C语言有很多的操作符,学好这些操作符可以提高我们的编程水平。

我们先对操作符进行分类,随后逐一分析。

操作符的分类

算数操作符

用于数学计算的操作符,分为加 +-*/ 取模%

移位操作符

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

位操作符

按位与 :& 按位或: | 按位异或:^

单目操作符

逻辑取反 : 求类型长度:sizeof

+ -

取地址:& 解引用: *

关系操作符

大于和小于 :> <

大于等于 :>=

小于等于 : <=

等于 :==

不等于:!=

逻辑操作符

逻辑与: &&

逻辑或 :||

条件操作符

条件操作符(三目操作符) : ? :

逗号表达式 ,

下标引用操作符 []

函数调用操作符 :()

结构成员访问 .->

说完分类,下面我们开始详细介绍!

算数操作符

用于数学计算的操作符,分为加 +-*/ 取模%

我们主要说一下/%

/ 除法

在C语言中,除法分为整数除法和浮点数除法

1.整数除法 除号两端都是整数的时候,计算默认以整数除法的方式,也就是除得的数默认取证

下面是代码示例:

#include <stdio.h>

int main()
{
	int a = 5;
	int b = 2;
	float c = a / b;//除号两端都是整数
	printf("%f\n", c);
	return 0;
}

在这里插入图片描述
答案不是2.5,说明这是整数除法。

2.浮点数除法 除号两端只要有一个小数,这样得出的数就是一个浮点数。

#include <stdio.h>

int main()
{
	int a = 5;
	float b = 2;//浮点数
	float c = a / b;//除号两端有至少一个浮点数
	printf("%f\n", c);
	return 0;
}

在这里插入图片描述

%取模

取模操作符两个操作数必须均为整数,得到的结果是两数相除的余数。

比如5除以2余数是1,那么5%2=1

代码演示:

#include <stdio.h>

int main()
{
	int a = 5;
	int b = 2;
	int c = a % b;//必须都整数
	printf("%d\n", c);//打印1
	return 0;
}

在这里插入图片描述

整除和取模的应用

在算法题中,整除和取模各自有不同作用,比如:一个整数模10,得到的数是它的最后一位;

一个整除除以10,会消去它的最后一位。这样循环下来可以取到一个整数的每一位。

移位操作符

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

这里简单的说一下数据的表示方式,以后会单独去说~

数据有三种表示方式,分别是原码、反码、补码

原码

原码是人能够直接看懂的码,是直接把十进制数转换成二进制数加上一个符号位,它的首位是符号位,0代表正数,1代表负数

比如在x86坏境中

5 表示为 00000000000000000000000000000110

-5表示为 10000000000000000000000000000110

人类读内存中数据都要转成原码去读,才能看懂。

正数的原码,反码,补码都相同,都是和原码一样!

反码

原码的符号位不变,其它位按位取反便是补码

-5的原码为 10000000000000000000000000000110

-5的反码表示为 11111111111111111111111111111001

补码

符号位不变,反码+1便是补码

接上面

-5的反码表示为 11111111111111111111111111111001

-5的补码表示为 10000000000000000000000000000010

为什么要说原码,反码补码呢?因为计算机内存中存放的就是补码!

以后会单独写博客来说这些,本篇主要还是操作符部分。

既然内存中存放的是补码,那也就说明计算机在计算的时候也是直接操作补码,移位操作符移动的就是补码的二进制位

右移操作符 >>

右移分为两种,一种是算数右移,一种是逻辑右移

**算数右移:**就是二进制整体向右移动,右边直接丢弃,左边补原来的符号位。

**逻辑右移:**就是二进制整体向右移动,右边直接丢弃,左边直接补0。

C语言没有明确规定到底是算数右移还是逻辑右移,一般的编译器都采用算数右移

需要强调的是,右移左移并不会真的改变数值,比如**a>>1,并不会真的改变a,a = a>>1或者a >>=1** 才会真的改变a。

算数右移的代码演示


#include<stdio.h>
int  main()
{
	int a = -15;
	int b = a >> 1;
	printf("%d\n", b);
	//算数右移
	//a 原码        10000000000000000000000000001111
	//a 补码        11111111111111111111111111110001
	//a 算数右移1位  11111111111111111111111111111000
	//转换为原码     10000000000000000000000000001000
	//-8    可以见得确实是算数右移
	return 0;
}

在这里插入图片描述

左移操作符<<

对于正数左移一位效果是扩大二倍,右移一位效果是除以2。

左移代码演示:

//左移
#include<stdio.h>
int main()
{
	int a = 6;
	int b = a << 1;//左移一位扩大二倍
	printf("%d \n", b);//输出12
	return 0;
}

在这里插入图片描述
移位千万不要移动负位数,这是标准未定义行为,比如左移-1位,这并不代表右移一位

位操作符 & | ^

按位与 &

按位与操作符就是两个数的每一位按照补码的二进制位进行与操作。

与操作就是
0&0 = 0
0&1 = 0
1&1 = 1 只有都为1的时候结果才为1

按位与代码演示

#include<stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011  3的补码
	int b = -5;
	//11111111111111111111111111111011  5的补码
	int c = a & b;
	//按位与  全是1才为1
	//00000000000000000000000000000011  3的补码
	//11111111111111111111111111111011  5的补码
	//00000000000000000000000000000011   3
	printf("%d\n", c);//3

	return 0;
}

在这里插入图片描述

按位或 |

和按位与类似,只不过换成了或操作
或操作就是
0&0 = 0
0&1 = 0
1&1 = 1 只有都为1的时候结果才为1

按位或代码演示

#include<stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011   3补码
	int b = -5;
	//11111111111111111111111111111011   5补码
	int c = a | b;
	//按位或  全是0才为0
	//00000000000000000000000000000011   3补码
	//11111111111111111111111111111011   5补码
	//11111111111111111111111111111011   结果的补码
	//10000000000000000000000000000101   结果的原码  -5
	printf("%d\n", c);//-5
	return 0;
}

在这里插入图片描述

按位异或 ^

按位异或就是两个二进制位相同结果为0,相异结果为1

0^0=0

0^1=1

1^0=1

1^1=0

按位异或代码演示

//位操作符
#include<stdio.h>
int main()
{
	int a = 3;
	//00000000000000000000000000000011  3补码
	int b = -5;
	//11111111111111111111111111111011  5补码
	int c = a ^ b;
	//按位异或  相同为0  相异为一
	//00000000000000000000000000000011  3补码
	//11111111111111111111111111111011  5补码
	//11111111111111111111111111111000  结果的补码
	//10000000000000000000000000001000  结果的原码  -8
	printf("%d\n", c);//-8
	return 0;
}

在这里插入图片描述

赋值操作符

复制操作符=,就是把右边的值传给左边,这个应该不用多说啦

可以组成复合效果的符合复制符 += -= *= /= %= >>= <<= &= |= ^=

比如:

a = a + 3;

a = a += 3;

单目操作符

单目操作符就是操作数只有一个的操作符,包括逻辑反操作符,取地址操作符,解引用操作符等

逻辑反操作符 !

如果对一个大于零的数逻辑取反,它将等于0

如果对0逻辑取反,它将等于1

这有什么作用?

这对逻辑判断有帮助

如果一个值大于0,那程序默认它逻辑true

如果一个值等于0,那程序默认它逻辑false

逻辑取反操作符就是进行真假转换的。

取地址操作符 &

取地址操作符就是返回一个量的地址,可以用指针变量来接收。

取地址操作符代码演示


int main()
{

	int a = 1;//创建一个变量a
	int* pa = &a;//用取地址操作符取出a的地址,传给pa
	printf("a = %d\n",a);//打印a的内容
	printf("pa = %p\n",pa);//打印a的地址
	return 0;
}

在这里插入图片描述

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

解引用操作符可以通过地址找到其所指向的空间,并可以修改里面的内容

解引用操作符代码演示

int main()
{

	int a = 1;//创建一个变量a 赋值为1
	int* pa = &a;//用取地址操作符取出a的地址,传给pa
	
	*pa = 10;//通过解引用操作符找到a所在的空间,更改a为10
	printf("a = %d\n",a);//打印a的内容 10
	return 0;
}

在这里插入图片描述

sizeof

这是C语言中比较特殊的操作符,使用它的格式为

sizeof(Type);Type为数据的类型。

sizeof的用途是求一个类型所占空间的大小,注意它是操作符,不是函数!

sizeof甚至可以不用括号直接加变量名(不能直接加类型名)这足以证明它不是函数。

举例分析

int main()
{
	int a = 1;//创建一个变量a 赋值为1
	printf("%d\n", sizeof a);//变量a所占空间的大小,无需函数调用操作符,它不是函数
	return 0;
}

当然我们在使用sizeof的时候还是要老老实实的把所求内容用括号括起来。

下面是sizeof操作符的代码演示

int main()
{
	//sizeof可以直接求出一个类型所占空间的大小(单位字节)
	printf("%zd\n", sizeof(char));
	printf("%zd\n", sizeof(short));
	printf("%zd\n", sizeof(int));
	printf("%zd\n", sizeof(long));
	printf("%zd\n", sizeof(long long));
	printf("%zd\n", sizeof(float));
	printf("%zd\n", sizeof(double));
	return 0;
}

给出的代码中为了易懂没有对齐,图片中为了更清晰这里注明了类型名,并且对齐

取反操作符 ~

对一个数的补码二进制位进行取反(注意这里是求得的数不是反码)

取反操作符代码演示

#include<stdio.h>

int main()
{
	int a = 0;
	printf("%d \n", ~a);
	//00000000000000000000000000000000补码
	//11111111111111111111111111111111取反
	//10000000000000000000000000000001原码  -1
	int b = 1;
	//00000000000000000000000000000001补码
	//11111111111111111111111111111110取反
	//10000000000000000000000000000010原码  -2
	printf("%d \n", ~b);
	return 0;
}

在这里插入图片描述

前置++和后置++

这里只需要区分++a 和 a++的区别

前置++ 就是先使用后++

后置++ 就是先++后使用

前置++和后置++代码演示

//后置++
#include<stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	printf("b = %d\n", b);//0
	b = a++;//先使用,后++
	printf("b = %d\n", b);//0
	b = a;
	printf("b = %d\n", b);//1
	return 0;
}

在这里插入图片描述

//前置++
#include<stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	printf("b = %d\n", b);//0
	b = ++a;//先++,后使用
	printf("b = %d\n", b);//1
	b = a;
	printf("b = %d\n", b);//1
	return 0;
}

在这里插入图片描述

关系操作符

大于> 小于< 大于等于>= 小于等于<= 不等于!= 等于==

关系操作符一般都是用于判断,比如if语句,while语句的判断条件中。

如果为真,返回1

如果为假,返回0

关系操作符代码演示

int main()
{
	int a = -1;
	if (a < 0)
	{
		printf("a < 0\n");
	}
	else
	{
		printf("a >= 0");
	}
	return 0;
}

在这里插入图片描述

逻辑操作符

逻辑与&&和逻辑或||

一定要区分好逻辑与&&和按位与&

                     逻辑或`||`和按位或`|`

上面说过,按位与、按位或是按照两个数的每一个二进制位进行计算。

逻辑与、逻辑或则是整个数进行操作。

逻辑与(并且)

比如3&&5=1 因为3不是0,5也不是0,所以表达式为真,返回1

3&&0=0 只要有一个表达式为假,那么整个值就为假。

逻辑或(或者)

表达式只要有一个为真,那么整个表达式就为真。

比如3||0=1

当然,在真正写代码的时候,逻辑与和或基本不用于判断两个数字的关系,都是用作判断两个表达式的关系。

比如我想判断a>1并且b<1时,执行…

就写a>1&&b<1

想判断a>1或者b<1时,执行…

就写a>1||b<1

逻辑与、逻辑或代码演示

#include<stdio.h>

int main()
{
	int a = -1;
	int b = -1;
	if ((a > 0) && (b > 0))//&&是指“并且”
	{
		printf("a和b都是正数\n");
	}
	else if((a < 0) && (b < 0))
	{
		printf("a和b都是负数\n");
	}
	else
	{
		printf("a和b符号不同\n");
	}
	return 0;
}

在这里插入图片描述

短路问题

对于&&来说,左边只要为0 (false)后边就不计算了

对于||来说,左边只要为1(true),后边就不计算了

条件操作符

? :

这是一个三目操作符

表达式1?表达式2:表达式3

表达式1为真→执行表达式2

表示式1为假→执行表达式3

是不是有点像if语句?

条件操作符代码演示求两个数的较大值


//   ? : 三目操作符

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("%d\n",( a > b ? a:b));//返回a和b中放入较大值

	return 0;
}

逗号表达式

逗号表达式就是:

表达式1**,**表达式2**,**表达式3**,**......

从左向右计算 ,整个表达式结果是最后一个表达式的结果。

逗号表达式代码演示

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
	//依次执行 先执行 a>b (因为a不大于b,此时返回值为0)
	//        再执行 a = b + 10 (a赋值12,返回值为12)
	//      最后执行 b = a + 1   (b赋值13, 返回值为13)
	//逗号表达式取最后一个返回值13赋值给c
	printf("%d\n", c);
	return 0;
}

依次执行
先执行 a>b (因为a不大于b,此时返回值为0)
再执行 a = b + 10 (a赋值12,返回值为12)
最后执行 b = a + 1 (b赋值13, 返回值为13)

逗号表达式取最后一个返回值13赋值给c

在这里插入图片描述
逗号表达式的应用(去除冗余)

在这里插入图片描述

下表引用,函数调用和结构成员访问操作符

下表引用操作符 []

下标引用操作符的操作数是数组名索引值

比如

创建一个数组 int arr[10];

arr[3] = 10 arr和3 是操作数 索引数组中第4个元素(索引值从零开始)

函数调用 () 操作符

函数调用 () 操作符的操作数是函数名和参数

最少有一个操作数 也就是函数名

比如**printf("Hello World!\n");**

printf和"Hello World!\n"是操作数,调用printf函数打印Hello World!

结构成员访问操作符 . 和 ->

. 结构体.成员名

-> 结构体指针->成员名(通过地址找到成员,在传址后用)

这两个的区别是
如果被操作的是结构体变量,用.
如果被操作的是结构体指针,用->

结构成员访问操作符 代码演示

我们定义两个函数,都用于给结构体赋值,一个传结构体(传值),一个传结构体地址(传址),看一下有什么区别。

#include <stdio.h>
#include <string.h>
struct Student
{
	char name[10];
	int age;
};
void SetStu1(struct Student stu)//传值函数   要用. 操作符
{
	strcpy(stu.name,"张三");//对字符串赋值不能直接用=,要用strcpy函数
	stu.age = 19;
}

void SetStu2(struct Student* stu)//传址函数  要用 -> 操作符
{
	strcpy(stu->name, "李四");
	stu->age = 20;
}
int main()
{
	struct Student stu1 = {"xxxxx",0};//用于传值
	struct Student stu2 = {"xxxxx",0 };//用于传址
	SetStu1(stu1);//传值
	SetStu2(&stu2);//传值

	//猜一猜哪个赋值成功了?
	printf("%s %d\n", stu1.name, stu1.age);
	printf("%s %d\n", stu2.name, stu2.age);

	return 0;
}

这个代码如果对strcpy函数不清楚可以看我之前发的有介绍:C语言常见的库函数的模拟实现(字符串、内存函数)

输出结果

在这里插入图片描述

传值调用很显然没有成功!这是因为形参是实参的临时拷贝,不会对实参有影响,传值调用是无法改变实参的!

本篇文章单纯说操作符如何使用,更对关于函数的细节欢迎点击:

一口气详细说完C语言的函数

函数的栈帧创建和销毁

以上就是关于C语言操作符的介绍!感谢你的支持,让我们一起进步~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值