a=b=c=18,三个设变量a,b,c分别存放被分配到相同的内存空间上

拍照搜题秒出答案,一键查看所有搜题记录

拍照搜题秒出答案,一键查看所有搜题记录

拍照搜题秒出答案,一键查看所有搜题记录

原标题:周立功:理解C语言设变量a,b,c分别存放三要素没那么难

周立功教授数年之心血之作《程序设计与数据结构》电子版已无偿性分享到电子工程师与高校群体,在公众號回复【程序设计】即可在线阅读书本内容公开后,在电子行业掀起一片学习热潮经周立功教授授权,本公众号特对本书内容进行连載愿共勉之。

“设变量a,b,c分别存放的值”保存在内存的某个地方如同使用门牌号确定地址一样,在内存中也给设变量a,b,c分别存放分配门牌號在C的内存世界中,门牌号被称为设变量a,b,c分别存放的地址即从设变量a,b,c分别存放中取值就是通过设变量a,b,c分别存放名找到相应的存储地址,然后读取该存储单元中的值而写一个设变量a,b,c分别存放就是将设变量a,b,c分别存放的值存放到与之相应的存储地址中去。

通常将用于存储数據的“位置”称为对象当将一个对象看作一个黑盒子时,如果将指定类型的值放入这个盒子则需要使用一个名字才能访问一个对象。假设命名后的对象称为设变量a,b,c分别存放iNum它有特定的类型int,类型决定将什么赋给对象比如,将0x64赋给int类型设变量a,b,c分别存放iNum以及可以使用嘚操作,比如多个int类型数据可以使用“*”操作进行乘法运算。

其中int为设变量a,b,c分别存放iNum的类型,iNum为设变量a,b,c分别存放名0x64为设变量a,b,c分别存放iNum的值。当声明一个设变量a,b,c分别存放时编译器会根据设变量a,b,c分别存放的类型预留足够的内存空间。设变量a,b,c分别存放的存储空间是系统自動分配的但此存储空间不会在程序的整个生命周期中永远存在。

值是被解释为一个类型的内存中的一组比特(bit)计算机内存不知道值嘚类型,只是将它保存起来因此只有决定内存如何解释时,内存中的bit才有意义比如,3.0的含义是什么只有使用单位时,才会决定3.0的含義

声明是命名一个对象的一条语句,定义是为一个对象分配内存空间的声明一个定义通常会提供一个初始值。比如:

当你声明一个设變量a,b,c分别存放时底层会分配一定大小的内存存储设变量a,b,c分别存放的信息。而分配多少内存则在编译期就已经确定了。为了能够访问无限量的内存C语言使用地址&操作符返回操作数的地址。当&运算符作用于一个设变量a,b,c分别存放时则返回的是设变量a,b,c分别存放的地址。对于設变量a,b,c分别存放iNum来说&iNum就是设变量a,b,c分别存放iNum的内存地址,详见图 1.3

图 1.3内存、设变量a,b,c分别存放与地址

从设变量a,b,c分别存放中取值就是通过设变量a,b,c分别存放名iNum找到与之相应的存储地址&iNum,然后读取存储在该地址中的值0x64写一个设变量a,b,c分别存放iNum就是将设变量a,b,c分别存放的值0x64存放到与之相應的存储地址&iNum中去。显然不能在&iNum前面再加“&”运算符,因为&iNum已经不是设变量a,b,c分别存放了而是一个不可修改的整型常量,即0x22FF74

为了便于描述设变量a,b,c分别存放,C语言将用于存储设变量a,b,c分别存放的内存地址的&iNum抽象为指向设变量a,b,c分别存放iNum的指针这些地址之所以称为指针,因为咜是“指向”一个设变量a,b,c分别存放的只需指出设变量a,b,c分别存放的地址(不是设变量a,b,c分别存放名),就可以确定该设变量a,b,c分别存放即指針的本质就是一个内存的地址,它指向内存的某个位置指针是一个地址,其强调的是当使用指针时要想到它是“内存地址”实际上,茬现实世界里既没有设变量a,b,c分别存放也没有指针设变量a,b,c分别存放和指针是对程序中的数据和存储空间的抽象。

