C语言课程设计(图形编程 全)

更新时间:2023-04-26 07:52:01 阅读量: 小学教育 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

第一讲C语言图形方式下的编程

学习目标

?了解PC显示系统的结构

?C语言图形初始化的一般方法

?C语言常用的图形处理函数

显示系统简介

PC机显示系统一般是由显示器和显示卡组成。显示器(Monitor)是独立于主机的一种外部设备。显示卡(Adapter)是插在Pc主机上的一块电路板。PC机对显示屏幕的所有操作都是通过显示卡来实现的。

显示系统的主要特性

1.显示分辨率

显示分辨率是指屏幕上所能显示的像素点数,通常用列数和行数的乘积宋表示。为了获得良好的显示效果,要求显示器的分辨率与对应分辨率的显示卡相匹配。通常,高分辨率的显示效果比低分辨率的效果好。但是,显示分辨率的提高对显示器与显示卡的硬、软件要求更高。特别是分辨率的提高在很大程度上受到显示器的显示尺寸和扫描频率的限制,也受到显示卡的显存的限制。

根据应用情况的不同,在不超过显示器最高分辨率的条件下,可以通过对显示卡的设置而使用不同的分辨率。

2.显示速度

显示速度是指在屏幕上显示图形和字符的速度。显示速度与显示分辨率和显示器的扫描频率密切相关。显示分辨率越高,整个屏幕上的像素点数就越多,显示速度就越慢。在这种情况下,为了提高显示速度,就需要提高扫描频率。

如果显示器只有一种扫描频率,则它只能与一种显示卡相匹配使用。随着显示技术的发展,目前一般的显示器可以适应具有多种分辨率与显示速度的显示卡。颜色与灰度是衡量显示系统的重要参数。单色显示器只有亮和暗两种灰度;彩色显示器的颜色和灰度要受显示内存的限制,分辨率越高,颜色越丰富,所需要的显示内存就越多。

4.图形显示能力

图形显示能力是显示系统对屏幕上的每一个像素点都可以设置成不同的值的能力。通常,图形显示对硬件的要求比字符显示要求高得多,同时,图形显示对显示缓冲区的要求也比字符显示时高得多。

显示卡的种类

常用的显示卡有以下几种

1.MDA卡

MDA卡(Monochrome Display Adapter——单色字符显示器适配卡)与单色字符显示器配接,它只支持字符显示功能,无图形功能。

2.HGC卡

HGC卡(Hercules GraphicsCard——单色显示图形卡)不仅支持字符显示,而且支持单色图形功能。在图形方式下,其图形显示分辨率最高可达720~348点阵。通过软件的设置,还可以支持640~400单色图形显示以及模拟CGA卡图形方式。3.CGA卡

CGA卡(ColorGraphicsAdapter--彩色图形显示卡)支持字符/图形两种方式。

在字符方式下,它支持80列、25行及奶列、25行的方式,颜色可选16种。但是字符的质量比较差,只有8X8点阵。在图形方式下,它支持最大为640~200

分辨率,但只有黑、白两种颜色。此外,它还支持320~200的分辨率,每个像素点可以有四种颜色。CGA卡有16K的显示缓冲区,用于显示字符及属性或图形方式下的图形数据。

4.EGA卡

EGA卡(Enhanced GraphicsAdapter——增强型图形显示卡)的字符显示能力和图形显示能力都比CGA卡有了较大的提高,显示分辨率达到640~350,最高分辨率图形方式的颜色达到了16种。EGA卡的显示模式也比CGA卡丰富,并且兼容CGA卡及MDA卡的显示模式。

5.VGA卡

VGA卡(Video GraphicsArray一一视频图形阵列)是一种功能十分强大、颜色丰富的显示卡。VGA的标准分辨率可达到640~480,并且具有16种颜色。一些兼容的VGA卡(如TVGA卡、EVGA卡)的分辨率可达640~480(256种颜色)、800~600(16种颜色)或1024~768(16种颜色)。VGA卡兼容MDA卡、CGA卡、EGA卡的所有显示模式。

随着显示技术的发展,目前的显示卡的分辨率一般都可以达到1024X768,显示32位真彩(232种颜色)。

视频BIOS

视频BIOS(BasicI/OSystem基本输入/输出系统)是与显示卡配套的一个重要组成部分。用户通过调用它可以完成一些与显示有关的控制功能。对于要用到显示系统的一般程序设计都可以通过调用视频BIOs或视频函数库来完成。但要想获得更高性能的显示程序,还需要对显示卡的寄存器和显示内存直接进行编程。显示模式

显示模式按功能可以分为字符模式和图形模式两大类。

字符模式也称为字母数字模式,即A/N模式(A1phaNumbermode)。在这种模式下,显示缓冲区中存放的是显示字符的代码和属性,而显示屏幕被分为若干个字符显示行和列。图形模式(Graphics mode)也称为APA模式(A1l Points Addressablemode)。在这种模式下,显示缓冲区中存放的是显示器屏幕上的每个像素点的颜色或灰度值,而显示屏幕被划分为像素行和像素列。

