数据类型转换

数据类型转换
各类整数之间的转换
C语言中的数分8位、16位和32位三种。属于8 位数的有:带符号
字符char,无符号字符unsigned char 。属于16位数的有:带符号整
数int,无符号整数unsigned int(或简写为unsigned), 近指针。属
于32位数的有:带符号长整数long,无符号长整数 unsigned long,
远指针。
IBM PC是16位机,基本运算是16位的运算,所以,当8位数和16
位数进行比较或其它运算时,都是首先把8 位数转换成16位数。为了
便于按2的补码法则进行运算,有符号8位数在转换为16位时是在左边
添加8个符号位,无符号8位数则是在左边添加8个0。当由16位转换成
8位时,无论什么情况一律只是简单地裁取低8位,抛掉高8 位。没有
char或usigned char常数。字符常数,像"C",是转换为int以后存储
的。当字符转换为其它 16 位数(如近指针)时,是首先把字符转换为
int,然后再进行转换。
16位数与32位数之间的转换也遵守同样的规则。
注意,Turbo C中的输入/输出函数对其参数中的int和unsigned
int不加区分。例如,在printf函数中如果格式说明是%d 则对这两种
类型的参数一律按2 的补码(即按有符号数)进行解释,然后以十进制
形式输出。如果格式说明是%u、%o、%x、%X,则对这两种类型的参数
一律按二进制 (即按无符号数) 进行解释,然后以相应形式输出。在
scanf函数中,仅当输入的字符串中含有负号时,才按2的补码对输入
数进行解释。
还应注意,对于常数,如果不加L,则Turbo C一般按int型处理。
例如,语句printf("%081x",-1L),则会输出ffffffff。如果省略1,
则输出常数的低字,即ffff。如果省略L,则仍会去1个双字,这个
双字的就是int常数-1,高字内容是不确定的,输出效果将是在4个乱
七八糟的字符之后再跟ffff。
在Turbo C的头文件value.h中,相应于3 个带符号数的最大值,
定义了3个符号常数: #define MAXSHORT 0X7FFF
#define MAXINT 0X7FFF
#define MAXLONG 0X7FFFFFFFL 在Turbo C Tools中,包括3对宏,分别把8位拆成高4位和低4位,
把16位拆成高8位和低8位,把32位拆成高16位和低16位。 uthinyb(char value) utlonyb(char value)
uthibyte(int value) utlobyte(int value)
uthiword(long value) utloword(long valueu)
在Turbo C Tools中,也包括相反的3 个宏,它们把两个4位组成
一个8位,把两个8位组成一个16位,把两个16位组成一个32位。 utnybbyt(HiNyb,LoNyb)
utwdlong(HiWord,Loword)
utbyword(HiByte,LoByte)实数与整数之间的转换
Turbo C中提供了两种实数:float和 double。float 由32 位组
成,由高到低依次是:1个尾数符号位,8个偏码表示的指数位(偏值=
127),23个尾数位。double由64位组成,由高到低依次是:1 个尾数
符号位,11个
偏码表示的指数位(偏值=1023), 52个尾数位。通过下
列公式,可以由尾数和指数计算出所代表的实数值: X=±1.尾数*2(指数-偏值) 下列几种情况下,此公式不成立: 指数=0且尾数 =00...0,则X=X=±0
指数=0且尾数!=00...0,则X=±0.尾数*2(1-偏值)
指数=11....1且尾数 =00...0,则X=±∞
指数=11....1且尾数!=00...0,则X是一个无效数
在Turbo C的头文件value.h中,相应于实数所能达到的最大最小
值,定义了如下几个符号常数: #define MAXFLOAT 3.37E+38
#define MINFLOAT 8.43E-37
#define MAXDOUBLE 1.797693E+308
#define MINDOUBLE 2.225074E-308 实常数是按double格式存放的,如果想按float 格式存放,则必
须加后缀F,如:1.23E+4F。
当把实数强制转换为整数时,采取的是“向零靠拢的算法",如: float值: 65432.6 -65432.6
转换为long; 65432 -65432
转换为unsigned: 65432 104
如果不希望“向零靠拢”,而希望“四舍五入”,则必须由程序员自
己处理。一种办法是先加上(符号位/2),然后再进行类型转换。应该
注意的是:如果被转换的实数值超过了目标类型的表达范围,则会产
生错误。例如上面的float值-65432,6转换为unsigned int值时,由
于超过了目标范围,所产生的104就是错误的。在65432。6转换为
unsigned int的65432以后,可以用printf的%u格式输出,如果用 %d
格式输出,则会得到错误的结果。指针说明
指针是包含另一变量的地址变量。它的一般说明形式,如
int *fd, 其fd是一个指向整型变量的指针。比较复杂的指针说明,
bopp-yt
如*(*pfpi)(),可按如下几个原则来理解:以标识符为中心,一对方
括号一般表示数组,一对圆括号一般表示函数或强调某一优先顺序,
方括号对和圆括号对为同一优先级,方括号和圆括号比*号优先级高。
以下几例解释了这些原则的应用。
int *fip(),因圆括号优先级高,帮fip 先与圆括号结合,说明
fip是一个函数,这个函数返回一个指向整数的指针。 int (*pfi)(),因两对圆括号为同一优先级,故从左到右,pfi
是一个指针,这个指针指向一个函数,这个函数返回一个整数。 int *par[],因方括号比*号优先级高,故par是一个数组,这个
数组的每一个元素是指向整数的指针。
int (*ptr)[],因方括号与圆括号为同一优先级,故ptr 是一个
指针,这个指针指向一个数,这个数的每一个元素是一个整数。 int *(*pfpi)(),pfpi是一指针,这个指
针指向一个函数,这个快装脚手架
函数返回一个提向整数的指针。指针与地址
指针在使用之前必须初始化,给指针赋地址的方法一般有如下几
种:
第一种很容易理解,通过取地址操作符取变量(包括结构变量)的
地址,如:
char_c="a", *ptr_char;
ptr_char=&c; 第
二种是数组,因为不带方括号的数组名等效于数组中第一个元
素的地址,即数组名也可看作是一个指针,所以有两种办法。如: char myname[31], *ptr;
ptr=myname;
或 ptr=&myname[0];
第三种是动态分配的一块内存,这时往往带有类型强制转换,但
应注意当内存不够时,可能返回一个空(NULL)指针。如: struect complex {double real, image; };
struct complex *ptr;
ptr={ struct complex *)malloc(sizeof(struct complex)); 第四种是函数,与数组名一样,函数名也可以当作一个地址,于
是可以把函数名赋给一个指向函数的指针。函数名后面跟一个带圆括
号的参数表意味着计算函数的值,但仅有一个函数名则意味着指向函
数的指针。如:
double (*fx)();
double quad_poly(double);
fx=quad_poly;指针运算
烘手机常见的指针运算有:指针加或减一个数,指针增量,指针减量,
指针比较等等。假设P是某数组第1个元素的指针,则P+N 就是这个数
组中第n 个元素的地址,每个元素占多少存储单元则由指针所指数组
的类型来确定。但有两点要注意:
第一,上面这段话是C语言对指针运算的普遍规律,但具体到种C
编译则有所不同,尤其是在80X86类型的机器上。Turbo C和
Microsoft C 6.0以前的版本把指针分为near、far、huge,
Microsoft C 6.0又增加了based。在这几种指针中,只有huge严格遵
守上面的指针运算规则,详见下一节。
第二,当指针应用于数组尤其是多维数组时,有时容易弄错,下
表说明了数组法与指针法的区别。
│ 1维 │ 2维 │ 3维
━━━━━━━━━━┿━━━━━┿━━━━━━━┿━━━━━━━━━━
数组说明 │int x[] │int y[][] │int z[][][]
指针说明 │int *xptr │int *yptr[] │int *zptr[][]
数组法表示某元素地址│&x[i] │&y[i][j] │&z[i][j][k]
指针法表示某元素地址│ptr+i │*(yptr+i)+j │*(*(zptr+i)+j)+k
数组法存取某元素 │x[i] │y[i][j] │z[i][j][k]
指针法存取某元素 │*(ptr+i) │*(*(yptr+i)+j)│*(*(*(zptr+i)+j)+k)指针分类
在C 语言教科书上,指针就是指针,不存在分类的问题。我们经
常说“指向整数的指针”,“指向结构的指针”,“指向函数的指针”
等等,只是说指针指向不同的目标,而不是说指针本身有什么区别。
但是,在以80X86为基础的微机上实现C 语言时,由于80X86的物理地
址空间不是线性连续的而是分段的,为了提高效率,就必须对指针加
以分类。各类指针的运算法则也不一样。Turbo C 2.0及以前的版本,
Microsoft C 6.0以前的版本,指针都是分类三类,近(near),远
(far),巨(huge)。Microsoft C 6.0版本中,出现了一种新的指外类
型,这就是基(based)指针。 基指针综合实现了近和远指针的优点,
像近指针那么小那么快,又像远指针那样可以寻址缺省数据段以外
的目标。基指针这个名字就反映了这类指针上的实现方法:它是以程
序员指定的某一地址为段基址。如果在C 源程序中使用了基指针,编
译程序一般先把所指定的段基址装在ES寄存器内。在缺省的数据段内,
程序员一般不会使用基指针。但若同时要使用多个数据段,基指则有
其明显的优点。
一、近(near)指针
近指针是用于不超过64K 字节的单个数据段或码段。对于数据指
针,在微、小和中编译模式下产生的数据指针是近指针,因为此时只
有一个不超过64K 字节的数据段。对于码(即函数指针)指针,在微、
小和紧凑编译模式下产生的码指针是近指针,因为此时只一个不超过
64K字节的码段。本章将只讨论数据指针。
近指针是16位指针,它只含有地址的偏移量部分。为了形成32位
的完整地址,编译程序一般是反近指针与程序的数据段的段地址组合
起来。因为在大部分情况下程序的数据段的段地址是装在DS寄存器内,
因此一般没有必要装载这个寄存器。此外,当用汇编语言和C 语言混
合编程时,汇编语言总是假设DS含有数据目标的地址。
虽然近指针占用空间最小,执行速度最快,但它有一个严格的限
制,即只能64K字节以内的数据,且只能存取程序的数据段内的数据。
如果在小模式下编译一个程序,而这个程序企图增量一个近指针使之
超过第65536个字节,则这个近的指针就会复位到0。下面就是这样一
个例子:
char _near *p=(char _near *)0xffff;
p++;
由于近指针的这个严重限制,所有在比较大或比较复杂的程序中,
都无法使用。二、远(far)指针
远指针不是让编译程序把程序数据段地址作为指针的段地址部分,
而是把指针的段地址与指针的偏移量直接存放在指针内。因此,远指
针是由4 个字节构成。它可以指向内存中的任一目标,可以用于任一
编译模式,尽管仅在紧凑、大和巨模式下远指针才是缺省的数据指针。
因为远指针的段地址在指针内,熟悉80X86 汇编语言的人都知道,这
意味着每次使用远指针时都需要重新装载段寄存器,这显然会降低速
度。
应该注意:尽管远指针可以寻址内存中的任一单元,但它所寻址
的目标也不能超过64K 字节。这是因为,远指针在增量或减量之类的
算术运算时,也只是偏移量部分参与运算,而段地址保持不变。因此,
当远指针增量或减量到超过64K字节段边界时就出错。例如: char far *fp=(char far *)0xb800ffff;
fp++; 在指针加1以后,fp将指向B800:0000,而不是所希望的
C800:0000。
此外,在进行指针比较时,far指针还会引起另外一些
问题。far
指针是由偏移量和段地址这样一对16位数来表示的,对于某一实际内japanese tecther2
存地址,far指针不是唯一的,例如,far指针1234:0005、1230:0045、
1200:0345、1000:2345、0900:9345等都是代表实际地址12345,这样
会引起许多麻烦。
第一,为了便于与“空”(NULL)指针(0000: 0000)进行比较,当
关系操作符“==”和“!=”用于对far 指针进行比较时,比较的是全
部32位。否则,如果只比较16位偏移量,那么任何偏移量为0 的指针
都将是“空”(NULL)指针,这显然不符合一般使用要求。但在进行这
32位比较时,不是按20位实际地址来比较,而是把段地址和偏移量当
作一个32位无符号长整数来比较。对于上面这个例子,假设这些指针
分别叫作a、b、c、d、e,尽管这5个far 指针指向的都是同一内存单
元,但下列表达式运算的结果却都为“假”,从而得出错误的结论:
if(a==b)....
if(b==c)....
if(c==d)....
if(d==e)....
if(a==c)....
if(a==d)....
数字电视接收器
第二,当用“>”、“>=”,“<”和“<=”关系操作符对指针进
行比较操作时,比较的仅仅是偏移量部分,即按无符号的16位整数进
行比较。因此,对于上面这个例子,下列表达式运算的结果将都为
“真”,也得出错误的结论:
if(e>d)....
if(d>c)....
if(c>b)....
催化剂12.1if(b>a)....
if(e>a)....三、巨(huge)指针
只有巨指针才是一般C 语言教科书上所说的指针,它像远指针也
占4个字节。与远指针的显著差别是:当增量或减量超过64K字节段边
界时,巨指针会自动修正段基址的值。因此,巨指针不但可以寻址内
存中的任一区域,而且所寻址的数据目标可以超过64K字节。例如:
char huge *hp=(char huge *)0xb800ffff;
hp++; 在指针加1后,hp将指向C800:0000。但是,巨指针总是比较慢的,
因为编译必须生成一小段程序对指针进行32位而不是16位的加减运算。
此外,由于huge指针是规则化指针,每一个实际内存地址只一个
huge指针,所有在指针比较时不会产生错误。四、基(based)指针
前面已经说过,巨指针综合了近指针和远指针的优点。像近指针
一样,基指针只占两个字节,这两个字节是地址的偏移量。像远指针
一样,基指针可以寻址内存中的任一区域。近指针的段地址隐含地取
自程序的数据段,远指针的段地址取自指针本身,基指针的段地址取
法以及基指针的许多技术和应用问题,请见第11章。
五、各类指针之间的转换
far指针可以强制转换为near 指针,做法很简单,抛掉段地址只
保留偏移量。near指针也可以转换为far指针,Turbo C的做法是从相
应的段寄存器中取得段地址。
far指针有时也需要转换为huge 指针,以便对指针进行比较或做
其它操作。一种方法是通

本文发布于:2024-09-22 07:11:21,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/171347.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:指针   地址   数据
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议