为了展示设变量a,b,c分别存放嘚地址与设变量a,b,c分别存放的值的对应关系最好的方式是直接打印输出。比如输入2个整数,交换两者的值后输出即先将输入的整数存叺设变量a,b,c分别存放iNum1和iNum2,然后交换详见程序清单 1.3。注意不同的编译环境(不同的编译器、同一编译器的不同版本、编译参数不同等),設变量a,b,c分别存放的地址值(0x22FF74)可能会不一样

程序清单 1.3设变量a,b,c分别存放交换范例程序

请读者仔细观察,为何要在程序清单 1.3(6)中的设变量a,b,c汾别存放前添加“&”程序清单 1.3(6~7)中的&iNum1、&iNum2有什么区别?

为了理解某些操作符的限制标准C发明了L-value和R-value两个名词。虽然其被解释为左值和右徝但实际上是一个美丽的误会。因为L-value是指“locator value”不是“left vaue”其字面意思是“(在内存中)有特定位置的值”,即内存的索引值——地址洏R-value是指“read value”不是“right value”,其字面意思是“可读的值”比如:

虽然编译器为设变量a,b,c分别存放iNum分配了地址(L-value),其L-value是在编译期就确定了的地址“&iNum”而R-value是存储在设变量a,b,c分别存放iNum中的值0x64,但其赋值是在运行时

尽管iNum有地址,但iNum++表达式没有地址因此iNum只有R-value。虽然任何表达式都有R-value但呮有部分表达式有L-value。

如果数据从低位到高位用最左位和最右位表述则一定会产生歧义,因此使用最低有效位(Least SignificantLSB)和最高有效位(Most Significant,MSB)汾别表示数据的最低位和最高位对于有符号数来说,最高有效位就是符号位假设设变量a,b,c分别存放的值用二进制表示为:

根据数据中各個字节在连续字节序列中排列顺序的不同,分为2种排列方式:大端和小端如果A中存放的是数据的MSB最高有效位,即为大端模式(详见图1.4(a));如果A中存放的是数据的LSB最低有效位即为小端模式(详见图1.4(b))。注意CPU究竟采用何种存储模式取决于硬件,与编译器无关Intel x86计算机的CPU就是小端模式。当计算机对存储单元进行编号时则每个地址编号中只存放一个字节。C规定多字节的int、float、doublie类型设变量a,b,c分别存放必须占用相邻的存储单元且将存储单元的最低地址作为设变量a,b,c分别存放的地址。假设一个32位设变量a,b,c分别存放占用地址为A、A+1、A+2和A+3存储单元则設变量a,b,c分别存放的地址为A。如果有“int iNum = 0x64;”那么A中到底存放的是4个字节00H、00H、00H、64H中的哪个字节呢?

1.4 32位设变量a,b,c分别存放的存储

大家可能知道莋者的名字叫周立功,但作者在家里还有一个别名——小兵其实都是同一个人。同样如此我们也可以给设变量a,b,c分别存放的类型取一个別名。如果在“int iNum;”定义前添加typedef即:

此时,iNum等同于int类型为了便于理解,将iNum替换成INT32比如:

typedef的用途是声明类型的别名,它只是为某个已经存在的类型增加了一个新的名字那么利用这一特性,就可以定义设变量a,b,c分别存放了比如:

除此之外,INT32还可用于类型转换比如:

为何還要为int再取一个名称呢?主要是为了提高程序的可移植性比如,某种微处理器的int为16位long为32位。如果要将该程序移植到另一种体系结构的微处理器假设其int为32位,long为64位而只有short才是16位的,因此必须将程序中的int全部替换为shortlong全部替换为int,不仅修改工作量巨大且容易出错如果茬程序中全部用新取的名称,那么只需要修改定义的这些新名称即只要将以前的:

在编程中使用typedef的好处,除了为设变量a,b,c分别存放取一个簡单易记且意义明确的新名称之外其最主要的作用是使用typedef构造新的数据类型。而不要误认为typedef的作用仅仅是简化更复杂的类型声明将在後续的章节中详细阐述。

由此可见C语言设变量a,b,c分别存放的内涵包括3个要素:设变量a,b,c分别存放的类型、设变量a,b,c分别存放的值和设变量a,b,c分别存放的地址。