由于显示卡的种类很多,其中有些显示模式在不同类型的显示卡及不同厂家的显示卡之间是通用的,这类显示模式称为标准模式。还有些模式是专用的,称为非标准模式。通常,显示模式号小于14H的是标准模式;其他则为非标准模式。图形模式的初始化

不同的显示器适配器有不同的图形分辨率。即使是同一显示器适配器,在不同模式下也有不同分辨率。因此,在屏幕作图之前,必须根据显示器适配器的种类将显示器设置成为某种图形模式。在未设置图形模式之前,微机系统默认屏幕为文本模式(80列,25行字符模式),此时所有图形函数均不能工作。

设置屏幕为图形模式,可用下列图形初始化函数:

void far initgraph (int far* gdriver,int far *gmode,char *path);

其中gdriver和gmode分别表示图形驱动器和模式,path是指图形驱动程序所在的目录路径。图形驱动程序由TurboC出版商提供,文件扩展名为.BGI。根据不同的图形适配器有不同的图形驱动程序。例如对于EGA、VGA图形适配器的图形驱动程序为EGA VGA.BGI。

有时编程者并不知道所用的图形显示器适配器种类,而且我们为了将编写的程序可以用于不同图形驱动器,增强程序的通用性,我们通常不指定图形显示器适配

器种类,而使用Turbo C提供了一个自动检测显示器硬件的函数,其调用格式为:void far detectgraph ( int *gdriver, *gmode);

其中gdriver和gmode意义同上。

例1:自动进行硬件测试后进行图形初始化

#include "graphics.h"

main( )

{

int gdriver, gmode;

detectgraph(&gdriver, &gmode); /*自动测试硬件*/

printf("driver is %d, mode is %d\n", gdriver,gmode); /*输出结果*/

getch();

initgraph(&gdriver, &gmode, ""); /* 根据测试结果初始化图形*/

circle(320,240,50);

circle(320,240,80);

circle(320,240,110);

getch();

closegraph();

}

上例程序中先对图形显示器自动检测,然后再用图形初始化函数进行初始化设置。其中,closegraph()为退出图形状态的函数,其调用格式为:voidfarclosegraph(void);调用该函数后可退出图形状态而进入文本方式,并释放用于保存图形驱动程序和字体的系统内存。同时TurboC提供了一种更简单的初始化图形的方法,即用gdriver=DETECT语句后再跟initgraph()函数就行了。比如,上例可改为例2的样子。

例2:

#include "graphics.h"

main()

{

int gdriver=DETECT, gmode;

initgraph(&gdriver, &gmode, "");

circle(320,240,50);

circle(320,240,80);

circle(320,240,110);

getch();

closegraph();

}

屏幕颜色的设置

对于图形模式的屏幕颜色设置,同样分为背景色的设置和前景色的设置。在Turbo c中分别使用以下两个函数:

void far setbkcolor (int color); 设置背景色

void far setcolor (int color); 设置作图色

其中color为图形方式下颜色的规定数值,符号常数及数值如下所示。

符号常数数值含义字符或背景

BLACK 0 黑两者均可

BLUE 1 兰两者均可

GREEN 2 绿两者均可

CYAN 3 青两者均可

RED 4 红两者均可

MAGENTA 5 洋红两者均可

BROWN 6 棕两者均可

LIGHTGRAY 7 淡灰两者均可

DARKGRAY 8 深灰只用于字符

LIGHTBLUE 9 淡兰只用于字符LIGHTGREEN 10 淡绿只用于字符LIGHTCYAN 11 淡青只用于字符

LIGHTRED 12 淡红只用于字符LIGHTMAGENTA 13 淡洋红只用于字符YELLOW 14 黄只用于字符

WHITE 15 白只用于字符

BLINK 128 闪烁只用于字符

清除图形屏幕内容使用清屏函数,其调用格式如下void far cleardevice(void);

有关颜色设置、清屏函数的使用请看例3。

例3:

#include "stdio.h"

#include "graphics.h"

main()

{

int gdriver, gmode, i,j;

gdriver=DETECT;

initgraph(&gdriver, &gmode, ""); /*图形初始化*/ setbkcolor(0); /*设置图形背景*/

cleardevice();

for(i=0; i<=15; i++)

{

setcolor(i); /*设置不同作图色*/

circle(319, 239, 20+i*15); /*画半径不同的圆*/ delay(3000); /*延迟3000毫秒*/

}

for(i=0; i<=15; i++)

{

setbkcolor(i); /*设置不同背景色*/

cleardevice();

for(j=0; j<=15;j++)

{

setcolor(j); /*设置不同作图色*/

circle(319, 239, 20+j*15); /*画半径不同的圆*/ delay(3000);

}

}

getch();

closegraph();

}

另外,TURBO C也提供了几个获得现行颜色设置情况的函数。

int far getbkcolor(void); 返回现行背景颜色值。

int far getcolor(void); 返回现行作图颜色值。

int far getmaxcolor(void); 返回最高可用的颜色值。

基本图形函数

基本图形函数包括画点,线以及其它一些基本图形的函数。本节对这些函数作一全面的介绍。

画点

