【C语言】操作符详解

目录

一、各种操作符的介绍

1.1算术操作符 + - * / %

1.2位移操作符 <<    >>

1.3位操作符 & | ^  

1.4赋值操作符 =  +=   -=  >>=   <<=  %=

1.5单目操作符 !  -  +  &  sizeof   ~  --  ++   *

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

1.7逻辑操作符 &&  ||(判断真假的)

1.8条件操作符(三目操作符)

1.9逗号表达式

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

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

二、表达式求值

隐式类型转换

整形提升

算术转换

操作符的属性


本章重点

1.各种操作符的介绍

2.表达式求值


一、各种操作符的介绍

操作符的分类举例
算术操作符+  -  *  /  %
位移操作符<<    >>
位操作符

&  |  ^

赋值操作符

=    +=     -=    >>=

<<=     %=    /=

单目操作符

!  -(负号)  +(正号)  &  sizeof   ~  

--   ++    *(解引用)

关系操作符<   >=   >   <=   !=   ==
逻辑操作符&&   ||
条件操作符(三目操作符)exp1 ? exp2 : exp3
逗号操作符exp1,exp2,exp3,....
下标引用、函数调用和结构成员操作符[ ]   ( )   .   -> 


1.1算术操作符 + - * / %

:> 加减乘除还有取余,都是比较基础的操作符。

但其中值得注意的是 "/",如果A/B ,其中A,B都是整形,那么结果也为整形。如果想要得到浮点数的结果,那么至少有一边得是浮点数形式。

eg.

#include<stdio.h>
int main()
{
	int a = 3 / 5;
	printf("%d\n", a);//0

	float b = 3.0 / 5;//两边至少有一个浮点数
	printf("%f\n", b);//0.6
	return 0;
}

#include<stdio.h>
int main()
{
	int a = 7 % 3;//必须两端都是整数
	printf("%d\n", a);
	return 0;
}

1.2位移操作符 <<    >>

:> 位移操作符是针对二进制位的运算

左移操作符:

int main()
{
	int a = 2;
	//把a的二进制位向左移动一位
	//00000000000000000000000000000010  ----  2
	//00000000000000000000000000000100  ----  4
	int b = a << 1;//左移操作符,左边丢弃,右边补0
	printf("b = %d\n", b);//4
	return 0;
}

    右移操作符:
    1.算术右移
    右边丢弃,左边补原符号位
    2.逻辑右移
    右边丢弃,左边补0 

int main()
{
	int a = 10;
	//把a的二进制位向右移动一位
	//00000000000000000000000000001010  ----  10
	//00000000000000000000000000000101  ----   5 
	int b = a >> 1;
	//右移操作符
	//1.算术右移
	//右边丢弃,左边补原符号位
	//2.逻辑右移
	//右边丢弃,左边补0
	printf("b = %d\n", b);//5
	printf("a = %d\n", a);//10,所以不会改变原本的a
	return 0;
}

测试当前编译器是算术位移还是逻辑的方法:

int main()
{
	int a = -1;
	//负数:-1
	//存放在内存中,内存中存的是二进制的补码
	//整数的二进制表示形式:3种
	//原码:直接根据数值写出的二进制序列就是原码
	//反码:原码符号位不变,其他位按位取反就是反码
	//补码:反码+1,就是补码
  //以上算法针对负数,对于正整数原码反码补码都一样

	//原码:100000000000000000000001 --- -1
	//反码:111111111111111111111110 
	//补码:111111111111111111111111(内存中是补码)

	//把a的二进制位向右移动一位
	int b = a >> 1;
	printf("b = %d\n", b);//-1
	//当前的右移操作符是算数操作符!
	printf("a = %d\n", a);

	return 0;
}

1.3位操作符 & | ^  


注:他们的操作数必须是整数。

按位与:

int main()
{
	int a = 3;
	int b = 5;
	//& - 按(2进制位)位与,都是1,才为1
	int c = a & b;
	//00000000000000000000000000000011 --- 3
	//00000000000000000000000000000101 --- 5
	// 每一位都按位与
	//00000000000000000000000000000001 --- 1
	printf("%d\n", c);
	return 0;
}