● 设变量a,b,c分别存放的类型:即设变量a,b,c分别存放存储的数据的类型程序如何解释设变量a,b,c分别存放保存的数据。比如int类型设變量a,b,c分别存放,任何引用存储在设变量a,b,c分别存放中的数据都被程序解释为整数数据类型分为基本类型(字符型与数值型)、构造类型(數组型、结构体型、联合体型、枚举型与位域型)、指针类型与void *类型,所有其它的类型都是通过组合的方式从基本类型构造而来的

● 设變量a,b,c分别存放的值:程序根据设变量a,b,c分别存放的类型解释存储在设变量a,b,c分别存放中的数据,数据分为不可修改的常量的值和可以修改的设變量a,b,c分别存放的值

● 设变量a,b,c分别存放的地址:即设变量a,b,c分别存放在内存中的位置,当利用一个设变量a,b,c分别存放存储一个数据时则程序將数据存储到设变量a,b,c分别存放的地址所指示的存储单元中。

在计算机中常用的数据单位有位、字节、半字和字,微处理器根据位数的不哃支持8位字节、16位半字或32位字的数据类型

位(bit):它是一个二进制数的位,位是计算机数据的最小单位一个位只有0和1 两种状态(21)。为叻表示更多的信息必须将更多位组合起来使用,比如两位二进制数就有00、01、10、11四种状态(22),依此类推

字节(Byte):一个8位二进制数称為一个字节,即1B=8bit那么一个字节就可以表示0-255种状态或十六进制0~FF之间的数,8位微处理器的数据是以字节方式存储的

半字:从偶数地址开始连续的2个字节构成一个半字,半字的数据类型为2个连续的字节有些32位微处理器的数据是以半字方式存储的,比如32位ARM微处理器支持的Thumb指令的长度刚好是一个半字。

字:以能被4整除的地址开始的连续的4个字节构成1个字字的数据类型为4个连续的字节,32位微处理器的数据铨部支持以字方式存储的格式

当需要带符号的signed整数时,虽然可以单独指定某位为1表示“符号”但计算机不会简单地将“符号位”加到┅个数上去。计算机如何存储负数

由于计算机中无减法器,那么最好的方法就是将减法也通过加法器来完成为了解决负数在计算机中嘚存储问题,这里有必要引入补码的概念可以举个例子说明一下,指针式钟表假如要将时针从5点拔到2点,有2种拔法一种是逆时针拔3個时格,相当于5减3等于2;另一种拔法是顺时针拔9个时格相当于5加9也等于2,即对于这种模为12计数制来说9和3互补,9是3的补码反之亦然。對于刚才时钟的拔法可以写出如下算式:

由此可见既然补码的概念是为了方便减法运算而引入的,那么不妨约定:其最高位为符号位吔就是说,其最高有效位的数字具有不同的“权值”当最高有效位为0时,其权值为2n-1否则其权值为-2n-1。比如当一个8位二进制数被解释为┅个无符号数数时,那么其十进制数的多项式求值结果为:

如果将()2解释为一个带符号数时则其十进制数的多项式求值结果为:

当约萣用最高有效位作为符号位来确定singned整数后,即可使用“补码”将符号位和其它位统一处理那么减法也就可以当作加法来处理了。求负数補码的规则如下:

当一个n位二进制数的原码为N时它的补码定义为(N)补=2n-N

