论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: Windows | Word2007 | Excel2007 | PowerPoint2007 | Dreamweaver 8 | Fireworks 8 | Flash 8 | Photoshop cs | CorelDraw 12
编程视频: C语言视频教程 | HTML | Div+Css布局 | Javascript | Access数据库 | Asp | Sql Server数据库Asp.net  | Flash AS
当前位置 > 文字教程 > C语言程序设计教程
Tag:新手,函数,指针,数据类型,对象,Turbo,入门,运算符,数组,结构,二级,,tc,游戏,试题,问答,编译,视频教程

C语言编程常见问题解答之杂项(Miscellaneous)(2)

文章类别:C语言程序设计 | 发表日期:2008-9-24 14:36:30

    请参见:
    20.12怎样把数据从一个程序传递到另一个程序?
    20.17可以使热启动(Ctrl+Alt+Delete)失效吗?
    21.10什么是动态连接?

    20.9 为什么要使用静态变量
    静态变量作为一个局部变量是很合适的,它在函数退出后不会失去其本身的值。例如,有一个要被调用很多次的函数,它的一部分功能就是计算自己被调用的次数。你不能用一个简单的局部变量来实现这部分功能,因为每次进入该函数时,这个变量都没有被初始化。假如把这个计数变量说明为静态的,那么它就会象一个全局变量那样保留自己的当前值。
    那么为什么不直接使用一个全局变量呢?你可以使用一个全局变量,而且这样做没有错误。问题是使用了大量全局变量的程序维护起来很麻烦,尤其是有许多函数都各自访问一个全局变量的程序。再说一遍,这样做没有错误,这只是一个程序设计和可读性是否好的问题。假如你把这样的变量说明为静态的,你就可以提醒自己(或者其它可能读你的程序的人)它是局部变量,但要象全局变量那样被处理(保留自己的值)。假如你把它说明为全局的,那么读这个程序的人一定会认为有很多地方要引用它,尽管实际上并不是这样。
    总而言之,当你需要一个能保持自己的值的局部变量时,使用静态变量是一种好的编程习惯。
    请参见:
    2.17可以头文件中说明static变量吗?

    20.10 怎样在一个程序后面运行另一个程序?
    显然,在一个程序后面运行另一个程序的最简单的办法是把它们依次列入一个批处理文件中,在执行该批处理文件时,其中所列的程序就会依次运行。然而,这是一种人们已经知道的办法。
    在c或DOS中,都没有一种特定的方法来完成“在一个程序结束后运行另一个程序”这样一种函数调用。然而,c提供了两组函数,它们答应一个程序随时可以运行另一个程序,而后者的运行将结束前者的运行。假如你将这样的一个函数调用放到第一个程序的末尾,你就能达到上述目的。C所提供的这两组函数实际上是由exec()和spawn()所代表的两个函数族,其中的每一个函数都具有一种区别于同族其它函数的功能。exec()函数族包括这样一些成员:execl(),execle(),execlp(),execlpe(),execv(),execve(),execvp()和execvpe()。下面列出了这此函数名中的e,l,p和v等后缀的含义:
    e    明确地把一个指向环境参数的指针数组传递给子进程
    l    把命令参数逐个传递给要执行的程序
    p    通过环境变量PATH找到要执行的文件
    v    把命令行参数以一个指针数组的形式传递给要执行的程序
    在程序中选用哪一个函数完全取决于你以及要执行的程序的需要。下例中的程序调用了其参数由命令行指定的另一个程序:
# include <stdio. h>
# include <process. h>
char * envString[] = {                 / * environment for the app * /
      "COMM VECTOR=0x63",      /* communications vector */
      "PARENT=LAUNCH. EXE",   / * name of this app * /
      "EXEC=EDIT. COM",           / * name of app to exec * /
      NULL} ;                          / * must be NULL-terminated * /
