📄 数据类型转换.01
字号:
函数返回一个double型数。然后把函数名quad_poly 赋给这个指针。
通过fx(x)引用这个函数,取得函数的返回值。
有时候,程序中要用到多个函数,这些函数有相同的参数要求和
相同的返回值类型。但这些函数不是同时都要用到,而是根据不同的
情况每次仅调用其中的一个。比较笨拙的办法就是用switch语句去实
现,虽然也还清楚,但程序显得冗长。用函数指针则显得精练多了,
如下面的例子所示: #include<stdio.h>
#include<conio.h>
#include<math.h>
#define MAX 3
main()
{
double x;
const double delta=1.0;
const double first=0.0;
const double last=1.0;
double (*fx[MAX])();
int i;
char ch;
double quad_poly(double);
fx[0]=quad_poly;
fx[1]=sqrt;
fx[2]=log;
for(i=0;i<MAX;i++){
x=first;
while(x<=last){
printf("f(%1f)=%1f\n",x,fx[i](x));
x+=delta;
}
printf("press any key to continue");
ch=getche();
clrscr();
}
}
double quad_poly(double x)
{
double a=1.0,b=-3.0,c=5.0;
return ((x*a)*x+b)*x+c;
}
在这个例子里,有三个数学函数 quad_poly, sqrt和log,它们
都只要一个dboule型参数,都返回一个double型数。程序中通过语句
double (*fx[MAX])()说明fx是函数指针数组,每一个函数都返回
double型数。然后用三个函数的名字初始化这个函数指针数组,通过
fx[i](x)在每次循环中各引用一个不同的函数,取得函数的返回值。
第2 种用途是通过函数指针,把一个函数作为参数传递给另一个
函数,请看下面这个例子: #include<stdio.h>
main()
{
double x;
const double delta=0.01;
const double first=0.0;
const double last=10.0;
double (*fx)();
double quad_poly(double);
double find_largest(double,double,double,double (*fx)());
fx=quad_poly;
printf("The largest value in the range %1f->%1f",first,last);
printf("is %1f\n",find_largest(first,last,delta,fx));
}
double quad_poly(double x)
{
double a=1.0,b=-3.0,c=5.0;
return ((x*a)*x+b)*x+c;
}
double find_largest(double a,double b,double step,double (*fx)())
{
double x=a,big=(*fx)(a);
while(x<=b){
if(big<(*fx)())
big=(*fx)(x);
x+=step;
}
return big;
}
在这个例子里,函数quad_poly计算(a*x3+b*x+c)的值,参数x和
返回值教师double型数。函数find_largest根据某一计算法则求出在
某一范围内(某一步长)的最大值。不但范围和步长是由调用参数指定,
计算法则也是由调用参数指定,所有的参数返回值都是double型数。
主函数main调用find_largest求最大值,通过函数指针把函数
quad_poly人微言轻参数传给find_largest。段与偏移量
8086和80286的寄存器都是8位或16位的,而8086以及工作于实地
址方式下的80286/80386的地址空间却是20 位的。这样在寻找下一条
将执行的指令时以及当用寄存器间接寻址内存中某一数据时,16位的
指针寄存器和地址寄存器却不足以存下20位地址。为了解决这个问题,
便把20位地址分为两部分,分别称为段地址和偏移量。段地址可以是
任一16字节边界处,即段地址的末4位一定是0,不需要保存,只把高
16位存入段寄存器中。偏移量也是16位的,一方面便于寄存器间接寻
址,但另一方面也限制了每段不能超过64K 字节。考虑到程序和数据
的寻址一般都是连续的,故这种设计还是合理的。
在80X86微机中,总是有4个16位段寄存器:CS,DS,SS,ES。在
Turbo C编译产生的目标码中,一般只用到了其中的3个寄存器,CS用
来存放码的段地址,DS用来存放全局变量和静态变量所在段的段地址,
SS用来存放局部变量,参数(以及其它属于某一个函数的信息)所在段
的段地址。在Microsoft C 6.0中,ES用来存放基指针的段地址。 如
果需要,在Turbo C 中,程序员可以通过伪变量_DS、_CS、_SS、_ES
取得这些段寄存器的值。
如果程序的码、数据和堆栈分别都不超过连续64K 字节,则在程
序的整修执行过程中,CS、DS和SS寄存器的值可以不变,仅仅通过对
单字偏移量的操作就可以寻址到所有的码和变量,这样速度比较快。
如果码或数据不能在边续64K 字节内放下,则必须用双字的段:偏移
量来寻址,但速度也就变慢。在所有程序中,堆栈的操作都较频繁,
所以都禁止使用双字的寻址方法,并限制堆栈不能超过64K 字节,即
不超过一段,尽管有时各个程序模块使用自己独立的堆栈。
有时,我们知道了某一个存储单元的段地址,也知道它的偏移量,
也知道它的偏移量,但这个段地址和偏移量并没有构成一个远指针。
Turbo C中提供了如下4 个宏,使得可以从这个内存单元中读(或往这
个内存单元中写)1个字节(或1个整数)。
char peekb(unsigned segment,unsigned offset)
int peek(unsigned segment,unsigned offset)
void pokeb(unsigned segment,unsigned offset,char value)
void poke(unsigned segment,unsigned offset,int value)
下面是从ROM的地址FFFF:000E中读1个字节的例子,这个字节实
际上就是微机类型的标志字节。
/* #include<general.h>*/
#include <dos.h>
void main(){
printf("PC model=hex%x",( char ) peekb(0xffff,0x000e));
}
这4个宏以及下面将介绍的3个宏的定义都在头文件dos.h 中,但
在头文件general.h中包含有#include<dos.h>这样一行,所以在上面
这个例子中只写了#include<general.h>。因为下面经常要用到
general.h,故单独把它列出来。
/* general.h */
#include<conio.h>
#include<dos.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define boolean int
#define TRUE 1
#define FALSE 0
#define BLANK ''
#define CR 13
#define beep putchar('\a')
#define newline putchar('\n')
#define EMPTYSTR ""
#define FARNULL (void far *)NULL
#define INTA00 0x20
#define EOI 0x20
#define strempty(s) (*(s)==0)
boolean strequalsf(char *s,char *t,char **ps,char **pt);
boolean strequalsb(char *s,char *t,char **pm,char **pn);
#define strchrf strchr
char *strchrb(char *s,char *p,char c);
#define strpbrkf strpbrk
char *strstrf(char *s,char *t);
char *strstrb(char *s,char *t);
char *stralloc(char ch,unsigned N);
char *strsubst(char *s,char *a,char *b);
char *strltoa(long N,char *s,int radix);
char *strshrte(double x,char *s);
char *strultoe(unsigned long N);
char *strtomoney(double x,int code);
char *strtime(char *timestr,const struct tm *t,
boolean twentyfourhours,char separator);
char *strday(char *datestr,const struct tm *t,int format,
char separator);
#define leap(year) ((year)%4==0 && ((year)%100!=0 || (year)%400==0))
int dayofyear(int day,int month,int leapyear);
long gregorian(int day,int month,int year);
#define weekday(day,month,year) (gregorian(day,month,year)+5)%7
unsigned envseg(unsigned PSP);
char *progname(unsigned PSP);
void memrymap(void);
unsigned extmem(vid);
void ctryinfo(unsigned code,struct country *c);
void peep(void);
int retorisr(int N,char *id);
int rstorint(char *id);
有时,我们知道了段地址和偏移量,需要把它们合并成一个远指
针。Turbo C的宏MK_FP可以用来做这件事。
(void far *)MK_FP(unsgined segment,unsigned offset) Turbo C 的另外两个宏正好做相反的事情,它们从一个远指针中
分解出段地址和偏移量。
(unsigned)FP_SEG(void far *p)
(unsigned)FP_OFF(void far *p)
Turbo C中还提供了其它一些手段,使得可以像汇编语言中一样,
知道了偏移量和段地址在哪一个段寄存器,就可以用如下这样的程序
来存取某一内存单元:
char peekb(unsigned segment,unsigned offset)
{
_ES=segment;
return *(unsigned char _es *)offset;
} 这个程序中用伪变量_ES把参数segment(段地址)存放到寄存器ES
中,用修饰符_es把参数offset(偏移量)强制转换为指向字符的指针,
但这个指针是相对于寄存器ES而不是缺省的DS,然后再去取1个字节。
由于这个程序(函数)的名字也是peekb,与Turbo C提供的宏同名,如
果要用用户定义的peekb,则在源程序中不应包含头文件dos.h。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -