茂隆's profileHorsehead NabulaBlogListsNetwork Tools Help

茂隆 林

Occupation
Interests
Just hope to be a good friend of you!

Horsehead Nabula

这里是我的工作和学习空间,欢迎光临!-__-
January 22

UNICODE编程资料

    (Unicode使Windows程序跑得更快,扩展性更强,但我常常看到Unicode就心虚,自己写则更不知从何写起,读下文受益匪浅,特转过来。不过这东西最重要还是实践,呵呵)
     以下分三部分,Unicode FAQ,Unicode编程摘要和VC下的Unicode编程

--------------------------------------------------------------------------------

一.  UNICODE FAQ(转贴)

 1. 如何取得一个既包含单字节字符又包含双字节字符的字符串的字符个数?
可以调用Microsoft Visual C++的运行期库包含函数_mbslen来操作多字节(既包括单字节也包括双字节)字符串。
调用strlen函数,无法真正了解字符串中究竟有多少字符,它只能告诉你到达结尾的0之前有多少个字节。

2. 如何对DBCS(双字节字符集)字符串进行操作?
函数 描述
PTSTR CharNext ( LPCTSTR ); 返回字符串中下一个字符的地址
PTSTR CharPrev ( LPCTSTR, LPCTSTR ); 返回字符串中上一个字符的地址
BOOL IsDBCSLeadByte( BYTE ); 如果该字节是DBCS字符的第一个字节,则返回非0值

3. 为什么要使用Unicode?
(1) 可以很容易地在不同语言之间进行数据交换。
(2) 使你能够分配支持所有语言的单个二进制.exe文件或DLL文件。
(3) 提高应用程序的运行效率。
Windows 2000是使用Unicode从头进行开发的,如果调用任何一个Windows函数并给它传递一个ANSI字符串,那么系统首先要将字符串转换成 Unicode,然后将Unicode字符串传递给操作系统。如果希望函数返回ANSI字符串,系统就会首先将Unicode字符串转换成ANSI字符串,然后将结果返回给你的应用程序。进行这些字符串的转换需要占用系统的时间和内存。通过从头开始用Unicode来开发应用程序,就能够使你的应用程序更加有效地运行。
Windows CE 本身就是使用Unicode的一种操作系统,完全不支持ANSI Windows函数
Windows 98 只支持ANSI,只能为ANSI开发应用程序。
Microsoft公司将COM从16位Windows转换成Win32时,公司决定需要字符串的所有COM接口方法都只能接受Unicode字符串。

4. 如何编写Unicode源代码?
Microsoft 公司为Unicode设计了WindowsAPI,这样,可以尽量减少代码的影响。实际上,可以编写单个源代码文件,以便使用或者不使用Unicode来对它进行编译。只需要定义两个宏(UNICODE和_UNICODE),就可以修改然后重新编译该源文件。
_UNICODE宏用于C运行期头文件,而UNICODE宏则用于Windows头文件。当编译源代码模块时,通常必须同时定义这两个宏。

5. Windows定义的Unicode数据类型有哪些?
数据类型 说明
WCHAR Unicode字符
PWSTR 指向Unicode字符串的指针
PCWSTR 指向一个恒定的Unicode字符串的指针
对应的ANSI数据类型为CHAR,LPSTR和LPCSTR。
ANSI/Unicode通用数据类型为TCHAR,PTSTR,LPCTSTR。

6. 如何对Unicode进行操作?
字符集 特性 实例
ANSI 操作函数以str开头 strcpy
Unicode 操作函数以wcs开头 wcscpy
MBCS 操作函数以_mbs开头 _mbscpy
ANSI/Unicode 操作函数以_tcs开头 _tcscpy(C运行期库)
ANSI/Unicode 操作函数以lstr开头 lstrcpy(Windows函数)
所有新的和未过时的函数在Windows2000中都同时拥有ANSI和Unicode两个版本。ANSI版本函数结尾以A表示;Unicode版本函数结尾以W表示。Windows会如下定义:
#ifdef UNICODE
#define CreateWindowEx CreateWindowExW
#else
#define CreateWindowEx CreateWindowExA
#endif // !UNICODE

7. 如何表示Unicode字符串常量?
字符集 实例
ANSI "string"
Unicode L"string"
ANSI/Unicode T("string")或_TEXT("string")if( szError[0] == _TEXT(‘J') ){ }

8. 为什么应当尽量使用操作系统函数?
这将有助于稍稍提高应用程序的运行性能,因为操作系统字符串函数常常被大型应用程序比如操作系统的外壳进程Explorer.exe所使用。由于这些函数使用得很多,因此,在应用程序运行时,它们可能已经被装入RAM。
如:StrCat,StrChr,StrCmp和StrCpy等。

9. 如何编写符合ANSI和Unicode的应用程序?
(1) 将文本串视为字符数组,而不是chars数组或字节数组。
(2) 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。
(3) 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。
(4) 将TEXT宏用于原义字符和字符串。
(5) 执行全局性替换(例如用PTSTR替换PSTR)。
(6)修改字符串运算问题。例如函数通常希望在字符中传递一个缓存的大小,而不是字节。这意味着不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用
malloc(nCharacters *sizeof(TCHAR)),而不是调用malloc(nCharacters)。

10. 如何对字符串进行有选择的比较?
通过调用CompareString来实现。
标志 含义
NORM_IGNORECASE 忽略字母的大小写
NORM_IGNOREKANATYPE 不区分平假名与片假名字符
NORM_IGNORENONSPACE 忽略无间隔字符
NORM_IGNORESYMBOLS 忽略符号
NORM_IGNOREWIDTH 不区分单字节字符与作为双字节字符的同一个字符
SORT_STRINGSORT 将标点符号作为普通符号来处理

11. 如何判断一个文本文件是ANSI还是Unicode?
判断如果文本文件的开头两个字节是0xFF和0xFE,那么就是Unicode,否则是ANSI。

12. 如何判断一段字符串是ANSI还是Unicode?
用IsTextUnicode进行判断。IsTextUnicode使用一系列统计方法和定性方法,以便猜测缓存的内容。由于这不是一种确切的科学方法,因此 IsTextUnicode有可能返回不正确的结果。

13. 如何在Unicode与ANSI之间转换字符串?
Windows函数MultiByteToWideChar用于将多字节字符串转换成宽字符串;函数WideCharToMultiByte将宽字符串转换成等价的多字节字符串。

--------------------------------------------------------------------------------
二.  Unicode 编程摘要

Visual C++ 概念:添加功能   
(From MSDN)
若要利用 MFC 和 C 运行时对 Unicode 的支持,需要:

定义 _UNICODE。
在生成程序之前定义 _UNICODE 符号。

指定入口点。
在项目的属性页对话框中“链接器”文件夹的“输出”页中,设置 wWinMainCRTStartup 的入口点符号。

使用“可移植的”运行时函数和类型。
为 Unicode 字符串处理使用正确的 C 运行时函数。可以使用 wcs 函数族,但您可能更喜欢使用完全“可移植的”(支持国际化的)_TCHAR 宏。这些宏都以 _tcs 为前缀;它们一对一地替换 str 函数族。在“运行时库参考”的国际化节中对这些函数有详细介绍。有关更多信息,请参见 TCHAR.H 中的一般文本映射。

使用支持 Unicode 中描述的 _TCHAR 和相关的可移植数据类型。

正确地处理字符串。
Visual C++ 编译器将编码的字符串解释为

L"this is a literal string"指出这是 Unicode 字符的字符串。可以对文字字符使用相同的前缀。一般使用 _T 宏对字符串进行编码,因此在 Unicode 下字符串编译为 Unicode 字符串,不使用 Unicode 时字符串编译为 ANSI 字符串(包括 MBCS)。例如,不使用:

pWnd->SetWindowText( "Hello" );而使用:

pWnd->SetWindowText( _T("Hello") );使用已定义的 _UNICODE,_T 将字符串翻译为以 L 为前缀的格式;否则 _T 将字符串翻译为不带 L 前缀的格式。

提示   _T 宏与 _TEXT 宏相同。
将字符串长度传递给函数时要小心。
一些函数需要获取字符串的字符数;另一些函数需要获取字符串的字节数。例如,如果已定义 _UNICODE,则下列对 CArchive 对象的调用无效(str 属于 CString):

archive.Write( str, str.GetLength( ) );    // invalid在 Unicode 应用程序中,由于每个字符都是双字节宽,因此长度会给出字符数但不给出正确的字节数。所以必须使用:

archive.Write( str, str.GetLength( ) * sizeof( _TCHAR ) );    // valid它指定要写入的正确字节数。

但是,MFC 成员函数是面向字符而非面向字节的,因此无需此额外的编码:

pDC->TextOut( str, str.GetLength( ) );CDC::TextOut 采用字符数而非字节数。

总之,MFC 和运行时库对 Windows 2000 下的 Unicode 编程提供下列支持:

除数据库类成员函数外,所有 MFC(包括 CString)函数都支持 Unicode。CString 还提供 Unicode/ANSI 转换函数。
运行时库提供所有字符串处理函数的 Unicode 版本。(运行时库还提供适合 Unicode 或 MBCS 的“可移植”版本。这些版本是 _tcs 宏。)
TCHAR.H 提供可移植的数据类型以及翻译字符串和字符的 _T 宏。请参见 TCHAR.H 中的一般文本映射。
运行时库提供 main 的宽字符版本。使用 wmain 使应用程序成为“Unicode 识别”。


--------------------------------------------------------------------------------
三.  VC中的Unicode编程

     在windows下编程还是支持unicode吧,大势所趋啊,window 2k以后的系统底层都是基于Unicode的,就算你调用ANSI的API(以A结尾比如SetWidowsTextA),系统也会在你的进程默认堆上动态分配一块内存,存放转换后的Unicode字符串,然后把转换后的字符串传递给API,如果调用了返回值为ANSI字符串的API,Windows会在后台进行相反的转换,多浪费时间啊!!就算不考虑效率问题,难道你不想让你的软件国际化吗?你还想面临半个汉字等尴尬的问题吗?

    其实VC中进行Unicode编程也不麻烦,大概如下:
1.为工程添加UNICODE和_UNICODE预处理选项,在VC.net中就是 项目 -> 属性 -> c/c++ -> 预处理器 在"预处理定义"中加入这两个宏定义(vc6中 project -> settings -> c/c++ -> general 中的 Preprocessor definitions).

2.Include <TCHAR.h>(一般在stdafx.h中)然后把所有使用char*定义变量的地方换为LPTSTR/TCHAR*或LPCTSTR/const TCHAR*(对应于const char*).

3.把所有的字符串常量用_T()宏包起来,比如 TCHAR* szText = _T("我的Text");

4.所有的C库字符串操作函数也做相应的替换,比如
strlen ->_tcslen
strcat ->_tcscat
strcmp ->_tcscmp
......
    注意,这些函数中的"文本长度"均为字符个数,而非char个数具体参看MSDN.

5.API调用一般不用做特殊处理,当定义了UNICODE和_UNICODE后,所有的API都会被宏指向W结尾的版本(不定义则指向A结尾的版本).

    其实,上面所说的并非强制你使用UNICODE,如果你还想回去使用ANSI,没有问题,把第一步定义的两个宏拿掉就OK了,继续我们的ANSI编程!!
:)

January 15

成为符合ANSI和Unicode的应用程序


即使你不打算立即使用Unicode,最好也应该着手将你的应用程序转换成符合Unicode的应
用程序。下面是应该遵循的一些基本原则:
• 将文本串视为字符数组,而不是chars数组或字节数组。
• 将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。
• 将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。
• 将TEXT宏用于原义字符和字符串。
• 执行全局性替换(例如用PTSTR替换PSTR)。
• 修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。这意味着你不应该传递sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)。另外,如果需要为字符串分配一个内存块,并且拥有该字符串中的字符数目,那么请记住要按字节来分配内存。这就是说,应该调用malloc(nCharacters *sizeof(TCHAR)), 而不是调用malloc(nCharacters)。在上面所说的所有原则中,这是最难记住的一条原则,如果操作错误,编译器将不发出任何警告。
 
--摘自《Windows核心编程》
December 26

Flash与VC的通信方法

基于做智能家居演示程序的需要,必须开发出较好的动画介面。主程序由VC开发通过串口与无线模块通信。动画界面用VC来做是不可取的,于是决定用Flash实现。做成控件的形式嵌入VC,然后通过Flash按钮触发VC的程序,实现相应的功能。
 
这里只说明Flash是如何给VC发消息,和VC是如何捕获消息的。方法比我们想像中简单。
 
1)首先是Flash
    把Flash动画都做好后,选择要触发的事件,如按钮或时间轴上的某个关键帧,然后在Action选项卡上代码区加入一个函数:fscommand(const char *str1,const char *str2);
    参数str1和str2是函数的两个参数均为字符串指针,有点像API消息函数里的WPARAM和LPARAM。调用如下:
    fscommand('1','2');
    这样,事件一但发生(按钮按下或运行到该关键帧),fscommand就会运行,就会向VC发送一个参数为'1'和'2'的消息。剩下的工作就是在VC里捕获此消息。
    运行Ctrl+Enter生成swf文件。
 
2)VC端
    在VC工程文件里选Project -> Add to project -> Conponents and Controls,然后选 Registered ActiveX Controls -> Shockwave Flash Object ,然后Insert即可。然后你会发觉工程里多了一个类CShockwaveFlash。这个类可用来播放Flash文件,但这不是我的重点。这里要做的是捕捉由Flash发送出来的消息:fscommand('1','2');
    在VC工程打开一个对话框资源,在控件栏里加入刚才添加的ShockwaveFlash控件。然后Ctrl+W打开Classwizard。在Class name里选择你要响应Flash消息的类。如CMyView或CMyDlg等。在Object ID里选择IDC_SHOCKWAVEFLASH1,在Message里双击FSCommand,然后Edit Code,添加如下代码:
 
