【C语言】操作符相关知识点

news2024/11/22 22:28:54

移位操作符

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

  • 左移操作符 移位规则:
    左边抛弃、右边补0
    在这里插入图片描述

  • 右移操作符 移位规则:
    首先右移运算分两种:
    1.逻辑移位 左边用0填充,右边丢弃
    2.算术移位 左边用原该值的符号位填充,右边丢弃

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

警告⚠ : 对于移位运算符,不要移动负数位,这个是标准未定义的。例如:

int num = 10;
num>>-1;//error

sizeof 和数组

#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));//(2)
}
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));//(1)
	printf("%d\n", sizeof(ch));//(3)
	test1(arr);
	test2(ch);
	return 0;
}

问:
(1)、(2)两个地方分别输出多少?
(3)、(4)两个地方分别输出多少?

答:

  1. (1) 输出为:40
  2. (2) 输出为:8
  3. (3) 输出为:10
  4. (4) 输出为:8

这些输出结果的原因如下所述:

在 main 函数中,sizeof(arr) 表示整型数组 arr 的大小,即 10 个整型元素,每个整型占据 4 个字节(32 位系统下)。因此,sizeof(arr) 的结果为 10 * 4 = 40 字节。

在 main 函数中,sizeof(ch) 表示字符型数组 ch 的大小,即 10 个字符元素,每个字符占据 1 个字节。因此,sizeof(ch) 的结果为 10 字节。

在 test1 函数中,arr 参数虽然声明为整型数组,但在函数参数中数组会被转换为指针,因此 sizeof(arr) 实际上返回的是指针的大小,而不是整型数组的大小。在这里,指针的大小是 8 字节(64 位系统下)。

在 test2 函数中,同样地,ch 参数虽然声明为字符型数组,但在函数参数中数组也会被转换为指针,因此 sizeof(ch) 返回的是指针的大小,而不是字符型数组的大小。在这里,指针的大小是 8 字节(64 位系统下)。

区分逻辑与(或)和按位与(或)

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

1&2 -----> 0
这里使用的是按位与(&)运算符,对应二进制的每一位进行与操作。1 的二进制表示为 01,2 的二进制表示为 10。按位与操作后,得到的结果是 00,即 0。

1&&2 ----> 1
这里使用的是逻辑与(&&)运算符,它是逻辑运算符,用于判断两个条件是否同时为真。在大多数编程语言中,逻辑与会进行短路求值,即如果第一个条件为假,则不会再计算第二个条件,直接返回假。因此,1&&2 中的 1 和 2 都被视为真,因此结果是 1。

1|2 -----> 3
这里使用的是按位或(|)运算符,对应二进制的每一位进行或操作。1 的二进制表示为 01,2 的二进制表示为 10。按位或操作后,得到的结果是 11,即 3。

1||2 ----> 1
这里使用的是逻辑或(||)运算符,用于判断两个条件是否有一个为真。逻辑或也会进行短路求值,即如果第一个条件为真,则不会再计算第二个条件,直接返回真。因此,1||2 中的 1 被视为真,因此结果是 1。

在很多编程语言中,比如 C、C++、Java 等,“&&” 是逻辑与运算符(logical AND operator)。当使用 “&&” 运算符时,它会对两个条件进行逻辑与操作,只有当两个条件都为真时,整个表达式的结果才为真(true),否则结果为假(false)。

在上述表达式"1&&2" 中,1 和 2 被视为条件,即非零值被视为真。因此,根据逻辑与运算符的规则,只有当两个条件都为真时,结果才为真。在这种情况下,1 和 2 都被视为真,所以整个表达式的结果为真,即 1。

一道笔试题

#include <stdio.h>
int main()
{
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;
	//i = a++||++b||d++;
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
	return 0;
}

现在我们来分析代码的执行过程:

初始时,a = 0, b = 2, c = 3, d = 4。
执行 i = a++ && ++b && d++:
首先计算 a++,a 先赋值给 i(i = 0),然后 a 自增为 1。此时 a 的值为 0,表示为假。
因为第一个条件已经为假,后续的条件不再执行,即 ++b 和 d++ 都不会被执行。
整个表达式因为第一个条件为假,所以结果为假,即 i 的值为 0。
因此,最终输出的结果是:

a = 1
b = 2
c = 3
d = 4

但是如果是注释掉的那一行,那么结果为,

a = 1
 b = 3
 c = 3
d = 4

逗号表达式

逗号表达式会依次计算每个表达式,并返回最后一个表达式的值作为整个表达式的值。

例如:

//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?

c为13

访问结构体

#include <stdio.h>
struct Stu
{
 	char name[10];
 	int age;
 	char sex[5];
 	double score;
}void set_age1(struct Stu stu)
{
 	stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
 	pStu->age = 18;//结构成员访问
}
int main()
{
 	struct Stu stu;
 	struct Stu* pStu = &stu;//结构成员访问
 
 	stu.age = 20;//结构成员访问
 	set_age1(stu);
 
	pStu->age = 20;//结构成员访问
	set_age2(pStu);
 	return 0;
}

这段代码中,首先定义了一个结构体 Stu,包括姓名、年龄、性别和分数四个成员变量。然后定义了两个函数 set_age1 和 set_age2,分别用来设置学生的年龄。在 main 函数中,创建了一个 Stu 类型的对象 stu,并创建了一个指向该对象的指针 pStu。

接下来分析代码的执行过程:

  1. stu.age = 20; // 将 stu 的年龄设置为 20
  2. set_age1(stu); // 传递参数时会复制结构体,所以在 set_age1 函数中对参数进行的修改不会影响原始的 stu 对象。
  3. pStu->age = 20; // 通过指针 pStu 访问 age 成员,将年龄设置为 20
  4. set_age2(pStu); // 传递指针参数,可以直接修改原始的结构体对象。

因此,经过上述步骤后,stu 对象的年龄应该是 20,而 pStu 指向的对象的年龄应该是 18。所以最终输出的结果是:stu.age = 20,pStu->age = 18。

大小端

大小端(Endian)是指在存储多字节数据时,字节序的不同排列方式。主要有两种类型:大端序(Big-endian)和小端序(Little-endian)。

  1. 大端序(Big-endian):数据的高位字节存储在低地址,低位字节存储在高地址。即数据的最高有效字节存储在最低的地址,依次类推。

举例:十六进制数 0x12345678 在大端序中存储为:

地址     数据
0x00 -> 12
0x01 -> 34
0x02 -> 56
0x03 -> 78

小端序(Little-endian):数据的低位字节存储在低地址,高位字节存储在高地址。即数据的最低有效字节存储在最低的地址,依次类推。

举例:十六进制数 0x12345678 在小端序中存储为:

地址     数据
0x00 -> 78
0x01 -> 56
0x02 -> 34
0x03 -> 12

在计算机系统中,不同的处理器架构采用不同的字节序,而网络通信和数据交换等需要统一字节序以确保数据正确传输和解析。因此,在跨平台开发和数据通信时,需要注意数据的字节序问题。

为什么要存在大小端?

大小端的存在主要是由于不同的计算机体系结构和处理器架构在存储和处理多字节数据时的方式不同。以下是一些原因:

  1. 处理器架构差异:不同的处理器架构采用了不同的字节序。例如,x86 架构使用小端序,而某些 RISC 架构(如 ARM、PowerPC)使用大端序。这种差异导致在进行跨平台开发、数据交换和网络通信时需要考虑字节序的转换。

  2. 数据传输:在网络通信中,不同的系统之间需要传输数据。为了确保数据的正确传输和解析,发送方和接收方需要在数据传输过程中统一字节序。否则,接收方可能会错误地解释数据,导致数据损坏或解析错误。

  3. 数据存储:在文件和存储设备上存储数据时,字节序的一致性也很重要。如果不同系统上的程序读取和写入数据时使用不同的字节序,那么数据的解析将会出错。

