导图社区 C语言全记录
暂无相关模板推荐
C语言全记录
源代码的执行过程
集成开发环境下
原生
变量名
变量名与内存的关系 int a = 500; //a只是地址的别名。编译器处理后: *(int *))xb0002345 = 500; //a被这个地址代替 变量名只是一个别名而已,其实并不是一个真实存在的东西。
CPU与内存
内存的存储与编址
这里的内存指的是地址寻址范围,而非真实的物理内存。
内存存储的最小单位是位bit, 但是CPU对内存进行寻址的寻址单位是1字节Byte,换句话说CPU对内存进行编址的最小单位是字节,编址即是对每一块(每一个字节)内存进行编号。也等于说访问内存的最小单位是以字节为单位来访问的。
浅谈如何判断CPU的寻址能力,描述其过程
第一步先要知道,一根地址总线一次只能放出一个数据,因为是电信号传递出来只能表示0或1,即一个二进制数。 第二步笼统分析两根地址总线的CPU的寻址能力,如果CPU有两根地址总线,那么这俩根地址总线就能同时放出2个1位的二进制数,组合排列在一起就有四种情况,00,01,10,11 ,也就是说能表示四种地址,即00到11.那么这个CPU的就只能够给四块内存进行编址编号,从低内存到高内存,分别是地址为第00字节的内存,地址为第01字节的内存,地址为第02字节的内存,地址为第03字节的内存。因此这个CPU的寻址(编址)能力就是0到3字节。 第三步严谨分析32位CPU的寻址能力,一共32根地址总线,一次能够放出32个1位二进制数用来编址,32个二进制数组合成一个32位的二进制数,从组合排列成最小00000000 00000000 00000000 00000000的二进制数 到组合排列成最大的11111111 11111111 11111111 11111111二进制数,也就是一共有4294967296种二进制数来当成编号供CPU编址使用(得来4294967296种的这个结果,用数学方式计算就是2的32次方种)。 因为能表示的地址编号是从00000000 00000000 00000000 00000000到11111111 11111111 11111111 11111111,用十六进制表示是0000 0000 到FF FF FF FF,用十进制表示是0 到4294967295。因为这些地址编号的单位是字节byte, 所以能表示4294967296个字节的内存块,换句话说就是能为4294967296个字节的内存进行编址,换句话说就是寻址能力就是能够寻找4294967296个字节的内存量,为了可读性可以再次进行单位转换:寻址到4294967296个字节的内存量/1024/1024/1024 -> 寻址到4个GB的内存量,所以32位CPU的寻址大小共为4GB(的内存)。用字节为单位来描述精确的寻址范围的话就是0到4294967295字节。这也是为啥在32位操作系统上想扩展内存条,必须得先换成64位操作系统的原因,注意前提CPU是64位的才能换装成64位系统,如果本身CPU本身是32位的,你连64位系统都安装不上,那么也没法再扩展内存到4G以上了。
CPU的三种总线
CPU的三类总线基本介绍
地址总线
CPU通过地址总线来寻址,指定存储单元,所以地址总线上能传送多少个不同的信息,CPU就可以对多少个存储单元进行寻址。 地址总线中的每根线只能发0与1两个状态,多根的话其实就是排列组合。 如果地址总线中只有一根线的话,就只能寻找识别到0和1这两个编号地址上的内存,也就是2的1次方。 如果地址总线中只有两根线的话,那就只能寻找到,00(转换为10进制0),01(转换为十进制1),10(转换为十进2),11(转换为十进3)上这0到3四个编号上的内存上所存储的内容。也就是2的2次方。 由此可见,CPU地址总线的宽度决定了CPU的寻址能力,CPU寻址范围 == 2的n(n=地址总线数量)次方。 内存条只占了地址(虚拟内存的地址)的一部分,其他部分都分给了其他硬件元素,像硬盘,显卡,网卡等等都以并行的关系统一参与了编址。
32机下,指针为什么占32位?
指针的大小是由地址总线宽度决定的,即地址编号的宽度,既地址编号是多少位的二进制数
数据总线
数据总线宽度决定了CPU和外界数据传输速度,单位为位。 1根数据总线,一次传送1位二进制数据,0或1。 8根数据总线一次可以传送8位二进制数据,也就是1个字节。 16根数据总线一次可以传16位二进制数据,也就是2个字节。 通俗了讲,也就是数据总线的宽度大小决定了CPU一次可以从其他器件中读写多大内容。
内存的扩展知识
图示
数据总线决定了CPU一次读写访问多少字节。创建变量时合理的选择类型来获取CPU的最高读写性能
如图所示,内存编址单位是一个字节,一个小格子是一个字节。 数据类型是用来定义变量的,而这些变量存储在内存中,数据的类型必须与内存相匹配,才能获得最高的性能。否则不工作或效率低下。 在32位系统中定义变量最好用int,因为这样效率高,32位系统,天生的就是一次操作32位。 32位系统下的一个int占4个字节,也就是32位。刚好与CPU本身的数据位宽是一样的。32位位宽就是4个字节。
关于内存对齐
如图所示,从0开始,每次跨一个位宽长度(32位就是四个字节),也就是0,4,8,C,10。对齐的边界编号应该就是位宽(4个字节)的整数倍。 多于位宽长度的内存,在逻辑上是连续的,在硬件上就有可能是不连续的。 比如上图0123在逻辑上和硬件上都是连续的。而1234在逻辑上是连续的,而硬件上是非连续的。 0123叫对齐访问, 1234叫非对齐访问。 对齐访问很配合硬件,所以效率很高; 非对齐访问因为和硬件本身不搭配,所以效率不高。(因为兼容性的问题,一般硬件也都提供非对齐访问,但是效率要低很多。)
说一个CPU是多少位CPU,是指的这个CPU的数据总线是多少位的,它决定了每次传输数据的大小,地址总线一般比数据总线大,就像8086CPU一样,为了解决数据总线和地址总线大小不一样才引入了4个段寄存器。
64位CPU这个位数指的是CPU GPRs(General-Purpose Registers,通用寄存器)的数据宽度为64位,64位指令集就是运行64位数据的指令,也就是说处理器一次可以运行64bit数据。 跟地址总线没关系 64根地址总线寻址能力为2^64(Byte),远不止4GB了。 64根数据总线的话,单次传输数据为8Byte,没错。 弄清楚两种总线的功能,也就明白计算方法了。 地址总线用来寻址的。地址总线上的二进制数组合标识的是地址号,寻址能力就是地址总线总共可以表示多少个不同的地址号。那么1根线可以标识两种地址号;N根线可以标识2^N种地址号,即寻址能力为2^N。 数据总线用来传输数据的,比如你只有一根数据线,即只能传输1bit数据,有N根,那就能传输Nbit的数据。
控制总线
控制总线的宽带决定了CPU对外部部件的控制能力。读还是写 CPU对外部部件的控制时通过控制总线来进行的,控制总线是个总称,控制总线是有不同的控制线来集合的。有多少根控制总线,就意味着CPU提供了对外部器件的多少种控制方式。
CPU读写内存的原理
了解CPU在读取操作内存数据前先明确三件事: 地址信息,存储单元的地址,基于地址总线来确定。 数据信息,读写的数据,基于数据总线来确定。 控制信息,控制器件的选择,读或者写,基于控制总线来确定。
读取流程
写入流程
补码
编码
如何把计算机硬件中存储的信息转换成人类可方便理解的形式?
如何来利用这些硬件的基础来表示我们需要的数据范围? 由此引入编码的概念
物理基础决定有多少种情况, 单单在一个字节中,二进制信息就有256种的组合排列情况,如何去解释这256种情况,解释成人类可以方便理解记忆的数据,就需要引入编码,用什么样的编码就会导致什么样子的数据形式与范围
那么,何为编码?
编码,就是一套规则,是为了服务于计算机而出现的,编码的目的是为了把物理内存中存储的信息,转变为人类逻辑上的现实数据。即:物理存储上的信息 ->编码规则 -> 人类思维上可理解看懂的数据(出现了人为逻辑上的数值范围,最大数据,最小数据)
引入补码的概念
补码,是众多编码规则中的一种,历史上出现了很多编码规则,自然码,原码,莫尔斯码,反码,补码......但是都被一一淘汰,最终把补码规则定为计算机编码规则 为什么只剩补码,其他的编码规则为什么被淘汰? 因为为了满足人类的需要,拥有只是能看懂理解的数据是远远不够的,因为数据出现的目的是为了干什么?是为了进行运算,所以要在现有的符合人类思维的数据之上还要架设运算机制, 且这个运算机制必须能满足符合人类的现实思维的计算逻辑(数学逻辑),才可以。 而大部分编码规则从计算机中提取信息后所转化的数据在参与运算上出现了与人类逻辑运算结果相矛盾的问题,所以这样的编码方式在计算机中被废弃淘汰。能够担负起人类与计算机信息存储之间的桥梁就只剩补码。
自然码与补码的对比,看看为何自然码会被淘汰,补码却应用下来
分析自然码的缺陷: 物理基础->自然码编码 -> 数据 -> 做运算 +1+-1 +2+-2- > 进行不下去了 运算后的二进制结果所映射到的自然码与数学逻辑不符。 1)数学运算逻辑 1+(-1)=0 2)自然码规则所解释出来的数据进行运算时不符合数学运算逻辑 1 +(-1) == -2 1 0000 0001 -1 1000 0001 -2 1000 0010 3)补码规则所解释出来的数据进行运算时符合数学运算逻辑 1 + (-1) == 0 1 0000 0001 -1 1111 1111 0 0000 0000
补码
模与补数
什么是模?
模是指一个计量系统的计数范围。 如时钟等。时钟的计量范围是0-11,那么模=12。 如计算机也可以看成一个计量机器,它也有一个计量范围,即都存在一个模。表示n位的计算机计量范围是0-2的次方-1,那么模=2的n次方。 模实质是计量器产生"溢出"的量,它的值在计量器上表达不出来,计量器上只能表示出模的余数。
什么是补数?
对模而言,8和4就是互为补数。互为补数的两个数相加等于模。 实际上以12为模的系统中,11和1, 10和2, 9和3, 7和5, 6和6,都有这个特性。均互为补数
什么是减法变加法?
任何有模的计量器,均可化减法为加法运算。 比如.利用时钟系统来解释的话,假设当前时钟指向10点,而准确时间是6点,那么调整时钟时间就有两种拨法: 一是倒拨4个小时,即10-4=6。 二是顺拨8个小时,即10+8=12+6=6。 在这个以12为模的系统中,加8和减4效果是一样的,因此凡是减4的运算,都可以用加8来代替。这就是所谓通过模而实现的减法变加法。也就是把减数用相应的补数表示即可。
等价推算公式:X减去一个数n == X加上n的补数 == X加上(模-n)
补码
何为补码
把补数用到计算机对数的处理上,就是补码,补码运算规则存在的意义是为了把物理存储上的信息转为现实逻辑数据而遵循的规则。不遵循补码规则是无法把物理存储上的信息正确的转换获取到有效的现实数据的。注意,谈现实数据为二进制形式的原码
先消化透几个重要的科普性常识
原码,反码,补码的诞生顺序
先有原码的概念,再有反码的概念,最后出现补码的概念。 原码,反码,补码,都是因计算机而生的,在应用于系统存储表示运算中原码反码逐一被摒弃。
谈原码反码补码根本离不开计算机层面
什么是原码?
目前原码所起的作用
原码是一种很初级的编码方案,它仅仅解决了编码问题,从此数字就有办法二进制表示了。但我们在计算机内部表示数字是用来计算的,那么想用原码计算的话,那可就麻烦了,原码的符号位不能直接参与运算,必须和其他位分开,这就增加了硬件的开销和复杂性,再一个原码的运算逻辑与人类数学逻辑不符,所以在计算机系统中摒弃原码
正数的原码怎么求
正数的原码是啥, 正数的原码就是用把十进制转成二进制的方式,把一个十进制数字不停地除以2求余数的方式,转换求得的那个二进制数,就是正数的原码。
负数的原码怎么求
负数的原码就是把该负数所对应的正数的原码的符号位变为1即可。
从一个负数的原码推导到补码的过程
原码的符号位不变,其他位全部取反, 得到反码。再对反码+1,得到补码。
上面求反码时,符号位没参与。与补码的符号位参与运算的特性并不冲突。因为根本就是两回事。补码是后来的
一个数的原码,反码,补码
0
0的原码,反码,补码都一样, 均是0000 0000
正数
正数的原码,反码,补码都一样, 比如127 原码 0111 1111 反码 0111 1111 补码 0111 1111
负数
负数的原码,反码,补码 均不一样, 比如-127 原码 1111 1111 反码 1000 0000 补码 1000 0001
计算器软件里所显示数据的二进制均是补码形式
补码机制的特性
在计算机系统中,数值一律用补码来表示和存储
1.使用补码,可以将符号位和数值域统一处理,也就是说符号位参与运算,不需要单独标识
2.同时,加法和减法也可以统一处理,也就是说,减法变加法,同样也适应于乘法和除法。
补码的减法变加法原理
以模的角度分析运算: 10 - 5 补码基于模理论,则可以把做等价操作 10 - 5 == 10 + (5的补数) 5的补数 = 模 - 5 =256-5 =251 251的二进制 == -5的补码 三者等价 10 - 5 == 10 + 251 == 10 +(-5) 但是由于用模思想的解释减法变加法太麻烦,于是为了简化这种思想,决策者直接将10-5的过程可以规定看成 10的补码+(-5)的补码形式。
计算机系统只能进行加法运算,计算机只有加法器。 只要计算机是基于模理论的前提下,也就是说只要计算机系统是个模系统(计算机的模系统完全等同于时钟模系统),那么就可以完全实现把减法变成加法。(乘除都能变加法)。总而言之,计算机只有加法的理论就是这么推导来的。
计算机中计算1-2是如何计算的?
计算机中只能够做加法运算,所以相当于1加上((-2)的补数的补码),通过模的计算,2的8次方为256,跟-2互为补数的是256-2=254,表示二进制位1111 1110,跟1的二进制0000 0001做加法运算,
3.此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。就是取反+1
4.表示方法摒除了正负0
补码的运算规则
所有数据全是以补码形式存储在物理设备上的。
最高位为0,则为正数。 最高位为1,则为负数。
一个数的补码与原码,互为逆运算,都是全部取反+1
十进制数负数<->补码
从十进制数负数->补码。即从数据推导出计算机内部信息的二进制存储形式的完整过程
传统方法是
对 |负数|的二进制 全部取反+1
demo
简便方法是
利用模系统的特点,可以快速计算出一个负数在内存中所存储的补码的二进制
先求得模,看这个数据是准备往多大字节中存储。 再用模减去这个|负数|得到一个正数, 那么这个正数的的补码就等于这个负数的补码。 正数的补码好算,求得它的原码即是它的补码。
demo
分析出-65在计算机内存中的补码形式的二进制是多少
子主题 2
子主题 3
从补码->十进制负数。即从计算机内部信息的二进制存储形式推导出数据的完整过程
编译器看符号位
demo
通过补码对char(8位)所解释出来的数据范围的展示
分支主题 6
解释一个问题
为什么计算器在1个字节模式下,选择十进制后,想查看128的二进制,却无法输入128
short a = 65535;//?? 为什么计算器的WORD两个字节,无法表示65535的各种进制 SHORT的取值范围-32768~32767,SHORT长度16。 计算器默认是表示有符号的数据
demo
unsigned char value = -127 unsigned char value = 129
char value = -127 char value = 129
这四种形式在存储上完全一样
常量/变量/数据类型
常变量与数据类型的由来
从语言设计的初衷的角度来看: 计算机语言是为了描述世界的,为了这个目的,计算机语言就需要常变量来存储现实世界的各种事物属性,用名字来标识,并需要类型来限定(看是需要多少字节来描述这个属性算合适)。因此各种语法基础概念为此而设计出现。
变量
变量名的本质
变量实质就是一段内存空间的别名。只是单纯给程序员看的,编译器只看地址。 int a = 500; //a只是地址的别名。 编译器处理后: *(int *))xb0002345 = 500; //a被这个地址代替 变量名只是一个别名而已,其实并不是一个真实存在的东西。
变量名的命名规则
变量名可以由字母,数字和_(下划线)组合而成。 变量名不能包含除_(下划线)以外的任何特殊字符,如:%,#,逗号,空格等。 变量必须以字母或_(下划线)开头。 变量名不能包含空白字符(换行符,空格,制表符统称为空白字符)。 C语言中的某些词(例如int和float等)称为保留字,具有特殊意义,不能用作变量名。 C语言区分大小写
驼峰命名法
数据类型
数据类型的作用
数据类型是对一块内存空间的格式化。 数据类型提供了申请内存单元的大小和访问规则,其是架设在线性内存上的一种逻辑关系。以后在研究任何一种数据类型的时候,都会从逻辑和存储两方面来研究。
一个数据类型所能解析出来的数据范围大小全是基于补码的编码机制
数据类型的分类
基本类型
数值类型
整型
短短整型char 短整型short 整型int 长整型long 长长整型longlong
32位机下每种整型所能表示的数据范围图
浮点型
单精度float 双精度double
32位机下浮点型所能表示的数据范围图
浮点型数据的表示形式与存储方式
浮点型的表示形式:一般形式(小数点形式),科学计数法(指数形式)
浮点型的存储方式
浮点数和整数在存储上以及运算处理过程上都有很大的区别。 小数存储是以科学计数法方式存在的。 一个浮点数 由三个基本成分构成:符号(S),阶码(E),尾数(M) 下图详解:
浮点数与0值做比较
浮点数之间做比较
字符类型char (本身是短短整型,被组织用来建立ASCII码映射表所用,用来表示字符,所以又称为字符型)
字符型的本质
字符型在C语言中本质就是是整型。 char c = 'a'; //变量c占1个字节大小。 注意: 常量'a'类型本质为Int类型占4个字节大小。 sizeof('a')==4
ASCII码表
计算机不能显示字符,只能在显示的时候进行转化。这种转化需要一张表,表里记载了转换规则,这张表就是ASCII码表,该表的本质就是一种对应关系。
转义序列,又称转义字符
何为转义字符
就是它本身己有一个字符的意义,我们给他另外一个别的意义。转意字符常起到控制的作用。
转义字符分为两类
A.字符语义已被征用,回归本意
比如 “ \ %三种 本身就是一个双引号字符,但是他被C语言征用了用于其他的用途,自身用途被占用,如果想使用自身用途,就需要构成转义序列,来转回本意 回归本意
B.有新的需求,通过转义实现新的功能
比如 n t b本身是一个字符,但是,C语言想实现一个换行的功能,则需要构成转义序列 \n ,想开创新意 转为新意
常见转义序列列表图示
demo
printf("%c",'\\');//打印一个斜杠 printf("%c",'\');//打印一个单引号 printf("%c", '\n');//打印一个换行
注意需要记住每种基本类型它有符号类型的范围走向
构造类型
数组 结构体 共用体 枚举
指针类型*
空类型void
void类型大小为内存编址最小的单位大小。
常量
分类
整型常量
表示形式
存储占用大小
默认的整型常量是int类型,占用4个字节。
实型常量
表示形式
存储占用大小
默认的实型常量是double类型,占用8个字节
demo
30 整型常量 int 占4个字节 1.234 浮点型常量 double 'm' 整型常量,int,占4个字节 "abc" 字符串常量
字符常量
C99标准的规定,'a'叫做整型字符常量(integer character constant),被看成是int型,所以在32位机器上占4字节。 ISO C++标准规定,'a'叫做字符字面量(character literal),被看成是char型,所以占1字节
本质为整型常量int类型,占用4个字节。
字符串常量
占用一个地址大小
注意常量背后的标识,多数是为了节省空间。
数据的存储与访问
类型转换
所有类型转换方式都基于这两个原理
原理1:小范围空间中的数据赋给大范围空间的变量。
不会造成数据的丢失,系统为了保证数据的完整正确性,还提供了符号扩充行为。
关于符号扩充
根据源数据的类型,判断有无符号进行扩充: 如果源数据为无符号,则直接高位全部扩充补0 如果源数据为有符号,按最高符号位是1还是0为准,进行扩充补1或者0。
原理2:大范围空间中的数据赋给小范围空间的变量。
可能会发生截断,造成数据丢失。(大范围空间变量里所承载的数据大小大于小范围空间变量的范围时)
可能会是不同的解释,不会造成数据丢失。(小范围空间变量足够容纳大范围空间变量赋过来的数据时)
类型转换的分类
隐式转换
算术转换
整型提升
整形提升的由来
整型提升的设计由来: 可能因素有二: 1.在K&R和C89的早期实现中,基于`short`和`char`的算术运算陷入两难的困境,因为可能会产生两种不同的结果。因此,在C99中很明确地定义了整型提升的规则(6.3.1.1) 2.通常情况下,在对int类型的数值作运算时,CPU的运算速度是最快的。在x86上,32位算术运算的速度比16位算术运算的速度快一倍。C语言是一个注重效率的语言,所以它会作整型提升,使得程序的运行速度尽可能地快。因此,你必须记住整型提升规则,以免发生一些整型溢出的问题。
发生的时机
在进行算术运算时,表达式中存在低于32位的整型数据
使用sizeof()运算符来测试判断是否出现整型提升现象
char a=1, b=2, c=0; c = a+b; printf("sizeof(a) = %d\n",sizeof(a));//1 printf("sizeof(a+b) = %d\n",sizeof(a+b));//4 printf("sizeof(c) = %d\n",sizeof(c));//1 //因为a和c不是表达式,不存在整型提升,所以打印1
转换规则
概述方式1
整型提升只发生在这些类型中 char /unsigned char /short/unsigned short -> int(->unsigned int) 在 32 位机中,所有位为低于 32 的整型数据,在运算过程中先要转化为 32位的整型数据,。也就是说,各种整形首先要提升为int类型,注意,如果int类型不足以表示则要提升为unsigned int类型;然后才执行表达式的运算。
概述方式2
如果在算术表达式中含有char、short int或者int型位段(bit-field),包括它们有符号或无符号变形,以及枚举类型的时候。如果int可以完整表示源类型的所有值,那么该源类型的值就转换为int,否则转换为unsigned int。
混合提升
发生的时机
不同类型数据之间进行算术运算时,会将所有操作数转换为同种类型,并以此作为结果的类型,这种方式称为普通算术类型的转换。
转换规则
基于原理
算术转换时只会发生原理1,即小数据赋给大数据。
整型转为实型时,原则是加小数点。
赋值转换
发生的时机
发生在不同类型间的变量在进行赋值运算时
发生在函数参数传值,以及函数返回值
转换规则
不同范围大小的整型间的赋值
不同范围大小的实型间的赋值
整型和实型之间是可以相互赋值的,赋值的原则是,一个是加零,一个是去小数位。
基于原理
赋值转换时两种原理情况都有可能发生。
强制转换
发生的时机
隐式类型转化,是有缺陷的,当隐式类型转化不能满足我们的需求时,就需要强制类型转化。,使用强制转换运算符()
转换规则
实型与整型间
不同范围大小的整型间
不同范围大小的实型间
基于原理
两种原理情况都有可能发生。
关于一些误区
格式输入输出函数中根本不存在类型转换! 而只是单纯的进行格式控制符选项与变量参数类型的之间的一一对应匹配问题。 printf("%d",50.78);//错误!,不会把浮点数50.78转成整型50
scanf,在从输入缓冲区搜集数据赋给变量的时候,不算类型转换 int main(void) { float a; scanf("%f",&a); printf("a = %f\n",a); return 0; } 控制台: 输入7回车 打印a = 7.000000 这不是类型转换。别搞混
用小数值常量给大空间变量(包括形参或变量)时,是不会发生整型提升的,所以不会有符号扩充现象。整型提升只发生在小变量赋给大空间变量的时候。
负数的数值常量赋给变量时也不是整型提升符号扩充。
printf函数的整型提升貌似也只是调用时把实参赋给形参时所发生的整型提升而已,但为什么只发生最多到32位的提升呢?
printf没有发生从32位以下,到64位的整型提升
代码佐证,QT5.14.1
int num = 0xffffffff;//等价于 int num = 4294967295; printf("%llu\n",num);//打印4294967295 或 printf("%llu\n",4294967295); //仍然打印的是4294967295,如果发生了整型提升就应该打印18446744073709551615
printf发生了从32位以下到32位的整型提升
代码佐证,QT5.14.1
//测试是否发生了short->int的整型提升 short int n = 0xFFFF; printf("%u\n",n);//打印4294967295 //测试是否发生了char->int的整型提升 char c = 0xFF; printf("%u\n",c);//打印4294967295 //都发生了整型提升,符号扩充全部1,即0xFFFFFFFF
在把数据赋给一个目标变量时,最终把什么样的数据赋到这个目标变量的空间中只取决于这个目标变量的空间大小,与其类型有无符号并没有关系
在读取访问一个变量时,访问出什么样的数据,取决于如何解释这个变量,与这个变量本身的类型有无符号并没有关系。
那何时会与变量的类型有关系?
在数据转存存储时,小变量赋给大空间变量时,
测试环境
把小变量赋给大空间变量时,会发生整型提升
代码佐证,QT5.14.1
//发生了整型提升,扩充1 1) int num2 = 0xffffffff;//等价于 int num = 4294967295; long long int num3 = num2;//变量赋给变量,发生了整型提升,并全面全部扩充1 printf("%llu\n",num3);//打印18446744073709551615 //发生了整型提升,扩充0 2.1) long long int num31 = 0xffffffff;//等价于 num31 = 4294967295;//常量数值初始化赋给变量,发生整型提升,扩充0 printf("%llu\n",num31);//打印4294967295 //发生了整型提升,扩充0 2.2) long long int num32; num32 = 0xffffffff;//等价于 num32 = 4294967295;//常量数值赋给变量,发生整型提升,扩充0 printf("%llu\n",num32);//打印4294967295
子主题 2
int m2 = 0xFFFF; printf("%u\n",m2);//0x0000FFFF long long int num32 = m2;//0x00000000 0000FFFF printf("%llu\n",num32);//65535 int m = 0xFF; //00000000 00000000 00000000 11111111 printf("%u\n",m);//00000000 00000000 00000000 11111111 long long int num3 = m;//00000000 00000000 00000000 00000000 00000000 00000000 00000000 11111111 printf("%llu\n",num3);//255 long long int num = 0xFF;//00000000 00000000 00000000 00000000 00000000 00000000 00000000 11111111 printf("%llu\n",num);//255 long long int num2; num2 = 0xFF;
子主题 9
在进行把数据(或变量里的数据)赋值给目标变量时,不看目标类型,原样赋进去,想想此时考虑符号扩充不?
在读取解释变量的数据时,要看原变量的类型是否是有无符号的, 想想此时考虑符号扩充不?
demo
int main2() { char a = 0xff; printf("%d\n",a); unsigned char b = 0xff; printf("%u\n",b);// unsigned int c = 255; printf("%u\n",c); unsigned int d = 0xff; printf("%u\n",d); char e = 0xff;// unsigned int f = e; printf("%u\n",f); }
demo2
微信群里的两个例子
分支主题 6
判断一个数有多少位数
子主题 1
打印一个数的二进制形式
打印一个数的十六进制形式
实现一个函数,实现一个数的各种进制之间的转换
打印一个数字的二进制,目的为了观察数值在计算机中的二进制表现形式
子主题 5
打印一个数字的二进制,目的为了观察数值在计算机中的二进制表现形式 #include <stdio.h> //C语言打印一个数值的二进制格式 void disBinary(char ch) { int i =8; while(i--) { if((1<<i)&ch) { printf("1"); } else { printf("0"); } if(i%4 ==0) { printf(" "); } } putchar(10); } int main() { char a =0; char b =1; char c =2; char d =127; char e =-128; char f =-2; char g =-1; disBinary(a); disBinary(b); disBinary(c); disBinary(d); disBinary(e); disBinary(f); disBinary(g); return 0; }
打印一个数存在内存中的补码二进制形式
与上边的不一样
交换两个变量内容,三种方法