当一个8位二进制数的原码为1时,其补码定义为 (28-1)10 = (255)10 = (即将该数绝对徝原码所有为取反,然后将结果加1其计算过程为:

当用补码表示两个数相加时,如果最高位(符号位)有进位则进位位被舍弃。正数嘚补码与其原码一样而负数的补码,其符号位为1将该数绝对值原码的所有位取反,然后将结果加1由此可见,在计算机中数值一律用補码表示(存储)比如,计算8位二进制数减法(图 1.5):

当一个n位二进制数用于表示unsigned数时其可能的取值范围为0~2n-1,比如一个8位数的范围在0~28-1(0~255)之间。假设在一个8位unsigned数248上加10那么需要9位才能存储正确的结果258,而正确结果258与实际结果2(最低8位有效位)之间的差(256)對应的第9位被丢弃了因此它不能存储在结果中。

当用于表示signed数时则只有一半用于正值,一半用于负值因此n位二进制数的补码带符号數的整个取值范围为-2n~2n-1,即一个8位二进制数可以保存一个范围在-27~27-1(-128~127)之间的带符号值因此对于signed数,当加上具有不同符号的数或減去具有相同符号的数时将永远不会溢出。如果将两个具有相同符号的整数相加或将两个具有不同符号的整数相减时,将可能发生溢絀比如,从一个8位signed整数-120中减去20:

而其正确的结果-140需要9位才能存储因此正确结果-140与实际结果116(最低8位有效位)之间的差(256)对应的第9位被丢弃了,它不能存储在结果中由于整数数据类型的溢出是悄悄发生的,因此一定要注意每个设变量a,b,c分别存放可能的取值范围

下面将從整数的二进制表示开始,这里的整数被数学家称为“自然数”即计算机程序员口中的“正整数”。此外数学家还定义了用两个整数嘚比值表示的一类数,称为有理数或比如,3/4是一个有理数也可以将3/4表示为十进制小数0.75。尽管可以将它写成十进制数的形式但它实际仩代表一个分数,即75/100

在十进制数字系统中,虽然小数点左边的数的每一位都和10的正整数次幂相关其右边的数的每一位都和10的负整数次冪相关,但还是有一些有理数很难表示为小数最明显的例子是1/3,其结果为0....在小数点后面有无穷个3尽管如此,将1/3表示为小数还是不方便但它毕竟是一个有理数,因为在本质上它是两个整数的比

无理数更是一些奇特的数,比如2的平方根等。它们不能表示为两个整数的仳即其小数部分是无穷的,而且毫无规律到此为止讨论的所有数——有理数和无理数都统称为实数。使用实数定义它们的目的是为了將其与虚数区别开来虚数是负数的平方根,而实数与虚数又构成了复数

通常人们习惯将数字看成连续的,任意给出的两个有理数都鈳以找出一个位于它们之间的数,实际上只要取这两个数的平均值即可但计算机却无能为力,因为二进制中的每一位非0即1两者之间没囿任何数。这一特点决定了计算机只能处理离散数据因此二进制数的位数直接决定了所能表示的离散数值的个数,比如对于8位数来说,所能表示的自然数的范围为0~255如果要在计算机中存储4.5这个数,则需要选择新的表示方式

小数可以表示二进制数吗?最简单的方法是使鼡BCD码(二进制编码的十进制数)通常将两个BCD数一起使用,这种方式称为压缩BCD由于2的补数不和BCD数一起使用,则压缩BCD需要增加1位用于标识數的正负该位被称作符号位。虽然用一个字节保存某个特定的BCD数是非常方便的但要为这个短小的符号位牺牲4位或8位的存储空间。假设計算机程序要处理的钱款数目在+/-100万之间则表示前的数目的范围为:

因此保存在存储器中的一笔钱的金额都需要5字节。比如-4,325,120.25可以表示为:

将每个字节转换成十六进制数,则上面的数可以等价地表示为14H 32H 51H 20H 25H最左边的半个字节所构成的1用于指明该数是负数,这个1即符号位如果這半个字节所构成的数是0,则说明该数是整数组成该数的每一个数字都需要用4位表示,从十六进制的表示形式中可以很直观地看到这一點如果将数的范围扩大,则需要更多的字节来实现

这种基于二进制的存储和标记方式被称为定点格式,所谓的“定点”是指小数点的位置总是在数的某个特定位置但在表示非常大或非常小的数时,使用定点格式数是不合适的因此科学家和工程师喜欢使用一种称为“科学计数法”的方法记录这类较大或较小的数,利用这种计数系统可以更好地在计算机中存储这些数

科学计数法将每个数表示为有效位與10的幂的乘积的形式,从而避免了写一长串的0比如,490,000,000,000可以表示为4.9×1011而0.可以表示为2.6×10-10。在这两个例子中4.9和2.6被称为小数部分或首数,有時也称为尾数在计算机术语中,这部分被称为有效数为了保持一致,在这里将科学计数法表示形式中的这一部分称为有效数

采用科學计数法表示的数可以分为两部分,其中的指数部分用于表示10的几次幂指数可以表示小数点相对于有效数移动的距离。为了便于操作規定有效数的取值范围是大于或等于1而小于10。比如4.9×1011这种写法被称为科学计数法的规范化。这里需要说明指数的正负性只是表明了数嘚大小,它不能指明数本身的正负性

在计算机中,对于小数的存储方式除了定点格式外还有一种选择,它被称为浮点格式因为浮点格式是基于科学计数法的,所以它是存储极大或极小数的理想方式但计算机的浮点格式是借助二进制数实现的科学计数法形式,因此需偠先了解如何用二进制表示小数

在二进制数中,二进制小数点右边的数字和2的负整数次幂相关比如,将101.1101转换为十进制数为:

将乘数和除数用2的整数次幂替换即:

2的负整数次幂等于从1开始反复除以2,即:

经过计算后得出101.1101与十进制数5.8125是相等的

在二进制的科学计数法中,規范化的有效数应该大于或等于1且小于10(十进制的2)则101.1101的规范格式为1.,这个规则暗示这样一个有趣的现象在规范化的二进制浮点数中,小数点的左边只有一个1除此之外没有其它数字。

在计算机中处理浮点数所遵循的标准是由IEEE于1985年制定的IEEE浮点数标准定义了两种基本格式:以2字节表示的单精度格式和8字节表示的双精度格式。单精度格式的4个字节分为三个部分:1位符号位(0代表整数1代表负数),8位用做指数最后的23位用做有效数。下面给出单精度格式的三部分的划分方式其中有效数的最低位在最右边:

对于二进制科学计数法的规范格式,其有效数的小数点左边有且仅有一个1而在IEEE浮点数标准中,这一位没有分配存储空间仅存储有效数的23位小数部分,尽管存储的只有23位但仍称其精度为24位,将在下面的内容中体会24位精度的含义

8位指数部分的取值范围为0~255,称为偏移指数它的意思是——对于有符号指數,为了确定其实际所代表的的值必须从指数中减去一个值——称为偏移量对于单精度浮点数,其偏移量为127如果指数的取值范围为0~254,那么对于一个特定的数可以用s(符号位)、e(指数)和f(有效数)来描述。即:

其中-1的s次幂是数学上所采用的一种巧妙的方法,其含義为:如果s=0则该数是正的(因为任何数的0次幂都是1);如果s=1,则该数是负的(因为-1的1次幂等于-1)

表达式中的中间部分1.f,其含义为:1的後面是小数点小数点后面跟着23位的有效数。1.f与2的幂相乘其中的指数等于内存中的8位偏移量指数减去127。现在我们来讨论一种特殊情况其详细介绍如下:

(a)如果e=0且f=0,则该数为0通常将23位都设置为0以表示该数为0,当符号位置1时这种数解释为负0,而负0可以表示非常小的数虽然这些数极小以至于不能在单精度格式下用数字和指数表示,但它仍然小于0

(b)如果e=0且f≠0,虽然该数是合法的但不符合规范。这類数可以表示为:

注意在有效数中,小数点的左边是0

(c)如果e=255且f=0,则该数被解释为无穷大或无穷小其取决于符号位s的值。

(d)如果e=255苴f≠0则该值被解释为“不是一个数”,通常被缩写为NaN(not a number)NaN用于表示未知的数或非法操作的结果。

在单精度格式下可以表示的规格化嘚最小正负二进制数为:

在单精度格式下,可以表示规格化的最大正负二进制数为:

在十进制下这两个数近似地等于1.×10-38和3.×1038,这就是单精度浮点数的有效表示范围

一般来说,10位二进制数可以近似地用3位十进制数表示其含义是,如果将10都置为1即十六进制的3FFH或十进制的1023,它近似地等于将十进制数的3位都置为9即999,可以表示为210≈103两者的关系意味着:单精度浮点格式存放的24位二进制数大体上与7位的十进制數相等。因此可以说单精度浮点数提供24位的二进制精度或7位的十进制精度其深层含义是什么呢?

当我们查看定点数时其精确度是非常奣显的。比如当用两位定点小数表示钱时,可以精确到分但对于浮点格式的数来说,就不能如此肯定了其精确度依赖于指数的值,囿时浮点数可以精确到比分还小的单位但有时其精确度甚至达不到元。因此这样说可能更合适:单精度浮点数的精度为1/224或1/,或百万分の六但其真正的含义是什么?

也就是说在单精度浮点格式下,16,777,216和16,777,217表示为同一个数不仅如此,处于两个数之间的所有的数都表示为同┅个数由此可见,在程序中使用单精度格式表示浮点数也会出现问题这时可以考虑使用双精度浮点数。

双精度浮点数需要用8字节表示它的结构如下:

双精度浮点数的指数偏移量为1023,或十六进制的3FFH因此以该格式存储的数可以表示为:

在单精度格式下,提到的单精度浮點格式下的0无穷大(小)和NaN的判断规则同样适用于双精度浮点格式。双精度浮点数所能表示的范围用十进制可以近似记为:

10的308次幂是┅个非常巨大的数,在1的后面跟着308个0双精度浮点格式的有效数为53位(IEEE754规定隐藏位1的位置在小数点之前),大致相当于十进制的16位

关于浮点数的用法,与整数不同的是浮点数是有精度的而初学者常在这些看起来似乎很不起眼的问题上阴沟里翻船,其相应的范例程序详见程序清单 1.4

程序清单1.4 浮点数误差测试范例程序(1)

通过上机实践你会发现,其输出结果不是111而是000,这是因为float型设变量a,b,c分别存放仅能接收浮点数常量的7位有效数字在有效数字后面输出的数字都是不准确的。也就是说浮点数在计算机中存储的都是近似值,比如10进制的0.1,鼡2进制来表示的话则是0.......的无限循环,所以不能将浮点设变量a,b,c分别存放用“==或!=”与任何数比较而初级程序员却常常容易犯以下这样的错誤,详见程序清单

程序清单 1.5 浮点数误差测试范例程序(2)

对于浮点数来说只要足够接近0,就应该认为它的值为0其合法的比较语句如下:

数据类型是值的集合(内置类型或其它类型)和在这些值上的操作(函数)集,当执行一个操作时必须确保操作数和结果具有正确的類型,忽视这一点是程序设计中的常见错误

如果两个表达式的类型相容,则编译器对操作数隐式地进行自动转换隐式类型转换是将范圍窄的的数类型转换为范围更宽的数的类型。也就是说一些设变量a,b,c分别存放在编译期与运行时的结果不一样。即:

计算的结果将以转换後的类型表示这样可以保证结果尽可能准确。如果表达式的操作数分别为int型和double型则int型的操作数被转换为double型。比如:

其结果为c = 7.521a = 7(即截尾操作)。另有:

其中a为unsigned int,b为int编译器会隐式地将int转换为unsigned int。9对应的二进制数为0xFFFFFFF94对应的二进制数为0xFFFFFFF4,而-4的补码形式为原码4取反加1即b为0xFFFFFFFC,则a/b为0如程序清单 1.6所示的范例程序,这是招聘软件工程师经常考察的经典试题

程序清单 1.6设变量a,b,c分别存放的类型自动转换范例程序

解题思路:大多数初学者会认为,0xFF取反后得到0x00因为a和b在编译期都是char类型设变量a,b,c分别存放,所以结果为a == ~b而实际上在32位计算机中,表达式在运荇时将unsigned char型的设变量a,b,c分别存放自动转换成了unsigned int型设变量a,b,c分别存放即~b = 0xFFFFFF00,显然其结果a != ~b

除了隐式类型转换外,有时还需要强制类型转换比如,i昰一个int值那么表达式“double i;”就会对i的值进行强制类型转换,使该表达式变成double类型

在公众号后台回复关键字【程序设计】,可在线阅读全書

致远电子官方微信公众号,一个汇聚500名工程师的研发测试分享平台为您提供电子行业领先的产品技术与解决方案

表达式1值为假时整个表达式嘚值一定为假,所以就不会再计算表达式2的值

今天做到一题面试题如下 读箌第9行的时候发现和自己理解的有出入。

个人理解函数名是函数的入口地址就是一个人指针那为什么还要取地址再赋值给p呢?

带着疑惑百度了一番发现网上没人发现程序中的问题,所以就记录一下希望以后做到这题的同学不再被误解。

//代码为网上复制的存在许多问題
//运行的结果提示: 程序中有游离的‘\343’等一系列错误
 


左: 定义一个形参为int型,返回值为int型的函数p(p为函数的入口地址)。
右: 将函数inc()的入ロ地址 取 地址;
左右明显是不同的数据类型

//请看到这篇文章的同学,帮我一下谢谢。

我要回帖

更多关于 设ab和c都是int型变量 的文章

 

随机推荐