void
main(int argc, char **argv)
{
      / * Call the one with variable argumets and an enviroffment * /
      _execvpe (" EDIT. COM", argv, envString ) ;
      printf("If you can read this sentence, the exec didn't happen!\n") ;
}
   上面这个短小的例子调用_execvpe()来执行DOS的文件编辑器EDIT.COM,EDIT程序的参数来自该例的命令行。在调用execvpe()函数后,上例中的程序就结束了;当EDIT程序退出时,你将返回到DOS提示符。假如printf()语句的打印内容显示在屏幕上,则说明_execvpe()函数调用出了问题,因为假如它调用成功,就不会有上述结果。注重,上例所提供的EDIT.COM的环境变量是没有任何意义的,然而,假如上例要执行一个需要环境变量的程序,那么所提供的环境变量就能供该程序使用了。
    用spawn()函数同样可以完成上例所做的工作。spawn()函数族包括这样一些成员:
    spawnl(),spawnle()。spawnlp(),spawnlpe(),spawnv(),spawnve(),spawnvp()和spawnvpe()。这些函数名中的e,l,p和v等后缀的含义与exec()族函数名中的相同。实际上,spawn()函数族与exec()函数族基本相同,只不过有一点小小的差别——spawn()函数既可以在结束原来的程序后启动另一个程序,也可以启动另一个程序并在该程序结束后返回到原来的程序。spawn()函数的参数与exec()函数的基本相同,只不过需要增加一个参数一你必须用_P_OVERLAY(结束原来的程序)或_P_WAIT(结束后返回到原来的程序)作为spawn()函数的第一个参数。下例用spawn()函数完成了与前面的例子相同的工作:
# include <stdio. h>
# include <process. h>
char * envString[] = {                 / * environment for the app * /
      "COMM VECTOR = 0x63",     / * communications vector * /
      "PARENT=LAUNCH. EXE",   / * name of this app * /
      "EXEC=EDIT. COM" ,           / * name of app to exec * /
      NULL} ;                          / * must be NULL-terminated * /
void
main(int argc, char **argv)
{
      / * Call the one with variable argumets and an environment * /
     _spawnvpe (_P_OVERLAY, "EDIT. COM", argv, envString) ;
     printf("If you can read this sentence, the exec didn't happen!\n" );
}
    这里唯一的区别是"exec”变为"spawn",并且增加了模式(mode)参数。spawn()函数有覆盖和等待两种相对立的功能,它使你可以在spawn()运行期间做出是等待还是离开的决定。实现上,P_WAIT参数回答了下一个问题。

    请参见:
    20.11 怎样在一个程序执行期间运行另一个程序?

    20.11 怎样在一个程序执行期间运行另一个程序?
    正如你在20.10的例子中看到的那样,spawn()函数族答应在一个程序中启动另一个程序,并在后者结束后返回到前者之中。有关spawn()函数的背景知识和例子(你只需把其中的_P_OVERLAY改为_P_WAIT) 请参见20.10。
    然而,还有另外一种方法可以完成这项工作,即使用system()函数。system()函数与exec()或spawn()函数相似,但也不有同之处。除了挂起(而不是结束)当前程序去执行新程序外,system()还要启动COMMAND.COM命令翻译程序(或者其它任何运行在你的计算机上的命令翻译程序)。假如它找不到COMMAND.COM或类似的程序,那么它就不会去执行所要求的程序(这一点与exec()或spawn()函数不同)。下例是调用EDIT.COM打开一个文件的另一个程 序版本,其中的文件名也来自该例的命令行:
# include <stdio. h>
# include <process. h>
# inclued <stdlib. h>
char argStr[255] ;
void
main(int argc, char **argv)
     int ret ;
      / * Have EDIT open a file called HELLO if no arg given * /
      sprintf (argStr ,"EDIT %s", (argv[1] == NULL?"HELLO" :argyll3) ) ;
      / * Call the one with variable arguments and an environment * /
      ret = sytem (argStr) ;
      printf("system() returned %d\n" ,ret) ;
}
    与20.10中的例子一样(使用_P_WAIT),在system()调用后面的print{()语句会被执行,因为原来的程序只是被挂起而不是被终止。在每一种情况下,system()都会返回一个表示是否成功地运行了所指定的程序的值,而不会返回所指定的程序的返回值。

    请参见:
    20.10怎样在一个程序后面运行另一个程序?

    20.12 怎样把数据从一个程序传给另一个程序?
    有好几种基本的方法可以完成这项任务----你可以通过文件或内存来传递这些数据。这些方法的步骤都相当简洁:首先,定义在何处存放数据,如何获取数据,以及如何通知另一个程序来获取或设置数据;然后,你就可以获取或设置数据了,尽管使用文件的技术定义和实现起来都比较简单,但它的速度往往比较慢(并且轻易引起混乱)。因此,这里重点讨论内存数据转移技术。下面将依次具体地分析这一过程的每一个环节:
    定义在何处存放数据。当你编写要共享数据的两个程序时,你应该让程序知道要访问的数据存放在何处。这个环节同样有几种实现方法:你可以在一个(或每个)程序中建立一个固定的内部缓冲区,并在两个程序之间传递指向这个缓冲区的指针;你也可以为数据分配动态内存,并在两个程序之间传递指向该数据的指针;假如要传递的数据很小,你还可以通过CPU的通用寄存器来传递数据(这种可能性很小,因为x86结构的寄存器很少)。分配动态内存是最灵活和模块性最强的方法。
    定义获取数据的方法。这个环节非常简洁——你可以使用fmemcpy()或等价的内存拷贝函数。显然,在获取和设置数据时都可以使用这个函数。
    定义通知另一个程序的方法。因为DOS并不是一个多任务操作系统,所以其中一个(或两个)程序的一部分必须已经驻留在内存中,并且可以接受来自另一个程序的调用。同样,这个环节也有几种方法可供选择:第一个程序可以是一个列入CONFIG.SYS中的驱动程序,它在系统启动时就被装入内存;第一个程序也可以是一个TSR(终止并驻留)程序,在它退出时会把与第二个程序相互作用的那部分程序驻留在内存中;此外,你也可以在第一个程序中利用system()或spawn()函数(见20.11)来启动第二个程序。你可以根据需要选择合适的方法。因为有关DOS驱动程序的数据传递在DOS文档中已经有详尽的描述,而有关system()和spawn()函数的内容也已经在前文中介绍过,因此下面介绍TSR方法。
    下面的例子给出了两个程序:第一个程序是一个完整的TSR程序,但为了突出整个过程中的要害环节,它写得比较单薄(见20.15中的解释)。这个TSR程序先是安装了一个中断63H的中断服务程序,然后调用终止并驻留退出函数,在执行这个TSR程序后,执行下文给出的另一个程序。这个程序只是简单地初始化一个对中断63H的调用(类似于使用中断21H调用),并且把“Hello There”传送给上述TSR程序
# include <stdlib. h>
# include <dos. h>
# include <string. h>
void SetupPointers (void) ;
void OutputString(char * );
# define STACKSIZE          4096
unsigned int near OldStackPtr;
unsigned int near OldStackSeg;
unsigned int _near MyStackOff ;
unsigned int _near MyStackSeg;
unsigned char_near MyStack[STACKSIZE];
unsigned char far * MyStackPtr= (unsigned char_far * )MyStack;
unsigned short AX, BX,CX, DX,ES;
/ * My interrupt handler * /
void_interrupt_far_cdecl NewCommVector (
         unsigned short es, unsigned short ds, unsigned short di,
         unsigned short si, unsigned short bp, unsigned short sp,
         unsigned short bx, unsigned short dx, unsigned short cx,
         unsigned short ax, unsigned short ip, unsigned short cs,
         unsigned short flags) ;
/ * Pointers to the previous interrupt handier * /
void(_interrupt_far_cdecl * CommVector)();
union REGS regs;
struet SREGS segregs ;
# define COMM_VECTOR         0x63       / * Software interrupt vector * /
/ * This is where the data gets passed into the TSR * /
char_far * eallerBufPtr;
char localBuffer[255];          / * Limit of 255 bytes to transfer * /
char_far * localBufPtr=(ehar_far * )loealBuffer;
unsigned int ProgSize= 276;       / * Size of the program in paragraphs * /
void
main(int argc,char * * argv)
{
      int i, idx;
      / * Set up all far pointers * /
      SetupPointers () ;
      / * Use a cheap hack to see if the TSR is already loaded
           tf it is, exit,doing nothing * /
      comm_veetor =_dos_getvect (COMM_VECTOR) ;
      if(((long)eomm_vector & 0xFFFFL) ==
                                ((long) NewCommVector & OxFFFFL ) ) {
            OutputString("Error :TSR appears to already be loaded. \n");
            return ;
       / * If everything's set,then chain in the TSR * /
       _dos_setvect (COMM_VECTOR ,NewCommVector) ;
       / * Say we are loaded * /
       OutputString("TSR is now loaded at 0x63\n");
       / * Terminate, stay resident * /
         dos_keep (0, ProgSize ) ;
}
/ * Initializes all the pointers the program will use * /
void
Set upPointers ( )
{
     int idx ;
     / * Save segment and offset of MyStackPtr for stack switching * /
     MyStackSeg = FP_SEG (MyStackPtr) ;
     MyStackOff = FP_OFF (MyStackPtr) ;
      / * Initialize my stack to hex 55 so I can see its footprint
           if I need to do debugging * /
     for (idx = 0 ;idx<STACKSIZE ; idx ++ ) {
           MyStack [idx] = 0x55 ;
     }
}
void _interrupt_ far_cdecl NewCommVector (
            unsigned short es, unsigned short ds, unsigned short di,
            unsigned short si, unsigned short bp, unsigned short sp,
            unsigned short bx, unsigned short dx, unsigned short cx,
            unsigned short ax, unsigned short ip, unsign ed short cs,
            unsigned short flags)
{
      AX = ax;
      BX = bx ;
      CX = cx;
      DX = dx ;
      ES = es ;
      / * Switch to our stack so we won't run on somebody else's * /
      _asm {
                                          ;set up a local stack
            eli                           ; stop interrupts
            mov       OldStackSeg,ss      ; save stack segment
            mov       OldStackPtr,sp      ; save stack pointer (offset)
            mov       ax,ds               ; replace with my stack s
            mov        ss,ax              ; ditto
            mov       ax,MyStackOff       ; replace with my stack s
            add       ax,STACKSIZE-2      ;add in my stack size
            mov        sp ,ax             ; ditto
            sti                           ; OK for interrupts again
     }
      switch (AX) {
            case 0x10;          / * print string found in ES:BX */
                   / * Copy data from other application locally * /
                   FP_ SEG (callerBufPtr) = ES ;
                   FP_OFF (callerBufPtr) = BX ;
                   _fstrcpy (localBufPtr, callerBufPtr ) ;
                   / * print buffer 'CX'   number of times * /
                   for(; CX>0; CX--)
                          OutputString (localBufPtr) ;
                   AX=1;             /* show success */
                   break ;
            case 0x30:               /* Unload~ stop processing interrupts * /
                   _dos_setvect (COMM_VECTOR ,comm_vector) ;
                   AX=2;                   /* show success */
                   break ;
             default :
                   OutputString (" Unknown command\r\n" ) ;
                   AX= 0xFFFF;                / * unknown command-1 * /
                   break ;
     }
      / * Switch back to the caller's stack * /
        asm {
             cli                               ;turn off interrupts
             mov       ss,OldStackSeg    ;reset old stack segment
             mov        sp,OldStackPtr ;reset old stack pointer
             sti                             ;back on again
     }
      ax=AX;                     /* use return value from switch() */
}
/ * avoids calling DOS to print characters * /
void
OutputString(char * str)
{
      int i ;
      regs. h. ah = 0x0E ;
      regs. x. bx = 0 ;
      for(i=strlen(str) ; i>0; i--,str++){
             regs. h. al= * str;
             int86 (0xl0, ®s, ®s) ;
     }
}
    上述程序是这两个程序中的TSR程序。这个程序中有一个NewCommVector()函数,它被安装在中断63H(63H通常是一个可用的向量)处作为中断服务程序。当它被安装好后,它就可以接收命令了。switch语句用来处理输入的命令,并作出相应的反应。笔者随意选择了0x1O和0x30来代表这样两条命令:“从ES:BX处复制数据,并打印到屏幕上,CX中的数值为打印次数”;“脱离中断63H,并停止接收命令”。下面是第二个程序——向中断63H发送命令的程序(注重它必须在Large模式下编译)。
# include <stdlib. h>
# include <dos. h>
# define COMM VECTOR 0x63
union REGS regs;
struct SREGS segregs ;
char buffer[80];
char _far * buf=(char_far *)buffer;
main (int argc,char * * argv)
{
      intcnt;
      cnt = (argo= =1 ? 1:atoi(argv[1])) ;
      strcpy (bur, "Hello There\r\n" ) ;
      regs. x. ax= 0x10;
      regs. x. cx=cnt ;
      regs. x. bx=FP OFF(buf);
      segregs, es=FP SEG(buf) ;
      int86x(COMM_VECTOR ,®s, &segregs) ;
      printf ("TSR returned %d\n" ,regs. x. ax) ;
}
     你可能会认为这个短小的程序看上去和那些通过调用int 21或int 10来在DOS中设置或检索信息的程序差不多。假如你真的这么想,那就对了。唯一的区别就是现在你所用的中断号是63H,而不是21H或10H。上述程序只是简单地调用前文中的TSR程序,并要求后者把es:bX所指向的字符串打印到屏幕上,然后,它把中断处理程序(即那个TSR程序)的返回值打印到屏幕上。
    当字符串"Hello There”被打印到屏幕上后,在两个程序之间传递数据的全部必要步骤就都完成了。这个例子的真正价值在于它能够举一反三。现在你能很轻松地编写一个这样的程序,它将发送一条类似于“把要求你打印的最后一个字符串传递给我”的命令。你所要做的就是在前述TSR程序的switch语句中加入这条命令,然后再写一个程序来发送这条命令。此外,你也可以在第二个程序中利用20.11中所介绍的system()或spawn()函数来启动前述TSR程序。由于TSR程序会检查自己是否已被装入,因此你只需装入一次TSR程序,就可以多次运行第二个程序了。在所有要和前述TSR程序通信的程序中,你都可以使用这里所说的方法。
    在建立前述TSR程序时,需要有几个前提条件。其一就是没有其它重要的中断服务程序也在处理中断63H。例如,笔者原来在程序中使用的是中断67H,结果该程序能正常装入并运行,但此后笔者就无法编译程序了,因为Microsoft用来运行C编译程序的DOS扩展程序也要使用中断67H。在笔者发送了命令0x30(让程序卸载自身)后,编译程序又能正常运行了,因为DOS扩展程序的中断处理程序已被该程序恢复了。
    第二个前提条件与驻留检查在关。笔者假设永远不会有另一个中断处理程序使用和NewCommVector()相同的近程型地址,尽管这种巧合的可能性极小,但读者应该知道该程序并不是万无一失的。在该程序中,笔者特意让NewCommVector()使用自己的栈,以避免它运行在调用它的程序的栈上,但是,笔者还是假设调用所需的任何函数都是安全的。注重,该程序没有调用printf(),因为它占用较多的内存,并且要调用DOS(int 21)来打印字符。在该程序中,当中断63H发生时,笔者不知道DOS是否可以被调用,因此不能假设可以使用DOS调用。
注重,在该程序中,可以调用那些没有用到DOS int21服务程序的函数来完成所需的任务,假如必须使用一个DOS服务程序,你可以在中断63H发生时检查DOS忙标志,以确定当时DOS是否可以被调用。最后,对dos_keep()作一点说明:该函数要求知道在程序退出时要在内存中保留多少段(每段16字节)数据。在本例这个TSR程序中,提供给该函数的段数(276)稍大于整个可执行程序的大小。当你的程序变大时,提供给该函数的段数也必须增大,否则就会出现一些异常现象。

    请参见:
    20.10 怎样在一个程序后面运行另一个程序?
    20.1l 怎样在一个程序执行期间运行另一个程序?
    20.15 本书的有些例子程序有许多缺陷,为什么不把它们写得更好?

    20.13 怎样判定正在运行的程序所在的目录?
    我们这些DOS程序员是很幸运的,因为DOS程序的装入程序会提供正在运行的可执行文件的路径全名。这个路径全名是通过指针argv[0]提供的,mai‘n()函数的argv变量指向该指针。只需去掉路径全名中的文件名,你就得到了正在运行的程序所在的目录。下面的例子演示了这种技巧:
# include <stdio. h>
# include <stdlib. h>
# include <string. h>
void main(int argc, char ** argv )
{
      char execDir [80];
      int i,t;
      / * set index into argv[0] to slash character prior to appname * /
      for(i= (strlen(argv[0])-1) ;
                 ((argv[O][i] ! ='/' ) && (argv[O][i]! =' \\' ));--i) ;
      / * temporarily truncate argv[] * /
      t =argv[O][i] ;
      argv[O][i]= O ;
       / * copy directory path into local buffer * /
      strcpy(execDir ,argv[O]) ;
       /* put back original character for sanity's sake * /
    argvEO]Ei]=t;
}

    请参见:
    20.1  怎样获得命令行参数?

    20.14 怎样找到程序中的重要文件(数据库,配置文件,等等)?
    DOS提供了一对函数,用来在一个目录下查找一个任何类型的文件。你可以查找普通文件、档案文件、隐含文件、系统文件、只读文件、目录文件,甚至卷标文件。下面这个小例子说明了如何在当前目录下查找一个特定文件:
# include <stdio. h>
# include <dos. h>
void main(void)
{
     struct    find_t myFile ;
     _dos_findfirst ("MYFILE. INI" ,_A_NORMAL ,&-nyFile) ;
     while (_dos_findnext (&myFile) == 0)
        printf("Found file %s of size %s\n", myFile, name,myFile, size) ;
}
    这个例子说明了函数_dos_findfirst()和_dos_findnext()是如何工作的。你可以进入一个目录,然后象上例这样用这两个函数查找一个指定名字的文件。这两个函数还答应使用通配符“*”和“?”,假如你用“*”作为文件名,它们就会返回一个目录中的所有文件。假如你要查找硬盘上的每一个文件,则要把上例中的代码放到一个递归的函数中,由它来进入每个子目录并查找指定的文件。

    20.15 本书的有些例子程序有许多缺陷,为什么不把它们写得更好?
    本书的有些例子尽管是完整的程序,但是比较短小,因此往往不是实用的、注释完整的程序。这样做的原因是为了尽量给读者提供明确的、知识性强的,但又非常简洁的答案。假如例子很长,它们就会破坏全书的连贯性,并且不利于读者把握基本的学习目标。

    请参见:    .
    20.12 怎样把数据从一个程序传给另一个程序?
    20.14 怎样找到程序中的重要文件(数据库,配置文件,等等)?


    20.16 怎样使Ctrl+Break失效?
    有好几种方法可以使Ctrl+Break功能失效,这里只讨论两种最常用的方法。
    第一种方法是用DOS来解除Ctrl+Break功能。你可能用DOS中断21H的33H函数来得到或设置Ctrl+Break检查标志,该标志告诉DOS是否在Ctrl+Break按下时对其进行处理。下面的例子说明了这种方法:
# include <stdio. h>
# include <dos. h>
void main(int argc,char **argv)
{
      union REGS regs;
      int ctrlBreakFlag ;
      / * find out the curre.nt state of the flag * /
      regs. x. ax= 0x3300 ;                       / * subfunction 0 gets flag state */
      int86 (0x21, ®s, ®s) ;
      ctrlBreakFlag == rags. h. dl ;                 / * save flag value from DL * /
      / * set the state of the flag to disable Ctrl+Break * /
      regs. x. ax=0x3301;
      regs. h. dl = 0 ;                              / * disable checking * /
      int86(0x21,®s, ®s) ;              /* subfunction 1 sets flag state * /
}
    上例首先调用DOS来查询Ctrl+Break检查标志的当前状态,并将其存入ctrlBreakFlag中。DOS调用的返回值存在DL中,假如解除了Ctrl+Break检查,则该值为O;假如答应Ctrl+Break检查,则该值为1。接着,上例清除DL并调用DOS的设置Ctrl+Break标志函数来解除Ctrl+Break检查。在调用上述函数重新设置Ctrl+Break标志之前,此次所设置的状态将
一直保留。上例没有用到子函数02(AX一0x3302),该函数能同时得到并设置Ctrl+Break标志的状态,为了完成这项任务,你应该把0x3302放到AX中,把要求的Ctrl+Break标志状态放到DL中,然后进行中断,并把原来的状态从DL中存入ctrlBreakFlag中。
    第二种方法是使用信号(signal)。信号是从过去的UNIX时代继续下来的。信号函数的作用是在某些事件发生时通知程序员,这些事件之一就是用户中断——在DOS下就是Ctrl+Break事件。下面的例子说明了如何用Microsoft的signal()函数来捕捉Ctrl+Break事件并作出反应(假设答应Ctrl+Break检查):
# include <[stdio. h>
# include <signal. h>
int exitHandler (void) ;
int main (int argc,char ** argv)
{
      int quitFlag = 0 ;
      / * Trap all Ctrl+Breaks * /
      signal (SIGINT, (void (__cdecl * ) (int))exitHandler);
      / * Sit in infinite loop until user presses Ctrl+Break * /
      while (quitFlag = = 0)
      printf (" % s\", (argv > 1 ) ? argv [ 1 ] : "Waiting for Ctrl + Break" ) ;
}
/ * Ctrl+Break event handler function * /
int exitHandler ()
{
     char ch ;
     / * Disable Ctrl+Break handling while inside the handler * /
     signal (SIGINT, SIG_ IGN ) ;
      / * Since it was an "interrupt",clear keyboard input buffer * /
     fflush(stdin) ;
      / * Ask if user really wants to quit program * /
     printf("\nCtrl+Break occurred. Do you wish to exit this program?
     →(Y or N)");
      / * Flush output buffer as well * /
     fflush (stdout) ;
      / * Get input from user, print character and newline * /
     ch =getche ( ) ;
      printf("\n" ) ;
      / * If user said yes, leave program * /
      if(toupper (eh) = 'Y' )
           exit (0) ;
      / * Reenable Ctrl+Break handling * /
      signal (SIGINT, (void (__cdecl * ) (int))exitHandler) ;
      return(0) ;
}

    上例的好处在于每当按下Ctrl+Break时都会调用一个函数,也就是说,你可以选择将要作出的反应一一你可以忽略这个事件,相当于解除Ctrl+Break功能;你也可以作出所需的其它任何反应;上例的另一个好处是当程序退出时,Ctrl+Break的正常操作就会恢复,不需要人为的干预。

    请参见:
    20.17可以使热启动(Ctrl+Break+Delete)失效吗?

视频教程列表
文章教程搜索
 
C语言程序设计推荐教程
C语言程序设计热门教程
看全部视频教程
购买方式/价格
购买视频教程: 咨询客服
tel:15972130058