1. 画点函数void far putpixel(int x, int y, int color);该函数表示有指定的象元画一个按color 所确定颜色的点。对于颜色color的值可从上表中获得而对x,y是指图形象元的坐标。在图形模式下,是按象元来定义坐标的。对VGA适配器,它的最高分辨率为640x480,其中640为整个屏幕从左到右所有象元的个数,480为整个屏幕从上到下所有象元的个数。屏幕的左上角坐标为(0,0),右下角坐标为(639, 479),水平方向从左到右为x轴正向,垂直方向从上到下为y轴正向。TURBO C的图形函数都是相对于图形屏幕坐标,即象元来说的。关于点的另外一个函数是:int fargetpixel(int x, int y); 它获得当前点(x, y)的颜色值。

2. 有关坐标位置的函数

int far getmaxx(void); 返回x轴的最大值。

int far getmaxy(void); 返回y轴的最大值。

int far getx(void); 返回游标在x轴的位置。

void far gety(void); 返回游标有y轴的位置。

void far moveto(int x, int y); 移动游标到(x, y)点,不是画点,在移动过程中亦画点。

void far moverel(int dx, int dy); 移动游标从现行位置(x, y)移动到(x+dx, y+dy)的位置,移动过程中不画点。

画线与线型设定

1. 画线函数

TURBO C提供了一系列画线函数,下面分别叙述:

void far line(int x0, int y0, int x1, int y1); 画一条从点(x0, y0)到(x1, y1)的直线。void far lineto(int x, int y); 画一作从现行游标到点(x, y)的直线。

void far linerel(int dx, int dy); 画一条从现行游标(x,y)到按相对增量确定的点(x+dx, y+dy)的直线。

void far circle(int x, int y, int radius); 以(x, y)为圆心,radius为半径,画一个圆。voidfar arc(int x, int y, int stangle, int endangle,int radius);以(x,y)为圆心,radius为半径,从stangle开始到endangle结束(用度表示)画一段圆弧线。在TURBO C中规定x轴正向为0度,逆时针方向旋转一周,依次为90,180, 270和360度(其它有关函数也按此规定不再重述)。

voidellipse(int x, int y, int stangle, int endangle,int xradius,intyradius);以(x, y)为中心,xradius,yradius为x轴和y轴半径,从角stangle开始到endangle结束画一

段椭圆线,当stangle=0,endangle=360时,画出一个完整的椭圆。

void far rectangle(int x1, int y1, int x2, inty2); 以(x1, y1)为左上角,(x2, y2)为右下角画一个矩形框。

voidfar drawpoly(int numpoints, int far *polypoints);画一个顶点数为numpoints,各顶点坐标由polypoints给出的多边形。polypoints整型数组必须至少有2倍顶点数个无素。每一个顶点的坐标都定义为x,y,并且x在前。值得注意的是当画一个封闭的多边形时,numpoints的值取实际多边形的顶点数加一,并且数组polypoints中第一个和最后一个点的坐标相同。

2. 设定线型函数

在没有对线的特性进行设定之前,TURBO C 用其默认值,即一点宽的实线,但TURBO C 也提供了可以改变线型的函数。线型包括:宽度和形状。其中宽度只有两种选择:一点宽和三点宽。而线的形状则有五种。下面介绍有关线型的设置函数。

void far setlinestyle(intlinestyle,unsigned upattern,int thickness); 该函数用来设置线的有关信息,其中linestyle是线形状的规定,

见下表:

该程序给用户三次输入密码的机会,如果正确则显示OK!welcome,否则显示Sorry!You cannot log in。

DOS系统下的批处理程序的加密程序

磁盘操作系统DOS的批处理程序是由后缀.BAT为扩展名的文本文件,该文件包括若干条DOS内部命令、外部命令及用户应用程序组成。当磁盘操作系统的命令解释程序6f3aa0e19b89680203d825af在命令所指定的路径上找到欲执行的批处理文件后,逐条解释并执行相应的有关命令,完毕后返回到DOS命令处理程序状态。由于批处理程序是以文本文件的形式存储在磁盘中,并能通过有关命令或文本编辑程序进行查阅、修改,所以批处理程序是完全透明的无保密性及安全性可言。为此,人们采取各种各样的保密方法、手段对批处理文本文件进行处理,以防止批处理程序被篡改、暴鼹。如何才能得到一种更安全保密的批处理文件的保密方法?我们通过实践找到了解决批处理程序进行加密的一种较好方法。

用DEBUG或PCTOOLS工具软件对批处理程序进行分析,可以发现每一个命令均以ODH,OAH作为该命令的结束符,而整个批处理文件以ODH,OAH,IAH 作为该处理程序的结束符。ODH,OAH,IAH分别是ASCII码的回车符,换行符、文件结束符,并且磁盘操作系统命令处理程序6f3aa0e19b89680203d825af是以ODH 作为批处理命令的结束符,以IAH作为批处理程序的结束符。通过以上分析,可以在不破坏原批处理程序文本文件组织结构的前提下,对批处理文本进行预处理,使批处理程序的换行符OAH全部改为返回OOH,从而使经过预处理的批处理程序的所有内容每一行层层覆盖,起到为批处理文件加密的作用。用此方法对批处理文本文件处理后,用TYPE,EDLIN,WS,DEBUG,PE,SK等命令或工具软件都无法破译批处理程序,最多只能破译最后两行的叠加结果,而不能确切得出最后两行的内容。

以下给出对批处理文本程序进行预处理的加密程序。此程序不仅能解决批处理程序的安全保密,还能够运用于文本文件形式的其他程序的安全保密,譬如BASIC

解释程序、dBASE及MFFOXPLAS源程序。

#include

#include

#include

/* 开始修改为固定返回符00H */

main()

{

char errorkkj[35],filename[20],*ptr, *data_all;

static char readerror[]="读文件失败!";

FILE *stream;

int length;

printf("请输入需要加密的文本文件名:");

gets (filename);

stream=fopen(filename,"rd+"); /* 打开二进制文本文件*/

if (stream=NULL)

{

printf ("007");

printf ("007");

strcpy (errorkkj, readerror);

strcat (errorkkj, filename);

printf ("%s",errorkkj);

printf("\007");

}

else

{

fseek(stream,2,SEEK_END);

length=ftell(stream);

data_all=(char *)malloc((unsigned)length,sizeof(char));

if(!data_all);

{

printf("出现错误,请检查后再运行!");

exit(1);

}

rewind(stream);

while(!feof(stream)) /* 修改为返回符00H */

fscanf(stream," [\x00]",data_all);

while(ptr=strrchr(data_all,'\n')) strnset(ptr,'\x00',1);

}

rewind(stream);

fwrite(data_all,1,length,stream);

fclose(stream);

}

数据文件的加密算法——替代加密法

在c语言中对数据文件利用替代法进行加密。计算机软件编程者往往对加密技术

比较感兴趣,其原因可能是加密是一种算法,它和编程一样很具有吸引力。在许多场合下加密是一种必须做的工作。

常用的加密方法有三种:①替代加密法:是以另外一个字符替代原来的字符并保持数据次序不变。②换位加密法:根据某一规则打乱原来的数据顺序。③位操作加密法:按照某种算法改变数据在计算机内的表示方法。

替代加密法是最简单的加密法,它是把文件中所有的字母都用另外一个字母代替而进行加密的,如:将字符串中所有的字符值都增加2,即:“abcde”将被加密为“cdefg”这种算法只是简单地把原字符串的值偏移一个固定的量,很容易被人识别。这是因为它最多只有26种可能偏移的情况,用不了多长时间就可以尝试出来,改进的办法就是每个字符的偏移情况各不相同,而使用多种偏移量。另外一个原因是它保留了各个单词之间的空格,破译者很容易将一个个单词区分开,改进的办法就是对空格也进行加密。

综合上述我们可采用随机偏移的办法进行加密,即对26个大小写字母和一个空格这53个字符小集合中,给每个字符随便指定一加密字符,这样加密后的加密文件可能有271种情况,被破译的机会相对小的多了。如在下例中我们将字符集zmb的加密密钥指定为mm,这样一来凡是在文件中出现a的地方都将用Q来替代,出现b的地方都将用A来替代,依次类推。其中这个密钥变量mm的顺序可随机更改,但对加密的文件必须用相同的密钥才能解密。

使用方法有两种:

①password <文件名1><文件名2>:将文件1加密为文件2。

②password <文件名1><文件名2>:将文件1解密到文件2。

/*password.c*/

#include

#include

char mm[54]="QAZWSXEDCRFVTGBYHNUJMIKOLPqwertyuiop asdfghjklzxcvbnm";

char zmb[54]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO PQRSTUVWXYZ ";

/* mm 为加密字符串即密钥, zmb 为原字母对照表*/

main(int argc,char *argv[])

{ FILE *fp1,*fp2;

int ch,flag=0;

if((toupper(*argv[3])=='E')||(toupper(*argv[3])=='D'))

flag=1;

if((argc!=4)||(flag!=1))

{ printf("\007 Error: 参数错误!!\n");

printf(" 格式:JM1 input output E/D \n");

exit(0); }

if((fp1=fopen(argv[1],"r"))==0){

printf("\007 Error: 不能打开输入文件!\n");

exit(0); }

if((fp2=fopen(argv[2],"w"))==0){

printf("\007 Error: 不能创建输出文件!\n");

exit(0); }

do {

ch=getc(fp1);

if(ch==EOF)

break;

if(isalpha(ch)||ch==' ')

if(toupper(*argv[3])=='E') /* E: 加密D:解密*/

ch=mm[find(zmb,ch)];

else

ch=zmb[find(mm,ch)];

putc(ch,fp2);

} while(1);

fclose(fp1);

fclose(fp2);

}

find(char *s,int ch)

{ register int t;

for(t=0;t< 54;t++)

if(ch==s[t])

return t; }

该加密方法可以说是比较科学,但是所生成的文件还是不难破译的,这是因为并不是所有字符出现的频率是一样的,而是有的多有的少。根据字母的频度表,比较容易破译密码文件,而且加密文件越长就越容易破译。为解决这个问题我们可以用多个替代密钥进行加密,即待加密文件中的每个字符不是始终用另一个字母来代替,而是轮翻由多个其他字符代替。这里我们就不详细讲了。

