C语言学习入门第五节————操作符详解(下)

系列文章目录

C语言学习入门第五节————操作符详解(下)

操作符详解

  1. 操作符分类
  2. 算术操作符
  3. 位移操作符
  4. 位操作符
  5. 赋值操作符
  6. 单目操作符
  7. 关系操作符
  8. 逻辑操作符
  9. 条件操作符
  10. 逗号表达式
  11. 下标引用、函数调用和结构成员
  12. 表达式求值


一、关系操作符

> : 大于

>= : 大于等于

<: 小于

<= : 小于等于

!= : 不等于(用于测试“不相等”)

== : 相等(用于测试“相等”)
在编程的过程中 == 和 = 不小心写错,容易导致错误

二、逻辑操作符

  1. && : 逻辑与(并且:两个为真,才为真)
  2. || : 逻辑或(或者:多个中一个为真,就为真)
  3. : 逻辑非

1.&& : 逻辑与(并且:两个为真,才为真)

#include <stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	//a 和 b 都是5,打印 hehe
	if (a == 5 && b == 5)
	{
		printf("hehe\n");
	}
	//a 或者 b是5 打印哈哈
	if (a == 5 || b == 5)
	{
		printf("haha\n");
	}
	return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;//整个表达式的结果为0,因为a = 0,为加a++执行完后,不会在执行b++
	                      //所以a = 1,b = 2
	                
	                     //因为前面的表达式结果为0,所以这个表达式的结果也为0
	                    //所以d++也不执行,d = 4,i= 0
	
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);//
	return 0;
}

在这里插入图片描述

2. || : 逻辑或(或者:多个中一个为真,就为真)

#include <stdio.h>
int main()
{

	int i = 0, a = 0, b = 2, c = 4;
	i = a++ || ++b || c++;//因为a = 0,为假。++b后b = 3,为真。
	                      //所以此时a = 1,b = 3,整体结果为真,即为1

	                     //因为c++左边已经为真,所以c++不执行
	                     //所以c = 4,i = 1

	printf("a = %d\nb = %d\nc = %d\n", a, b, c);
	return 0;
}

在这里插入图片描述

3.判断闰年( *

//判断闰年
#include <stdio.h>
 
int main()
{
	int y = 0;
	scanf("%d", &y);
 
	//1. 能被4整除,并且不能被100整除
	//2. 能被400整除是闰年
	if ( ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
	{
		printf("闰年\n");
	}
	else
	{
		printf("不是闰年");
	}
 
	return 0;
}

在这里插入图片描述

三、条件操作符(三目操作符,有三个操作数)

exp1 —— ? —— exp2 —— : —— exp3

(表达式1) (表达式2) (表达式3)

 真     -->     计算       -->   不计算       

==> exp2 是整个表达式的结果

exp1 —— ? —— exp2 —— : —— exp3

 假    -->      不计算      -->     计算

==> exp3 是整个表达式的结果

#include <stdio.h>
 
int main()
{
	int a = 0;
	int b = 0;
 
	if (a > 5)
	{
		b = 3;
	}
	else
	{
		b = -3;
	}
 
	//第一种写法:
	(a > 5) ? (b = 3) : (b = -3);
 
	//第二种写法:
	b = ((a > 5) ? (3) : (-3));
 
	return 0;
}

在这里插入图片描述

四、逗号表达式( * * *

逗号表达式:用逗号隔开的多个表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果
exp1 , exp2 , exp3 , …expN

#include <stdio.h>

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//从左向右依次执行,整个表达式的结果是最后一个表达式的结果 
	printf("%d\n", a);//所以a = 12
	printf("%d\n", b);//b = 13
	printf("%d\n", c);//c = b = 13
	return 0;
}

在这里插入图片描述

五、下标引用,函数调用和结构函数

1. [] : 下标引用操作符

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

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5 };
	// 下标:		0 1 2 3 4
	//数组的起始是有下标的,下标是从0开始的
 
	printf("%d\n", arr[4]);
	
 
	return 0;
}

在这里插入图片描述

2.(): 函数调用操作符

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

#include <stdio.h>
 
int Add(int x, int y)
{
	return x + y;
}
 
int main()
{
	int c = Add(6, 9); // 函数调用操作符
 
	printf("%d", c);
 
	return 0;
}

在这里插入图片描述

3.访问一个结构的成员

. : 结构体.成员名

#include <stdio.h>
//创建一个结构体(类似java中的类)
struct Book //使用struct创建结构体
{
	//定义成员
	char name[30]; //书名
	char author[20]; //作者
	float price; //价格
 
}; //结尾要加分号
int main()
{
	//一本书:
	struct Book b1 = { "C语言详解", "王五", 66.5f };
	//另一本书:
	struct Book b2 = { "数据结构", "李四", 66.5f };
	printf("%s %s %.1f\n", b1.name, b1.author, b1.price);
	// . --> 访问结构体成员:结构体变量.成员名
	printf("%s %s %.1f\n", b2.name, b2.author, b2.price);
	return 0;
}

在这里插入图片描述

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

#include <stdio.h>

//创建一个结构体(类似java中的类)
struct Book //使用struct创建结构体
{
	//定义成员
	char name[30]; //书名
	char author[20]; //作者
	float price; //价格

}; //结尾要加分号

//写一个打印结构体变量信息的函数
void Print(struct Book* p)
// * 表示 p 是指针变量
// struct Book 表示 指针变量的 类型
{
	//第一种方法:
	printf("%s %s %.1f\n", (*p).name, (*p).author, (*p).price);
	//第二种方法:
	printf("%s %s %.1f\n", p->name, p->author, p->price);
	// 结构指针 -> 成员名
}

int main()
{
	//一本书:
	struct Book b1 = { "C语言第一课", "张三", 66.5f };

	//另一本书:
	struct Book b2 = { "数据结构第一课", "李四", 66.5f };

	//写一个打印结构体变量信息的函数
	Print(&b1);
	Print(&b2);
	return 0;
}

在这里插入图片描述

六、表达式求值

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

1 隐式类型转换(整型提升) * * * * *

  1. C语言的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。
  2. 为了获得这个精度,表达式中的字符(char)和短整型(short)操作数在使用之前被转换为普通整型(char为1字节,int为4字节),这种转换称为整型提升,这里的转换只是计算时临时转换一下,变量本身不会转换类型。

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器(register)的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general-purpose
CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能由这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整形值,都必须先转换为
int 或 unsigned int ,然后才能送入CPU去执行运算。

整型提升是按照变量的数据类型的符号位来提升的
(一个有符号的数字,在二进制序列里面就把最高位规定为符号位)

 
#include <stdio.h>
int main()
{
	// 存放时截断,使用时整型提升
 
	char c1 = 5; 
	//00000000000000000000000000000101 -- 整型5的32为bit位(四个字节:32位)
	//00000101 -- char类型5的8为bit位(一个字节:8位)(截断)
	//00000101 -- c1
	char c2 = 127;
	//00000000000000000000000001111111 - 整型
	//01111111 -- c2
	char c3 = c1 + c2;
	//计算时要整型提升:整型提升是按照变量的数据类型的符号位来提升的
	//char类型是有符号的char,此时高位就是它的符号位,为 0,所以 补 0 到 32位,
	// 
	//00000101 --》00000000000000000000000000000101
	//							相加
	//01111111 --》00000000000000000000000001111111
	//			从 char 变成 int
	//得		   00000000000000000000000010000100 -- 32位
	// 
	// 要把32位放进8位得char中,再进行截断
	//00000000000000000000000010000100 --》10000100
	//所以 c3 = 10000100
 
	//		这时打印 c3 ,会打印 -124
	//原因:
	//	%d - 以10进制的形式打印有符号的整数
	// 因为 c3 是 char 类型的,不是 %d 要的整型,所以这里也要进行整型提升
	// c3 = 10000100 ,char类型是有符号的char,此时高位就是它的符号位,为 1 
	// 所以 补 1 到 32位,
	//  (无符号整形提升,直接高位补0)
	// 10000100 --》
	// 11111111111111111111111110000100 -- 补码
	// 11111111111111111111111110000011 -- 反码(补码 - 1)
	// 10000000000000000000000001111100 -- 原码(反码按位取反)
	 //				得 打印时得 -124
	printf("%d\n", c3);
	return 0;
}

在这里插入图片描述

证明 整形提升 的存在:

有符号数字和无符号数字:

10010100,如果这是有符号数字,最高位就是符号位,不是有效数字如果这是无符号数字,最高位就是有效数字,表示 2的7次方
(截断:以char类型为例,截断会直接获取二进制序列最低的8位,即一个字节,其它位全部丢弃)

#include <stdio.h>
//%d  - 以10进制的形式打印有符号的整数
//%u  - 以10进制的形式打印无符号的整数
int main()
{
	char c = 1;//char类型占一个字节
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));//%u打印无符号 c是有符号的,要转换为无符号整数,整形提升为四个字节
	printf("%u\n", sizeof(-c));//%u打印无符号 c是有符号的,要转换为无符号整数,整形提升为四个字节
	return 0;
}

在这里插入图片描述

2.算术转换( * * * * *

  1. 如果某个操作符的各个操作数属于不同的类型(>=4个字节),那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换(向上转换),这里的转换只是计算时临时转换一下,变量本身不会转换类型。

  2. 如果某个操作数的类型在下面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

  3. 警告: 算术转换要合理,要不然会有一些潜在的问题,如精度丢失。

在这里插入图片描述

3.操作符的属性

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

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

操作符优先级 (图中从高到下,优先级从高到低)

在这里插入图片描述

七、sizeof 和 strlen 的对比( * * * *

  1. sizeof 是 操作符 ;strlen 是 库函数

  2. sizeof 计算的是占用内存的大小,单位是字节,不关注内存中存放的具体数据

  3. strlen 是求字符串长度的,只能针对字符串,会寻找字符串中的 \0,统计的是字符串中 \0 之前出现的字符的个数

使用函数完成整型函数的打印、元素逆置、初始化

//实现函数init() 初始化数组为全0
//实现print()  打印数组的每个元素
//实现reverse()  函数完成数组元素的逆置。
//要求:自己设计以上函数的参数,返回值。
#include <stdio.h>
 
 
//实现print()  打印数组的每个元素
void print(int* arr, int sz)
{
	//输出:
	int j = 0;
	for (j = 0; j < sz; j++)
	{
		printf("%d ", arr[j]);
	}
 
 
	//换行:
	printf("\n");
}
 
//实现reverse()  函数完成数组元素的逆置。
void reverse(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < (sz / 2); i++)
		//循环条件???
		//6个元素调3次,5个元素掉两次
		//sz/2 肯定够用
	{
		int* left = arr + i;
		int* right = arr + sz - 1 - i;
		// 使用指针移位,类似下标,易混
		// 让左右指针慢慢往中间靠
 
 
		int tmp = *left;
		*left = *right;
		*right = tmp;
		// 获取指针值是用 * 
	}
}
 
//实现函数init() 初始化数组为全0
void init(int* arr, int sz)
{
	//arr:首元素地址 ; sz为几就循环几次
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		int* tmp = arr + i; //创建指针变量存放指针
		*tmp = 0; //取指针中的值,并赋为0
	}
}
 
int main()
{
	int arr[5] = { 1,2,3,4,5 };
 
 
	int sz = sizeof(arr) / sizeof(arr[0]); //元素个数
 
 
	//实现print()  打印数组的每个元素
	print(arr, sz);
 
	//实现reverse()  函数完成数组元素的逆置。
	reverse(arr, sz);
	print(arr, sz);
 
	//实现函数init() 初始化数组为全0
	init(arr, sz);
	print(arr, sz);
 
	return 0;
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值