因此,大小端的存在是为了解决不同系统间数据交换和解析的问题,确保数据的正确性和一致性。

判断大小端的程序:

#include<stdio.h>
#include<windows.h>
int main()
{
	union
	{
		int a;
		char c;
	}un;
	un.a = 1;
	if (un.c) {
		printf("小端\n");
	}
	else {
		printf("大端\n");
	}
	system("pause");
	return 0;
}

解释:
这段代码使用了 C 语言中的联合(union)来判断当前系统的字节序是大端序还是小端序。具体解释如下:

  • 定义了一个联合 un,其中包含一个整型变量 a 和一个字符变量 c。
  • 将整型变量 a 赋值为 1。
  • 利用联合的特性,修改 a 的同时也会影响到 c,因为它们共享同一块内存空间。
  • 判断 c 的值,如果 c 的值为非零,则说明当前系统采用小端序;如果 c 的值为 0,则说明当前系统采用大端序。
  • 最后通过打印输出来显示当前系统的字节序。

整型提升

整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

整形提升是按照变量的数据类型的符号位来提升的。

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

用一个例子来体会一下:

#include<stdio.h>
//实例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");
	return 0;
}

上述代码的速出结果为 c ,因为 a,b 要进行整形提升,但是c不需要整形提升 a,b整形提升之后,变成了负数,所以表达式a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表达式 c==0xb6000000 的结果是真。

另一个例子:

#include<stdio.h>
//实例2
int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(!c));
	return 0;
}

结果分析:

  1. sizeof ( c ):c 是一个字符型变量,占用一个字节。所以 sizeof© 的结果是 1。

  2. sizeof(+c):在 C 语言中,一元正号操作符会将操作数提升为整数类型。因此,+c 的结果将是一个 int 类型,占用 4 个字节。所以 sizeof(+c) 的结果应该是 4 而不是 1。

  3. sizeof(!c):逻辑非操作符 ! 会返回 0 或 1,而不改变数据类型的大小。所以 sizeof(!c) 的结果应该是 1。

  4. 表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof© ,就是1个字节

截断

截断(truncation)是指将一个值的小数部分舍弃,只保留整数部分的操作。截断通常发生在从浮点数到整数的类型转换过程中。
如果将字节多的数据类型赋给一个占字节少的变量类型,会发生“截断”。

举例说明:

  1. 将浮点数截断为整数:
  • 假设有一个浮点数 x = 3.75,通过截断操作可以将其转换为整数 3。小数部分 .75 被舍弃,只保留了整数部分 3。
  • 可以使用不同的编程语言提供的截断函数或类型转换函数来进行这样的操作,例如在 Python 中使用 int() 函数,或在 C 语言中使用 (int) 强制类型转换。
  1. 截断位操作:
  • 在计算机领域,有时候我们需要对二进制数进行截断操作。例如,假设有一个 8 位的二进制数 10101101,如果我们只需要保留前 4 位,那么截断操作就可以将其变为 1010。
  • 在具体实现中,可以通过与运算(bitwise AND)来实现截断位操作。例如,在 C 语言中可以使用按位与操作符 &,如 result = number & 0xF0,其中 number 是原始的二进制数,0xF0 是一个掩码,表示前 4 位都为 1,其余位都为 0。这样的截断操作就可以保留目标位上的数值。

举个例子:

#include<stdio.h>
#include<windows.h>
int main()
{ 
	char a = -1;
	signed char b = -1;
	unsigned char c = -1;
	printf("a=%d,b=%d,c=%d\n", a, b, c);
	system("pause");
	return 0;
}

以上代码的输出结果是:
a=-1, b=-1, c=255

这是因为在C语言中,char类型默认被定义为有符号类型(signed char),其取值范围为-128到127。当使用-1赋值给char类型变量a时,会将-1视为有符号数,因此a的值也为-1。

而对于signed char类型的变量b,虽然也是将-1赋值给它,但由于已经明确指定为有符号类型,所以它的值仍然是-1。

对于unsigned char类型的变量c,它是无符号类型,其取值范围为0到255。当将-1赋值给unsigned char类型变量c时,会发生截断操作。由于c是无符号类型,截断后的结果相当于对256取余,即-1+256=255。因此c的值为255。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1511252.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

桥接模式以及在JDBC源码剖析

介绍&#xff1a; 1、桥接模式是指&#xff1a;将实现和抽象放在两个不同类层次中&#xff0c;使两个层次可以独立改变 2、是一种结构型设计模式 3、Bridge模式基于类的最小设计原则&#xff0c;通过使用封装、聚合以及继承等行为让不同的类承担不同的职责。 4、特点&#xff1…

【智能算法】樽海鞘群算法(SSA)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.代码实现4.参考文献 1.背景 2017年&#xff0c;Mirjalili受到樽海鞘集群行为启发&#xff0c;提出了樽海鞘群算法(Salp Swarm Algorithm, SSA)。 2.算法原理 2.1算法思想 樽海鞘集群是领导者-追随者类型算法&#xff0c;整体…

基于SpringBoot的“医院信管系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“医院信管系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 功能结构图 系统首页界面图 用户注册界面图 医生…

CANopen转Profinet网关连接西门子PLC与变流器通讯

CANopen转Profinet网关&#xff08;XD-COPNm20&#xff09;在智能领域&#xff0c;变流器的应用非常广泛&#xff0c;变流器一般会采用CANopen协议。现场采用台达的变流器&#xff08;支持CANopen协议&#xff09;作为CANopen从站&#xff0c;S7-1500系列PLC做主站&#xff0c;…

软件设计不是CRUD(14):低耦合模块设计理论——行为抽象与设计模式(上)

是不是看到“设计模式”四个字,各位读者就觉得后续内容要开始讲一些假大空的内容了?各位读者是不是有这样的感受,就是单纯讲设计模式的内容,网络上能找到很多资料,但是看过这些资料后读者很难将设计模式运用到实际的工作中。甚至出现了一种声音:设计模式是没有用的,应用…

Python安装第三方库

前言&#xff1a;大部分时候我们都是使用pip install去安装一些第三方库&#xff0c;但是偶尔也会有部分库无法安装&#xff08;最典型的就是dlib这个库&#xff09;&#xff0c;需要采取别的方法解决&#xff0c;这里做笔记记录一下。 使用国内镜像源安装 因为pypi的服务器在…

Java后端八股文之Redis

文章目录 1. Redis是什么&#xff1f;2. Redis为什么这么快&#xff1f;3. 为什么要使用缓存&#xff1f;4. Redis几种使用场景&#xff1a;5. Redis的Zset底层为什么要使用跳表而不是平衡树、红黑树或者B树&#xff1f;6.Redis持久化6.1 什么是RDB持久化6.1.1RDB创建快照会阻塞…

大数据开发-FLUME安装部署与实战案例

文章目录 前言安装部署配置修改案例:采集文件内容上传至HDFS案例:采集网站日志上传HDFS前言 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简…

es 查询案例分析

场景描述&#xff1a; 有这样一种场景&#xff0c;比如我们想搜索 title&#xff1a;Brown fox body&#xff1a;Brown fox 文章索引中有两条数据&#xff0c;兔子和狐狸两条数据 PUT /blogs/_bulk {"index": {"_id": 1}} {"title": "…

Oracle Primavera P6 数据库升级