按位或:

int main()
{
	int a = 3;
	int b = 5;
	//| - 按(2进制位)位或,有一个是1就是1
	int c = a | b;
	//00000000000000000000000000000011 --- 3
	//00000000000000000000000000000101 --- 5
	// 每一位都按位与
	//00000000000000000000000000000111 --- 7
	printf("%d\n", c);
	return 0;
}

按位异或:

int main()
{
	int a = 3;
	int b = 5;
	//^ - 按(2进制位)位异或
	//规则:相同为0,相异为1
	int c = a ^ b;
	//00000000000000000000000000000011 --- 3
	//00000000000000000000000000000101 --- 5
	// 每一位都按位异或
	//00000000000000000000000000000110 --- 6
	printf("%d\n", c);
	return 0;
}

思考:在不使用第三个变量的情况下交换a和b的值

int main()
{
	int a = 3;
	int b = 5;
	//法一:缺陷—数值太大会溢出
	a = a + b;
	b = a - b;
	a = a - b;

	//法二:异或一下
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	return 0;
}

练习:编写代码实现:求一个整数储存在内存中的二进制中1的个数

提示:可以用该数和1进行按位与

int main()
{
	int a = 13;
	//00000000000000000000000000001101
	//00000000000000000000000000000001
	//00000000000000000000000000000001
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		int b = 1;
		int c = 0;
		c = a & b;
		a = a >> 1;
		if (c == 1)
		{
			count++;
		}
	}
	printf("count  = %d\n", count);

	return 0;
}

1.4赋值操作符 =  +=   -=  >>=   <<=  %=

:>比较简单,就不过多赘述了^a^

1.5单目操作符 !  -  +  &  sizeof   ~  --  ++   *

! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换

运用举例:

int main()
{
	short s = 5;
	int a = 10;
	printf("%d\n", sizeof(s = a + s));//2
	//sizeof 括号中放的表达式不参与运算的!
	printf("%d\n", s);//5
	return 0;
}

强制类型转换
int main()
{
	int a = (int)3.14;
	return 0;
}


void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//4
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));//4
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//40
	printf("%d\n", sizeof(ch));//10
	test1(arr);
	test2(ch);
	return  0;
}

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

tip:
1.在编程的过程中==和=不小心写错,导致的错误
2.比较两个字符串不能用==,要用strcmp

1.7逻辑操作符 &&  ||(判断真假的)

tip:区分逻辑与和按位与   区分逻辑或和按位或

//1&2 -----> 0
//1&&2-----> 1
//1|2------> 3
//1||2-----> 1

int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	//i = a++ && ++b && d++;//&&时候,只要发现a = 0,右边不用算了已经
	i = a++||++b||d++;//||的时候,只要发现a为真,后面也不用算了
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	
	return 0;
}

1.8条件操作符(三目操作符)

举例:
 

int main()
{
	int a = 3;
	int b = 0;
	if (a > 5)
		b = 1;
	else
		b = -1;
	//三目操作符exp1 ? exp2 : exp3
	b = a > 5 ? 1 : -1;
	return 0;
}

1.9逗号表达式

- 从左向右依次计算,但是整个表达式结果是最后一个表达式的结果

int main()
{
	int a = 3;
	int b = 5;
	int c = 0;
	int d = (c = 5, a = c + 3, b = a - 4, c += b);
	printf("d = %d\n", d);
	return 0;
}

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

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n", arr[4]);//[]-就是下标引用操作符
	//下标引用操作符的操作数是2个:arr和4
	return 0;
}
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);//() - 函数调用操作符
	printf("%d\n", ret);
	return 0;
}

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

结构体
创建了一个自定义类型
struct Book
{
	char name[20];
	char id[20];
	int price;
};

int main()
{
	//初始化
	struct Book b = { "C语言","C2023482",55 };
	//结构体变量名.成员名
	//printf("书名:%s\n", b.name);//(.为成员访问操作符)
	//printf("书号:%s\n", b.id);
	//printf("价格:%d\n", b.price);

	struct Book* pb = &b;
	
	//printf("书名:%s\n", (*pb).name);//(.为成员访问操作符)
	//printf("书号:%s\n", (*pb).id);
	//printf("价格:%d\n", (*pb).price);

	//结构体指针->成员名
	printf("书名:%s\n", pb->name);
	printf("书号:%s\n", pb->id);
	printf("价格:%d\n", pb->price);
	return 0;
}

二、表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称之为整型提升

实例1
char a,b,c;
...
a = b + c;
b 和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之和,结果将被截断,然后再储存于a中

整形提升

1、整型提升的定义:
C的整型算术运算总是至少已缺省整型类型的精度来进行。为了获得这种精度,表达式中的字符和短整型操作数在使用前辈转换为普通整型,这种转换被称为整型提升。
我的理解:再进行整型算数运算时,要把字符和短整型的二进制位数提升到整型的位数(32位)。字符原来为8位,要提升到32位,在前补24位。短整型原来为16为,在前补16位。
2、为什么要整型提升
整型算术运算要在计算机cpu内的整型运算器中进行。而整型运算器的操作数字节长度是整形int的字节长度。所以在整型计算时,要将所有数据提升至整形int的字节长度32位,即4字节。
3、如何进行整型提升
按照变量的数据类型的符号位来补足。
若为负数(符号位为1),整型提升时补1;
若为0和正数(符号位为0),整型提升时补0。
!!注意:
1、整型提升针对的是补码。
2、整型提升完成计算后,若变量定义为字符或短整形,要根据变量类型进行截断。

例如:char a = 1
整型提升前(补码):00000001
整型提升后(补码):00000000 00000000 00000000 00000001
char b = -1
整型提升前(补码):11111111
整型提升后(补码):11111111 11111111 11111111 11111111
 

int main()
{
	char a = 3;
	//00000000000000000000000000000011 - 3
	//00000011 - a
	char b = 127;
	//00000000000000000000000001111111 - 127
	//01111111 - b
	char c = a + b;
	//提升👇
	//00000000000000000000000000000011
	//00000000000000000000000001111111
	//00000000000000000000000010000010
	//10000010 - c
	//11111111111111111111111110000010(补码)
	//11111111111111111111111110000001(反码)
	//10000000000000000000000001111110(原码) 
	//发现a和b都是char类型,都没有达到一个int的大小
	//这里就会发生整形提升
	//如何进行整型提升
	//按照变量的数据类型的符号位来补足。
	//若为负数(符号位为1),整型提升时补1;
	//若为0和正数(符号位为0),整型提升时补0。

	//!!注意:
	//1、整型提升针对的是补码。
	//2、整型提升完成计算后,若变量定义为字符或短整形,要根据变量类型进行截断。
	printf("%d\n", c); 
	return 0;
}

整形提升

实例1: 

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");//只打印c
	return 0;
}

实例2:

int main()
{
	char c = 1;
	//%u无符号十进制
	printf("%u\n", sizeof(c));//1
	printf("%u\n", sizeof(+c));//4
	printf("%u\n", sizeof(-c));//4
	//c只要参与表达式运算,就会发生整形提升
	return 0;
}

算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个
操作数的类型,否则就无法进行。下面的层次体系称为寻常算术转换
//long double
//double
//float
//unsighted long int
//long int
//unsighted int
//int

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另一个操作数的类型后执行运算
警告:但是算术转换要合理,不然会有一些潜在问题。

int main()
{
	int a = 0;
	float f = 4.5f;
	a + f;//计算时a要转为float类型
	return 0;
}

操作符的属性

复杂表达式的求值有三个影响因素

1.操作符的优先级
2.操作符的结合性
3.是否控制求值顺序
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性
操作符优先级

int main()
{
	int a = 4;
	int b = 5;
	int c = a + b * 7;//优先级决定计算顺序
	int c = a + b + c;//结合性决定计算顺序
	return 0;
}

tip:sizeof 和 strlen区别

主要区别如下:
1、sizeof是运算符,strlen是C语言标准库函数。
2、 strlen 测量的是字符串的实际长度,以’\0’ 结束,返回结果不包括’\0’ 。
3、而sizeof 测量的是字符的分配大小,它的参数可以是数组、指针、类型、对象、函数等。
具体而言,当参数分别如下时,sizeof返回的值含义如下:

数组 - 编译时分配的数组空间大小;

指针 - 存储该指针所用的空间大小;

类型 - 该类型所占的空间的大小;

对象 - 对象的实际占用空间大小;

函数 - 函数返回类型所占空间的大小;

int main()
{
	char* str1 = "absde";
	char str2[] ="absde";
	char str3[8] = { 'a',};
	int str4[8] = { 'a',};
	char ss[] = "0123456789";

	//输出:

	sizeof(str1); // 4,计算的是指针内存的大小,包括’\0’
	sizeof(str2); // 6 ,计算的是字符串的内存大小,包括’\0’
	sizeof(str3); // 8 ,计算的是char型数组的内存大小
	sizeof(str4); // 32 ,计算的是int型数组的内存大小
	sizeof(ss); // 11 ,计算的是字符串的大小,包括'\0’
	strlen(str1); // 5 ,计算的是字符串长度,不包括‘\0’
	strlen(str2); // 5 ,计算的是字符串长度,不包括‘\0’
	strlen(str3); // ? ,因为字符串需要找到’\0’才可结束,要在’a’之后找到’\0’,所以是个随机值
	strlen(str4); // ? ,因为字符串需要找到’\0’才可结束,要在’a’之后找到’\0’,所以是个随机值
	strlen(ss); // 10 ,计算的是字符串长度,不包括‘\0’
	return 0;
}

基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究”展开,提出了一种结合数据驱动方法与Koopman算子理论的递归神经网络(RNN)模型线性化方法,旨在提升纳米定位系统的预测控制精度与动态响应能力。研究通过构建数据驱动的线性化模型,克服了传统非线性系统建模复杂、计算开销大的问题,并在Matlab平台上实现了完整的算法仿真与验证,展示了该方法在高精度定位控制中的有效性与实用性。; 适合人群:具备一定自动化、控制理论或机器学习背景的科研人员与工程技术人员,尤其是从事精密定位、智能控制、非线性系统建模与预测控制相关领域的研究生与研究人员。; 使用场景及目标:①应用于纳米级精密定位系统(如原子力显微镜、半导体制造设备)中的高性能预测控制;②为复杂非线性系统的数据驱动建模与线性化提供新思路;③结合深度学习与经典控制理论,推动智能控制算法的实际落地。; 阅读建议:建议读者结合Matlab代码实现部分,深入理解Koopman算子与RNN结合的建模范式,重点关注数据预处理、模型训练与控制系统集成等关键环节,并可通过替换实际系统数据进行迁移验证,以掌握该方法的核心思想与工程应用技巧。
基于粒子群算法优化Kmeans聚类的居民用电行为分析研究(Matlb代码实现)内容概要:本文围绕基于粒子群算法(PSO)优化Kmeans聚类的居民用电行为分析展开研究,提出了一种结合智能优化算法与传统聚类方法的技术路径。通过使用粒子群算法优化Kmeans聚类的初始聚类中心,有效克服了传统Kmeans算法易陷入局部最优、对初始值敏感的问题,提升了聚类的稳定性和准确性。研究利用Matlab实现了该算法,并应用于居民用电数据的行为模式识别与分类,有助于精细化电力需求管理、用户画像构建及个性化用电服务设计。文档还提及相关应用场景如负荷预测、电力系统优化等,并提供了配套代码资源。; 适合人群:具备一定Matlab编程基础,从事电力系统、智能优化算法、数据分析等相关领域的研究人员或工程技术人员,尤其适合研究生及科研人员。; 使用场景及目标:①用于居民用电行为的高效聚类分析,挖掘典型用电模式;②提升Kmeans聚类算法的性能,避免局部最优问题;③为电力公司开展需求响应、负荷预测和用户分群管理提供技术支持;④作为智能优化算法与机器学习结合应用的教学与科研案例。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解PSO优化Kmeans的核心机制,关注参数设置对聚类效果的影响,并尝试将其应用于其他相似的数据聚类问题中,以加深理解和拓展应用能力。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值