成批文件的简易加密法

下面这个程序可以使用通配符“,”和“?”,一次加密多个文件,不管是数据文件还是其他文件(包括各种可执行文件)。

程序采用替换加密法,对文件中所有字母数字和空格进行替换;即把串s1中每个字符用串S2中相应的字符替换,达到加密目的,带参数E(encode)是加密过程,D(decode)是解密过程。

例如:敲入CODE A*.* E就把当前目录下所有以A开头的文件加密;再敲入CODE A*.* E便把所有A*.*文件恢复。

程序加密过程可反复运行多次,以达到多重替换的目的。解密时只需逆向运行相同的次数即可。

/*CODE.c*/

#include "dir.h"

#include "ctype.h"

#include "stdio.h"

void encode(char * );

void decode(char * );

int at(char,char * );

char s[64]="abcdefghijklmnopqrstuvwxyz123456789\ ABCDEFGHIJKLMNOPQRSTUVWXYZ";

char t[64]= "qwertopasdfghjkizxcvbnm\

MIN2B3V4C5X6Z7L8K9J0HGFDSAPOIUYTREWQ"; void main(int argc,char *argv[ ])

{

struct ffblk ptr;

int done;

char pd;

pd=toupper(* argv[2]);

if ((argc!=3)|| (strlen(argv[2])>1)||(pd!='E'&&pd!='D')){ printf("usage error.\n");

exit ();

}

done=findfirst(argv[1],&ptr,0);

while(! done){

if(pd=='E'){

printf("Encoding file:%s\n",ptr.ff_name);

encode(ptr.ff_name);

}

else{

printf("Decoding file%s\n",ptr.ff_name);

decode(ptr.ff_name);

}

done=findnext(&ptr);

};

}

void encode(char *filename)

{

FILE *fp1, *fp2;

int ch;

if((fp1=fopen(filename,"rb"))==NULL){

printf("cannot open file:%s\n",filename);

exit();

}

if((fp2=fopen("is","wb"))==NULL){

printf("can not open file:is\n");

exit();

}

while((ch=getc(fp1))!=EOF){

if(isalnum(ch)||ch==" ")

ch=t[at(ch,s)];

putc(ch,fp2);

}

fclose(fp1);

fclose(fp2);

fp1=fopen("is", "rd");

fp2=fopen(filename,"wb");

while((ch=getc(fp1))!=EOF){

putc(ch,fp2);

}

fclose(fp1);

fclose(fp2);

}

void decode(char *filename)

{

int ch ;

FILE * fp1,* fp2;

if((fp1=fopen(filename,"rd"))==NULL){

printf("cannot open file:%s\n",filename);

exit();

}

if((fp2=fopen("is","wb"))==NULL){

printf("can not open file:is\n");

exit();

}

while((ch=getc(fp1))!=EOF){

if(isalnum(ch)||ch==" ")

ch=s[at(ch,t)];

putc(ch,fp2);

};

fclose(fp1);fclose(fp2);

fp1=fopen("is","rb");

fp1=fopen("filename,","wb");

while((ch=getc(fp1))!=EOF){

putc(ch,fp2);

}

fclose(fp1);

fclose(fp2);

}

int at(char ch,char *ss)

{

register int i;

for(i=0;i<64;i++)

if(ch==ss) return i;

}

限制软件的使用次数

限制软件使用的方法有很多,关键是把次数这个值储存在哪里,在这里我们介绍这么一种方法,通过改变文件的时间来存储试用次数,从而限制软件的试用。在MS-DOS中,文件的时间是通过一个16位的值来保存的,位15~11用来保存小时数,位10~5用来保存分钟数,位4~0用来保存秒钟数。这个16 位的值正

好可以被我们用来保存试用次数。例如,在下面的示例程序中,我们就使用了保存分钟数的位来保存试用次数。运行一下示例程序,我们可以看到,只是文件时间的分钟数加了1,一般人很难察觉,这种方法有比较好的隐蔽性。

下面我们简要介绍一下示例程序中所涉及的中断。

1.中断21H的功能3DH

用途:打开文件用于读、写或补充。

调用寄存器:AH=3DH,AL--00H,DS:DX=文件名;

返回寄存器:CF复位—成功,AX:文件把柄。

CF置位—出错,AX=错误代码。

2.中断21H的功能3EH

用途:强迫清除所有缓冲区,更新目录,使文件把柄无效。

调用寄存器:AH=3EH,BX二文件把柄;

返回寄存器:CF复位—成功,AX被破坏,

CF置位--出错,AX:错误代码。

3.中断21H的功能57H、子功能00H

用途:读出文件被最后修改的日期和时间。

调用寄存器:AX=5700H,BX=文件把柄;

返回寄存器:CF复位一成功,CX=文件时间

CF置位—出错,AX:错误代码。

4.中断21H的功能57H、子功能01H

DX=文件日期

用途:将文件最后被修改过的日期和时间信息变成指定值。

调用寄存器:AX=5701H,BX=文件把柄,CX=新时间,DX=新日期

