⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 数据类型转换.01

📁 经典C教程,带所有ANSI C的库函数说明及例子
💻 01
📖 第 1 页 / 共 3 页
字号:
元,但下列表达式运算的结果却都为“假”,从而得出错误的结论:
    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)....
    if(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 指针,以便对指针进行比较或做
其它操作。一种方法是通过下面这样一个规则化函数:
    void normalize(void far **p) {
        *p=(void far *)(((long)*p^0xffff000f)+
            (((long)*p^0x0000fff0)<<12));
    }
    另一种办法就是通常的强制类型转换,但强制类型转换不能自动
使转换后的结果规则化。解决的办法是使转换后的huge指针再做一次
加法。例如,设转换后的huge指针是Hp,做一次Hp+=0就使Hp 规则化
了。有返回值的函数
    这类函数是最常见的函数,如返回一个字符,返回一个整数,返
回一个指针等,在函数的说明中就说明了要返回什么类型的数据。因
返回整数是最常见的,所以在这种情况下函数说明前的int可以省略。
这里要强调一点的是:函数除可以返回一些C语言标准类型的数外, 
还可以返回用户自定义的数据类型,如结构、联合、枚举等。例如,
下面这个对两个字符串相加的程序就是返回一个结构。
    struct string {
        char str[256];
        int strlen;
    };
    struct string concat(struct string str1,struct string str2)
    {
        struct string result;
        int i,j;
    result.str[0]='\0';
    result.strlen=0;
    if(str1.strlen>0){
	for(i=0;i<str1.strle;i++)
	    result.str[i]=str1.str[i];
	result.strlen=str1.strlen;
    }
    if(str2.strlen>0) {
	j=stre1.strlen;
	for(i=0;i<str2.strlen;i++)
	    result.str[i+j]=str2.str[i];
	result.strlen+=str1.strlen;
    }
    return result;
}
无返回值的函数
    无返回值的函数与PASCAL等其它结构化语言中的过程很相似,它
们既不返回结果,又不修改参数,而只是执行某一特定的任务。例如,
下面的清屏函数就是这样一个函数。    void clrscr(void)
    {
        printf("\xlb[2J");
    }    既然不返回值,则调用的办法也不一样,不是把函数名放在某一
表达式内调用,而是把函数名连同其调用参数单独人微言轻一个语句。

修改参数的函数
    由于C 语言是按传值方式把参数传递给函数的,因此,被调用的
函数不能直接改变调用函数中的变量。但有时确实需要修改调用函数
的参数,尤其在返回值多于一个的函数中必须再借用参数来返回结果。
在这些情况下,必须利用指针来从函数的参数,典型的例子是交换两
个变量的值的函数。如下所示:    void swap int(int *i,int *j)
    {
        int temp;
        temp=*i;
        *i=*j;
        *j=temp;
    }

递归函数
    C 语言是支持递归调用的。显然,当一个问题蕴含递归关系且结
构比较复杂时,采用递归调用技巧将使程序变得简洁,并增加程序的
可读性。但递归调用技巧的使用是在牺牲存储空间的基础上得到的,
因为它必须在某处维护一个要处理的值的栈。同时,递归也不能提高
执行速度,只是其代码比较紧凑易读。对于像树和链表这样的递归定
义的数据结构,递归函数尤为适用。下面是用递归计算阶乘的例子。
    double factorial(int n)
    {
        if(n>1) return factorial(n-1)*(double)n;
        else return 1.0L;
    }
参数个数不定的函数
    C语言中的某些函数,如vfprintf和vprintf,允许在一些固定参
数之后再带一些不定数目的可变参数。不但如此,C 语言还允许用户
自定义的函数也这样做。为了便于用户编程,Turbo C中提供了以
“va”开头的4个定义va_list数据类型,va_stat,va_arg和va_end 
3个宏(函数)。这些定义都在头文件stdarg.h中。 借助于这些宏可以
一步一步地通过整修参数表,尽管被调用函数事先不知道有多少个参
数,也不知道这些参数的类型。
    为了编写具有不定数目的可变参数函数,应遵守如下几点:
    第1,在C源中包含stdarg.h文件。
    第2,如果函数的返回值不是int型,则在调用函数中应做如下形
式的函数说明:    <类型><函数名>(<固定参数表>,....);
  这个调用形式表明,参数表中至少必须有一个参数是固定的。
    第3,函数应按如下形式定义:
	    <类型><函数名>(<固定参数表>,....);
    第4,定义一个表指针,其类型应是va_list,以表明它指向可变
参数表。如下所示:
    va_list <可变参数表指针>
    第5,调用va_start,初始化表指针:    va_start(<可变参数表指针>,<最后一个固定参数的名字>)
    这样初始化后,表指针就指向了调用函数传来的可变参数中的第1 个参数。
    第6,调用va_arg,取可变参数:
    <变量>=va_arg(<可变参数表指针>,<参数的数据类型>) 
第1次调用va_arg时,它返回可变参数表中的和第1个参数。随后每一
次调用,它返回表中的下一个参数。每次调用之后自动修正表指针的
值,使它指向随后的一个参数。为了正确地停止读可变参数表,应该
在调用函数可变参数表的最后放一个表结束符(例如-1 或0),在被调
用函数中再去检查这个表结束符。While 循环很适合做这件事情,如
下面的例子所示。
    第7,调用va_end,返回到调用函数;
    va_end(<可变参数表指针>)  它帮助被调用函数正常返回到调用函数。应在va_arg读完所有参数之后,才调用va_end返回,否则可能
会引起意想不到的结果。
    下面这个例子利用一个具有可变参数表的函数,从一个数字表中
挑选值最大的那个数。
    #include<stdio.h>
    #include<stdarg.h>
    #define EOL -1
    main()
    {
	int big;
	void vmax(int *,char *,...);
	vmax(&big,"The largest of 55,67,41 and 28 is",55,67,41,28,EOL);
	printf("%d\n",big);
    }
    void vmax(int *large,char *message,...)
    {
	int num;
	va_list num_ptr;
	va_start(num_ptr,message);
	printf("%s",message);
	*large=-1;
	while((num=va_arg(num_ptr,int))!=EOL)
	    if(num>*(large)) *(large)=num;
	va_end(num_ptr);
    }
函数指针及其应用
    函数名后面跟一对圆括号(兴许括号内还有参数),将导致去计算
这个函数。仅仅一个函数名则意味着是一个指针,是指向这个函数的
指针。函数指针有两个特殊用途,不太熟练的程序员可能很少使用函
数指针,但在某些场合下若借助于函数指针,则会使程序显得非常精
练。
    第1 种用途是把函数名赋给一个指针,然后用这个指针去间接引
用函数。请看下面这个例子:    #include<stdio.h>
    main()
    {
	double x;
	const double delta=1.0;
	const double first=0.0;
	const double last=10.0;
	double (*fx)();
	double quad_poly(double);
	fx=quad_poly;
	x=first;
	while(x<=last){
	    printf("f(%1f)=%1f\n",x,fx(x));
	    x+=delta;
	}
    }
    double quad_poly(double x)
    {
	double a=1.0,b=-3.0,c=5.0;
	return ((x*a)*x+b)*x+c;
    }
    在这个例子里,语句double(*fx)() 说明fx是一个函数指针,该

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -