论坛交流
首页办公自动化| 网页制作| 平面设计| 动画制作| 数据库开发| 程序设计| 全部视频教程
应用视频: 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)(3)

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

    20.17 可以使热启动(Ctrl+Alt+Delete)失效吗?
    可以。尽管不轻易讲清楚如何使热启动失效,但其编程实现却并非难事。为了捕捉Ctrl+Alt+Delete击键序列,你必须利用键盘的中断服务程序(interrupt service routine,缩写为ISR)。从一个高的层次上讲,键盘ISR是这样工作的:监视(捕捉)所有的键盘输入,等待Ctrl+Alt+Delete击键序列。假如捕捉到的不是Ctrl+Alt+Delete击键序列,就把它传给“计算机”;假如捕捉到Ctrl+Alt+Del击键序列,就把它抹掉(从键盘的字符缓冲区中删掉),或者把它转换为其它击键(你的程序知道这个击键表示用户想要热启动计算机)。在程序结束时,停止监视键盘输入并恢复正常的操作。这就是ISR的工作方式一~它们把自己连接到键盘和其它中断上,这样它们就能知道在什么时候出面去完成自己的任务。
    那么你应该如何完成这些工作呢?当然,要用一个C程序。下面是一个简化了的例子,它
监视所有的ctrl+Alt+Del击键序列,在该组合键按下时不作任何反应:
# include <stdlib. h>
# include <dos. h>
/ * function prototypes * /
      void (  interrupt far_cdecl KbIntProc)(
                             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);
void( interrupt far cdecl * OldKbIntProc) (void);
unsigned char far * kbFlags;          / * pointer to keyboard flags * /
int key char, junk;                       * miscellaneous variables * /
/ * keyboard scancode valuss * /
# define AI.T            0x8
# define CTRL            0x4
# define KEY MASK        0x0F
# define DELETE          0x53
void
main(int argc,char ** argv)
{
       int i,idx ;
       / * Save old interrupt vectors * /
       OldKbIntProc= dos_getvect (0x9)
       /* Set pointer to keyboard flags * /
       FP SEG(kbFlags) = 0;
       FP OFF(kbFlags) = 0x417;
       / * Add my ISR to the chain * /
         dos setvect(0xg,KbIntProc) ;
       / * Print something while user presses keys... * /
       / * Until ESCAPE is pressed, then leave * /
       while (getch() !=27){
              printf ("Disallowing Ctrl+Alt+Delete... \n" );
      }
       / * Remove myself from the chain * /
         dos setvect(0x9,OldKbIntProc) ;
 }

void _interrupt_far_cdecl KblntProc
                             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)
{
      / * Get keystroke input from keyboard port * /
      key_char =inp (0x60) ;
      if( (( * kbFlags & KEY_MASK)= = (CTRL | ALT))
                                       && (key_char = =DELETE) ) {
             / * Reset the keyboard * /
             junk = inp(0x61) ;
             outp(0x61,(junk ] 0x80));
             outp (0x61 ,iunk) ;
             outp(0x60,(key_char | 0x80));
             outp (0x60,0xgC) ;
     }
       / * Reset the interrupt counter * /
       outp (0x20,0x20) ;
       / * Now call the next ISR in line * /
       ( * OldKbIntProc) () ;
}

    这个程序只有两个部分:main()函数体和键盘中断服务程序KbIntProc().main()函数先是通过dos getvect()来检索当前键盘中断服务程序(ISR)的地址,然后通过_dos_setvect()把键盘中断服务程序换为KbIntProc().while循环不断地接收键盘输入,并反复打印同一条消息,直到Escape键(其ASCII码为十进制数27)被按下为止。当Escape键被按下后,程序就调用dos_setvect()来恢复原来的键盘中断服务程序。
    然而,好戏全在KbIntProc()之中。当它被装入后(通过前面所介绍的__dos_setvect()调用),它将在任何其它函数(或程序)之前看到所有的键盘输入,因此,它能首先处理或全部删掉键盘输入。当它接收到一个键时,它会检查键盘,看一看Ctrl键和Alt键是否已经被按下,并且检查所接收的键是否是Delete键。假如这两种情况都发生了,它就会复位键盘,从而删掉接收到的Delete键。不管所接收的键是否被忽略、处理或删掉,这个键盘ISR总要调用原来的键盘中断服务程序(OldKbIntProc());否则,计算机会立即停止运行。
    假如你认真思考一下,你就会发现这个程序可以捕捉任何击键或组合键,包括Ctrl+c和Ctrl+Break。因此,你完全可以考虑用这种办法来捕捉Ctrl+Break组合键。应该指出的是,这种方法的入侵性比较强—— 一个很小的错误都会导致计算机停止运行。但是,你不要因此就不去学习或使用这种方法。

    请参见:
    20.16 怎样使Ctrl+Break失效?

    20.18 怎样判定一个字符是否是一个字母?
    字母表中的所有字母(包括计算机键盘上的所有键)都被赋予了一个值,这些字符及其相应的值一起组成了ASCII字符集,该字符集在北美、欧洲和许多讲英语的国家中得到了广泛的使用。
    字母字符被分成大写和小写两组,并按数字顺序排列。有了这种安排,就能很方便地检查一个字符是否是一个字母以及是大写还是小写。下面这段代码说明了如何检查一个字符是否是一个字母:
int ch ;
ch=getche() ;
if((ch>=97) && (ch<=122))
      printf(" %c is a lowercase letter\n" ,ch);
else if ((ch>=65) && (ch<=90))
      print(" %c is an uppercase letter\n" ,ch);
else
      printf(" %c is not an alphabet letter\n" ,ch) ;
    在上例中,变量ch的值与十进制值进行比较。当然,它也可以与字符本身进行比较,因为ASCII字符既是按字符顺序定义的,也是按数字顺序定义的。请看下例:
int ch ;
ch=getche() ;
if((ch>='a') && (ch<='z'))
      printf("%c is a lowercase letter\n" ,ch);
else if ((ch>='A') && (ch<='Z'))
      print(" %c is a uppercase letter\n" ,ch);
else
      printf(" %c is not an alphabet letter\n" ,ch);
    你可以随便选择一种方法在程序中使用。但是,后一种方法的可读性要好一些,因为你很难记住ASCII码表中每个字符所对应的十进制值。

    请参见:
    20.19 怎样判定一个字符是否是一个数字?

    20.19 怎样判定一个字符是否是一个数字?
    在ASCII码表中,数字字符所对应的十进制值在48到57这个范围之内,因此,你可以用如下所示的 代码来检查一个字符是否是一个数字:
int ch ;
ch=getche() ;
if((ch>=48) && (ch<=57))       
      printf(" %c is a number character between 0 and 9\n" ,ch) ;
else
      printf(" %c is not a number\n" ,ch) ;
与20.18相似,变量ch也可以和数字本身进行比较:
int ch ;
ch=getche () ;
if((ch>='O') && (ch<='9'))
      printf(" %c is a number character between 0 and 9\n" ,oh) ;
else
      printf(" %c is not a number~n" ,ch) ;

    同样,选用哪一种方法由你决定,但后一种方法可读性更强。

    请参见:
    20.18 怎样判定一个字符是否是一个字母?

    20.20 怎样把一个十六进制的值赋给一个变量?
    c语言支持二进制、八进制、十进制和十六进制的计数系统,在表示一个数字时,用某个非凡的字符来区别其所属的计数系统是必要的。在表示二进制数时,要在数字的末尾加上“b”(如101b);在表示八进制数时,要使用反斜杠(如\014);在表示十六制数时,要使用“0x”字符序列(如0x34);显然,在表示十进制数时,不需要任何标识符,因为十进制是缺省的计数系统。
    要把一个十六进制的值赋给一个变量,你可以象下面这样做:
int x ;
x=0x20;                 /* put hex 20(32 in decimal) into x */
x='0x20' ;                / * put the ASCII character whose value is
                                    hex 20 into x * /
只有了解了十六进制计数系统,你才能知道要赋的值应该如何表示,详见20.24。

    请参见:
    20.24 什么是十六进制?

    20.21 怎样把一个八进制的值赋给一个变量?
    把一个八进制的值赋给一个变量与把一个十六进制的值赋给一个变量一样简单:
int x ;
x=\033;                 / * put octal 33 (decimal 27) into x * /
x='\033' ;               / * put the ASCII character whose value is
                                    octal 33 into x * /
同样,只有了解了八进制计数系统,你才能知道要赋的值应该如何表示,详见20.23。
    请参见:
    20.23 什么是八进制?

    20.22 什么是二制制?
    在数学计算中,二进制计数系统的公分母是最小的,它以2为基数。你还记得在小学或中学时所学的不同的计数系统吗?笔者在上小学时,曾在一堂数学课中学过以6为基数的计数系统;你先数1,2,3,4,5,然后是10,11,12,13,14,15,然后是20,等等,实际上,应该先数0,1,2,3,4,5,然后是10,1l,12,13,14,15,等等。从O开始数,能比较清楚地看出每6个数字组成一组——因此6就是基数。注重,你应该从O开始一起数到比基数小1的数(因为基数是6,所以你应该从O数到5)。当你数到5后,接着应该开始数两位数。假如你思考一下,你就会发现这与以10为基数(十进制)的计数系统是类似的——在你数到比基数小1的数(9)后,就转到两位数,并继续往下数。
    计算机中的计数系统以2为基数——即二进制。由于以2为基数,所以你先数O,1,然后是10,11,然后是100,101,110,111,然后是1000,1001,1010,1011,1100,1101,1110,1111,等等。与以6为基数时不同,在以2为基数时,在数到两位数之前,只需从O数到1。
    那么,为什么在计算机中要以2为基数呢?其原因在于计算机中使用了晶体管。晶体管使现代计算机的出现成为可能。晶体管就象电灯开关,电灯开关有“开”和“关”两种状态,晶体管也是如此。你可以认为“关”表示0,“开”表示1,这样,你就可以用一个晶体管(假如你愿意,也可以用一个电灯开关)来进行从。到1的计数了。仅仅使用两个数字(O到1)还不能做任何复杂的计算,但是我们还可以继续下去。假设有一个电灯开关控制面板,上面有4个大电灯开关,尽管每个开关只有两种状态,但是这些开关组合起来就会有16或2。(4个开关,每个2种状态)种不同的状态。这样,你就可以用4个开关来进行从。到15的计数了,见表20.22。

  表20.22  进制计数
-------------------------------------------------------
      开关         十进制值            幂
-------------------------------------------------------
       O             O
       1             1                 20
      10             2                 21
      11             3
     100             4                 22
     101             5
     110             6
     111             7
    1000             8                 23
    1001             9
    1010            10
    1011            11
    1100            12
    1101            13
    1110            14
    1111            15
-------------------------------------------------------
    上表说明了很重要的三点:(1)通过把开关并排放在一起,你就可以用它们来计数了——在本例中最多可以数到15(总共16次计数);(2)你可以把每个开关看作是一个二进制位,就象十进制系统中的十进制位一样;(3)假如每个开关都代表一个二进制位,那么它们刚好也都代表一个2的幂(20,21,22,23,等等)。
    此外,请注重,在表中出现2的幂的地方,计数结果就要增加一个二进制位。这与十进制系统是相同的,每增加一个十进制位时,这个新的十进制位也正是一个10的幂(1=100,10=101,100=102,等等)。明白了这一点后,你就可以很轻易地把二进制数转换为十进制数了,例如,二进制数10111就是(1×24)+(O×23)+(1×22)+(1×21)+(1×20),它等于十进制的(16+0+4+2+1)或23。10 1110 1011,一个大得多的二进制数,就是(1×29)+(O×28)+(1×27)+(1×26)+(1×25)+(0×24)+(1×23)+(O×22)+(1×21)+(1×20),它等于十进制的(512+0+128+64+32+0+8+0+2+1)或747。
    那么所有这些和我们有什么关系呢?在计算机领域中,存在着位(bit),半字节(nibble)和字节(byte)。一个半字节是4位,一个字节是8位。什么是一个位呢?它就是一个晶体管。因此,一个字节就是8个相邻的晶体管,就象表20.1中的4个开关一样。记住,假如你有4个组合在一起的开关(或晶体管),你就可以数24或16,你可以把这看作是由开关组成的一个半字节。假如一个半字节是4个晶体管组合在一起,那么一个字节就是8个晶体管组合在一起。你可以用8个晶体管数到2。或256,从另一个角度看,这意味着一个字节(含8个晶体管)可以表示256个不同的数字(从0到 255)。再深入一点,Intel 386,486和Pentium处理器被叫做32位处理器,这意味着这些Intel芯片所进行的每一次运算都是32位宽或32个晶体管宽的。32个晶体管,或32位,等价于232或4,294,967,296,即它们能表示超过40亿个不同的数字。
    当然,上述介绍还不能解释计算机是如何利用这些数字产生那种神奇的计算能力的,但它至少解释了计算机为什么要使用以及是如何使用二进制计数系统的。

    请参见:
    20.23 什么是八进制?
    20.24 什么是十六进制?

    20.23  什么是八进制?
    八进制是以8为基数的一种计数系统。在八进制系统中,你是这样计 数的:O,1,2,3,4,5,6,7,10,ll,12,13,等等。下面比较了八进制(第二行)和十进制(第一行)中的计数过程:
    O,l,2,3,4,5,6,7,8,9,10.11,12,13,14,15,16
    0,1,2.3,4,5,6,7,10,11,12,13,14,15,16,17,20
    注重,在八进制中,在数到7后,就要增加一个八进制位,第二个八进制位显然就是8?(等于十进制的8)。假如你数到第三个八进制位(八进制的100),那将是8?或十进制的64,因此,八进制的100等于十进制的64。
    现在,八进制已经不象以前那样常用了,这主要是因为现在的计算机使用的是8,16,32或64位处理器,最适合它们的计数系统是二进制或十六进制(见20.24中有关十六进制计数系统的介绍)
    C语言支持八进制字符集,这种字符要用反斜杠字符来标识。例如,在C程序中,下面的语句并不少见:
    if(x=='\007')break;
    这里的"\007"恰好就是ASCII值为7的字符;该语句用来检查终端鸣笛字符。另一个常见的八进制数是"\033",即Escape字符(在程序中它通常表示为"\033")。然而,八进制数现在已经很少见了——它们被十六进制数代替了。

    请参见:
    20.22 什么是二进制?
    20.24 什么是十六进制?

    20.24 什么是十六进制?
    十六进制(hexadecimal,缩写为hex)是以16为基数的计数系统,它是计算机中最常用的计数系统。十六进制中的计数过程为:O,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,等等。十六进制中的字母是几个单位数标识符,表示十进制的10到15。要记住在不同基数下的计数规则,即从O数到比基数小1的数字,在十六进制中这个数就是十进制的15。因为西式数字中没有表示大于9的单位数,所以就用A,B,c,D,E和F来表示十进制的10到15。在十六进制中,数到F之后,就要转到两位数上,也就是1OH或
Ox1O。下面对十六进制(第二行)和十进制(第一行)的计数过程作一下比较:
    1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,……
    1,2,3,4,5,6,7,8,9,A, B, C, D, E, F, 10,……
 
    注重,十进制的10等于十六进制的A。与前面讨论过的计数系统一样,每增加一个十六进制位,实际上就增加了一个16的幂,即160(1),161(16),162(256),163(4096),等等。因此,十六进制数3F可以展开为(3×161)+(F×160),等于十进制的(48+15)或63;十六进制数13F可以展开为(1×162)+(3×161)+(F×160),等于十进制的(256+48+15)或319。在c程序中,这两个数用0x3F或Oxl3F这样的形式来表示,其中的“0x”前缀用来告诉编译程序(和程序员)该数字应被当作十六进制数来处理。假如不加“0x”前缀,你就无法判定一个数究竟是十六进制数还是十进制数(或者是八进制数)。
    对表20.22稍作改进,加入十六进制的计数过程,就得到了表20.24:
—————————————————————————————————
  二进制    十进制值    二进制幂    十六进制    十六进制幂
—————————————————————————————————
  0000        O                       O
  0001        1    20                 1            160
  0010        2    21                 2
  0011        3                       3
  0100        4    22                 4
  0101        5                       5
  0110        6                       6
  0111        7                       7
  1000        8    23                 8
  1001        9                       9
  1010        10                      A
  1011        11                      B
  1100        12                      C
  1101        13                      D
  1110        14                      E
  1111        15                      F
  10000       16   24                10             161
—————————————————————————————————
    笔者在上表的最后又加了一行,使计数达到十进制的16。通过比较二进制、十进制和十六进制·你就会发现:“十”在二进制中是“1010”,在十进制中是“10”,在十六进制中是“A”;。。十六”在二进制中是“1 0000"或“10000”,在十进制中是“16”,在十六进制中是“1O”,,(见上表的最后一行)。这意味着什么呢?因为今天的16,32和64位处理器的位宽恰好都是16的倍数,所以在这些类型的计算机中用十六进制作为计数系统是非常合适的。
    十六进制位和二进位之间有一种“倍数”关系。在上表的最后一行中,二进制值被分为两部分(1 0000)。4个二进制位(或者4位)可以计数到15(包括O在内共16个不同的数字),而4位(bit)正好等于一个半字节(nibble)。在上表中你还可以发现,一个十六进制位同样可以计数到15(包括。在内共l 6个不同的数字),因此,一个十六进制位可以代表4个二进制位。一个很好的例子就是用二进制表示十进制的15和16,在二进制中,十进制的15就是1111,正好是4个二进制位
能表示的最大数字;在十六进制中,十进制的15就是F,也正好是一个十六进制位能表示的最大数字。十进制的16要用5个二进制位(1 0000)或两个十六进制位(10)来表示。下面把前文提到过的两个数字(0x3F和0x13F)转换为二进制:
    3F              111111
    l3F          100111111
    假如把前面的空格换为O,并且把二进制位分成4位一组,那么看起来就会清楚一些:
    3F     0 0011 1111
    l3F    1 0011 1111
    你并不一定要把二进制位分成4位一组,只不过当你明白了4个二进制位等价于一个十六进制位后,计数就更轻易了。为了证实上述两组数字是相等的,可以把二进制值转换为十进制值(十六进制值到十进制值的转换已经在前文中介绍过了);二进制的111111就是(1×25)+(1×24)+(1×23)+(1×22)+(1×21)+(1×20),等于十进制的(32+16+8+4+2+1)或63,与0x3F的转换结果相同。二进制的1 0011 1111就是(1×28)+(O×27)+(0×26)+(1×25)+(1×24)+(1×23)+(1×22)++(1×21)+(1×20),等于十进制的(256+32+1 6+8+4+2+1)或319。因此,十六进制和二进制能象手掌和手套那样相互匹配。

    请参见:
    20.22 什么是二进制?
    20.23 什么是八进制?

    20.25 什么是换码符(escape character)?
    换码符是用来执行一个命令或一项任务的字符,它们不会被打印到屏幕上。例如,一个换码符可以是这样一个字符,它被传递给一个设备,告诉计算机屏幕以红色而不是通常的白色来显示下一行。这个换码符将和真正要被设备以红色来显示的字符一起被传递给设备。那么设备如何知道这个字符是一个换码符呢?一般来说,在发送换码符之前,要先发送Escape键(十进制为27,八进制为\033),这样设备就会知道下一个到达的字符是换码符。当设备接收到这个换码符后,它先执行该换码符所代表的命令,然后恢复正常操作,即接收字符并把它们打印到屏幕上。因为通常要用两个或更多个字符来表示所要求的命令(Escape键加上命令字符本身),所以通常 称这些字符为换码序列。
    这听起来有些混乱(Escape键后面跟着换码符),但这也正是这些字符之所以被叫做换码符的原因。Escape键用来通知字符的接收者下一个字符是换码符,而不是普通的字符。换码符本身可以是任何字符,甚至可以是另一个Escape键。具体用什么字符来代表所要求的命令,由读入这些字符并等待相应命令的程序来决定。   
    这方面的一个例子是ANSI.SYS设备驱动程序,该程序由CONFIG.SYS文件装入,它会拦截所有送往屏幕的字符,并按换码序列的要求处理这些字符。ANSI.SYS的作用是提供一种方法来打印彩色的、带下划线的或闪烁的文本,或者执行象清屏这样的高级命令。ANSI.SYS的优点在于你不必知道你使用的是哪种显示器或显示卡,因为ANSI.SYS会替你处理这个问题。你只需在要送往屏幕的字符串中的合适位置加入换码符,ANSI.SYS会替你处理其余的事情。例如,假如你输入了“\033H4Hello there,”ANSI.SYS就会在屏幕上打印出红色的“Hello there”——它将发现Escape键(\033),读入命令(在这里是H4,即以红色打印其余的字符),然后打印其余的字符("Hello there")。
    在ANSI.SYS之前,换码符被用在老式的集中化计算机环境(一个主机连接着很多哑终端)中。在那个时代,终端自身没有计算能力,不能显示图形,而且大部分都是单色的,不能显示彩色。但是,每台显示器都有一套由主机发送给显示器的换码符,用来指示显示器做清屏、加下划线或闪烁这样一些事情。与使用ANSI.SYS一样,只要程序员在发送给显示器的字符串中加入换码符,显示器就会执行相应的命令。
    今天,这种类型的换码序列已经不再使用了。然而,当时还有许多其它类型的被定义为换码符的字符序列,它们一直被延用至今,并且仍然在被广泛使用。例如,在介绍如何把一个八进制值或十六进制值赋给一个变量的问题中,笔者就使用了一种换码符(在十六进制中使用“Ox”,在八进制中使用“\”)。注重,这些字符并没有用Escape键来作非凡标识,但它们的确被用来表示其后的字符有某种非凡性。实际上,反斜杠(\)经常被当作一个换码符来使用。例如,
在c语言中,你可以用“\n”来通知计算机“执行一次换行操作”,或者用“\t”来执行前进一个tab符的操作,等等。

    请参见:
    20.23 什么是八进制?
    20.24 什么是十六进制?

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