void CFlashToVCDlg::OnFSCommandShockwaveflash1(LPCTSTR command, LPCTSTR args)
{
     // TODO: Add your control notification handler code here
     UCHAR com,arg;
     com = *command;
     arg = *args;
     switch(com) {
     case '1':
          switch(arg) {
          case '1':
              break;
          case '2':
              MessageBox(_T("12"));
              break;
          default:
              break;
          }
          break;
     case '2':
          break;
     default:
          break;
     }
 
运行结果输出12的MessageBox。具体怎样跑的应该看得出来。
 
功能就这样,是Flash向VC通信的。基于VC向Flash通信,网上说可以在Flash设置一变量,用定时器不断检测变量值。用VC来改变这个值即可。至于具体怎么做有空可以试试。呵呵。
 
还有一点奇怪的是fscommand中参数是字符串,但我用fscommand(1,2)调用也行,但换成字母就不行了。一般用一个字符即可。多个字符好像只识别第一个。不过其实一个字符都足够我们实现足够多的功能了。
 
终于写了一篇原创,高兴,呵呵。
December 11

以后来点原创的

在看一些Windows核心编程的书,以后写点笔记,当是一个总结吧。这样对别人对自己都可能有好处。
December 05

驱动程序与应用程序之间的通信

总的来说驱动程序与应用程序之间的通信可概括为以下两点
1)应用程序通过API和WDM设备进行通信,进行读写和控制的请求;
2)驱动程序通过事件句柄(应用程序调用API时传送给驱动程序的事件句柄,由应用程序建立)设置事件,触发应用程序。应用程序通过一个辅助线程等待事件的发生,或用WaitForSingleObject等函数等待。
具体说明如下:
 
  在Windows程序中,应用程序是通过与驱动程序的通讯对硬件进行操控的。而驱动程序对于用户接口软件来说应该是透明的,即应该使用户能感觉到似乎直接在对硬件进行操作。应用程序实现与WDM驱动程序的通讯过程是:应用程序通过调用Win32函数CreateFile打开和创建到设备的连接,然后调用DeviceIoControl发出特许的请求和WDM进行通讯,可以发送数据给驱动程序也可以从驱动程序中读取数据,还可以通过调用ReadFile函数从WDM中读取数据或是调用WriteFile函数将数据发送到WDM程序中去。当一个进程完成时,Win32应用程序保证所有的句柄都被关闭,并取消任何在等待的I/O请求,CloseHandle就是在完成设备文件使用时关闭文件句柄。例如在应用程序中:
 m_hDevice = CreateFile("\\\\.\\Pci",
 GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 
 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
//用异步方式打开设备驱动程序句柄
  当驱动程序捕捉到特定的事件(如中断)发生时,应通知应用程序。一般,WDM可以使用WIN32事件与应用程序通信,应用程序创建一个事件后,直接将该事件句柄传递给驱动程序,然后等待驱动程序发送事件消息。当驱动程序获得这个事件的一个对象指针后,可以在IRQL≤DISPATCH_LEVEL级别的例程中设置事件信号状态来触发应用程序。因此在应用程序中用一个辅助线程来等待驱动程序发送的事件消息:
  m_hSealTBEvent = CreateEvent(NULL, FALSE, FALSE,NULL); //创建事件
    CardSourceInfo.Ring3Application = m_hSealTBEvent; //将事件句柄封装以传递给驱动
    DeviceIoControl(Ring0_DrvHandel, IOCtrl_Set_SourceInfo, &CardSourceInfo,
  sizeof(CardSourceInfo), NULL, 0, &nBytesRead, NULL)
  //将事件句柄传递给驱动程序
    WaitForSingleObject(m_hSealTBEvent, INFINITE);
    //当事件被驱动设置为信号态时,此线程才向下执行。
在驱动程序中m_pEventToSignal->Set(); //设置事件为信号态,以通知应用程序。
 
最后一句还不明白是为什么是m_pEventToSignal,不知道哪里来的变量。
内容网址:
http://bbs.gd-emb.org/?display=topic&id=6958
 

延时过程调用的应用

延时调用可用在Windows驱动程序的中断处理中。由于中断处理的中断级别较高--在DIRQL级别,所以应该使其执行时间尽可能短。于是一般的情况是在中断处理程序里判断中断发生,然后调用延时过程调用例程实现中断程序的功能。说明如下:
 
  当硬件设备产生中断请求时,驱动程序则需要注册一个处理程序,在中断到达时进行正确的处理。在DriverWorks中通过KInterrupt类实现硬件中断处理,该类封装了IoConnectInterrupt函数来初始化中断及将一个中断服务例程连接到一个中断上。中断服务例程是运行在DIRQL级别上,因此处理时间应该尽可能的短,并且在该级别上还有很多内核函数不能调用。所以一般在中断服务例程中只判断该中断是否由自己的设备产生,若是则调用一个在DISPATCH_LEVEL级别上运行的延迟过程调用。在延迟过程调用例程中可完成大部分的中断处理工作。
  例如,在中断服务程序中通过判断是不是该设备产生的中断,以响应中断(以下程序都只给出关键代码):
 status = m_IoPortRange0.ind(INTCSR); //获取设备的中断状态
 if ((status & 0x800000) != 0x800000) { return FALSE; }
  //判断是不是由该设备产生的中断,如果不是则返回
 m_IoPortRange0.outd(INTCSR, status & 0xff02ffff); //屏蔽中断
 if (!m_DpcFor_Irq.Request(NULL, NULL)) ; //调用延时过程调用例程,处理中断事件
 
文档网址
December 04

笑脸C程序

程序如下:
#include "stdio.h"
 
typedef struct {         
 char *c;
}C;

#define PRINT_ME  &(((C *)2)->c)
 
int main(int argc, char* argv[])
{
 printf("%c\n",PRINT_ME); 
 return 0;
}
 
这段程序,猜猜输出了什么,呵呵。……
程序首先把2强制转换为指向C类型的指针,即2已经不是常量,是一个地址,2这个地址存放的是一个C类型的数据;C类只有一个成员c,所以取c的地址,实际上就是C的地址,(即结构体C中成员指针c的地址)值为2;
打印出ASCII码为2的字符……一个笑脸吧……
绕了半天,其实和下面的结果是一样的……
#include <stdio.h>
void main() {        
   printf("%c\n", 2);
}
 
呵呵,C语言真能玩花样,如果是考试题,见了那不郁闷才怪。
 
December 03

#pragma 预处理指令详解

在别的网页考过来的,关于#pragma预处理指令详解如下:
 
       在所有的预处理指令中,#Pragma  指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。其格式一般为:
       #Pragma  Para  
       其中Para  为参数,下面来看一些常用的参数。  
 
       (1)message  参数。  Message  参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:  
             #Pragma  message(“消息文本”)  
             当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。  
       当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法  
             #ifdef  _X86  
             #Pragma  message(“_X86  macro  activated!”)  
             #endif  
             当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86  macro  activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。  
       
     (2)另一个使用得比较多的pragma参数是code_seg。格式如:  
           #pragma  code_seg(  ["section-name"[,"section-class"]  ]  )  
           它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。  
 
     (3)#pragma  once  (比较常用)  
           只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。  
       
     (4)#pragma  hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。    
         有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma  startup指定编译优先级,如果使用了#pragma  package(smart_init)  ,BCB就会根据优先级的大小先后编译。    
       
     (5)#pragma  resource  "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体  
外观的定义。    
         
     (6)#pragma  warning(  disable  :  4507  34;  once  :  4385;  error  :  164  )  
           等价于:  
           #pragma  warning(disable:4507  34)    //  不显示4507和34号警告信息  
           #pragma  warning(once:4385)                //  4385号警告信息仅报告一次  
           #pragma  warning(error:164)                //  把164号警告信息作为一个错误。  
           同时这个pragma  warning  也支持如下格式:  
           #pragma  warning(  push  [  ,n  ]  )  
           #pragma  warning(  pop  )  
           这里n代表一个警告等级(1---4)。  
           #pragma  warning(  push  )保存所有警告信息的现有的警告状态。  
           #pragma  warning(  push,  n)保存所有警告信息的现有的警告状态,并且把全局警告  
等级设定为n。    
           #pragma  warning(  pop  )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的  
一切改动取消。例如:  
           #pragma  warning(  push  )  
           #pragma  warning(  disable  :  4705  )  
           #pragma  warning(  disable  :  4706  )  
           #pragma  warning(  disable  :  4707  )  
           //.......  
           #pragma  warning(  pop  )    
           在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。  
       (7)pragma  comment(...)  
             该指令将一个注释记录放入一个对象文件或可执行文件中。  
           常用的lib关键字,可以帮我们连入一个库文件。  
   
每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能:  
#pragma  loop_opt(on)            //  激活  
#pragma  loop_opt(off)    //  终止  
有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter  xxx  is  never  used  in  function  xxx”,可以这样:  
#pragma  warn  —100            //  Turn  off  the  warning  message  for  warning  #100  
int  insert_record(REC  *r)  
{  /*  function  body  */  }  
#pragma  warn  +100                        //  Turn  the  warning  message  for  warning  #100  back  on  
函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。  
每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。