前言 为了模拟各种P6测试&#xff0c;我常常会安装各种不同版本的p6系统&#xff0c;无论是P6服务&#xff0c;亦或是P6客户端工具Professional&#xff0c;在今天操作p6使用时&#xff0c;无意识到安装在本地的P6 数据库&#xff08;21.12&#xff09;出现了与Professional软…

对于stm32中printf函数的移植方法

一、准备工作 使用printf之前需要先打开工程选项&#xff0c;把use microLIB选项打开。microlib是keil为嵌入式平台优化的一个精简库&#xff0c;本文使用到的printf将会用到这个microlib。 二、对printf进行重定向 将printf打印的东西输出到串口&#xff0c;由于printf默认输…

关于分布式分片,你该知道的事儿

关于分布式分片&#xff0c;你该知道的事儿 前言一、关于分片方式的那些事儿1.1 按照Hash划分1.2 按照区间范围划分1.3 按照数据量划分1.4 来些例子1.4.1 Redis的分片划分1.4.2 Mongo的分片划分 二、关于分区再平衡的那些事儿2.1 基于固定分片数量2.2 基于动态分片数量2.3 基于…

让生活更加精致的APP?

晚上好&#xff0c;今天博主来介绍几款帮助你条理生活的APP&#xff0c;让你的生活更加精致&#xff0c;充满仪式感。 一&#xff0e;格志日记 一款以“格子”的方式记录日记的APP&#xff0c;非常简单明了&#xff0c;用户可以依据自己的喜好&#xff0c;来自由定义或者删除格…

初阶数据结构之---堆的应用(堆排序和topk问题)

引言 上篇博客讲到了堆是什么&#xff0c;以及堆的基本创建和实现&#xff0c;这次我们再来对堆这个数据结构更进一步的深入&#xff0c;将讲到的内容包括&#xff1a;向下调整建堆&#xff0c;建堆的复杂度计算&#xff0c;堆排序和topk问题。话不多说&#xff0c;开启我们今…

Python面向对象——程序架构

需求 创建图形管理器 -记录多种图形(圆形、矩形.) --提供计算总面积的方法&#xff0c; 要求:增加新图形&#xff0c;不影响图形管理器 测试: 创建图形管理器&#xff0c;存储多个图形对象。 通过图形管理器&#xff0c;调用计算总面积方法 思路 ​​​​​​​ 代码 # ------…

C# SM2加解密 ——国密SM2算法

SM2 是国家密码管理局组织制定并提出的椭圆曲线密码算法标准。 本文使用第三方密码库 BouncyCastle 实现 SM2 加解密&#xff0c;使用 NuGet 安装即可&#xff0c;包名&#xff1a;Portable.BouncyCastle&#xff0c;目前最新版本为&#xff1a;1.9.0。 using Org.BouncyCastl…

SpringBoot中MD5使用

SpringBoot中MD5使用 新建md5类 public final class MD5 {public static String encrypt(String strSrc) {try {char[] hexChars {0, 1, 2, 3, 4, 5, 6, 7, 8,9, a, b, c, d, e, f};byte[] bytes strSrc.getBytes();MessageDigest md MessageDigest.getInstance("MD5…

设计模式前置了解uml图

在开发前&#xff0c;会进行系统的设计&#xff0c;而数据模型的设计大多通过 UML 类图实现。为了在 UML 类图中清晰地表达类之间的关系&#xff0c;需要对类之间的关系有一定的认识&#xff0c;并且了解相关的表达符号。 类之间的关系有以下几种&#xff1a; 组合 聚合 关联…

IPC:管道

一、管道的概念 1.原理 在进程3G~4G的内核空间中&#xff0c;创建一个特殊的文件&#xff08;管道&#xff09;&#xff0c;管道的数据直接保存在内存中。 2.特性 1&#xff09;管道可以看成是一个特殊的文件&#xff0c;一般的文件存储在外存中&#xff0c;而管道内容是存储…

“光谱视界革新:ChatGPT在成像光谱遥感中的智能革命“

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…