返回寄存器:CF复位—成功,

CF置位—出错,AX:错误代码

下面我们就来看这个程序:

#include

#include

#include

int count()

{

union REGS inregs,outregs;

struct SREGS segs;

unsigned handle;

unsigned times;

/*打开文件*/

inregs.h.ah =0x3d;

inregs.h.al =0;

segread(&segs);

inregs.x.dx="test.exe";

intdosx(&inregs, &outregs, &segs);

if(outregs.x.cflag) return(0);

else

{

/*读取文件日期时间*/

handle=outregs.x.ax;

inregs.x.ax=0x5700;

inregs.x.bx=handle;

intdosx(&inregs, &outregs, &segs);

times=(outregs.x.cx &0x7e0)>> 5;

if(times <40)/*试用次数可以通过改变此处的'40'来实现*/

{

inregs.x.ax=0x5701;

inregs.x.bx=handle;

inregs.x.cx=outregs.x.cx + 0x20; /*试用次数计数*/

inregs.x.dx=outregs.x.dx;

intdosx(& inregs, &outregs, &segs); /*重置日期时间*/

return(2);

}

else return(1);

/*关闭文件*/

inregs.h.ah = 0x3e;

inregs.x.bx = handle;

intdosx(& inregs, & outregs, & segs);

}

}

main()

{

int flag;

flag=count();

if(flag==0)printf("此软件为试用版,请勿任意修改!\n");

if(flag==1)printf("此软件为试用版,您的试用次数已满!\n");

if(flag==2)printf("此软件为试用版,欢迎试用!\n");

}

小结

这一讲我们简要介绍了C语言中加密的应用,在D版软件充斥市场的今天,我们有必要好好探讨一下加密算法。虽然本讲的内容比较简单,但是还是希望大家努力学好。

第十二讲C语言大作业设计思路

今天,我们来做C语言高级程序设计讲座的最后一讲,主要讲一下C语言大作业的设计思路。

当前,很多院校在C语言结课的时候都要求做一份大作业,作为考核的一部分,很多同学为此挠头,在这里我们将一些设计的思路,希望能给大家一点提示。所谓大作业,其实质目的就是让学生在制作过程中体验这种设计思路,一般来说,思路有了,其他的就不在话下了。毕竟我们不是要求来做这种Windows之类的

大型软件,Microsoft的精英开发了好几年,如果我们用C语言来做,恐怕也不是一朝一夕能完成的。我们要求学生能够设计出一个小型的游戏或者一个小型的数据库管理软件就很好了。

我们在设计之前,一般考虑以下几点:

1、问题定义

我们首先要知道应该做什么。

2、可行性分析

然后确定这个方案能否实施,不要做那种自己不可能完成的程序,“只有想不到,没有做不到”这句话以马克思主义哲学的观点来看是不成立的。

3、需求分析

如果这个方案可行,就要考虑这个程序需要实现什么功能。

4、系统设计

前面的都确定后,要设计出宏观的框架,例如主界面。

5、详细设计

详细的设计每一项功能。

6、编译并测试

一般来讲,我们提倡每完成一项内容都要编译测试。

7、运行维护

自己用自己的软件,用所有想到的错误操作来操作它,找出所有的内在BUG。

8、发布

如果你自己没有找到错误了,该软件就差不多可以交上去了,结合开发过程我们就可以写出论文来。

下面我们来介绍一个简单的人工智能程序。

初始化人机界面

/*exper_1.c*/

#include

#include

#include

#define N 11

char *pre[]={

"p1","p2","p3","p4","p5","p6","p7",

"p8","p9","p10","p11"

};

char *symptom[]={

"咳嗽","流鼻涕","怕冷","打喷嚏","痰多","体温高","血象偏高",

"泛酸","呕吐","上腹隐痛","食欲不振",

};

main()

{

int i,j;

FILE *fp;

char answer;

char midpre[N][5];

if((fp=fopen("c:\\bc\\bin\\pre.txt","wt"))==NULL)

{

printf("Can not open flie");

exit(0);

}

for(i=0;i

{

printf("\n你%s吗?\n",symptom);

answer=strlwr(getch());

switch(answer)

{

case 'y':

putchar(answer);

fprintf(fp,"%s",pre);

fprintf(fp," ");

break;

case 'n':

putchar(answer);

break;

}

}

fclose(fp);

printf("\n你的症状是:");

if((fp=fopen("c:\\bc\\bin\\pre.txt","rt"))==NULL) {

printf("Can not open flie");

exit(0);

}

i=0;

while(fscanf(fp,"%s",midpre)!=EOF)

{

for(j=0;j

{

if(!strcmp(midpre,pre[j]))

printf("%s ",symptom[j]);

}

}

fclose(fp);

printf("\n按任意键返回\n");

getch();

}

具体的实例程序

#include

#include

#include

#include

#define N 10

#define M 11

struct RL{

int rnum;

int rpsum;

char pre[N][5];

char res[5];

};

struct RL zrul[N];

char *pre[]={

"p1","p2","p3","p4","p5","p6","p7",

"p8","p9","p10","p11"

};

char *symptom[]={

"咳嗽","流鼻涕","怕冷","打喷嚏","痰多","体温高","血象偏高", "泛酸","呕吐","上腹隐痛","食欲不振",

};

char *result[]={

"r1","r2","r3"

};

char *diagnose[]={

"上感","支气管炎","胃炎"

};

int have()

{

FILE *fp;

int i,ri=-1,rs=0;

char mstr[5];

char dstr[N][5];

if((fp=fopen("c:\\bc\\bin\\jingyan.txt","rt"))==NULL)

{

printf("Can not open flie");

exit(0);

}

while(fscanf(fp,"%s",mstr)!=EOF)

{

if((!strstr(mstr,"p"))&&(!strstr(mstr,"r")))

{

ri++;

zrul[ri].rnum=atoi(mstr);

if(ri>0)

{

zrul[ri-1].rpsum=rs-1;

for(i=0;i

strcpy(zrul[ri-1].pre,dstr);

strcpy(zrul[ri-1].res,dstr[rs-1]);

}

rs=0;

}

else

{

strcpy(dstr[rs],mstr);

rs++;

}

}

zrul[ri].rpsum=rs-1;

for(i=0;i

strcpy(zrul[ri].pre,dstr);

strcpy(zrul[ri].res,dstr[rs-1]);

fclose(fp);

return(ri+1);

}

query(int n)

{

FILE *fp,*fpi;

char lstr[N][5];

char mstr[5];

int i=0,j=0,k=0;

int psum=0;

int agrees=0;

int m;

if((fp=fopen("c:\\bc\\bin\\pre.txt","rt"))==NULL) {

printf("Can not open flie");

exit(0);

}

if((fpi=fopen("c:\\bc\\bin\\reason.txt","wt"))==NULL) {

printf("Can not open flie");

exit(0);

}

while(fscanf(fp,"%s",mstr)!=EOF)

{

strcpy(lstr,mstr);

i++;

}

fclose(fp);

psum=i;

for(i=0;i

{

if(zrul.rpsum==psum)

{

for(j=0;j

{

for(k=0;k

{

if(!strcmp(lstr[j],zrul.pre[k]))

agrees++;

}

}

if(agrees==zrul.rpsum)

fprintf(fpi,"%s ",zrul.res);

agrees=0;

for(m=0;m<11;m++)

{

if(!strcmp(zrul.res,result[m]))

printf("\n对你的症状做出的诊断为:%s\n",diagnose[m]);

}

}

}

fclose(fpi);

}

main()

{

int n;

system("c:\\bc\\bin\\exper_1.exe");

n=have();

query(n);

}

文本经验数据库

1 p1 p

2 p

3 p

4 p

5 p

6 p

7 r1

2 p1 p5 p6 r2

3 p8 p9 p10 p11 r3

大家可以通过对本例程的进一步完善使之成为一个专家系统,让自己的电脑成为家庭医生。

到这里,你是不是对C语言大作业的设计有了一定的思路了呢?我们这一讲就到这里了。

小结

这一讲首先对VGA卡的结构原理作了介绍,然后分析了BIOS中断调用来设置图形方式的方法,最后给出了一个完整的调整图像灰度的程序,这些知识在图像处理编程中是有很大作用的。

讲座到这里,我们对于程序外壳编写的讨论就要告一段落了,我们的后继课程将讨论一些编程技术,主要包括:中断调用、底层编程技术、鼠标的使用、程序的内存驻留与释放、软件加密算法等。

第七讲中断调用

鹏程C语言教学网站庆祝建站一周年特别企划

本文技术支持网站:202.112.86.129

从这一讲起我们将过渡到研究程序内部的一些编程方法,不同于前6讲,接下来的内容可能会比较枯燥,但是却是C语言高级程序设计必不可少的部分,我尽量用比较简短的语言来叙述,以避免大家一看页数就头晕。

对于中断调用,其实是很复杂的,这里讲得或许只是一个皮毛,更深入的研究还要大家自己努力。

学习目标

?了解什么是中断

?了解几个中断函数

?了解中断服务程序的编写

什么是中断

所谓中断,是指CPU在正常运行程序时,由于程序的预先安排或内外部事件,引起CPU中断正在运行的程序,而转到为预先安排的事件或内外部事件服务的程序中,这些引起程序中断的事件称为中断源。预先安排的事件是指PC机的中断指令,执行到此,立即转相应的服务程序去执行。内部事件是指系统板上出现的一些事件信号,中断指令也可看作内部事件,外部事件是指某些接口设备所发出的请示中断程序执行的信号,这些信号称为中断请求信号。中断请求信号何时发生是不能预知的,然而,它们一旦请求中断,则会向CPU的接收中断信号的引脚发出电信号,因此这些信号CPU是马上可以知道的。这样CPU就无需花大量的时间去查询这些信号是否产生。因为中断请示信号一旦产生,便会马上通知CPU。如键盘,何时有键按下,是随机的。因而CPU可以对键盘不加理睬,而去执行其他程序,一旦有键按下,键盘马上产生中断请求信号,CPU得知这信号后,便立即去执行为键盘服务的中断程序,服务完后,CPU又恢复执行被中断了的程序。中断服务程序执行完,返回原来执行程序的中断处(称为断点)继续往下执行,称为中断返回。有时中断请求信号(即中断源)可能有好几个,因此CPU 响应这些中断就得有先后次序,这称为中断的优先级。CPU首先响应优先级高的中断,优先级低的中断,暂不响应,称为挂起。有些中断源产生的中断,可以用编程的办法使CPU 不予理睬,这叫中断的屏蔽。CPU响应中断,转去执行中断服务程序前,需将被中断程序的现场信息保存下来,以便执行完中断服务程序后,接着从被中断程序的断点处继续往下执行。现场信息是指程序计数器的内容、CPU的状态信息、执行指令后的结果特征和一些通用寄存器的内容,有些信息的保存和程序计数器的内容等由机器硬件预先安排完成,称为中断处理的隐操作。有些信息保存是在中断服务程序中预先安排的。CPU响应中断时,由中断源提供地址信息,引导程序转移到中断程序中去执行。这个地址信息称为中断向

量,它一般是和中断源相对应的,PC机采用类型号来标识中断源。

中断方式以其执行速度快,可实时处理,不占用CPU过多的时间等优点,在一些高级应用场合中较多地被采用。PC机中断系统不仅具备一般中断系统的特点,而且有所创新,比如,中断不仅可由外部事件引起,也可由预先安排的事件,或称为内部的事件引起,这些内部事件是指中断指令和执行一些指令引起的特殊事件等。

几个中断函数

在我们了解了什么是中断后,我们通过实例来介绍几个DOS中断函数。

int86()函数

函数说明如下:

int int86(int intr_num, union REGS *inregs, union REGS *ourregs);

这个函数的三个参数如上述的BIOS功能调用一样,其中第一个参数mtr_num表示BIOS调用类型号,即中断类型号,第二个参数表示是指向联合类型REGS 的指针,它用于接收调用的功能号及其他一些指定的入口参数,以便传给相应的寄存器,第三个参数也是一个指向联合类型REGS的指针,它用于接收功能调用后的返回值,即出口参数,如调用的结果,状态信息,这些值从相关寄存器中得到。

联合类型PEGS在dos.h头文件中定义,因而使用该函数时,应将dos.h文件包括在程序中,在dos.h中PEGS定义如下:

struct WORDREGS

{

unsigned int ax,bx,cx,dx,si,di,cfiag,flags;

};

struct BYTEREGS

{

unsigned char al,ah,bl,bh,cl,ch,dl,dh;

}

unionPEGS

{

struct WORDREGS x;

struct BYTEREGS h;

}

它表示REGS是一个联合类型名。该联合的成员由结构类型的x和h组成,其中x代表16位寄存器变量,它的成员用8088CPU的相关寄存器名表示(注意! 它们并不是真正的物理寄存器)。其中cilag代表标志寄存器的进位标志位,一般调用后,从该标志位是0还是1可知调用成功还是失败。结构类型h代表8位寄存器变量,即长度为字节的结构变量,它的成员与相关的8088的8位寄存器同名。union REGS表示两个成员x,h共用一个内存区域,即可以是16位长,也可以是8位长,我们可以用这个内存区向有关的寄存器赋值,或取得有关寄存器的返回值,这样就和BIOS调用格式一致起来了,如定义一个ax变量并赋值为2,可写作:

union REGS regs;

regs.x.ax=2;

它表示regs变量是一个REGS联合类型,取其16位的结构成员x中的ax井赋值2。

上述的定义与赋值过程,实际上就完成了对该变量代表的寄存器的赋值。

下面示例INT 10H,功能号为2的显示中断调用,它的功能是设置光标位置,其中DH为光标所在行号,DL为光标所在列号,BH为显示光标的页号,选0。出口参数无,下面的程序在屏幕35行10列出显示出Hello字样。

#include

#include

#include

#define VIDEO 0x10

void movetoxy(int x,int y)

{

union REGS regs;

regs.h.ah=2;

regs.h.dh=y;

regs.h.dl=x;

regs.h.bh=0;

int86(VIDEO,?s,?s); /*BIOS中断调用*/

}

int main(void)

{

clrscr(); /*清屏幕*/

movetoxy(35,10); /*调用movetoxy函数*/

printf("Hello\n");

return 0;

}

intdos()函数

它的说明格式是:

int intdos(union REGS *inregs, union REGS *outregs);

该函数的第一个参数足表示相应功能号的入口参数,第二个参数是出口参数,调用后,返回值在outregs的AX寄存器中,并将设置标志寄存器flags,若进位标志器cilag被置位,则表示调用出错,此时AX中为错误代码,若为零,则表示成功。

由于该函数的参数中没有涉及到有关段寄存器,因而当进行要改变段的一些调用时,该函数就不能用了。

下面的示例程序是利用该函数进行DOS系统调用,删除一个当前目录下的文件(不需要改变段值):

#include

#include

int delete_file(char near *filename)

{

union REGS regs;

int ret;

regs.h.ah=0x41;

本文来源:https://www.bwwdw.com/article/ybaq.html

Top