当前位置: 首页 > news >正文

C语言中整数类型及其类型转换

1.数据的存储和排列

 是的,在C语言中,整数类型通常以补码(two's complement)形式存储在内存中。这是因为补码表示法在处理有符号整数的加减运算上更为简便和高效。

基本类型所占字节数:

大端方式存储

 就相当于我们平时的习惯,位权高的数写在最左端,例如:100 元。

内存地址0123
存储内容数据的最高有效字节数据的次高有效字节数据的次低有效字节数据的最低有效字节
以 0x12345678 为例子:
内存地址0123
存储内容0x120x340x560x78

小端方式存储

 位权低的数写在最左端,计算机内部存储数据最常用:

例如8086cpu的寄存器中所存数据:

该汇编语言是将1234H存入寄存器AX中在送进内存1000:0000处

 

可以看到1000:0000出存的是1234H的低位字节34H,1000:0001处存的是1234H的高位字节12H. 

内存地址0123
存储内容数据的最低最高有效字节数据的次低有效字节数据的次高有效字节数据的最高有效字节
以 0x12345678 为例子:
内存地址0123
存储内容0x780x560x340x12

边界对齐方式存储 

 

字节对齐是一种计算机内存存储数据的方式。

在计算机系统中,为了提高内存访问效率,数据通常按照特定的规则在内存中进行存储,这就是字节对齐。

其基本规则通常是:结构体或类中的成员变量,其存储地址应该是其自身大小的整数倍。例如,一个 4 字节大小的整数类型变量,通常会存储在地址能被 4 整除的内存位置。

字节对齐的主要目的有两个:一是提高 CPU 访问内存的效率,因为大多数计算机体系结构在访问对齐的数据时效率更高;二是便于不同硬件平台和编译器之间的数据交换和移植。

假设一个结构体包含一个 char 类型(1 字节)和一个 int 类型(4 字节)。如果没有字节对齐,char 类型之后紧接着存储 int 类型,可能导致 int 类型的存储地址不是 4 的倍数,从而在访问 int 类型数据时降低效率。但如果进行字节对齐,char 类型之后可能会填充若干字节,使得 int 类型从能被 4 整除的地址开始存储。

假设我们有这样一个结构体:

struct MyStruct {int num;  // 4 字节char ch;  // 1 字节short sh; // 2 字节
};

在大多数常见的编译器中,存储方式通常如下:

内存地址0123
存储内容int num 的低字节int num 的次低字节int num 的次高字节int num 的高字节
内存地址4567
存储内容char ch填充字节short sh 的低字节short sh 的高字节

首先存储 int 类型的 num,由于 int 通常是 4 字节对齐,所以 num 会从能被 4 整除的地址开始存储。

然后是 char 类型的 ch,它占用 1 字节。但为了满足后面 short 类型的 2 字节对齐要求,可能会在 ch 后面填充 1 字节。

最后是 short 类型的 sh,它从能被 2 整除的地址开始存储。

总的来说,字节对齐可能会导致结构体在内存中占用的空间比成员变量实际大小之和要大一些,这是为了提高内存访问效率。

 

2.有符号数和无符号数之间的转换

在C语言中,有符号数和无符号数之间的转换可以通过类型转换(type casting)实现。需要注意的是,这种转换会影响数值的解释。

有符号数转无符号数

将有符号数转换为无符号数时,二进制表示保持不变,但解释方式不同。

#include <stdio.h>int main(){int signed_num = -1;unsigned int unsigned_num = (unsigned int)signed_num; // 0xFFFFFFFFprintf("signed_num = %d, unsigned_num = %u\n", signed_num, unsigned_num);return 0;
} 

输出结果:

 

有符号数转无符号数在内存中的解释:

变量名内存地址二进制表示解释
signed_num0x7fffeeaee1011111111 11111111 11111111 11111111-1 (有符号数)
unsigned_num0x7fffeeaee1411111111 11111111 11111111 111111114294967295 (无符号数)

 可以看到二进制表示保持不变,只是改变了解释方式

无符号数转有符号数

将无符号数转换为有符号数时,同样二进制表示保持不变,但解释方式不同。

#include <stdio.h>int main(){unsigned int unsigned_num = 4294967295; // 0xFFFFFFFFint signed_num = (int)unsigned_num; // -1printf("unsigned_num = %u, signed_num = %d\n", unsigned_num, signed_num);return 0;
} 

 输出结果:

 

无符号数转有符号数在内存中的解释:

变量名内存地址二进制表示解释
unsigned_num0x7fffeeaee1811111111 11111111 11111111 111111114294967295 (无符号数)
signed_num0x7fffeeaee1c11111111 11111111 11111111 11111111-1 (有符号数)

 可以看到二进制表示保持不变,只是改变了解释方式

3.不同字长整数之间的转换

在C语言中,不同字长的整数之间的转换主要涉及位宽扩展和截断操作。

从小字长向大字长扩展

对于有符号数,可以通过符号扩展实现扩展;对于无符号数,可以通过零扩展实现扩展。

#include <stdio.h>int main(){char small_num = -1;int large_num = (int)small_num; // 符号扩展,小数到大数printf("small_num = %d, large_num = %d\n", small_num, large_num);unsigned char usmall_num = 255;unsigned int ularge_num = (unsigned int)usmall_num; // 零扩展printf("usmall_num = %u, ularge_num = %u\n", usmall_num, ularge_num);return 0;
} 

输出结果: 

 

从小字长向大字长扩展在内存中的解释:

变量名内存地址二进制表示解释
small_num0x7fffeeaee2011111111-1 (8位有符号数)
large_num0x7fffeeaee2411111111 11111111 11111111 11111111-1 (32位有符号数)
变量名内存地址二进制表示解释
usmall_num0x7fffeeaee2811111111255 (8位无符号数)
ularge_num0x7fffeeaee2c00000000 00000000 00000000 11111111255 (32位无符号数)

 对于有符号数,从小字长向大字长可以通过符号扩展实现扩展;

从大字长向小字长截断

当将大字长整数转换为小字长整数时,高位会被截断,只保留低位。

#include <stdio.h>int main(){int large_num = 257; // 0x00000101char small_num = (char)large_num; // 0x01printf("large_num = %d, small_num = %d\n", large_num, small_num);unsigned int ularge_num = 65537; // 0x00010001unsigned char usmall_num = (unsigned char)ularge_num; // 0x01printf("ularge_num = %u, usmall_num = %u\n", ularge_num, usmall_num);return 0;
} 

 输出结果:

 大字长到小字长整数之间的转换在内存中的解释:

数据类型变量名内存地址示例二进制表示解释
intlarge_num2570x701000000000 00000000 00000001 00000001257
charsmall_num10x7000000000011(截断)
unsigned intularge_num655370x701400000000 00000001 00000000 0000000165537
unsigned charusmall_num10x7008000000011(截断)

可以看到 大字长到小字长整数之间的转换在是直接截去多余的高位,只保留对应得低位。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn
  • 深入解析 Nginx 反向代理:配置、优化与故障排除
  • Visual Studio vs VSCode:深入剖析两款开发工具的优劣与应用场景
  • 网络安全 - 应急响应检查表
  • 【基础算法模板】堆
  • Linux OOM Killer详解
  • pytest-bdd 行为驱动自动化测试
  • 防止老年痴呆的小学题
  • DLMS/COSEM中的信息安全:加密算法(下)2
  • 批发行业进销存-登录适配 android 横竖屏幕 源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构
  • 基于 Redis 的分布式锁 Spring Boot 集成 Redisson 使用分布式锁确保对共享资源的互斥访问
  • 【深度学习】【语音】TTS,StyleTTS 2,论文
  • Android中的沉浸式丝滑转场之共享元素转场动画
  • 机器学习之主成分分析(PCA)
  • Mipi SoundWire Spec 详解4.1
  • (三)从jvm层面了解线程的启动和停止
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 3.7、@ResponseBody 和 @RestController
  • bootstrap创建登录注册页面
  • extract-text-webpack-plugin用法
  • springboot_database项目介绍
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • webgl (原生)基础入门指南【一】
  • Webpack入门之遇到的那些坑,系列示例Demo
  • 分布式事物理论与实践
  • 基于遗传算法的优化问题求解
  • 如何编写一个可升级的智能合约
  • 思维导图—你不知道的JavaScript中卷
  • 协程
  • ​​​【收录 Hello 算法】10.4 哈希优化策略
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • %check_box% in rails :coditions={:has_many , :through}
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (限时免费)震惊!流落人间的haproxy宝典被找到了!一切玄妙尽在此处!
  • (转)c++ std::pair 与 std::make
  • (转)Mysql的优化设置
  • (转)使用VMware vSphere标准交换机设置网络连接
  • .Net 8.0 新的变化
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET Conf 2023 回顾 – 庆祝社区、创新和 .NET 8 的发布
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET Framework 4.6.2改进了WPF和安全性
  • .Net Redis的秒杀Dome和异步执行
  • .Net Winform开发笔记(一)
  • .NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)...
  • .NET和.COM和.CN域名区别
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • @data注解_一枚 架构师 也不会用的Lombok注解,相见恨晚
  • @RequestBody与@RequestParam
  • @Resource和@Autowired的区别