﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-笑看风云淡</title><link>http://www.cppblog.com/totti1006/</link><description>宠辱不惊，看庭前花开花落；去留无意，望天空云卷云舒</description><language>zh-cn</language><lastBuildDate>Wed, 08 Apr 2026 17:35:09 GMT</lastBuildDate><pubDate>Wed, 08 Apr 2026 17:35:09 GMT</pubDate><ttl>60</ttl><item><title>VC键盘消息</title><link>http://www.cppblog.com/totti1006/archive/2010/05/18/115701.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Tue, 18 May 2010 09:37:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/05/18/115701.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/115701.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/05/18/115701.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/115701.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/115701.html</trackback:ping><description><![CDATA[<p>在Microsoft Windows 中，键盘和鼠标是两个标准的用户输入源，在一些交叠的操作中通常相互补充使用。当然，鼠标在今天的应用程序中比10年前使用得更为广泛。甚至在一些应用程序中，我们更习惯于使用鼠标，例如在游戏、画图程序、音乐程序，以及Web创览器等程序中就是这样。然而，我们可以不使用鼠标，但绝对不能从一般的PC中拆掉键盘。<br>　　相对于个人计算机的其他组件，键盘有非常久远的历史，它起源于1874年的第一台Remington打字机。早期的计算机程序员用键盘在 Hollerith卡片上打孔，以后在哑终端上用键盘直接与大型主机通讯。PC上的键盘在某些方面进行了扩展，包括了功能键、光标定位键和（通常都带有的）单独的数字键盘，但它们的输入原理基本相同。</p>
<p>键盘基础</p>
<p>　　Windows程序获得键盘输入的方式：键盘输入以消息的形式传递给程序的窗口过程。实际上，第一次学习消息时，键盘就是一个明显的例子：消息应该传递给应用程序的信息类型。<br>　　Windows用8种不同的消息来传递不同的键盘事件。这好像太多了，但是（就像我们所看到的一样）程序可以忽略其中至少一半的消息而不会有任何问题。并且，在大多数情况下，这些消息中包含的键盘信息会多于程序所需要的。处理键盘的部分工作就是识别出哪些消息是重要的，哪些是不重要的。<br>一、键盘基础知识<br>　　虽然应用程序在很多情况下可以通过鼠标实现信息的输入，但到现在为止键盘仍然是PC机中不可替代的重要输入设备。<br>　　用键盘当作输入设备，每当用户按下或释放某一个键时，会产生一个中断，该中断激活键盘驱动程序KEYBOARD.DRV来对键盘中断进行处理。 KEYBOARD.DRV程序会根据用户的不同操作进行编码，然后调用Windows用户模块USER.EXE生成键盘消息，并将该消息发送到消息队列中等候处理。<br>1．扫描码和虚拟码<br>　　扫描码对应着键盘上的不同键，每一个键被按下或释放时，都会产生一个唯一的扫描码作为本身的标识。扫描码依赖于具体的硬件设备，即当相同的键被按下或释放时，在不同的机器上可能产生不同的扫描码。在程序中通常使用由Windows系统定义的与具体设备无关的虚拟码。在击键产生扫描码的同时，键盘驱动程序KEYBOARD.DRV截取键的扫描码，然后将其翻译成对应的虚拟码，再将扫描码和虚拟码一齐编码形成键盘消息。所以，最后发送到消息队列的键盘消息中，既包含了扫描码又包含了虚拟码。<br>　　经常使用的虚拟码在WINDOWS．H文件中定义，常用虚拟码的数值、常量符号和含义如表所示。</p>
<p>取值（16进制） 常量符号 含义<br>01 VK_LBUTTON 鼠标左键<br>02 VK_RBUTTON 鼠标右键<br>03 VK_CANCEL Break中断键<br>04 VK_MBUTTON 鼠标中键<br>05-07 -- 未定义<br>08 VK_BACK (BackSpace)键<br>09 VK_TAB Tab键<br>0A-0B -- 未定义<br>0C VK_CLEAR Clear键<br>0D VK_RETURN Enter键<br>0E-0F -- 未定义<br>10 VK_SHIFT Shift键<br>11 VK_CONTROL Ctrl键<br>12 VK_MENU Alt键<br>13 VK_PAUSE Pause键<br>14 VK_CAPTIAL CapsLock键<br>15-19 -- 汉字系统保留<br>1A -- 未定义<br>1B VK_ESCAPE Esc键<br>1C-1F -- 汉字系统保留<br>20 VK_SPACE 空格键<br>21 VK_PRIOR PageUp键<br>22 VK_NEXT PageDown键<br>23 VK_END End键<br>24 VK_HOME Home键<br>25 VK_LEFT &#8592;(Left Arrow)键<br>26 VK_UP &#8593;(Up Arrow)键<br>27 VK_RIGHT &#8594;(Right Arrow)键<br>28 VK_DOWN &#8595;(Down Arrow)键<br>29 VK_SELECT Select键<br>2A -- OEM保留<br>2B VK_EXECUTE Execute键<br>2C VK_SNAPSHOT Print Screen键<br>2D VK_INSERT Insert键<br>2E VK_DELETE Delete键<br>2F VK_HELP Help键<br>30-39 VK_0-VK_9 数字键0-9<br>3A-40 -- 未定义<br>41-5A VK_A-VK_Z 字母键A-Z<br>5B-5F -- 未定义<br>60-69 VK_NUMPAD0-VK_NUMPAD9 小键盘数字键0-9<br>6A VK_MULTIPLY *(乘号)键<br>6B VK_ADD +(加号)键<br>6C VK_SEPAPATOR 分隔符键<br>6E VK_SUBTRACT -(减号)键<br>6F VK_DECIMAL .(小数点)键<br>70-87 VK_DIVIDE /(除号)键<br>88-8F VK_F1-VK_F24 F1-F24功能键<br>90 VK_NUMBERLOCK Number lock键<br>91 VK_SCROLL Scroll lock键<br>92-B9 -- 未定义<br>BA-C0 -- OEM保留<br>C1-DA -- 未定义<br>DB_E4 -- OEM保留<br>E5 -- 未定义<br>E6 -- OEM保留<br>E7-E8 -- 未定义<br>E9-F5 -- OEM保留<br>F6-FE -- 未定义</p>
<p><br>2．输入焦点<br>　　同一时刻，Windows中可能有多个不同的程序在运行，也就是说有多个窗口同时存在。这时，键盘由多个窗口共享，但只有一个窗口能够接收到键盘消息，这个能够接收键盘消息的窗口被称为拥有输入焦点的窗口。<br>　　拥有输入焦点的窗口应该是当前的活动窗口，或者是活动窗口的子窗口，其标题和边框会以高亮度显示，以区别于其他窗口。拥有输入焦点的也可以是图标而不是窗口，此时，Windows也将消息发送给图标，只是消息的格式略有不同。<br>　　窗口过程可以通过发送WM_SETFOCUS和 WM_KILLFOCUS消息使窗体获得或失去输入焦点。程序也可以通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来判断窗体何时获得或失去输入焦点。其中WM_SETFOCUS消息表示窗口正获得输入焦点，WM_ KILLFOCUS消息表示窗口正失去输入焦点。<br>3．键盘消息<br>　　键盘消息分为系统键消息和非系统键消息。系统键消息是指由Aft键和其他键组合而产生的按键消息。当系统键被按下时产生WM_ SYSKEYDOWN消息，当系统键被释放时产生WM_SYSKEYUP消息。 Aft键与其他键形成的组合键通常用于对程序菜单和系统菜单进行选择，或用于在不同的程序之间进行切换。因此，系统键消息应该交由Windows进行处理，用户所编制的程序一般不处理系统键消息，而是将这些消息交由DefWindowProc函数进行处理。如果用户想对系统键消息进行处理，应该在处理完这些消息后，再将其发送给DefWindowProc函数，使得Windows系统能够正常工作。<br>　　某些击键消息可以被转换成字符消息，例如字母键、数字键等。而有些键只能产生按键消息而没有字符消息，例如 Shift键、Insert键等。消息循环中的 TranslateMessage函数可以实现从击键消息向字符消息的转化。当GetMessage函数捕获一个WM_SYSKEYDOWN消息或 WM_KEYDOWN消息后，TranslateMessage函数判断产生该消息的键是否能够被转换成字符消息，如果能，就将该消息转换成字符消息，再通过DispatchMessape函数将转换后的字符消息发送到消息队列中去。字符消息共有以下四种，如表所示。</p>
<p>字符 系统字符 非系统字符<br>普通字符 WM_SYSCHAR WM_CHAR<br>死字符 WM_SYSDEADCHAR WM_DEADCHAR</p>
<p>　　其中死字符是由某些特殊键盘上的按键所造成的，Windows一般忽略死字符所产生的消息。<br>　　Windows的消息一般是通过一个MSG结构体变量传送给消息处理函数的。对于键盘消息， MSG结构体变量的各个域中较重要的是lParam域和 wParam域。wParam域用于保存按键的虚拟键代码或字符的ASCII码。对于非字符消息，wParam域保存按键的虚拟健代码；对于字符消息， wParam域不保存字符的ASCII码。lParam域则用于保存击键时产生的附加信息，实际上一个32位的lParam变量被分为六部分，记录了以下相关信息：重复次数、OEM扫描码、扩展键标志、关联键标志、前一击键状态和转换状态。lParam域各位的含义如表所示。</p>
<p>位数 含义<br>0-15 击键重复次数累加<br>16-23 OEM扫描码<br>24 是否为扩展键<br>25-28 未定义<br>29 是否便用关联键，及Alt键是否同时按下。<br>30 前一次击键状态，0表示该键前一次状态为抬起，1表示前一次状态为按下<br>31 转换状态</p>
<p>　　按键的次序不同，产生的消息也不相同。例如，按下并释放1键，读过程依次产生如表所示三条消息。按下1键所产生的消息和wParam的取值</p>
<p>消息 wParam变量取值<br>WM_KEYDOWN 虚拟码1<br>WM_CHAR ASCII码&#8220;1&#8221;<br>WM_KEYUP 虚拟码1</p>
<p>　　如果按下Shift键后再按下1键并释放，则依次产生如表所示的消息。按下 Shift键后按 1健所产生的消息和 wParam的取值</p>
<p>消息 wParam变量取值<br>WM_KEYDOWN 虚拟码 VK_SHIFT<br>WM_KEYDOWN 虚拟码 VK_1<br>WM_CHAR ASCII码　&#8220;1&#8221;<br>WM_KEYUP 虚拟码 VK_1<br>WM_KEYUP 虚拟码 VK_SHIFT</p>
<p>二、键盘应用实例<br>　　下面通过一个应用程序实例来说明在实际编程中如何处理键盘消息。<br>#include &lt;windows.h&gt;<br>#include &lt;stdio.h&gt;<br>// 全局变量<br>RECT rc; //记录滚屏的矩形区域<br>?<br>int xChar, yChar; //文本输入点坐标</p>
<p>WNDCLASSEX wnd; //窗口类结构变量</p>
<p>char szAppName[] = "键盘消息监视程序"; //窗口类名<br>//函数声明<br>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);<br>BOOL MyRegisterClass(HINSTANCE hInstance);<br>BOOL InitInstance(HINSTANCE hInstance,int iCmdShow);<br>//函数：WinMain<br>//作用：入口函数<br>int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow)<br>{<br>　　MSG msg;<br>　　if(!MyRegisterClass(hInstance))<br>　　{<br>　　　　return FALSE;<br>　　}<br>　　<br>　　 if(!InitInstance(hInstance,iCmdShow))<br>　　{<br>　　　　return FALSE;<br>　　}<br>　　<br>　　while (GetMessage (&amp;msg, NULL, 0, 0))<br>　　{<br>　　　　TranslateMessage (&amp;msg);<br>　　　　DispatchMessage (&amp;msg);<br>　　}<br>　　return msg.wParam;<br>}<br>//函数：ShowKey<br>//作用：实现在窗口中显示按键信息<br>void ShowKey (HWND hwnd, int iType,char *szMessage,WPARAM wParam,LPARAM lParam)<br>{<br>　　static char *szFormat[2] ={"%-14s %3d %c %6u %4d %5s %5s %6s %6s",<br>　　　　　　　　　　　　　 　　"%-14s %3d %c %6u %4d %5s %5s %6s %6s" };<br>　　char szBuffer[80];<br>　　HDC hdc;<br>　　ScrollWindowEx(hwnd, 0, -yChar, &amp;rc,&amp;rc,NULL,NULL,SW_INVALIDATE);<br>　　hdc = GetDC (hwnd);<br>　　SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT));<br>　　TextOut (hdc,<br>　　　　　　 xChar,<br>　　　　　　 rc.bottom - yChar,<br>　　　　　　 szBuffer,<br>　　　　　　 wsprintf szBuffer,<br>　　　　　　 szFormat[iType],<br>　　　　　　 szMessage, //消息<br>　　　　　　 wParam, //虚拟键代码<br>　　　　　　 (BYTE) (iType ? wParam :&#8216; &#8217;),//显示字符值<br>　　　　　　 LOWORD (lParam), // 重复次数<br>　　　　　　 HIWORD (lParam) &amp; 0xFF, // OEM键盘扫描码<br>　　　　　　 //判断是否为增强键盘的扩展键<br>　　　　　　 (PSTR) (0x01000000 &amp; lParam ? &#8220;是&#8221; : &#8220;否&#8221;),<br>　　　　　　 //判断是否同时使用了ALT键<br>　　　　　　 (PSTR) (0x20000000 &amp; lParam ? &#8220;是&#8221; : &#8220;否&#8221;),<br>　　　　　　 (PSTR) (0x40000000 &amp; lParam ? &#8220;按下&#8221; : &#8220;抬&#8221;),<br>　　　　　　 //判断前一次击键状<br>　　　　　　 (PSTR)(0x80000000 &amp; lParam ? &#8220;按下&#8221; : &#8220;抬起&#8221;))<br>　　　　　　 //判断转换状态?<br>　　　　　　 );<br>　　ReleaseDC (hwnd, hdc); ?<br>　　ValidateRect (hwnd, NULL); ?<br>}<br>//函数：WndProc<br>//作用：处理主窗口的消息<br>LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)<br>{<br>　　static char szTop[] ="消息键 字符 重复数 扫描码 扩展码 ALT 前一状态 转换状态";<br>　　static char szUnd[] ="_______ __ ____ _____ ______ ______ ___ _______ ______";</p>
<p>　　//在窗口中输出文字作为信息标题<br>　　HDC hdc;<br>　　PAINTSTRUCT ps;<br>　　TEXTMETRIC tm;</p>
<p>　　switch (iMsg)<br>　　{<br>　　　　case WM_CREATE://处理窗口创建的消息<br>　　　　hdc = GetDC (hwnd); //设定字体<br>　　　　SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)); //检取当前字体的度量数据<br>　　　　GetTextMetrics (hdc, &amp;tm);<br>　　　　xChar = tm.tmAveCharWidth;//保存字体平均宽度<br>　　　　yChar = tm.tmHeight; //保存字体高度<br>　　　　ReleaseDC (hwnd, hdc);<br>　　　　rc.top = 3 * yChar / 2;<br>　　　　return 0;</p>
<p>　　　　case WM_SIZE://处理窗口大小改变的消息<br>　　　　//窗体改变后保存新的滚屏区域右下角坐标<br>　　　　rc.right = LOWORD (lParam);<br>　　　　rc.bottom = HIWORD (lParam);<br>　　　　UpdateWindow (hwnd);<br>　　　　return 0;</p>
<p>　　　　case WM_PAINT: //处理窗口重绘消息<br>　　　　InvalidateRect (hwnd, NULL, TRUE);<br>　　　　hdc = BeginPaint (hwnd, &amp;ps);<br>　　　　SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;<br>　　　　SetBkMode (hdc, TRANSPARENT) ;<br>　　　　TextOut (hdc, xChar, yChar / 2, szTop, (sizeof szTop) - 1) ;<br>　　　　TextOut (hdc, xChar, yChar / 2, szUnd, (sizeof szUnd) - 1) ;<br>　　　　EndPaint (hwnd, &amp;ps);<br>　　　　return 0;</p>
<p>　　　　case WM_KEYDOWN:<br>　　　　//处理键盘上某一键按下的消息<br>　　　　ShowKey (hwnd, 0, "WM_KEYDOWN",wParam, lParam);<br>　　　　return 0;</p>
<p>　　　　case WM_KEYUP:<br>　　　　//处理键盘上某一按下键被释放的消息<br>　　　　ShowKey (hwnd, 0, "WM_KEYUP", wParam, lParam);<br>　　　　return 0;</p>
<p>　　　　case WM_CHAR:<br>　　　　//处理击键过程中产生的非系统键的可见字符消息<br>　　　　howKey (hwnd, 1, "WM_CHAR", wParam, lParam);<br>　　　　return 0;</p>
<p>　　　　case WM_DEADCHAR:<br>　　　　//处理击键过程中产生的非系统键"死字符"消息<br>　　　　ShowKey (hwnd, 1, "WM_DEADCHAR", wParam, lParam);<br>　　　　return 0;</p>
<p>　　　　case WM_SYSKEYDOWN:<br>　　　　//处理系统键按下的消息<br>　　　　ShowKey (hwnd, 0, "WM_SYSKEYDOWN",wParam, lParam);<br>　　　　break;</p>
<p>　　　　case WM_SYSKEYUP:<br>　　　　//处理系统键抬起的消息<br>　　　　ShowKey (hwnd, 0, "WM_SYSKEYUP", wParam, lParam);<br>　　　　break;</p>
<p>　　　　case WM_SYSCHAR://处理系统键可见字符消息<br>　　　　ShowKey (hwnd, 1, "WM_SYSCHAR", wParam, lParam);<br>　　　　break;</p>
<p>　　　　case WM_SYSDEADCHAR://处理系统键"死字符"消息<br>　　　　ShowKey (hwnd, 1, "WM_SYSDEADCHAR", wParam, lParam);<br>　　　　break;</p>
<p>　　　　case WM_DESTROY:<br>　　　　//处理结束应用程序的消息<br>　　　　PostQuitMessage (0);<br>　　　　return 0;<br>　　}<br>　　return DefWindowProc (hwnd, iMsg, wParam, lParam);<br>}<br>//函数：MyRegisterClass<br>//作用：注册窗口类<br>BOOL MyRegisterClass(HINSTANCE hInstance)<br>{<br>　　wnd.cbSize= sizeof (wnd);<br>　　wnd.style = CS_HREDRAW | CS_VREDRAW;<br>　　wnd.lpfnWndProc = WndProc;<br>　　wnd.cbClsExtra = 0;<br>　　wnd.cbWndExtra = 0;<br>　　wnd.hInstance = hInstance;<br>　　wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION);?<br>　　wnd.hCursor = LoadCursor (NULL, IDC_ARROW);<br>　　wnd.hbrBackground = (HBRUSH)<br>　　GetStockObject (WHITE_BRUSH);<br>　　wnd.lpszMenuName = NULL;<br>　　wnd.lpszClassName = szAppName;<br>　　wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION);<br>　　return RegisterClassEx (&amp;wnd);<br>}<br>//函数：InitInstance<br>//作用：创建主窗口<br>BOOL InitInstance(HINSTANCE hInstance,int iCmdShow)<br>{<br>　　HWND hwnd;<br>　　hwnd = CreateWindow (szAppName,<br>　　　　　　　　　　　　 "键盘消息监视程序",<br>　　　　　　　　　　　　 WS_OVERLAPPEDWINDOW,<br>　　　　　　　　　　　　 CW_USEDEFAULT,CW_USEDEFAULT,<br>　　　　　　　　　　　　 CW_USEDEFAULT,CW_USEDEFAULT,<br>　　　　　　　　　　　　 NULL,NULL,hInstance,NULL<br>　　　　　　　　　　　　 );<br>　　if(!hwnd)<br>　　{<br>　　　　return FALSE;<br>　　}</p>
<p>　　ShowWindow (hwnd, iCmdShow);<br>　　UpdateWindow (hwnd);<br>　　return TRUE;<br>}</p>
<p>　　本实例的作用是通过程序捕获键盘消息，然后将wParam参数所包含的数据进行分解，最后将各项信息通过窗口显示出来。实例的源文件包含了 Initlnstance、MyRegisterClass、ShowKey、WinMain和WndProc五个函数。程序的基本思路是以 WinMain函数作为程序入口，再调用 MyRegisterClass函数和 InitInstance函数注册窗口类并创建和保存窗日，然后创建和显示窗口，最后进入消息循环。<br>　　下面重点分析函数WndProc和 ShowKey。<br>1．WndProc函数<br>在本实例中WndProc函数处理的消息主要有WM_CREATE、WM_SIZE、WM_PAINT和键盘消息。<br>　　case WM_CREATE://处理窗口创建的消息<br>　　hdc = GetDC (hwnd);//设定字体<br>　　SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT));//检取当前字体的度量数据<br>　　GetTextMetrics (hdc, &amp;tm);<br>　　xChar = tm.tmAveCharWidth;//保存字体平均宽度<br>　　yChar = tm.tmHeight;//保存字体高度<br>　　ReleaseDC (hwnd, hdc);<br>　　rc.top = 3 * yChar / 2;<br>　　return 0;<br>　　这一程序段的主要作用是将字体对象选入当前窗体的设备描述表中，同时取得字体高度和平均宽度，再初始化编辑区的滚屏区域的右上角Y坐标。进入该程序段后，首先通过GetDC函数获得当前窗体的设备描述表，再通过GetStockObject函数获得系统字体，然后用 SelectObject函数将字体对家选入窗体的设备描述表中。其中，hdc为设备描述表句柄。在完成所有操作后，程序还必须通过ReleaseDC函数释放设备描述表。在该程序段中使用了GetTextMetrics函数来获得字体的几何尺寸。GetTextMetrics函效的原型定义如下：<br>BOOL GetTextMetrics(HDC hdc，// 指向设备描述表的句柄<br>　　　　　　　　　　LPTEXTMETRIC lptm // TEXTMETRIC结构体变量的指针<br>　　　　　　　　　　// 所获得的所有信息保存在TEXTMETRIC结构体变量中<br>　　　　　　　　　　)；<br>　　其中lptm是一个指向 TEXTMETRIC结构体的指针。TEXTMETRIC结构体包含了与字体的几何尺寸相关的基本信息。该结构体的具体定义如下：<br>typedef struct tagTEXTMETRIC<br>{ // tm<br>　　LONG tmHeight；// 字体高度<br>　　LONG tmAscent；//字体高于基准线的高度<br>　　LONG tmDescent；// 字体低于基准线的高度<br>　　LONG tmInternalLeading；// 给大写字母留出的空间<br>　　LONG tmExtenalLeading； // 由字体设计者推荐的附加行距<br>　　LONG tmAveCharWidth；// 字体平均宽度<br>　　LONG tmMaxCharWidth；// 字体最大宽度<br>　　LONG tmWeight； // 字体黑度<br>　　LONG tmOverhang； // 在合成斜体或黑体时加在字符上的附加宽度值<br>　　LONG tmDigitizedAspectX；// 字体所适合的高宽比的宽<br>　　LONG tmDigitizedAspectY; // 字体所适合的高宽比的高<br>　　BCHAR tmFirstChar； // 字体中定义的第一个字符<br>　　BCHAR tmLastChar； //字体中定义的最后一个字符<br>　　BCHAR trnDefaultChar； //字体中的默认字符<br>　　BCHAR trnBreakChar； // windows在调整文本时用于分裂词的字符<br>　　BYTE tmItalic； // 取非零值时表示斜体字体<br>　　BYTE tmUnderLined； // 取非零值时表示下划线字体<br>　　BYTE tmStruckOut；// 取非零值时为删除线字体<br>　　BYTE tmPitchAndFamily； // 低二位为字符间距，高四位为系列值<br>　　BYTE tmCharSet； // 指定字符集<br>} TEXTMETRIC；<br>　　该结构中所有的字体大小都是按逻辑单位给出的，这就是说字体的大小取决于当前显示设备的映射模式。<br>　　在例中，所获得的字体几何尺寸保存在TEXTMETRIC结构体变量tm中。滚屏区域的范围是通过RECT结构体变量re保存的，RECT结构体变量可以通过记录矩形区域的右上角和左下角的坐标来确定一个矩形区域。<br>RECT结构的原型定义如下：<br>typedef struc RECT｛<br>　　LONG left； // 矩形左上角 X坐标<br>　　LONG top； // 左上角 Y坐标<br>　　LONG right； // 右下角 X坐标<br>　　LONG bottom； // 右下角Y坐标<br>} RECT;<br>　　该结构定义了一个矩形区域的左上角和右下角的坐标。由结构的原型定义我们可以知道该结构包括四个域，其中left域表示矩形的左上角X坐标，top域表示左上角Y坐标，right域表示右下角X坐标，bottom域表示右下角Y坐标。通常用于一个矩形区域范围的记录和传递。<br>　　例如，通过RECT结构的变量将一个矩形区域范围的四个角的值传递FillRect函数，则调用该函数后，矩形区域除了最下方的一行和最右方一列外都被填充。在本实例中，初始化编辑区的滚屏区域的左上角Y坐标时，使用了如下程序：<br>　　rc.top＝ 3 * yChar／2；<br>　　这是因为在窗口中首先要输出两行的题头信息，一行为中文，一行为下划线。中文字符的高度为1个字体高度单位，而下划线的高度为半个字体高度单位。这两行信息是一直保持，不参与滚屏的。因此，滚屏区域的左上角Y坐标从3／2个字体高度处开始。<br>在WndProc函数中，处理WM_ SIZE<br>消息的程序段如下：<br>　　case WM_SIZE: //处理窗口大小改变的消息<br>　　//窗体改变后保存新的滚屏区域右下角坐标<br>　　rc.right = LOWORD (lParam);<br>　　rc.bottom = HIWORD (lParam);<br>　　UpdateWindow (hwnd);<br>　　return 0;<br>　　该程序段比较简单，只是当窗口的尺寸改变时重新设定滚屏区域的右下角坐标，并更新窗口。值得注意的是， WM_SIZE消息的wParam变量保存了窗体新尺寸的左上角坐标，变量的32位分为两个部分，低16位保存X坐标，高16位保存Y坐标。 lParam变量保存了窗体新尺寸的右下角坐标，保存方式与wParam变量相同。在编程过程中，通常通过LOWORD宏定义来获得32位变量的低16位数值，通过HIWORD宏定义来获得32位变量的高历位数值。<br>　　该程序段比较简单，只是当窗口的尺寸改变时重新设定滚屏区域的右下角坐标，并更新窗口。值得注意的是，WM_SIZE消息的wParam变量保存了窗体新尺寸的左上角坐标，变量的32位分为两个部分，低16位保存X坐标，高16位保存Y坐标。 lParam变量保存了窗体新尺寸的右下角坐标，保存方式与wParam变量相同。在编程过程中，通常通过LOWORD宏定义来获得32位变量的低16位数值，通过HIWORD宏定义来获得32位变量的高历位数值。<br>WndProc函数中，处理WM＿PAINT消息的程序段如下：<br>　　case WM_PAINT: //处理窗口重绘消息 ?<br>　　InvalidateRect (hwnd, NULL, TRUE); ?<br>　　hdc = BeginPaint (hwnd, &amp;ps); ?<br>　　SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; ?<br>　　SetBkMode (hdc, TRANSPARENT) ; ?<br>　　TextOut (hdc, xChar, yChar / 2, szTop, (sizeof szTop) - 1) ; ?<br>　　TextOut (hdc, xChar, yChar / 2, szUnd, (sizeof szUnd) - 1) ; ?<br>　　EndPaint (hwnd, &amp;ps); ?<br>　　return 0;<br>　　该程序段首先调用InvalidateRect函数使窗口无效，InvalidateRect函数的功能是使窗口的某一部分无效，也就是通知Windows该部分需要被刷新和重画。<br>　　在InvalidateRect函数之后，程序调用函数BeginPaint准备重画窗口。<br>BeginPaint 函数的原型定义如下：<br>HDC BeginPaint (HWND hwnd ， // 重画窗口的句柄<br>　　　　　　　　LPPAINTSTRUCT lpPaint // 指向一个用于保存所有重<br>　　　　　　　　// 画信息的 PAINTSTRUCT 结构体变量的指针)；<br>　　BeginPaint 函数的作用是完成重画窗体之前的准备，并将重画窗体 的数据保存在一个 PAINTSTRUCT 结构体变量中。 PAINTSTRUCT 结构体可以用于保存窗口重画时的数据以方便以后使用。<br>PAINTSTRUCT结构体的定义如下：<br>typedef struct tagPAINTSTRUCT｛ // ps<br>　　HDC hdc； // 重画区域所在窗口的句柄<br>　　BOOL fErase；// 是否擦去背景<br>　　RECT rcPaint； // 指定重画窗体的范围<br>　　BOOL fRestore； // 系统保留域<br>　　BOOL fIncUpdate；// 系统保留域<br>　　BYTE rgbReserved［32］；// 系统保留<br>｝PA INTSTRU CT;<br>　　BeginPaint函数如果操作成功会返回一个被操作窗口的设备描述表的句柄。如果操作不成功则函数返回NULL值，表明显示设备不可用。该函数在运行过程中，会进行自动调整，使得所有区域都包含在刷新区域的范围内。而原有需要刷新的区域是由InvalidateRect函数或 InvalidateRgn函数指定的。一般来说，只有当程序处理 WM_PAINT消息时才调用BeginPaint函数，而且，每次调用BeginPaint函数都需要对应调用一个EndPaint函数来结束重画过程。在BeginPaint函数调用后，会将插入符光标自动隐藏。EndPaint函数原型定义如下：<br>BOOL EndPaint ( HWND hWnd, // 窗口句柄<br>　　　　　　　　CONST PAINTSTRUCT* lpPaint // 指向 PAINTSTRUCT结构体变量的指针<br>　　　　　　　)；<br>　　EndPaint函数标志着窗口重画过程的结束。该函数执行后总返回一个非零值。如果在BeginPaint函数执行时将插入符号隐藏了，那么EndPaint函数会重新显示插入符号。<br>消息处理函数 WndProc处理的键盘消息有：<br>WM_ KEYDOWN、WM_KEYUP<br>WM_CHAR、WM_DEADCHAR、<br>WM_SYSKEYDOWN、WM_SYSKEYUP、<br>WM_SYSCHAR 和 WM_SYSDEADCHAR。<br>　　根据不同的消息，程序会用不同的参数调用 ShowKey函数在窗口中显示各键盘消息的相关信息。<br>2．ShowKey函数<br>ShowKey函数是用户自定义函数，其作用是从键盘消息的各域中提取信息并显示在窗口中。<br>ShowKey函数的具体定义如下：<br>// 作用：实现在窗口中显示按键信息<br>void ShowKey (HWND hwnd, int iType, char *szMessage,WPARAM wParam, LPARAM lParam)<br>{<br>　　static char *szFormat[2] = {"%-14s %3d %c %6u %4d %5s %5s %6s %6s",<br>　　　　　　　　　　　　　　　　"%-14s %3d %c %6u %4d %5s %5s %6s %6s" } ;<br>　　char szBuffer[80];<br>　　HDC hdc;<br>　　SelectObject( hdc,<br>　　　　　　　　　GetStockObject(SYSTEM_FIXED_FONT));<br>　　　　　　　　　TextOut (hdc, xChar, rc.bottom - yChar,<br>　　　　　　　　　szBuffer,wsprintf (szBuffer, szFormat [iType],<br>　　　　　　　　　szMessage, //消息<br>　　　　　　　　　wParam, //虚拟键代码<br>　　　　　　　　　(BYTE) (iType ? wParam : ' '),//显示字符值<br>　　　　　　　　　LOWORD (lParam), //重复次数<br>　　　　　　　　　HIWORD (lParam) &amp; 0xFF, //OEM键盘扫描码<br>　　　　　　　　　//判断是否为增强键盘的扩展键<br>　　　　　　　　　(PSTR) (0x01000000 &amp; lParam ? "是" : "否"),<br>　　　　　　　　　//判断是否同时使用了ALT键<br>　　　　　　　　　(PSTR) (0x20000000 &amp; lParam ? "是" : "否"),<br>　　　　　　　　　(PSTR) (0x40000000 &amp; lParam ? "按下" : "抬起"),<br>　　　　　　　　　//判断前一次击键状态<br>　　　　　　　　　(PSTR) (0x80000000 &amp; lParam ? "按下" : "抬起"))<br>　　　　　　　　　//判断转换状态<br>　　　　　　　　　);<br>}</p>
<p>　　ShowKey函数首先定义了szFormat字符串，并在其中针对字符消息和非字符消息定义了两种不同的输出格式。然后调用ScrollWindowEx函数使显示区域滚屏，为信息输出作准备。ScrollWindowEx函数的主要功能是使窗口编辑区中的某一矩形区域产生滚屏效果。<br>ScrollWindowEx函数的原型定义如下：<br>int ScrollWindowEx (HWND hwnd， // 发生滚屏的窗口的句柄<br>　　　　　　　　　　int dx， // 水平滚屏的数值<br>　　　　　　　　　　int dy, // 垂直滚屏的数值<br>　　　　　　　　　　CONST RECT*prcScroll，//记录发生滚屏的矩形区域的RECT结构体的地址<br>　　　　　　　　　　CONST RECT* prcClip， //记录发生剪切的矩形区域的 RECT结构体的地址<br>　　　　　　　　　　HRGN hrgnUpdate，// 需要更新区域的句柄<br>　　　　　　　　　　LPRECT prcUpdate， // 记录需要更新矩形区域的 RECT结构体的地址<br>　　　　　　　　　　UINT flags // 滚屏控制标志<br>　　　　　　　　　　)；<br>　　其中，dx参数给出了以设备单位尺寸（对于显示器为像素）为单位的每一次水平滚屏的度量值。dx参数取正值表示向右滚屏，取负值表示向左滚屏。如参数给出了以设备单位尺寸（对于显示器为像素）为单位的每一次垂直滚屏的度量值。如参数取正值表示向下滚屏，取负值表示向上滚屏。dx和dy两个参数不能同时取非零值，也就是说，ScrollWindowEx函数不能使编辑区同时向水平和垂直方向滚屏。<br>　　prcScroll参数为一个指向记录滚屏的矩形区域的RECT结构体变量的指针，如果取值为NULL，则整个编辑区发生滚屏。<br>　　hrgnUpdate参数为因滚屏而变得无效的矩形区域的句柄，多数情况下可以取NULL。 prcUpdate参数指向一个记录因为滚屏而变得无效的矩形区域的 RECT结构体变量。多数情况下取NULL。<br>flags变量可以通过不同的取值来控制滚屏的状况，其取值和意义如下所示。<br>　　SW_ ERASE当和 SW_INVALIDATE值同时使用时，会通过向 window发送一个WM_ ERASEBKGND消息将最近变得无效的区域抹去；<br>　　SW_INVALIDATE在发生滚屏后使由hrgnUpdate参数指定的区域无效；<br>　　SW_SCROLLCHILDREN使所有的子窗口都发生滚屏；<br>　　SW_ SMOOTHSCROLL在 Windows 95及以后的版本中使窗口发生平滑滚屏。如果ScrollWindowEx函数执行成功，则返回值为以下三者之一：<br>　　SIMPLEREGION表示有一个矩形的无效区域；<br>　　COMPLEXREGION表示没有无效区域和重叠区域；<br>　　NULLREGION表示没有无效区域。<br>　　如果ScrollWindowEx函数执行不成功，则返回ERROR。<br>ScrollWindowEx函数的功能也可以通过ScrollWindow函数来实现，ScrollWindow 函数的原型定义如下：<br>BOOL Scrollwindow(HWND hwnd //窗口句柄<br>　　　　　　　　　int XAmount， // 水平滚屏的数值<br>　　　　　　　　　int YAmount， // 垂直滚屏的数值<br>　　　　　　　　　CONST RECT* lpReCt， //记录发生滚屏的矩形区域的 RECT结构体的地址<br>　　　　　　　　　CONST RECT* lpClipRect， //记录发生剪切的矩形区域的 RECT结构体的地址<br>　　　　　　　　　)；<br>　　可以看出，ScrollWindow函数与ScrollWindowEx函数十分相似，其参数的意义也基本相同。事实上，ScrollWindow函数是为了保持对较低版本的Windows兼容而设计的，用户在编程时，除非需要考虑程序的向下兼容，否则一般都应使用ScrollWindowEx函数。<br>　　在滚屏后，函数开始调用TextOut函数进行信息输出。TextOut函数的原型定义如下：<br>BOOL TextOut( HDC hdc，// 设备描述表句柄<br>　　　　　　　int nXStart， // 文本输出起始点 X坐标<br>　　　　　　　int nYStart， // 文本输出起始点 Y坐标<br>　　　　　　　LPCTSTR lpString， // 指向输出字符串的指针<br>　　　　　　　int cbString // 字符串中字符的数目<br>　　　　　　　);<br>　　TextOut函数能够用当前设定的字体在窗口的指定部位输出一段文本信息。如果操作成功则返回一非零值，否则返回零值。捕获键盘消息的信息主要根据表中的描述，通过使用按位操作确定某些特定位的值，然后再判断具体的状态。<br>　　在TextOut函数调用过程中，还调用了wsprintf函数，并使其返回值作为TextOut函数的一个参数值。wsprintf函数的原型定义如下：<br>int wsprintf (LPTSTR lpOut，// 指向需要输出的字符串的指针<br>　　　　　　　LPCTSTR lpFmt， //指向格式控制字符串的指针<br>　　　　　　　&#8230;&#8230; // 其他可选参数<br>　　　　　　　)；<br>　　wsprintf函数能够将一组字符序列按lpFmt参数指定的格式转换，然后保存在lpOut参数指定的字符缓冲区中等待输出。其中，字符序列由可选参数决定，而可选参数的数目和具体内容应该与lpFmt所指定的格式一致。<br>　　如果wsprintf函数操作成功，则返回输出字符的数目，但这个字符数目不包括表示结束的NULL标志。如果操作失败，返回的整数值将与输出的字符数目不相符。<br>　　实例主要说明了如何处理键盘消息，读者应该着重理解各种信息在MSG结构体变量中是如何保存的，怎样才能够对其中的具体信息进行识别和提取。程序运行后将产生一个背景色为灰色的简单窗口，并在窗口的顶部出现标题提示信息。这时用户如果进行键盘操作，则窗体中便会显示该操作所产生的键盘消息，每显示一条消息程序都会滚屏和重绘窗口，滚屏区域的颜色为白色。</p>
<p>键盘消息实例2:<br>#include &lt;windows.h&gt;<br>int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int);<br>LRESULT CALLBACK WndProc( HWND,UINT, WPARAM,LPARAM);<br>int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)<br>{<br>　　WNDCLASSEX wcex;<br>　　wcex.cbSize = sizeof(WNDCLASSEX);<br>　　wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;<br>　　wcex.lpfnWndProc = (WNDPROC)WndProc;<br>　　wcex.cbClsExtra = 0;<br>　　wcex.cbWndExtra = 0;<br>　　wcex.hInstance = hInstance;<br>　　wcex.hIcon= LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION);<br>　　wcex.hCursor = LoadCursor(NULL, IDC_ARROW);<br>　　wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);<br>　　wcex.lpszMenuName = NULL;<br>　　wcex.lpszClassName = "SeeKeyMessage";<br>　　wcex.hIconSm = LoadIcon(NULL,(LPCTSTR)IDI_APPLICATION);</p>
<p>　　if(!RegisterClassEx(&amp;wcex)) return FALSE;</p>
<p>　　int SW_XFS = GetSystemMetrics(SM_CXSCREEN);<br>　　int SW_YFS = GetSystemMetrics(SM_CYSCREEN);<br>　　HWND hWnd;<br>　　hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,<br>　　　　　　　　　　　　　"SeeKeyMessage",<br>　　　　　　　　　　　　　"Trace Key Operation",<br>　　　　　　　　　　　　　WS_OVERLAPPEDWINDOW,<br>　　　　　　　　　　　　　0, 0, SW_XFS, SW_YFS-25,<br>　　　　　　　　　　　　　NULL,<br>　　　　　　　　　　　　　NULL,<br>　　　　　　　　　　　　　hInstance,<br>　　　　　　　　　　　　　NULL);<br>　　if(!hWnd) return FALSE;</p>
<p>　　ShowWindow(hWnd, nCmdShow);<br>　　UpdateWindow(hWnd);<br>　　MSG msg;</p>
<p>　　while(GetMessage(&amp;msg, NULL, 0, 0))<br>　　{<br>　　　　TranslateMessage(&amp;msg);<br>　　　　DispatchMessage(&amp;msg);<br>　　}<br>　　return msg.wParam;<br>}<br>LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)<br>{<br>　　HDC hDC;<br>　　PAINTSTRUCT ps;<br>　　static char Buffer[256];<br>　　switch(message)<br>　　{<br>　　　　case WM_KEYDOWN:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,40,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_KEYDOWN %3d %3d%3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,40,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_KEYUP:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,60,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_KEYUP %3d %3d %3d",wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,60,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_PAINT:<br>　　　　hDC = BeginPaint(hWnd,&amp;ps);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,20,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," Message wParam LOWORD(lParam) HIWORD(lParam)");<br>　　　　TextOut(hDC,20,20,Buffer,strlen(Buffer));<br>　　　　EndPaint(hWnd,&amp;ps);<br>　　　　break;</p>
<p>　　　　case WM_DESTROY:<br>　　　　PostQuitMessage(0);<br>　　　　break;</p>
<p>　　　　default:<br>　　　　return DefWindowProc(hWnd,message,wParam,lParam);<br>　　}<br>　　return 0;<br>}</p>
<p>鼠标消息</p>
<p>　　随着 Windows 操作系统的流行，鼠标因为其精确定位和操作方便的优点而成为计算机不可缺少的输入设备。<br>一、鼠标的基础知识<br>　　本节将介绍在程序中用鼠标作为输入设备的方法和技巧。 1 ．鼠标操作和鼠标消息用户在使用鼠标操作的过程中，经常会使用的主要方式有五种 ，如表所示。</p>
<p>操作名称 描述<br>单击（Click） 按下并迅速释放鼠标按钮。<br>双击（Double Click） 连续快速完成两次单击操作。<br>移动（Move） 鼠标光标移动。<br>拖动（Drag） 按下鼠标一键不放，同时执行鼠标移动操作。<br>与键盘的特殊键组合 在按下Ctrl键或Shift键的同时执行鼠标单击操作。</p>
<p>　　其中，前三种操作是最为基本的操作，可以产生Windows内部定义的消息，并通过这些消息来判断用户具体执行了哪种操作。<br>　　Windows定义的鼠标消息共有20条，其中非编辑区的鼠标消息一般交由系统处理，程序只处理编辑区内的鼠标消息。编辑区内的鼠标消息共有10条，如表所示。</p>
<p>消息常量 操作描述<br>WM_MOUSEMOVE 移动鼠标<br>WM_LVBUTTONDOWN 按下鼠标左键<br>WM_LBUTTONUP 释放鼠标左键<br>WM_LBUTTONDBLCLK 双击鼠标左键<br>WM_RVBUTTONDBLCLK 按下鼠标右键<br>WM_RBUTTONUP 释放鼠标右键<br>WM_RBUTTONDBLCLK 双击鼠标右键<br>WM_MVBUTTONDOWM 按下鼠标中键<br>WM_MBUTTONUP 释放鼠标中键<br>WM_MBUTTONDBLCLK 双击鼠标中键</p>
<p>　　对于前表所列的鼠标操作中的最后两种，不能直接使用Windows定义的消息来判断，只能通过编程，将多种消息和数据组合之后判断。例如，判断用户是否按下鼠标左键之后进行拖动操作可以通过以下程序段来实现，用case语句来实现：<br>case WM_MOUSEMOVE：</p>
<p>if (wParam&amp;MK_LBUTTON) //只处理鼠标拖动的消息<br>{ &#8230;&#8230; // 处理程序<br>}</p>
<p>　　在处理鼠标消息的过程中，消息的wParam参数和lParam参数起了重要的作用。wParam参数中保存了在消息产生时其他操作进行的状态；用户可以通过位屏蔽操作来判断在该消息产生的同时，其余操作是否正在进行。这正是在程序中判断复杂鼠标操作的基本方法。例如，上面判断拖动操作的程序段就用了位操作 wParam＆ MK_LBUTTON, 判断在鼠标移动（WM_MOUSEMOVE）的同时鼠标左键是否同时被接下。如果，鼠标左键同时按下，则位操作的结果为TRUE，说明当前操作为拖动操作，程序可以继续进行下一步处理。又如需要判断单击鼠标左键时是否同时按下了Ctrl键或Shift键，可以用以下程序段来处理：<br>case WM_ LBUTTONDOWN：<br>if（wParam＆ MK_CTROL）<br>{//Ctrl键同时按下<br>　　if (wParam＆MK_ SHIFT)<br>　　{// Ctrl 键和Shift键都同时按下<br>　　　　&#8230;&#8230; // 处理程序<br>　　}<br>　　else { // Ctrl健同时按下，但 Shift键没有被按下<br>　　　　&#8230;&#8230;. // 处理程序<br>　　}<br>}<br>else if（wParam＆MK_ SHIFT）<br>{ // Shift键同时按下，但 Ctrl键没有被接下<br>　　&#8230;&#8230; // 处理程序<br>}<br>else<br>{// Shift 键和Ctrl键都未按下<br>　　&#8230;&#8230; // 处理程序<br>}<br>　　lParam参数保存了消息产生时鼠标所在点的坐标，其中低16位为X坐标，高16位为Y坐标。<br>　　在处理鼠标消息的时候，如果需要处理鼠标双击消息，则在注册窗口类时，窗口的风格必须包括CS_DBCLCKS。否则即使执行了双击操作，窗口也只能收到两条WM_ BUTTONUP和 WM_BUTTONDOWN消息。区分双击操作和两次单击操作是以两次击键的时间间隔为标准的。当两次击键的时间间隔小于 500毫秒时， Windows将其视为双击操作：如果两次击键的时间间隔大于500毫秒，Windows将其视为两次单击操作。500毫秒为默认的时间间隔，用户可以通过调用SetDoubleClickTime函数来修改这一时间间隔。SetDoubleClickTime函数的原型定义如下：<br>BOOL SetDoubleClickTime（UINT uInterval // 新的击键时间间隔）<br>2．鼠标捕捉<br>　　在通常情况下，只有当鼠标位于窗体内时，窗体才能接收到鼠标的消息。如果需要接收所有的鼠标消息而不论鼠标是否在窗口内，这时可以调用SetCapture函数来实现。SetCapture函数的原型定义如下：<br>HWND SetCapture (<br>　　HWND hwnd // 窗口句柄<br>）；<br>　　调用SetCapture函数后，所有鼠标操作所产生的消息都直接发送到指定窗口。因为此时鼠标可能位于窗口之外，所以鼠标的坐标可能为负值。由于调用该函数会使其他窗口不能接收到键盘和鼠标的消息，因此在完成操作后应及时调用ReleaseCapture 函数释放鼠标捕获。ReleaseCapture函数的原型定义如下：<br>BOOL ReleaseCapture（VOID）；<br>二、鼠标应用实例<br>下面是一个在程序设计中如何捕获鼠标消息的实例。<br>#include &lt;windows.h&gt;<br>//全局变量<br>WNDCLASSEX wnd;<br>static char szAppName[] = "mouse";//窗口类名<br>//函数声明<br>long WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);<br>BOOL MyRegisterClass(HINSTANCE hInstance);<br>BOOL InitInstance(HINSTANCE hInstance, int iCmdShow);<br>//函数：WinMain<br>//作用：入口函数<br>int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow) ?<br>{<br>　　MSG msg;<br>　　if(!MyRegisterClass(hInstance))<br>　　{<br>　　　　return FALSE;<br>　　}</p>
<p>　　if(!InitInstance(hInstance,iCmdShow))<br>　　{<br>　　　　return FALSE;<br>　　}</p>
<p>　　while (GetMessage (&amp;msg, NULL, 0, 0))<br>　　{<br>　　　　TranslateMessage (&amp;msg);<br>　　　　DispatchMessage (&amp;msg);<br>　　}<br>　　return msg.wParam;<br>}<br>//函数：WndProc<br>//作用：处理主窗口的消息<br>long WINAPI WndProc (HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)<br>{<br>　　static POINT points[256];//保存点坐标<br>　　static POINT center;//保存中心点坐标<br>　　static int iCount;//点数目累加值<br>　　HDC hdc;<br>　　PAINTSTRUCT ps;<br>　　int i;//循环计数<br>　　RECT rect;<br>　　switch (msg)<br>　　{<br>　　　　case WM_MBUTTONDOWN:<br>　　　　//处理鼠标中键按下的消息<br>　　　　iCount = 0;//重新初始化点数目<br>　　　　InvalidateRect (hwnd, NULL, TRUE);<br>　　　　//通知系统重画窗口<br>　　　　hdc = BeginPaint (hwnd,&amp;ps);<br>　　　　GetClientRect(hwnd,&amp;rect);<br>　　　　if(wParam&amp;MK_CONTROL)//判断Shift键和Ctrl键是否被按下<br>　　　　{<br>　　　　　　if(wParam&amp;MK_SHIFT)<br>　　　　　　{ //根据不同的情况给出不同的提示<br>　　　　　　　　DrawText(hdc,"Ctrland Shift", -1,&amp;rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER?);<br>　　　　　　}<br>　　　　　　else<br>　　　　　　{<br>　　　　　　　　DrawText(hdc,"Ctrl Only" ,-1,&amp;rect, DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　　　　　}<br>　　　　}<br>　　　　else if(wParam&amp;MK_SHIFT)<br>　　　　{<br>　　　　　　DrawText(hdc,"Shift Only",-1,&amp;rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　　　}<br>　　　　else<br>　　　　{<br>　　　　　　DrawText(hdc,<br>　　　　　　　　　　 "Middle Button of mouse only",<br>　　　　　　　　　　 -1,<br>　　　　　　　　　　 &amp;rect,<br>　　　　　　　　　　 DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　　　}<br>　　　　EndPaint(hWnd,&amp;ps);<br>　　　　return 0;</p>
<p>　　　　case WM_RBUTTONDOWN:<br>　　　　//处理鼠标右键按下的消息<br>　　　　iCount = 0;//重新初始化点数目<br>　　　　center.x=LOWORD (lParam);<br>　　　　//保存新的中心点坐标<br>　　　　center.y=HIWORD (lParam);<br>　　　　InvalidateRect (hwnd, NULL, TRUE);//通知系统重画窗口<br>　　　　return 0;</p>
<p>　　　　case WM_MOUSEMOVE://处理鼠标移动的消息<br>　　　　if (wParam &amp; MK_LBUTTON &amp;&amp; iCount &lt; 256)//只处理鼠标拖动的消息<br>　　　　{<br>　　　　　　points[iCount].x = LOWORD (lParam);//保存点的X坐标<br>　　　　　　points[iCount++].y = HIWORD (lParam);//保存点的Y坐标<br>　　　　　　hdc = GetDC (hwnd);//获得窗口的设备描述表句柄<br>　　　　　　SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0L);//绘点<br>　　　　　　ReleaseDC (hwnd, hdc);//释放设备描述表句柄<br>　　　　}?return 0;</p>
<p>　　　　case WM_LBUTTONUP:<br>　　　　//处理鼠标左键抬起的消息<br>　　　　InvalidateRect (hwnd, NULL, FALSE);<br>　　　　//通知系统重画窗口<br>　　　　return 0;</p>
<p>　　　　case WM_PAINT://处理窗口重画的消息<br>　　　　hdc = BeginPaint (hwnd, &amp;ps);//获得设备描述表句柄<br>　　　　SetCursor (LoadCursor (NULL, IDC_WAIT));//设置新的鼠标光标<br>　　　　ShowCursor (TRUE);//显示鼠标光标<br>　　　　for (i = 0 ; i &lt; iCount ; i++)<br>　　　　{<br>　　　　　　MoveToEx(hdc, center.x, center.y,NULL);//绘制直线<br>　　　　　　LineTo(hdc, points.x, points.y);<br>　　　　}<br>　　　　ShowCursor(FALSE);//隐藏鼠标<br>　　　　SetCursor(LoadCursor (NULL, IDC_ARROW));<br>　　　　//恢复原来的鼠标光标 ?<br>　　　　EndPaint(hwnd, &amp;ps);<br>　　　　return 0;</p>
<p>　　　　case WM_DESTROY://处理销毁窗口的消息<br>　　　　PostQuitMessage (0);<br>　　　　return 0;<br>　　}<br>　　return DefWindowProc (hwnd, msg, wParam, lParam);<br>}<br>//函数：MyRegisterClass<br>//作用：注册窗口类<br>BOOL MyRegisterClass(HINSTANCE hInstance)<br>{<br>　　wnd.cbSize= sizeof (wnd);<br>　　wnd.style= CS_HREDRAW | CS_VREDRAW;<br>　　wnd.lpfnWndProc = WndProc;<br>　　wnd.cbClsExtra = 0;<br>　　wnd.cbWndExtra = 0;<br>　　wnd.hInstance = hInstance;<br>　　wnd.hIcon = LoadIcon (NULL, IDI_APPLICATION);<br>　　wnd.hCursor = LoadCursor (NULL, IDC_ARROW);<br>　　wnd.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);<br>　　wnd.lpszMenuName= NULL;<br>　　wnd.lpszClassName = szAppName;<br>　　wnd.hIconSm = LoadIcon (NULL, IDI_APPLICATION);<br>　　return RegisterClassEx (&amp;wnd);<br>}<br>//函数：InitInstance<br>//作用：创建窗口<br>BOOL InitInstance(HINSTANCE hInstance, int iCmdShow)<br>{<br>　　HWND hwnd;<br>　　hwnd = CreateWindow(szAppName,<br>　　　　　　　　　　　　"跟踪鼠标移动",<br>　　　　　　　　　　　　WS_OVERLAPPEDWINDOW,<br>　　　　　　　　　　　　CW_USEDEFAULT, CW_USEDEFAULT,<br>　　　　　　　　　　　　CW_USEDEFAULT, CW_USEDEFAULT,<br>　　　　　　　　　　　　NULL, NULL, hInstance, NULL);<br>　　if(!hwnd) return FALSE;<br>　　ShowWindow (hwnd, iCmdShow);<br>　　UpdateWindow (hwnd);<br>　　return TRUE;<br>}<br>　　例题的主要功能是在窗口中某个位置单击鼠标右键时，程序保存捕获的鼠标点处的坐标。紧接着按下鼠标左键在窗口中拖动，程序会记录下鼠标运动的轨迹，并以刚才右击鼠标时确定的点为中心绘制一簇射线。本实例最多可以绘制256条射线。程序的另一目的是为了让读者进一步了解如何捕获鼠标与Ctrl键或 Shift键组合时的复杂鼠标消息。如果在窗口中单击鼠标中键，程序会在窗口中央显示文本信息说明用户是否同时按下Ctrl键和Shift键。<br>　　源文件与本书前面所介绍的其他实例一样，都具有基本的 Windows API 程序的结构。即以WinMain函数作为程序入口，调用MyRegisterClass函数和InitInstance函数注册窗口类和创建窗口，再进入消息循环。并在消息循环中调用WndProc函数处理鼠标消息。下面主要介绍WndProc函数处理鼠标消息的方法和技巧。<br>　　在例中WndProc函数能够处理的消息包括 WM_MBUTTONDOWN、WM_RBUTTONDOWN、WM_MOUSEMOVE、WM_LBUTTONUP和WM_PAINT。<br>处理WM_ MBUTTONDOWN消息的程序段如下：<br>case WM_MBUTTONDOWN:<br>//处理鼠标中键按下的消息<br>iCount = 0;//重新初始化点数目<br>InvalidateRect (hwnd, NULL, TRUE);//通知系统重画窗口<br>hdc = BeginPaint (hwnd,&amp;ps);<br>GetClientRect(hwnd,&amp;rect);<br>if(wParam&amp;MK_CONTROL)//判断Shift键和Ctrl键是否被按下<br>{<br>　　if(wParam&amp;MK_SHIFT)<br>　　{ //根据不同的情况给出不同的提示<br>　　　　DrawText(hdc,"Ctrland Shift", -1,&amp;rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　}<br>　　else<br>　　{<br>　　　　DrawText(hdc,"Ctrl Only" ,-1,&amp;rect,<br>　　　　DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　}<br>}<br>else<br>{<br>　　if(wParam&amp;MK_SHIFT)<br>　　{<br>　　　　DrawText(hdc,"Shift Only",-1,&amp;rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　}<br>　　else<br>　　{<br>　　　　DrawText(hdc,"Middle Button of mouse only",-1,&amp;rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);<br>　　}<br>}<br>EndPaint(hWnd,&amp;ps);<br>return 0;<br>　　这种判断复杂鼠标操作的方法在基础知识部分已经详细介绍过，这里不再赘述。这一段程序的作用是根据在按下鼠标中键的同时，再按Ctrl键或Shift键的不同情况而在窗口中输出不同的信息。<br>处理WM_ RBUTTONDOWN消息的程序段如下：<br>case WM_RBUTTONDOWN://处理鼠标右键按下的消息<br>iCount = 0;//重新初始化点数目<br>center.x=LOWORD (lParam);//保存新的中心点坐标<br>center.y=HIWORD (lParam);<br>InvalidateRect (hwnd, NULL, TRUE);//通知系统重画窗口<br>return 0;<br>　　这一段程序的主要作用是将鼠标右击点的坐标保存在POINT结构体变量center中，再将计数变量iCount归零，为绘制一簇射线作准备。完成以上工作后，程序调用InvalidateRect函数通知系统重画窗口，将窗口中原有的射线族擦去。处理WM_MOUSEMOVE消息的程序段如下：<br>case WM_MOUSEMOVE://处理鼠标移动的消息<br>if (wParam &amp; MK_LBUTTON &amp;&amp; iCount&lt;256)//只处理鼠标拖动的消息<br>{<br>　　points[iCount].x = LOWORD (lParam);//保存点的X坐标<br>　　points[iCount++].y = HIWORD (lParam);//保存点的Y坐标<br>　　hdc = GetDC (hwnd);//获得窗口的设备描述表句柄<br>　　SetPixel (hdc, LOWORD (lParam), HIWORD (lParam), 0L);<br>　　//绘点<br>　　ReleaseDC (hwnd, hdc);//释放设备描述表句柄<br>}<br>　　事实上，该程序只是处理鼠标左键拖动操作的消息。在执行拖动操作的过程中，程序不断将鼠标点的坐标记录在points数组中。points数组是 WndProc函数中定义的一个静态的POINT结构体数组。在记录点坐标的同时，程序调用了SetPixel函数在窗口上的绘制点，对被记录的点进行标志。对于WM_ LBUTTONUP消息，程序只调用了IvalidateRect函数通知系统拖动操作已经结束，可以开始绘制射线族了。<br>处理WM _PAINT消息的程序段如下：<br>case WM_PAINT://处理窗口重画的消息<br>hdc = BeginPaint (hwnd, &amp;ps);//获得设备描述表句柄<br>SetCursor (LoadCursor (NULL, IDC_WAIT));//设置新的鼠标光标<br>ShowCursor (TRUE);//显示鼠标光标<br>for (i = 0 ; i &lt; iCount ; i++)<br>{<br>　　MoveToEx(hdc, center.x, center.y,NULL);//绘制直线<br>　　LineTo(hdc, points.x, points.y);<br>}<br>ShowCursor(FALSE);//隐藏鼠标<br>SetCursor(LoadCursor (NULL, IDC_ARROW));<br>//恢复原来的鼠标光标<br>EndPaint(hwnd, &amp;ps);<br>return 0;<br>　　以上程序的功能是实现射线族的绘制。程序使用了一个for循环，循环次数为iCoun变量记录的点数。在循环体中反复调用 MoveToEx函数和 LineTo函数绘制直线。MoveToEx函数使直线的起点回到有center变量记录的中点，LineTo函数实现由中点向points数组中的各点绘制直线。<br>　　在处理WM_PAINT消息的程序中还使用了SetCursor函数和ShowCursor函数。SetCursor函数能设定一个鼠标图标，ShowCursor函数能将设定好的图标显示出来。在本实例中，调用这两个函数的目的是当程序绘制射线族的时候将鼠标图标转换成沙漏图案，表示程序正在执行某次操作。当给制完成后，又重新设置鼠标图像为箭头图标。<br>　　程序运行后，首先生成一个窗口，等待用户执行鼠标操作。用户右击后，再按下鼠标左键并拖动，则程序会绘制出一簇美丽的射线。运行结果如图所示。</p>
<p>&nbsp;</p>
<p>鼠标消息实例2<br>#include &lt;windows.h&gt;<br>int WINAPI WinMain(HINSTANCE, HINSTANCE,LPSTR,int);<br>LRESULT CALLBACK WndProc(HWND,UINT, WPARAM,LPARAM);<br>int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)<br>{<br>　　WNDCLASSEX wcex;<br>　　wcex.cbSize = sizeof(WNDCLASSEX);<br>　　wcex.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;<br>　　wcex.lpfnWndProc = (WNDPROC)WndProc;<br>　　wcex.cbClsExtra = 0;<br>　　wcex.cbWndExtra = 0;<br>　　wcex.hInstance = hInstance;<br>　　wcex.hIcon = LoadIcon (NULL,(LPCTSTR)IDI_APPLICATION);<br>　　wcex.hCursor = LoadCursor (NULL,IDC_ARROW);<br>　　wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);<br>　　wcex.lpszMenuName = NULL;<br>　　wcex.lpszClassName = "SeeMouseMessage";<br>　　wcex.hIconSm = LoadIcon(NULL,(LPCTSTR)IDI_APPLICATION);<br>　　if(!RegisterClassEx(&amp;wcex)) return FALSE;<br>　　int SW_XFS = GetSystemMetrics(SM_CXSCREEN);<br>　　int SW_YFS = GetSystemMetrics(SM_CYSCREEN);<br>　　HWND hWnd;<br>　　hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,<br>　　　　　　　　　　　　　"SeeMouseMessage",<br>　　　　　　　　　　　　　"Trace Mouse Operation",<br>　　　　　　　　　　　　　WS_OVERLAPPEDWINDOW,<br>　　　　　　　　　　　　　0,<br>　　　　　　　　　　　　　0,<br>　　　　　　　　　　　　　SW_XFS,<br>　　　　　　　　　　　　　SW_YFS-25,<br>　　　　　　　　　　　　　NULL,<br>　　　　　　　　　　　　　NULL,<br>　　　　　　　　　　　　　hInstance,<br>　　　　　　　　　　　　　NULL);<br>　　if(!hWnd) return FALSE;<br>　　ShowWindow(hWnd, nCmdShow);<br>　　UpdateWindow(hWnd);<br>　　MSG msg;</p>
<p>　　while(GetMessage(&amp;msg, NULL, 0, 0))<br>　　{<br>　　　　TranslateMessage(&amp;msg);<br>　　　　DispatchMessage(&amp;msg);<br>　　}<br>　　return msg.wParam;<br>}</p>
<p>LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)<br>{<br>　　HDC hDC;<br>　　PAINTSTRUCT ps;<br>　　static char Buffer[256];<br>　　switch(message)<br>　　{<br>　　　　case WM_MOUSEMOVE:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,40,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_MOUSEMOVE %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,40,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_LBUTTONDOWN:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,60,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_LBUTTONDOWN %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,60,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_LBUTTONUP:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,80,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_LBUTTONUP %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,80,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_LBUTTONDBLCLK:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,100,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_LBUTTONDBLCLK %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,100,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_RBUTTONDOWN:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,120,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_RBUTTONDOWN %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,120,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_RBUTTONUP:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,140,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," WM_RBUTTONUP %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,140,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_RBUTTONDBLCLK:<br>　　　　hDC = GetDC(hWnd);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,160,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer,"WM_RBUTTONDBLCLK %04x %3d %3d", wParam,LOWORD(lParam),HIWORD(lParam));<br>　　　　TextOut(hDC,20,160,Buffer,strlen(Buffer));<br>　　　　ReleaseDC(hWnd,hDC);<br>　　　　break;</p>
<p>　　　　case WM_PAINT:<br>　　　　hDC = BeginPaint(hWnd,&amp;ps);<br>　　　　wsprintf(Buffer," ");<br>　　　　TextOut(hDC,20,20,Buffer,strlen(Buffer));<br>　　　　wsprintf(Buffer," Message wParam x y");<br>　　　　TextOut(hDC,20,20,Buffer,strlen(Buffer));<br>　　　　EndPaint(hWnd,&amp;ps);<br>　　　　break;</p>
<p>　　　　case WM_DESTROY:<br>　　　　PostQuitMessage(0);<br>　　　　break;</p>
<p>　　　　default:<br>　　　　return DefWindowProc(hWnd,message,wParam,lParam);<br>　　}<br>　　return 0;<br>}</p>
<p><br>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/forzy/archive/2007/06/27/1668256.aspx">http://blog.csdn.net/forzy/archive/2007/06/27/1668256.aspx</a></p>
<img src ="http://www.cppblog.com/totti1006/aggbug/115701.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-05-18 17:37 <a href="http://www.cppblog.com/totti1006/archive/2010/05/18/115701.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>printf 格式化输出</title><link>http://www.cppblog.com/totti1006/archive/2010/03/10/109341.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Wed, 10 Mar 2010 03:11:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/03/10/109341.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/109341.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/03/10/109341.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/109341.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/109341.html</trackback:ping><description><![CDATA[<div id=art style="MARGIN: 15px" twffan="done">
<div style="FONT-FAMILY: 黑体" align=justify twffan="done">
<table borderColor=#000000 cellSpacing=0 cellPadding=4 border=0>
    <tbody>
        <tr vAlign=top>
            <td vAlign=top width=119 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff><strong>格式代码</strong></font></div>
            </font></td>
            <td vAlign=top width=149 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>A</font></div>
            </font></td>
            <td vAlign=top width=134 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>ABC</font></div>
            </font></td>
            <td vAlign=top width=134 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>ABCDEF</font><font color=#ffffff>GH</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=119 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>S</strong></font></div>
            </font></td>
            <td vAlign=top width=149><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>A</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABC</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABCDEFGH</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=119 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%5</strong></font><font color=#000000><strong>S</strong></font></div>
            </font></td>
            <td vAlign=top width=149><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>####A</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>##ABC</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABCDEFGH</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=119 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%.5</strong></font><font color=#000000><strong>S</strong></font></div>
            </font></td>
            <td vAlign=top width=149><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>A</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABC</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABCDE</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=119 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%5.5</strong></font><font color=#000000><strong>S</strong></font></div>
            </font></td>
            <td vAlign=top width=149><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>####A</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>##ABC</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABCDE</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=119 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%-5</strong></font><font color=#000000><strong>S</strong></font></div>
            </font></td>
            <td vAlign=top width=149><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>A####</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABC##</font></div>
            </font></td>
            <td vAlign=top width=134><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>ABCDEFGH</font></div>
            </font></td>
        </tr>
    </tbody>
</table>
</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done"><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Printf </font><font color=#000000>格式化字符串</font></div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done"><font color=#000000></font>&nbsp;</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done">
<table borderColor=#000000 cellSpacing=0 cellPadding=4 border=0>
    <tbody>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff><strong>格式代码</strong></font></div>
            </font></td>
            <td vAlign=top width=115 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>1</font></div>
            </font></td>
            <td vAlign=top width=93 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>-12</font></div>
            </font></td>
            <td vAlign=top width=107 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>12345</font></div>
            </font></td>
            <td vAlign=top width=117 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>-12</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%6</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>#####1</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>###-12</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>#12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%.4</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>0001</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>-0012</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%6.4</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>##0001</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>#-0012</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>#12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%-4</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1####</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>-12#</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%04</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>0001</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>-012</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>123456789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%+</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>+1</font></div>
            </font></td>
            <td vAlign=top width=93><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>-12</font></div>
            </font></td>
            <td vAlign=top width=107><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>+12345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>+123456789</font></div>
            </font></td>
        </tr>
    </tbody>
</table>
</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done"><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Printf</font><font color=#000000>格式化整型数值</font></div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done"><font color=#000000></font>&nbsp;</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done">
<table borderColor=#000000 cellSpacing=0 cellPadding=4 border=0>
    <tbody>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff><strong>格式代码</strong></font></div>
            </font></td>
            <td vAlign=top width=115 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>1</font></div>
            </font></td>
            <td vAlign=top width=96 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>.01</font></div>
            </font></td>
            <td vAlign=top width=102 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>.00012345</font></div>
            </font></td>
            <td vAlign=top width=117 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>12345.6789</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>f</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.000000</font></div>
            </font></td>
            <td vAlign=top width=96><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>0.010000</font></div>
            </font></td>
            <td vAlign=top width=102><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>0.000123</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>12345.678900</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%10.2</strong></font><font color=#000000><strong>d</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>######1.00</font></div>
            </font></td>
            <td vAlign=top width=96><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>#####0.01</font></div>
            </font></td>
            <td vAlign=top width=102><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>######0.00</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>##12345.67</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>e</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.000000e+00</font></div>
            </font></td>
            <td vAlign=top width=96><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.000000e-02</font></div>
            </font></td>
            <td vAlign=top width=102><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.234500e-04</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.234568e+04</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%.4</strong></font><font color=#000000><strong>e</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.0000e+00</font></div>
            </font></td>
            <td vAlign=top width=96><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.0000e-02</font></div>
            </font></td>
            <td vAlign=top width=102><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.2345e-04</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1.2346e+04</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>g</strong></font></div>
            </font></td>
            <td vAlign=top width=115><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>1</font></div>
            </font></td>
            <td vAlign=top width=96><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>0.01</font></div>
            </font></td>
            <td vAlign=top width=102><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>0.00012345</font></div>
            </font></td>
            <td vAlign=top width=117><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>12345.7</font></div>
            </font></td>
        </tr>
    </tbody>
</table>
</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done"><font color=#000000></font>&nbsp;</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done">
<table borderColor=#000000 cellSpacing=0 cellPadding=4 border=0>
    <tbody>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff><strong>格式代码</strong></font></div>
            </font></td>
            <td vAlign=top width=457 bgColor=#800080><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#ffffff>6.023e23</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>f</strong></font></div>
            </font></td>
            <td vAlign=top width=457><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>60229999999999975882752.000000</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%10.2</strong></font><font color=#000000><strong>e</strong></font></div>
            </font></td>
            <td vAlign=top width=457><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>60229999999999975882752.00</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>e</strong></font></div>
            </font></td>
            <td vAlign=top width=457><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>6.023000e+23</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%.4</strong></font><font color=#000000><strong>e</strong></font></div>
            </font></td>
            <td vAlign=top width=457><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>6.0230e+23</font></div>
            </font></td>
        </tr>
        <tr vAlign=top>
            <td vAlign=top width=96 bgColor=#c0c0c0><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000><strong>%</strong></font><font color=#000000><strong>g</strong></font></div>
            </font></td>
            <td vAlign=top width=457><font color=#000000 size=2>
            <div align=justify twffan="done"><font color=#000000>6.023e+23</font></div>
            </font></td>
        </tr>
    </tbody>
</table>
</div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done"><font color=#000000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Printf</font><font color=#000000>格式化浮点值</font></div>
<div style="FONT-FAMILY: 黑体" align=justify twffan="done">&nbsp;</div>
<p style="FONT-FAMILY: 黑体" align=justify>printf()格式转换的一般形式如下<br>%(flags)(width)(.prec)type<br>以中括号括起来的参数为选择性参数，而%与type则是必要的。底下先介绍type的几种形式</p>
<p style="FONT-FAMILY: 黑体" align=justify>整数<br>%d 整数的参数会被转成一有符号的十进制数字<br>%u 整数的参数会被转成一无符号的十进制数字<br>%o 整数的参数会被转成一无符号的八进制数字<br>%x 整数的参数会被转成一无符号的十六进制数字，并以小写abcdef表示<br>%X 整数的参数会被转成一无符号的十六进制数字，并以大写ABCDEF表示浮点型数</p>
<p style="FONT-FAMILY: 黑体" align=justify>%f double 型的参数会被转成十进制数字，并取到小数点以下六位，四舍五入。<br>%e double型的参数以指数形式打印，有一个数字会在小数点前，六位数字在小数点后，而在指数部分会以小写的e来表示。<br>%E 与%e作用相同，唯一区别是指数部分将以大写的E 来表示。<br>%g double 型的参数会自动选择以%f 或%e 的格式来打印，其标准是根据欲打印的数值及所设置的有效位数来决定。<br>%G 与%g 作用相同，唯一区别在以指数形态打印时会选择%E 格式。</p>
<p style="FONT-FAMILY: 黑体" align=justify>字符及字符串<br>%c 整型数的参数会被转成unsigned char型打印出。<br>%s 指向字符串的参数会被逐字输出，直到出现NULL字符为止<br>%p 如果是参数是&#8220;void *&#8221;型指针则使用十六进制格式显示。</p>
<p style="FONT-FAMILY: 黑体" align=justify>prec 有几种情况<br>1. 正整数的最小位数。<br>2. 在浮点型数中代表小数位数<br>3. 在%g 格式代表有效位数的最大值。<br>4. 在%s格式代表字符串的最大长度。<br>5. 若为&#215;符号则代表下个参数值为最大长度。</p>
<p style="FONT-FAMILY: 黑体" align=justify>width为参数的最小长度，若此栏并非数值，而是*符号，则表示以下一个参数当做参数长度。</p>
<p style="FONT-FAMILY: 黑体" align=justify>flags 有下列几种情况<br>#NAME?<br>+ 一般在打印负数时，printf( )会加印一个负号，整数则不加任何负号。此旗标会使得在打印正数前多一个正号（+）。<br># 此旗标会根据其后转换字符的不同而有不同含义。当在类型为o 之前（如%#o），则会在打印八进制数值前多印一个o。<br>而在类型为x 之前（%#x）则会在打印十六进制数前多印&#8217;0x&#8217;，在型态为e、E、f、g或G 之前则会强迫数值打印小数点。在类型为g 或G之前时则同时保留小数点及小数位数末尾的零。<br>0 当有指定参数时，无数字的参数将补上0。默认是关闭此旗标，所以一般会打印出空白字符。</p>
</div>
<img src ="http://www.cppblog.com/totti1006/aggbug/109341.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-03-10 11:11 <a href="http://www.cppblog.com/totti1006/archive/2010/03/10/109341.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WinPcap和VC的配置</title><link>http://www.cppblog.com/totti1006/archive/2010/03/08/109175.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Mon, 08 Mar 2010 03:46:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/03/08/109175.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/109175.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/03/08/109175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/109175.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/109175.html</trackback:ping><description><![CDATA[<p><span lang=EN-US style="FONT-SIZE: 10.5pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-fareast-font-family: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">到<a href="http://www.winpcap.org/install/default.htm"><u><font color=#800080>http://www.winpcap.org/install/default.htm</font></u></a>上面下载安装包，<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">到</span><span lang=EN-US style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-fareast-font-family: 宋体; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a href="http://www.winpcap.org/devel.htm"><span style="FONT-SIZE: 12pt"><u><font color=#800080>http://www.winpcap.org/devel.htm</font></u></span></a></span><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 'Times New Roman'; mso-fareast-font-family: 宋体; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">.</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">下载</span><span lang=EN-US style="FONT-SIZE: 10.5pt; FONT-FAMILY: Verdana; mso-bidi-font-size: 12.0pt; mso-fareast-font-family: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">winpcap</span><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana">开发包，解压到指定目录。</span></span>宽带上网助手就是普通的安装包，而开发要使用的则是Developer&#8217;s Pack。</p>
<p>下来之后，安装到某个目录，然后配置VC，把那个目录的include和lib两个目录放到VC的选项-&gt;目录里面。在project-&gt;settings-&gt;link-&gt;Object/library Modules下面添加&#8220;wpcap.lib Packet.lib&#8221;，用空格隔开；在project-&gt;settings-&gt;C/C++-&gt;Preprocessor difinitions下面添加&#8220;WPCAP,HAVE_REMOTE&#8221;，用逗号隔开。</p>
<p>WinPcap目录下有doc文档，里面的东西相当全面。<br><br>如果程序出现如下错误：<br>F:\学习\test.cpp(155) : error C2065: &#8217;socklen_t&#8217; : undeclared identifier<br>F:\学习\test.cpp(155) : error C2146: syntax error : missing &#8216;;&#8217; before identifier &#8217;sockaddrlen&#8217;<br>F:\学习\test.cpp(155) : error C2065: &#8217;sockaddrlen&#8217; : undeclared identifier<br>F:\学习\test.cpp(164) : error C2065: &#8216;getnameinfo&#8217; : undeclared identifier<br>F:\学习\test.cpp(170) : error C2065: &#8216;NI_NUMERICHOST&#8217; : undeclared identifier</p>
<p>错误根由是因为WinPcap支持ipv6,而VC的winsock2.h太老了，很多结构都没有被支持，所以产生错误。网上有人说可以用VS新版本调试，我电脑里面是VS2008，但是仍然产生错误。</p>
<p>既然VS也无法通过，我实在没能力去修改WinPcap的头文件，所以就用最后一个方案，使用VC6.0的最新的PlatForm SDK开发包，里面包含了新的头文件，就可以支持WinPcap了。</p>
<p>有人说PSDK只有Windows2003的版本，我在微软里面找到了WinXPSP2的PSDK。</p>
<p>网址如下</p>
<p sizset="35" sizcache="4"><a href="http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm"><u><font color=#800080>http://www.microsoft.com/msdownload/platformsdk/sdkupdate/XPSP2FULLInstall.htm</font></u></a></p>
<p>里面好几个Cab，网页里面有完整的安装说明。<br><br>下面是PlatFormSDK安装步骤</p>
<p>(1)安装过程:</p>
<p>CMD运行PSDK-FULL.bat，参数为一个目录，里面会被解压缩安装包，然后Setup，一路Next就可以了。</p>
<p>(2)配置过程</p>
<p>打开Visual C++6.0，在选项里面连接，把PSDK安装后的include和lib加入相应的位置。</p>
<p>特别注意，要把这些目录的顺序调高，我直接放到了最高层去了。</p>
<p>呵呵，编译一下，通过了，好Happy啊。</p>
<script src="http://china-addthis.googlecode.com/svn/trunk/addthis.js" type=text/javascript charset=utf-8></script>
<img src ="http://www.cppblog.com/totti1006/aggbug/109175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-03-08 11:46 <a href="http://www.cppblog.com/totti1006/archive/2010/03/08/109175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Winpcap4编程中Winsock的版本和IPv4与IPv6的兼容问题</title><link>http://www.cppblog.com/totti1006/archive/2010/03/05/108973.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Fri, 05 Mar 2010 08:09:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/03/05/108973.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/108973.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/03/05/108973.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/108973.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/108973.html</trackback:ping><description><![CDATA[在从 ipv4 到 ipv6 的升级过程中，有不少函数发生了变化。新版的 winpcap 正在慢慢地从 ipv4 向 ipv6 迁移。在这个过程中，有不少函数已经被废弃。旧的例程在新版的 winpcap 下无法使用，这其中也包括了一些著名的基于 winpcap/libpcap 的软件。比如 windump 和 snort for windows。作者在编程过程中收集了相关的资料，提出了解决方法。本文的最新版本和讨论保存在广州大学信息安全研究所 http://www.gzisi.com 网站的入侵检测文章部分。
<p>&#160;</p>
<p>很多程序员下载了 winpcap 3.1 或更新的版本后，会发现原来自己运行得好好的程序突然不能使用了。而且其中涉及到一些相当重要的函数，比如 pcap_loop。 winpcap 对这些函数的修改使得很多基于它的应用程序（比如windump和snort）都将作出不小的改动。当然，还有你自己编写的代码&#8230;&#8230;</p>
<p>以下是 windows 本身的 winsock 编程对此问题的影响。由于新版的 winpcap 完全使用了新的 winsock（支持ipv6），因此下列问题可能影响到每一个程序。</p>
<p>这是新旧两版的 packet32.h 之间的差异</p>
<p>//packet32.h<br>typedef struct npf_if_addr {<br>struct sockaddr ipaddress; ///&lt; ip address.<br>struct sockaddr subnetmask; ///&lt; netmask for that address.<br>struct sockaddr broadcast; ///&lt; broadcast address.<br>//struct sockaddr_storage ipaddress; ///&lt; ip address.<br>//struct sockaddr_storage subnetmask; ///&lt; netmask for that address.<br>//struct sockaddr_storage broadcast; ///&lt; broadcast address.<br>}npf_if_addr; </p>
<p>&#160;</p>
<p>http://msdn.microsoft.com/library/default.html?url=/library/en-us/winsock/winsock/sockaddr_storage_2.html<br><br>很多程序员仍然使用 visual c++6 编译程序，不幸的是，vc++6中的 winsock2.h 太老了，它根本不认得 struct sockaddr_storage。因此，winpcap 自带的例程在 vc++6 下编译时会无情地抛出无数错误。事实上，该结构完全可以使用老的 sockaddr 代替。手工改动 packet32.h，将 sockaddr_storage 换成 sockaddr，编译将顺利通过。当然，这样的代码自然无法支持 ipv6 了。到目前为止，我没有找到在 vc++6 与windows 2003中成功编译 ipv6 的例程。windows 2000 的用户可以升级sdk，使自己的vc++6支持ipv6编程，但不幸的是这个sdk升级版检查操作系统的版本，不是2195就停止了安装，使我在 windows xp 和 win 2003 下无法安装。在作者写本文时，没有找到对 win xp 和 win 2003 的 ipv6 sdk。我没有安装那个巨达 106m 的 windows 2003 开发升级包，但估计那个包中可能有支持 ipv6 开发所需的库和头文件。我也不排除对 for 2000 的sdk包做做手脚，提取其中的文件后能够成功运行的可能。因为 microsoft visual studio.net 2002/2003 已经对此提供了很好的支持。<br></p>
<p>躲过了 sockaddr_storage 一劫的朋友可能很快会遇到 socklen_t 和 getnameinfo 函数的错误。这两个函数包含在头文件 ws2tcpip.h 中。很不幸，它也是一个 for ipv6 的函数，在vc++6中同样没有支持。编译时发生的错误如下：</p>
<p>--------------------configuration: iflist - win32 debug--------------------<br>compiling...<br>iflist.c<br>g:\security\ids\wpdpack\examples\iflist\iflist.c(151) : error c2065: socklen_t : undeclared identifier<br>g:\security\ids\wpdpack\examples\iflist\iflist.c(151) : error c2146: syntax error : missing ; before identifier sockaddrlen<br>g:\security\ids\wpdpack\examples\iflist\iflist.c(151) : error c2065: sockaddrlen : undeclared identifier<br>g:\security\ids\wpdpack\examples\iflist\iflist.c(160) : warning c4013: getnameinfo undefined; assuming extern returning int<br>g:\security\ids\wpdpack\examples\iflist\iflist.c(166) : error c2065: ni_numerichost : undeclared identifier<br>error executing cl.exe. </p>
<p>&#160;</p>
<p>iflist.exe - 4 error(s), 1 warning(s)</p>
<p>关于该函数，可以参考微软msdn：</p>
<p>http://msdn.microsoft.com/library/default.html?url=/library/en-us/winsock/winsock/getnameinfo_2.html</p>
<p>同样，如果你不介意让程序仅能在 ipv4 上工作的话，可以用原来的函数 <strong><font color=#000000>gethostbyname</font></strong> 代替。<br><br>原文地址：<a href="http://blog.chinaunix.net/u2/64540/showart_573227.html">http://blog.chinaunix.net/u2/64540/showart_573227.html</a></p>
<img src ="http://www.cppblog.com/totti1006/aggbug/108973.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-03-05 16:09 <a href="http://www.cppblog.com/totti1006/archive/2010/03/05/108973.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Windows编程中的堆管理</title><link>http://www.cppblog.com/totti1006/archive/2010/01/21/106118.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Thu, 21 Jan 2010 03:52:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/01/21/106118.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/106118.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/01/21/106118.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/106118.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/106118.html</trackback:ping><description><![CDATA[<strong>1 引言<br><br></strong>　　在大多数Windows应用程序<a class=bluekey href="http://design.yesky.com/" target=_blank><font color=#204890>设计</font></a>中，都几乎不可避免的要对内存进行操作和管理。在进行大尺寸内存的动态分配时尤其显的重要。本文即主要对内存管理中的堆管理技术进行论述。<br><br>　　堆（Heap）实际是位于保留的虚拟地址空间中的一个区域。刚开始时，保留区域中的多数页面并没有被提交物理存储器。随着从堆中越来越多的进行内存分配，堆管理器将逐渐把更多的物理存储器提交给堆。堆的物理存储器从系统页文件中分配，在释放时有专门的堆管理器负责对已占用物理存储器的回收。堆管理也是Windows提供的一种内存管理机制。主要用来分配小的数据块。与Windows的其他两种内存管理机制虚拟内存和内存映射文件相比，堆可以不必考虑诸如系统的分配粒度和页面边界之类比较烦琐而又容易忽视的问题，可将注意力集中于对<a class=bluekey href="http://dev.yesky.com/" target=_blank><font color=#204890>程序</font></a>功能代码的设计上。但是使用堆去分配、释放内存的速度要比其他两种机制慢的多，而且不具备直接控制物理存储器提交与回收的能力。<br><br>　　在进程刚启动时，系统便在刚创建的进程虚拟地址空间中创建了一个堆，该堆即为进程的默认堆，缺省大小为1MB，该值允许在链接程序时被更改。进程的默认堆是比较重要的，可供众多Windows函数使用。在使用时，系统必须保证在规定的时间内，每此只有一个线程能够分配和释放默认堆中的内存块。虽然这种限制将会对访问速度产生一定的影响，但却可以保证进程中的多个线程在同时调用各种Windows函数时对默认堆的顺序访问。在进程中允许使用多个堆，进程中包括默认堆在内的每个堆都有一个堆句柄来标识。与自己创建的堆不同，进程默认堆的创建、销毁均由系统来完成，而且其生命期早在进程开始执行之前就已经开始，虽然在程序中可以通过GetProcessHeap（）函数得到进程的默认堆句柄，但却不允许调用HeapDestroy（）函数显式将其撤消。<br><br>　　<strong>2 对动态创建堆的需求</strong><br><br>　　前面曾提到，在进程中除了进程默认堆外，还可以在进程虚拟地址空间中动态创建一些独立的堆。至于在程序设计时究竟需不需要动态创建独立的堆可以从是否有保护组件的需要、是否能更加有效地对内存进行管理、是否有进行本地访问的需要、是否有减少线程同步开销的需要以及是否有迅速释放堆的需要等几个方面去考虑。<br><br>　　对于是否有保护组件的需要这一原则比较容易理解。在图1中，左边的图表示了一个链表（节点结构）组件和一个树（分支结构）组件共同使用一个堆的情况。在这种情况下，由于两组件数据在堆中的混合存放，如果节点3（属于链表组件）的后几个字节由于被错误改写，将有可能影响到位于其后的分支2（属于树组件）。这将致使树组件的相关代码在遍历其树时由于内存被破坏而无法进行。究其原因，树组件的内存是由于链表组建对其自身的错误操作而引起的。如果采用右图所示方式，将树组件和链表组件分别存放于一个独立的堆中，上述情况显然不会发生，错误将被局限于进行了错误操作的链表组件，而树组件由于存放在独立的堆中而受到了保护。<br><br><img onerror="this.src='http://www.yesky.com/image20010518/120368.jpg';" hspace=3 src="http://www.yesky.com/image20010518/120368.jpg" align=center vspace=1 border=1 twffan="done"><br>图1 动态创建堆在保护组件中的作用<br><br>　　在上图中，如果链表组件的每个节点占用12个字节，每个树组件的分支占用16个字节如果这些长度不一的对象共用一个堆（左图），在左图中这些已经分配了内存的对象已占满了堆，如果其中有节点2和节点4释放，将会产生24个字节的碎片，如果试图在24个字节的空闲区间内分配一个16字节的分支对象，尽管要分配的字节数小于空闲字节数，但分配仍将失败。只有在堆栈中分配大小相同的对象才可以实行更加有效的内存管理。如果将树组件换成其他长度为12字节的组件，那么在释放一个对象后，另一个对象就可以恰好填充到此刚释放的对象空间中。<br><br>　　进行本地访问的需要也是一条比较重要的原则。系统会经常在内存与系统页文件之间进行页面交换，但如果交换次数过多，系统的运行性能就将受很大的影响。因此在程序设计时应尽量避免系统频繁交换页面，如果将那些会被同时访问到的数据分配在相互靠近的位置上，将会减少系统在内存和页文件之间的页面交换频率。<br><br>　　线程同步开销指的是默认条件下以顺序方式运行的堆为保护数据在多个线程试图同时访问时不受破坏而必须执行额外代码所花费的开销。这种开销保证了堆对线程的<a class=bluekey href="http://soft.yesky.com/security/" target=_blank><font color=#204890>安全</font></a>性，因此是有必要的，但对于大量的堆分配操作，这种额外的开销将成为一个负担，并降低程序的运行性能。为避免这种额外的开销，可以在创建新堆时通知系统只有单个线程对访问。此时堆对线程的安全性将有应用程序来负责。<br><br>　　最后如果有迅速释放堆的需要，可将专用堆用于某些数据结构，并以整个堆去释放，而不再显式地释放在堆中分配的每一个内存块。对于大多数应用程序，这样的处理将能以更快的速度运行。<br><strong>3 创建堆<br><br></strong>　　在进程中，如果需要可以在原有默认堆的基础上动态创建一个堆，可由HeapCreate（）函数完成：<br><br>
<table width="100%" bgColor=#ffffff>
    <tbody>
        <tr>
            <td>HANDLE HeapCreate(<br>　DWORD flOptions,<br>　DWORD dwInitialSize,<br>　DWORD dwMaximumSize<br>);</td>
        </tr>
    </tbody>
</table>
<br>　　其第一个参数flOptions指定了对新建堆的操作属性。该标志将会影响一些堆函数如HeapAlloc（）、HeapFree（）、HeapReAlloc（）和HeapSize（）等对新建堆的访问。其可能的取值为下列标志及其组合：<br><br>
<table cellSpacing=0 width="100%" border=1>
    <tbody>
        <tr>
            <td>属性标志</td>
            <td>说明</td>
        </tr>
        <tr>
            <td>HEAP_GENERATE_EXCEPTIONS</td>
            <td>在遇到由于内存越界等而引起的函数失败时，由系统抛出一个异常来指出此失败，而不是简单的返回NULL指针。</td>
        </tr>
        <tr>
            <td>HEAP_NO_SERIALIZE </td>
            <td>指明互斥现象不会出现</td>
        </tr>
    </tbody>
</table>
<br>　　参数dwInitialSize和dwMaximumSize分别为堆的初始大小和堆栈的最大尺寸。其中，dwInitialSize的值决定了最初提交给堆的字节数。如果设置的数值不是页面大小的整数倍，则将被圆整到邻近的页边界处。而dwMaximumSize则实际上是系统能为堆保留的地址空间区域的最大字节数。如果该值为0，那么将创建一个可扩展的堆，堆的大小仅受可用内存的限制。如果应用程序需要分配大的内存块，通常要将该参数设置为0。如果dwMaximumSize大于0，则该值限定了堆所能创建的最大值，HeapCreate（）同样也要将该值圆整到邻近的页边界，然后再在进程的虚拟地址空间为堆保留该大小的一块区域。在这种堆中分配的内存块大小不能超过0x7FFF8字节，任何试图分配更大内存块的行为将会失败，即使是设置的堆大小足以容纳该内存块。如果HeapCreate（）成功执行，将会返回一个标识新堆的句柄，并可供其他堆函数使用。<br><br>　　需要特别说明的是，在设置第一个参数时，对HEAP_NO_SERIALIZE的标志的使用要谨慎，一般应避免使用该标志。这是同后续将要进行的堆函数HeapAlloc（）的执行过程有关系的，在HeapAlloc（）试图从堆中分配一个内存块时，将执行下述几步操作：<br><br>　　1） 遍历分配的和释放的内存块的链接表<br><br>　　2） 搜寻一个空闲内存块的地址<br><br>　　3） 通过将空闲内存块标记为"已分配"来分配新内存块<br><br>　　4） 将新分配的内存块添加到内存块列表<br><br>　　如果这时有两个线程1、2试图同时从一个堆中分配内存块，那么线程1在执行了上面的1和2步后将得到空间内存块的地址。但是由于CPU对线程运行时间的分片，使得线程1在执行第3步操作前有可能被线程2抢走执行权并有机会去执行同样的1、2步操作，而且由于先执行的线程1并没有执行到第3步，因此线程2会搜寻到同一个空闲内存块的地址，并将其标记为已分配。而线程1在恢复运行后并不能知晓该内存块已被线程2标记过，因此会出现两个线程军认为其分配的是空闲的内存块，并更新各自的联接表。显然，象这种两个线程拥有完全相同内存块地址的错误是非常严重而又是难以发现的。<br><br>　　由于只有在多个线程同时进行操作时才有可能出现上述问题，一种简单的解决的办法就是不使用HEAP_NO_SERIALIZE标志而只允许单个线程独占地对堆及其联接表拥有访问权。如果一定要使用此标志，为了安全起见，必须确保进程为单线程的或是在进程中使用了多线程，但只有单个线程对堆进行访问。再就是使用了多线程，也有多个线程对堆进行了访问，但这些线程通过使用某种线程同步手段。如果可以确保以上几条中的一条成立，也是可以安全使用HEAP_NO_SERIALIZE标志的，而且还将拥有快的访问速度。如果不能肯定上述条件是否满足，建议不使用此标志而以顺序的方式访问堆，虽然线程速度会因此而下降但却可以确保堆及其中数据的不被破坏。<br><br>　　<strong>4 从堆中分配内存块<br></strong><br>　　在成功创建一个堆后，可以调用HeapAlloc（）函数从堆中分配内存块。在此，除了可以从用HeapCreate（）创建的动态堆中分配内存块，也可以直接从进程的默认堆中分配内存块。下面先给出HeapCreate（）的函数原型：<br><br>
<table width="100%" bgColor=#ffffff>
    <tbody>
        <tr>
            <td>LPVOID HeapAlloc(<br>　HANDLE hHeap,<br>　DWORD dwFlags,<br>　DWORD dwBytes<br>);</td>
        </tr>
    </tbody>
</table>
<br>　　其中，参数hHeap为要分配的内存块来自的堆的句柄，可以是从HeapCreate（）创建的动态堆句柄也可以是由GetProcessHeap（）得到的默认堆句柄。参数dwFlags指定了影响堆分配的各个标志。该标志将覆盖在调用HeapCreate（）时所指定的相应标志，可能的取值为：<br><br>
<table cellSpacing=0 width="100%" border=1>
    <tbody>
        <tr>
            <td>标志 </td>
            <td>说明</td>
        </tr>
        <tr>
            <td>HEAP_GENERATE_EXCEPTIONS</td>
            <td>该标志指定在进行诸如内存越界操作等情况时将抛出一个异常而不是简单的返回NULL指针</td>
        </tr>
        <tr>
            <td>HEAP_NO_SERIALIZE</td>
            <td>强制对HeapAlloc（）的调用将与访问同一个堆的其他线程不按照顺序进行</td>
        </tr>
        <tr>
            <td>HEAP_ZERO_MEMORY</td>
            <td>如果使用了该标志，新分配内存的内容将被初始化为0</td>
        </tr>
    </tbody>
</table>
<br>　　最后一个参数dwBytes设定了要从堆中分配的内存块的大小。如果HeapAlloc（）执行成功，将会返回从堆中分配的内存块的地址。如果由于内存不足或是其他一些原因而引起HeapAlloc（）函数的执行失败，将会引发异常。通过异常标志可以得到引起内存分配失败的原因：如果为STATUS_NO_MEMORY则表明是由于内存不足引起的；如果是STATUS_ACCESS_VIOLATION则表示是由于堆被破坏或函数参数不正确而引起分配内存块的尝试失败。以上异常只有在指定了HEAP_GENERATE_EXCEPTIONS标志时才会发生，如果没有指定此标志，在出现类似错误时HeapAlloc（）函数只是简单的返回NULL指针。<br><br>　　在设置dwFlags参数时，如果先前用HeapCreate（）创建堆时曾指定过HEAP_GENERATE_EXCEPTIONS标志，就不必再去设置HEAP_GENERATE_EXCEPTIONS标志了，因为HEAP_GENERATE_EXCEPTIONS标志已经通知堆在不能分配内存块时将会引发异常。另外，对HEAP_NO_SERIALIZE标志的设置应慎重，与在HeapCreate（）函数中使用HEAP_NO_SERIALIZE标志类似，如果在同一时间有其他线程使用同一个堆，那么该堆将会被破坏。如果是在进程默认堆中进行内存块的分配则要绝对禁用此标志。<br><br>　　在使用堆函数HeapAlloc（）时要注意：堆在内存管理中的使用主要是用来分配一些较小的数据块，如果要分配的内存块在1MB左右，那么就不要再使用堆来管理内存了，而应选择虚拟内存的内存管理机制。<br><strong>5 再分配内存块<br><br></strong>　　在程序设计时经常会由于开始时预见不足而造成在堆中分配的内存块大小的不合适（多数情况是开始时分配的内存较小，而后来实际需要更多的数据复制到内存块中去）这就需要在分配了内存块后再根据需要调整其大小。堆函数HeapReAlloc（）将完成这一功能，其函数原型为：<br><br>
<table width="100%" bgColor=#ffffff>
    <tbody>
        <tr>
            <td>LPVOID HeapReAlloc(<br>　HANDLE hHeap, <br>　DWORD dwFlags,<br>　LPVOID lpMem, <br>　DWORD dwBytes <br>);</td>
        </tr>
    </tbody>
</table>
<br>　　其中，参数hHeap为包含要调整其大小的内存块的堆的句柄。dwFlags参数指定了在更改内存块大小时HeapReAlloc（）函数所使用的标志。其可能的取值为HEAP_GENERATE_EXCEPTIONS、HEAP_NO_SERIALIZE、HEAP_REALLOC_IN_PLACE_ONLY和HEAP_ZERO_MEMORY，其中前两个标志的作用与在HeapAlloc（）中的作用相同。HEAP_REALLOC_IN_PLACE_ONLY标志在内存块被加大时不移动堆中的内存块，在没有设置此标志的情况下如果对内存进行增大，那么HeapReAlloc（）函数将有可能将原内存块移动到一个新的地址。显然，在设置了该标志禁止内存快首地址进行调整时，将有可能出现没有足够的内存供试图增大的内存块使用，对于这种情况，函数对内存块增大调整的操作是失败的，内存块将仍保留原有的大小和位置。HEAP_ZERO_MEMORY标志的用处则略有不同，如果内存快经过调整比以前大，那么新增加的那部分内存将被初始化为0；如果经过调整内存块缩小了，那么该标志将不起任何作用。<br><br>　　函数的最后两个参数lpMem和dwBytes分别为指向再分配内存块的指针和再分配的字节数。如果函数成功执行，将返回新的改变了大小的内存块的地址。如果在调用时使用了HEAP_REALLOC_IN_PLACE_ONLY标志，那么返回的地址将与原内存块地址相同。如果因为内存不足等原因而引起函数的执行失败，函数将返回一个NULL指针。但是HeapReAlloc（）的执行失败并不会影响原内存块，它将保持原来的大小和位置继续存在。可以通过HeapSize（）函数来检索内存块的实际大小。<br><br>　　<strong>6 释放堆内存、撤消堆</strong><br><br>　　在不再需要使用堆中的内存块时，可以通过HeapFree（）将其予以释放。该函数结构比较简单，只含有三个参数：<br><br>
<table width="100%" bgColor=#ffffff>
    <tbody>
        <tr>
            <td>BOOL HeapFree(<br>　HANDLE hHeap,<br>　DWORD dwFlags, <br>　LPVOID lpMem<br>);</td>
        </tr>
    </tbody>
</table>
<br>　　其中，hHeap为要包含要释放内存块的堆的句柄；参数dwFlags为堆栈的释放选项可以是0，也可以是HEAP_NO_SERIALIZE；最后的参数lpMem为指向内存块的指针。如果函数成功执行，将释放指定的内存块，并返回TRUE。该函数的主要作用是可以用来帮助堆管理器回收某些不使用的物理存储器以腾出更多的空闲空间，但是并不能保证一定会成功。<br><br>　　最后，在程序退出前或是应用程序不再需要其创建的堆了，可以调用HeapDestory（）函数将其销毁。该函数只包含一个参数--待销毁的堆的句柄。HeapDestory（）的成功执行将可以释放堆中包含的所有内存块，也可将堆占用的物理存储器和保留的地址空间区域全部重新返回给系统并返回TRUE。该函数只对由HeapCreate（）显式创建的堆起作用，而不能销毁进程的默认堆，如果强行将由GetProcessHeap（）得到的默认堆的句柄作为参数去调用HeapDestory（），系统将会忽略对该函数的调用。<br><strong>7 对new与delete操作符的重载<br><br></strong>　　new与delete内存空间动态分配操作符是C++中使用堆进行内存管理的一种常用方式，在程序运行过程中可以根据需要随时通过这两个操作符建立或删除堆对象。new操作符将在堆中分配一个足够大小的内存块以存放指定类型的对象，如果每次构造的对象类型不同，则需要按最大对象所占用的空间来进行分配。new操作符在成功执行后将返回一个类型与new所分配对象相匹配的指针，如果不匹配则要对其进行强制类型转换，否则将会编译出错。在不再需要这个对象的时候，必须显式调用delete操作符来释放此空间。这一点是非常重要的，如果在预分配的缓冲里构造另一个对象之前或者在释放缓冲之前没有显式调用delete操作符，那么程序将产生不可预料的后果。在使用delete操作符时，应注意以下几点： <br><br>　　1） 它必须使用于由运算符new返回的指针<br><br>　　2） 该操作符也适用于NULL指针<br><br>　　3） 指针名前只用一对方括号符，并且不管所删除数组的维数，忽略方括号内的任何数字<br><br>
<table width="100%" bgColor=#ffffff>
    <tbody>
        <tr>
            <td>class CVMShow{<br>　private:<br>　　static HANDLE m_sHeap;<br>　　static int m_sAllocedInHeap;<br>　public:<br>　　LPVOID operator new(size_t size);<br>　　void operator delete(LPVOID pVoid);<br>}<br><br>&#8230;&#8230;<br>HANDLE m_sHeap = NULL;<br>int m_sAllocedInHeap = 0;<br>LPVOID CVMShow::operator new(size_t size)<br>{<br>　if (m_sHeap == NULL)<br>　　m_sHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0);<br>　　LPVOID pVoid = HeapAlloc(m_sHeap, 0, size);<br>　　if (pVoid == NULL)<br>　　　return NULL;<br>　　　m_sAllocedInHeap++;<br>　　return pVoid;<br>}<br>void CVMShow::operator delete(LPVOID pVoid)<br>{<br>　if (HeapFree(m_sHeap, 0, pVoid))<br>　　m_sAllocedInHeap--;<br>　　if (m_sAllocedInHeap == 0)<br>　　{<br>　　　if (HeapDestory(m_sHeap))<br>　　　　m_sHeap = NULL;<br>　　}<br>}<br></td>
        </tr>
    </tbody>
</table>
<br>　　在程序中除了直接用上述方法使用new和delete来建立和删除堆对象外，还可以通过为C++类重载new和delete操作符来方便地利用堆栈函数。上面的代码对它们进行了简单的重载，并通过静态变量m_sHeap和m_sAllocedInHeap在类CVMShow的所有实例间共享唯一的堆句柄（因为在这里CVMShow类的所有实例都是在同一个堆中进行内存分配的）和已分配类对象的计数。这两个静态变量在代码开始执行时被分别初始化为NULL指针和0计数。<br><br>　　重载的new操作符在第一次被调用时，由于静态变量m_sHeap为NULL标志着堆尚未创建，就通过HeapCreate（）函数创建一个堆并返回堆句柄到m_sHeap。随后根据入口参数size所指定的大小在堆中分配内存，同时已分配内存块计数器m_sAllocedInHeap累加。在该操作符的以后调用过程中，由于堆已经创建，故不再创建堆，而是直接在堆中分配指定大小的内存块并对已分配的内存块个数进行计数。<br><br>　　在CVMShow类对象不再被应用程序所使用时，需要将其撤消，由重载的delete操作符完成此工作。delete操作符只接受一个LPVOID型参数，即被删除对象的地址。该函数在执行时首先调用HeapFree（）函数将指定的已分配内存的对象释放并对已分配内存计数递减1。如果该计数不为零则表明当前堆中的内存块没有全部释放，堆暂时不予撤消。如果m_sAllocedInHeap计数减到0，则堆中已释放完所有的CVMShow对象，可以调用HeapDestory（）函数将堆销毁，并将堆句柄m_sHeap设置为NULL指针。这里在撤消堆后将堆句柄设置为NULL指针的操作是完全必要的。如果不执行该操作，当程序再次调用new操作符去分配一个CVMShow类对象时将会认为堆是存在的而会试图在已撤消的堆中去分配内存，显然将会导致失败。<br><br>　　象CVMShow这样设计的类通过对new和delete操作符的重载，并且在一个堆中为所有的CVMShow类对象进行分配，可以节省在为每一个类都创建堆的分配开销与内存。这样的处理还可以让每一个类都拥有属于自己的堆，并且允许派生类对其共享，这在程序设计中也是比较好的一种处理方法。<br><br>　　<strong>8 小结</strong><br><br>　　在使用堆时有时会造成系统运行速度的减慢，通常是由以下原因造成的：分配操作造成的速度减慢；释放操作造成的速度减慢；堆竞争造成的速度减慢；堆破坏造成的速度减慢；频繁的分配和重分配造成的速度减慢等。其中，竞争是在分配和释放操作中导致速度减慢的问题。基于上述原因，建议不要在程序中过于频繁的使用堆。
<img src ="http://www.cppblog.com/totti1006/aggbug/106118.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-01-21 11:52 <a href="http://www.cppblog.com/totti1006/archive/2010/01/21/106118.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++ 日期 时间 time_t与struct tm转换</title><link>http://www.cppblog.com/totti1006/archive/2010/01/14/105663.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Thu, 14 Jan 2010 08:33:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/01/14/105663.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/105663.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/01/14/105663.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/105663.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/105663.html</trackback:ping><description><![CDATA[<p>本文从介绍基础概念入手，探讨了在C/C++中对日期和时间操作所用到的数据结构和函数，并对计时、时间的获取、时间的计算和显示格式等方面进行了阐述。本文还通过大量的实例向你展示了time.h头文件中声明的各种函数和数据结构的详细使用方法。</p>
<p>关键字：UTC（世界标准时间），Calendar Time（日历时间），epoch（时间点），clock tick（时钟计时单元）</p>
<p><br>1．概念<br>在C/C++中，对字符串的操作有很多值得注意的问题，同样，C/C++对时间的操作也有许多值得大家注意的地方。最近，在技术群中有很多网友也多次问到过C++语言中对时间的操作、获取和显示等等的问题。下面，在这篇文章中，笔者将主要介绍在C/C++中时间和日期的使用方法.</p>
<p>通过学习许多C/C++库，你可以有很多操作、使用时间的方法。但在这之前你需要了解一些&#8220;时间&#8221;和&#8220;日期&#8221;的概念，主要有以下几个：</p>
<p>Coordinated Universal Time（UTC）：协调世界时，又称为世界标准时间，也就是大家所熟知的格林威治标准时间（Greenwich Mean Time，GMT）。比如，中国内地的时间与UTC的时差为+8，也就是UTC+8。美国是UTC-5。</p>
<p>Calendar Time：日历时间，是用&#8220;从一个标准时间点到此时的时间经过的秒数&#8221;来表示的时间。这个标准时间点对不同的编译器来说会有所不同，但对一个编译系统来说，这个标准时间点是不变的，该编译系统中的时间对应的日历时间都通过该标准时间点来衡量，所以可以说日历时间是&#8220;相对时间&#8221;，但是无论你在哪一个时区，在同一时刻对同一个标准时间点来说，日历时间都是一样的。</p>
<p>epoch：时间点。时间点在标准C/C++中是一个整数，它用此时的时间和标准时间点相差的秒数（即日历时间）来表示。</p>
<p>clock tick：时钟计时单元（而不把它叫做时钟滴答次数），一个时钟计时单元的时间长短是由CPU控制的。一个clock tick不是CPU的一个时钟周期，而是C/C++的一个基本计时单位。</p>
<p>我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法，无论是在结构定义，还是命名，都具有明显的C语言风格。下面，我将说明在C/C++中怎样使用日期的时间功能。</p>
<p>2． 计时</p>
<p>C/C++中的计时函数是clock()，而与其相关的数据类型是clock_t。在MSDN中，查得对clock函数定义如下：</p>
<p>clock_t clock( void );</p>
<p>这个函数返回从&#8220;开启这个程序进程&#8221;到&#8220;程序中调用clock()函数&#8221;时之间的CPU时钟计时单元（clock tick）数，在MSDN中称之为挂钟时间（wall-clock）。其中clock_t是用来保存时间的数据类型，在time.h文件中，我们可以找到对它的定义：</p>
<p>#ifndef _CLOCK_T_DEFINED<br>typedef long clock_t;<br>#define _CLOCK_T_DEFINED<br>#endif</p>
<p>很明显，clock_t是一个长整形数。在time.h文件中，还定义了一个常量CLOCKS_PER_SEC，它用来表示一秒钟会有多少个时钟计时单元，其定义如下：</p>
<p>#define CLOCKS_PER_SEC ((clock_t)1000)</p>
<p>可以看到可以看到每过千分之一秒（1毫秒），调用clock（）函数返回的值就加1。下面举个例子，你可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间：</p>
<p>void elapsed_time()<br>{<br>printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);<br>}</p>
<p>当然，你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间：</p>
<p>＃i nclude &#8220;stdio.h&#8221;<br>＃i nclude &#8220;stdlib.h&#8221;<br>＃i nclude &#8220;time.h&#8221;</p>
<p>int main( void )<br>{<br>&nbsp;&nbsp; long&nbsp;&nbsp;&nbsp; i = 10000000L;<br>&nbsp;&nbsp; clock_t start, finish;<br>&nbsp;&nbsp; double duration;<br>&nbsp;&nbsp; /* 测量一个事件持续的时间*/<br>&nbsp;&nbsp; printf( "Time to do %ld empty loops is ", i );<br>&nbsp;&nbsp; start = clock();<br>&nbsp;&nbsp; while( i-- )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;<br>&nbsp;&nbsp; finish = clock();<br>&nbsp;&nbsp; duration = (double)(finish - start) / CLOCKS_PER_SEC;<br>&nbsp;&nbsp; printf( "%f seconds\n", duration );<br>&nbsp;&nbsp; system("pause");<br>}</p>
<p>在笔者的机器上，运行结果如下：</p>
<p>Time to do 10000000 empty loops is 0.03000 seconds</p>
<p>上面我们看到时钟计时单元的长度为1毫秒，那么计时的精度也为1毫秒，那么我们可不可以通过改变CLOCKS_PER_SEC的定义，通过把它定义的大一些，从而使计时精度更高呢？通过尝试，你会发现这样是不行的。在标准C/C++中，最小的计时单位是一毫秒。</p>
<p>3．与日期和时间相关的数据结构</p>
<p>在标准C/C++中，我们可通过tm结构来获得日期和时间，tm结构在time.h中的定义如下：</p>
<p>#ifndef _TM_DEFINED<br>struct tm {<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_sec;&nbsp;&nbsp;&nbsp; /* 秒 &#8211; 取值区间为[0,59] */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_min;&nbsp;&nbsp;&nbsp; /* 分 - 取值区间为[0,59] */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_hour;&nbsp;&nbsp;&nbsp; /* 时 - 取值区间为[0,23] */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_mday;&nbsp;&nbsp;&nbsp; /* 一个月中的日期 - 取值区间为[1,31] */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_mon;&nbsp;&nbsp;&nbsp; /* 月份（从一月开始，0代表一月） - 取值区间为[0,11] */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_year;&nbsp;&nbsp;&nbsp; /* 年份，其值等于实际年份减去1900 */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_wday;&nbsp;&nbsp;&nbsp; /* 星期 &#8211; 取值区间为[0,6]，其中0代表星期天，1代表星期一，以此类推 */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_yday;&nbsp;&nbsp;&nbsp; /* 从每年的1月1日开始的天数 &#8211; 取值区间为[0,365]，其中0代表1月1日，1代表1月2日，以此类推 */<br>&nbsp;&nbsp;&nbsp;&nbsp; int tm_isdst;&nbsp;&nbsp;&nbsp; /* 夏令时标识符，实行夏令时的时候，tm_isdst为正。不实行夏令时的进候，tm_isdst为0；不了解情况时，tm_isdst()为负。*/<br>&nbsp;&nbsp;&nbsp;&nbsp; };<br>#define _TM_DEFINED<br>#endif</p>
<p>ANSI C标准称使用tm结构的这种时间表示为分解时间(broken-down time)。</p>
<p>而日历时间（Calendar Time）是通过time_t数据类型来表示的，用time_t表示的时间（日历时间）是从一个时间点（例如：1970年1月1日0时0分0秒）到此时的秒数。在time.h中，我们也可以看到time_t是一个长整型数：</p>
<p>#ifndef _TIME_T_DEFINED<br>typedef long time_t;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 时间值 */<br>#define _TIME_T_DEFINED&nbsp;&nbsp;&nbsp; /* 避免重复定义 time_t */<br>#endif</p>
<p>大家可能会产生疑问：既然time_t实际上是长整型，到未来的某一天，从一个时间点（一般是1970年1月1日0时0分0秒）到那时的秒数（即日历时间）超出了长整形所能表示的数的范围怎么办？对time_t数据类型的值来说，它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间，一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual C++中采用了__time64_t数据类型来保存日历时间，并通过_time64()函数来获得日历时间（而不是通过使用32位字的time()函数），这样就可以通过该数据类型保存3001年1月1日0时0分0秒（不包括该时间点）之前的时间。</p>
<p>在time.h头文件中，我们还可以看到一些函数，它们都是以time_t为参数类型或返回值类型的函数：</p>
<p>double difftime(time_t time1, time_t time0);<br>time_t mktime(struct tm * timeptr);<br>time_t time(time_t * timer);<br>char * asctime(const struct tm * timeptr);<br>char * ctime(const time_t *timer);</p>
<p>此外，time.h还提供了两种不同的函数将日历时间（一个用time_t表示的整数）转换为我们平时看到的把年月日时分秒分开显示的时间格式tm：</p>
<p>struct tm * gmtime(const time_t *timer);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>struct tm * localtime(const time_t * timer);</p>
<p>通过查阅MSDN，我们可以知道Microsoft C/C++ 7.0中时间点的值（time_t对象的值）是从1899年12月31日0时0分0秒到该时间点所经过的秒数，而其它各种版本的Microsoft C/C++和所有不同版本的Visual C++都是计算的从1970年1月1日0时0分0秒到该时间点所经过的秒数。</p>
<p>4．与日期和时间相关的函数及应用<br>在本节，我将向大家展示怎样利用time.h中声明的函数对时间进行操作。这些操作包括取当前时间、算时间间隔、以不同的形式显示时间等内容。</p>
<p>4.1 获得日历时间</p>
<p>我们可以通过time()函数来获得日历时间（Calendar Time），其原型为：</p>
<p>time_t time(time_t * timer);</p>
<p>如果你已经声明了参数timer，你可以从参数timer返回现在的日历时间，同时也可以通过返回值返回现在的日历时间，即从一个时间点（例如：1970 年1月1日0时0分0秒）到现在此时的秒数。如果参数为空（NULL），函数将只通过返回值返回现在的日历时间，比如下面这个例子用来显示当前的日历时间：</p>
<p>＃i nclude "time.h"<br>＃i nclude "stdio.h"<br>int main(void)<br>{<br>struct tm *ptr;<br>time_t lt;<br>lt =time(NULL);<br>printf("The Calendar Time now is %d\n",lt);<br>return 0;<br>}</p>
<p>运行的结果与当时的时间有关，我当时运行的结果是：</p>
<p>The Calendar Time now is 1122707619</p>
<p>其中1122707619就是我运行程序时的日历时间。即从1970年1月1日0时0分0秒到此时的秒数。</p>
<p>4.2 获得日期和时间</p>
<p>这里说的日期和时间就是我们平时所说的年、月、日、时、分、秒等信息。从第2节我们已经知道这些信息都保存在一个名为tm的结构体中，那么如何将一个日历时间保存为一个tm结构的对象呢？</p>
<p>其中可以使用的函数是gmtime()和localtime()，这两个函数的原型为：</p>
<p>struct tm * gmtime(const time_t *timer);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>struct tm * localtime(const time_t * timer);</p>
<p>其中gmtime()函数是将日历时间转化为世界标准时间（即格林尼治时间），并返回一个tm结构体来保存这个时间，而localtime()函数是将日历时间转化为本地时间。比如现在用gmtime()函数获得的世界标准时间是2005年7月30日7点18分20秒，那么我用localtime()函数在中国地区获得的本地时间会比时间标准时间晚8个小时，即2005年7月30日15点18分20秒。下面是个例子：</p>
<p>＃i nclude "time.h"<br>＃i nclude "stdio.h"<br>int main(void)<br>{<br>struct tm *local;<br>time_t t;<br>t=time(NULL);<br>local=localtime(&amp;t);<br>printf("Local hour is: %d\n",local-&gt;tm_hour);<br>local=gmtime(&amp;t);<br>printf("UTC hour is: %d\n",local-&gt;tm_hour);<br>return 0;<br>}</p>
<p>运行结果是：</p>
<p>Local hour is: 15<br>UTC hour is: 7</p>
<p>4.3 固定的时间格式</p>
<p>我们可以通过asctime()函数和ctime()函数将时间以固定的格式显示出来，两者的返回值都是char*型的字符串。返回的时间格式为：</p>
<p>星期几 月份 日期 时:分:秒 年\n\0<br>例如：Wed Jan 02 02:03:55 1980\n\0</p>
<p>其中\n是一个换行符，\0是一个空字符，表示字符串结束。下面是两个函数的原型：</p>
<p>char * asctime(const struct tm * timeptr);<br>char * ctime(const time_t *timer);</p>
<p>其中asctime()函数是通过tm结构来生成具有固定格式的保存时间信息的字符串，而ctime()是通过日历时间来生成时间字符串。这样的话， asctime（）函数只是把tm结构对象中的各个域填到时间字符串的相应位置就行了，而ctime（）函数需要先参照本地的时间设置，把日历时间转化为本地时间，然后再生成格式化后的字符串。在下面，如果lt是一个非空的time_t变量的话，那么：</p>
<p>printf(ctime(&lt;));</p>
<p>等价于：</p>
<p>struct tm *ptr;<br>ptr=localtime(&lt;);<br>printf(asctime(ptr));</p>
<p>那么，下面这个程序的两条printf语句输出的结果就是不同的了（除非你将本地时区设为世界标准时间所在的时区）：</p>
<p>＃i nclude "time.h"<br>＃i nclude "stdio.h"<br>int main(void)<br>{<br>struct tm *ptr;<br>time_t lt;<br>lt =time(NULL);<br>ptr=gmtime(&lt;);<br>printf(asctime(ptr));<br>printf(ctime(&lt;));<br>return 0;<br>}</p>
<p>运行结果：</p>
<p>Sat Jul 30 08:43:03 2005<br>Sat Jul 30 16:43:03 2005</p>
<p>4.4 自定义时间格式</p>
<p>我们可以使用strftime（）函数将时间格式化为我们想要的格式。它的原型如下：</p>
<p>size_t strftime(<br>&nbsp;&nbsp; char *strDest,<br>&nbsp;&nbsp; size_t maxsize,<br>&nbsp;&nbsp; const char *format,<br>&nbsp;&nbsp; const struct tm *timeptr<br>);</p>
<p>我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字符串中，最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中放置的字符数。</p>
<p>函数strftime()的操作有些类似于sprintf()：识别以百分号(%)开始的格式命令集合，格式化输出结果放在一个字符串中。格式化命令说明串strDest中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面，它们是区分大小写的。</p>
<p>%a 星期几的简写<br>%A 星期几的全称<br>%b 月分的简写<br>%B 月份的全称<br>%c 标准的日期的时间串<br>%C 年份的后两位数字<br>%d 十进制表示的每月的第几天<br>%D 月/天/年<br>%e 在两字符域中，十进制表示的每月的第几天<br>%F 年-月-日<br>%g 年份的后两位数字，使用基于周的年<br>%G 年分，使用基于周的年<br>%h 简写的月份名<br>%H 24小时制的小时<br>%I 12小时制的小时<br>%j 十进制表示的每年的第几天<br>%m 十进制表示的月份<br>%M 十时制表示的分钟数<br>%n 新行符<br>%p 本地的AM或PM的等价显示<br>%r 12小时的时间<br>%R 显示小时和分钟：hh:mm<br>%S 十进制的秒数<br>%t 水平制表符<br>%T 显示时分秒：hh:mm:ss<br>%u 每周的第几天，星期一为第一天 （值从0到6，星期一为0）<br>%U 第年的第几周，把星期日做为第一天（值从0到53）<br>%V 每年的第几周，使用基于周的年<br>%w 十进制表示的星期几（值从0到6，星期天为0）<br>%W 每年的第几周，把星期一做为第一天（值从0到53）<br>%x 标准的日期串<br>%X 标准的时间串<br>%y 不带世纪的十进制年份（值从0到99）<br>%Y 带世纪部分的十制年份<br>%z，%Z 时区名称，如果不能得到时区名称则返回空字符。<br>%% 百分号</p>
<p>如果想显示现在是几点了，并以12小时制显示，就象下面这段程序：</p>
<p>＃i nclude &#8220;time.h&#8221;<br>＃i nclude &#8220;stdio.h&#8221;<br>int main(void)<br>{<br>struct tm *ptr;<br>time_t lt;<br>char str[80];<br>lt=time(NULL);<br>ptr=localtime(&lt;);<br>strftime(str,100,"It is now %I %p",ptr);<br>printf(str);<br>return 0;<br>}</p>
<p>其运行结果为：<br>It is now 4PM</p>
<p>而下面的程序则显示当前的完整日期：</p>
<p>＃i nclude<br>＃i nclude</p>
<p>void main( void )<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; struct tm *newtime;<br>&nbsp;&nbsp;&nbsp;&nbsp; char tmpbuf[128];<br>&nbsp;&nbsp;&nbsp;&nbsp; time_t lt1;<br>&nbsp;&nbsp;&nbsp;&nbsp; time( &lt;1 );<br>&nbsp;&nbsp;&nbsp;&nbsp; newtime=localtime(&lt;1);<br>&nbsp;&nbsp;&nbsp;&nbsp; strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);<br>&nbsp;&nbsp;&nbsp;&nbsp; printf(tmpbuf);<br>}</p>
<p>运行结果：</p>
<p>Today is Saturday, day 30 of July in the year 2005.</p>
<p>4.5 计算持续的时间长度</p>
<p>有时候在实际应用中要计算一个事件持续的时间长度，比如计算打字速度。在第1节计时部分中，我已经用clock函数举了一个例子。Clock()函数可以精确到毫秒级。同时，我们也可以使用difftime()函数，但它只能精确到秒。该函数的定义如下：</p>
<p>double difftime(time_t time1, time_t time0);</p>
<p>虽然该函数返回的以秒计算的时间间隔是double类型的，但这并不说明该时间具有同double一样的精确度，这是由它的参数觉得的（time_t是以秒为单位计算的）。比如下面一段程序：</p>
<p>＃i nclude &#8220;time.h&#8221;<br>＃i nclude &#8220;stdio.h&#8221;<br>＃i nclude &#8220;stdlib.h&#8221;<br>int main(void)<br>{<br>time_t start,end;<br>start = time(NULL);<br>system("pause");<br>end = time(NULL);<br>printf("The pause used %f seconds.\n",difftime(end,start));//&lt;-<br>system("pause");<br>return 0;<br>}</p>
<p>运行结果为：<br>请按任意键继续. . .<br>The pause used 2.000000 seconds.<br>请按任意键继续. . .</p>
<p>可以想像，暂停的时间并不那么巧是整整2秒钟。其实，你将上面程序的带有&#8220;//&lt;-&#8221;注释的一行用下面的一行代码替换：</p>
<p>printf("The pause used %f seconds.\n",end-start);</p>
<p>其运行结果是一样的。</p>
<p>4.6 分解时间转化为日历时间</p>
<p>这里说的分解时间就是以年、月、日、时、分、秒等分量保存的时间结构，在C/C++中是tm结构。我们可以使用mktime（）函数将用tm结构表示的时间转化为日历时间。其函数原型如下：</p>
<p>time_t mktime(struct tm * timeptr);</p>
<p>其返回值就是转化后的日历时间。这样我们就可以先制定一个分解时间，然后对这个时间进行操作了，下面的例子可以计算出1997年7月1日是星期几：</p>
<p>＃i nclude "time.h"<br>＃i nclude "stdio.h"<br>＃i nclude "stdlib.h"<br>int main(void)<br>{<br>struct tm t;<br>time_t t_of_day;<br>t.tm_year=1997-1900;<br>t.tm_mon=6;<br>t.tm_mday=1;<br>t.tm_hour=0;<br>t.tm_min=0;<br>t.tm_sec=1;<br>t.tm_isdst=0;<br>t_of_day=mktime(&amp;t);<br>printf(ctime(&amp;t_of_day));<br>return 0;<br>}</p>
<p>运行结果：</p>
<p>Tue Jul 01 00:00:01 1997</p>
<p>现在注意了，有了mktime()函数，是不是我们可以操作现在之前的任何时间呢？你可以通过这种办法算出1945年8月15号是星期几吗？答案是否定的。因为这个时间在1970年1月1日之前，所以在大多数编译器中，这样的程序虽然可以编译通过，但运行时会异常终止。</p>
<img src ="http://www.cppblog.com/totti1006/aggbug/105663.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-01-14 16:33 <a href="http://www.cppblog.com/totti1006/archive/2010/01/14/105663.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于VC++中预编译头文件</title><link>http://www.cppblog.com/totti1006/archive/2010/01/13/105587.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Wed, 13 Jan 2010 11:27:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2010/01/13/105587.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/105587.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2010/01/13/105587.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/105587.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/105587.html</trackback:ping><description><![CDATA[<p>所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是 </p>
<p>以.pch为扩展名的)，这个文件就称为预编译头文件这些预先编译好的代码可以是任何的 </p>
<p>C/C++代码--------甚至是inline的函数，但是必须是稳定的，在工程开发的过程中不会 </p>
<p>被经常改变。如果这些代码被修改，则需要重新编译生成预编译头文件。注意生成预编 </p>
<p>译头文件是很耗时间的。同时你得注意预编译头文件通常很大，通常有6-7M大。注意及 </p>
<p>时清理那些没有用的预编译头文件。 </p>
<p>也许你会问：现在的编译器都有Time stamp的功能，编译器在编译整个工程的时候，它 </p>
<p>只会编译那些经过修改的文件，而不会去编译那些从上次编译过，到现在没有被修改过 </p>
<p>的文件。那么为什么还要预编译头文件呢？答案在这里，我们知道编译器是以文件为单 </p>
<p>位编译的，一个文件经过修改后，会重新编译整个文件，当然在这个文件里包含的所有 </p>
<p>头文件中的东西（.eg Macro, Preprocesser ）都要重新处理一遍。VC的预编译头文件 </p>
<p>保存的正是这部分信息。以避免每次都要重新处理这些头文件。 </p>
<p>预编译头的作用： </p>
<p>根据上文介绍，预编译头文件的作用当然就是提高便宜速度了，有了它你没有必要每次 </p>
<p>都编译那些不需要经常改变的代码。编译性能当然就提高了。 </p>
<p>预编译头的使用： </p>
<p>要使用预编译头，我们必须指定一个头文件，这个头文件包含我们不会经常改变的 </p>
<p>代码和其他的头文件，然后我们用这个头文件来生成一个预编译头文件（.pch文件） </p>
<p>想必大家都知道 StdAfx.h这个文件。很多人都认为这是VC提供的一个&#8220;系统级别&#8221;的 </p>
<p>，编译器带的一个头文件。其实不是的，这个文件可以是任何名字的。我们来考察一个 </p>
<p>典型的由AppWizard生成的MFC Dialog Based　程序的预编译头文件。（因为AppWizard </p>
<p>会为我们指定好如何使用预编译头文件，默认的是StdAfx.h，这是VC起的名字）。我们 </p>
<p>会发现这个头文件里包含了以下的头文件： </p>
<p>#include &lt;afxwin.h&gt; // MFC core and standard components </p>
<p>#include &lt;afxext.h&gt; // MFC extensions </p>
<p>#include &lt;afxdisp.h&gt; // MFC Automation classes </p>
<p>#include &lt;afxdtctl.h&gt; // MFC support for Internet Explorer 4 </p>
<p>Common Controls </p>
<p>#include &lt;afxcmn.h&gt; </p>
<p>这些正是使用MFC的必须包含的头文件，当然我们不太可能在我们的工程中修改这些头文 </p>
<p>件的，所以说他们是稳定的。 </p>
<p>那么我们如何指定它来生成预编译头文件。我们知道一个头文件是不能编译的。所以我 </p>
<p>们还需要一个cpp文件来生成.pch 文件。这个文件默认的就是StdAfx.cpp。在这个文件 </p>
<p>里只有一句代码就是：#include &#8220;Stdafx.h&#8221;。原因是理所当然的，我们仅仅是要它能 </p>
<p>够编译而已?D?D?D也就是说，要的只是它的.cpp的扩展名。我们可以用/Yc编译开关来指 </p>
<p>定StdAfx.cpp来生成一个.pch文件，通过/Fp编译开关来指定生成的pch文件的名字。打 </p>
<p>开project -&gt;Setting-&gt;C/C++ 对话框。把Category指向Precompiled Header。在左边的 </p>
<p>树形视图里选择整个工程　 </p>
<p>Project Options(右下角的那个白的地方)可以看到 /Fp &#8220;debug/PCH.pch&#8221;，这就是指 </p>
<p>定生成的.pch文件的名字，默认的通常是　&lt;工程名&gt;.pch（我的示例工程名就是PCH）。 </p>
<p>然后，在左边的树形视图里选择StdAfx.cpp.//这时只能选一个cpp文件！ </p>
<p>这时原来的Project Option变成了 Source File Option（原来是工程，现在是一个文件 </p>
<p>，当然变了）。在这里我们可以看到 /Yc开关，/Yc的作用就是指定这个文件来创建一个 </p>
<p>Pch文件。/Yc后面的文件名是那个包含了稳定代码的头文件，一个工程里只能有一个文 </p>
<p>件的可以有YC开关。VC就根据这个选项把 StdAfx.cpp编译成一个Obj文件和一个PCH文件 </p>
<p>。 </p>
<p>然后我们再选择一个其它的文件来看看，//其他cpp文件 </p>
<p>在这里，Precomplier 选择了 Use ⋯⋯⋯一项，头文件是我们指定创建PCH 文件的stda </p>
<p>fx.h </p>
<p>文件。事实上，这里是使用工程里的设置，（如图1）/Yu&#8221;stdafx.h&#8221;。 </p>
<p>这样，我们就设置好了预编译头文件。也就是说，我们可以使用预编译头功能了。以 </p>
<p>下是注意事项： </p>
<p>1):如果使用了/Yu，就是说使用了预编译，我们在每个.cpp文件的最开头，我强调一遍 </p>
<p>是最开头，包含 你指定产生pch文件的.h文件（默认是stdafx.h）不然就会有问题。如 </p>
<p>果你没有包含这个文件，就告诉你Unexpected file end. 如果你不是在最开头包含的， </p>
<p>你自己试以下就知道了，绝对有很惊人的效果⋯.. </p>
<p>fatal error C1010: unexpected end of file while looking for precompiled </p>
<p>header directive </p>
<p>Generating Code... </p>
<p>2）如果你把pch文件不小心丢了，编译的时候就会产生很多的不正常的行为。根据以上 </p>
<p>的分析，你只要让编译器生成一个pch文件。也就是说把 stdafx.cpp（即指定/Yc的那个 </p>
<p>cpp文件）从新编译一遍。当然你可以傻傻的 Rebuild All。简单一点就是选择那个cpp </p>
<p>文件，按一下Ctrl + F7就可以了。不然可是很浪费时间的哦。</p>
<img src ="http://www.cppblog.com/totti1006/aggbug/105587.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2010-01-13 19:27 <a href="http://www.cppblog.com/totti1006/archive/2010/01/13/105587.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++的64位整型</title><link>http://www.cppblog.com/totti1006/archive/2009/12/24/103944.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Thu, 24 Dec 2009 07:44:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2009/12/24/103944.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/103944.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2009/12/24/103944.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/103944.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/103944.html</trackback:ping><description><![CDATA[<p>在C/C++中，64为整型一直是一种没有确定规范的数据类型。现今主流的编译器中，对64为整型的支持也是标准不一，形态各异。一般来说，64位整型的定义方式有long long和__int64两种(VC还支持_int64)，而输出到标准输出方式有printf(&#8220;%lld&#8221;,a)，printf(&#8220;%I64d&#8221;,a)，和cout &lt;&lt; a三种方式。</p>
<p>本文讨论的是五种常用的C/C++编译器对64位整型的支持，这五种编译器分别是gcc(mingw32)，g++(mingw32)，gcc(linux i386)，g++(linux i386)，Microsoft Visual C++ 6.0。可惜的是，<strong>没有一种定义和输出方式组合，同时兼容这五种编译器</strong>。为彻底弄清不同编译器对64位整型，我写了程序对它们进行了评测，结果如下表。</p>
<table border=1 sizset="3" sizcache="2">
    <tbody sizset="3" sizcache="2">
        <tr class=alt jQuery1261639340203="126">
            <th scope=col>变量定义</th>
            <th scope=col>输出方式</th>
            <th scope=col>gcc(mingw32)</th>
            <th scope=col>g++(mingw32)</th>
            <th scope=col>gcc(linux i386)</th>
            <th scope=col>g++(linux i386)</th>
            <th scope=col>MicrosoftVisual C++ 6.0</th>
        </tr>
        <tr class="" jQuery1261639340203="127">
            <td>long long</td>
            <td>&#8220;%lld&#8221;</td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
        </tr>
        <tr class=alt jQuery1261639340203="128">
            <td>long long</td>
            <td>&#8220;%I64d&#8221;</td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
        </tr>
        <tr class="" jQuery1261639340203="129">
            <td>__int64</td>
            <td>&#8220;lld&#8221;</td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
            <td><span style="COLOR: #ff0000" twffan="done">错误</span></td>
        </tr>
        <tr class=alt jQuery1261639340203="130">
            <td>__int64</td>
            <td>&#8220;%I64d&#8221;</td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
        </tr>
        <tr class="" jQuery1261639340203="131">
            <td>long long</td>
            <td>cout</td>
            <td><span style="COLOR: #800080" twffan="done">非C++</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #800080" twffan="done">非C++</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
        </tr>
        <tr class=alt jQuery1261639340203="132">
            <td>__int64</td>
            <td>cout</td>
            <td><span style="COLOR: #800080" twffan="done">非C++</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #800080" twffan="done">非C++</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
        </tr>
        <tr class="" jQuery1261639340203="133">
            <td>long long</td>
            <td>printint64()</td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #008000" twffan="done">正确</span></td>
            <td><span style="COLOR: #800080" twffan="done">无法编译</span></td>
        </tr>
    </tbody>
</table>
<p>上表中，<span style="COLOR: #008000" twffan="done">正确</span>指编译通过，运行完全正确；<span style="COLOR: #ff0000" twffan="done">错误</span>指编译虽然通过，但运行结果有误；<span style="COLOR: #800080" twffan="done">无法编译</span>指编译器根本不能编译完成。观察上表，我们可以发现以下几点：</p>
<ol sizset="71" sizcache="1">
    <li>long long定义方式可以用于gcc/g++，不受平台限制，但不能用于VC6.0。
    <li>__int64是Win32平台编译器64位长整型的定义方式，不能用于Linux。
    <li>&#8220;%lld&#8221;用于Linux i386平台编译器，&#8221;%I64d&#8221;用于Win32平台编译器。
    <li>cout只能用于C++编译，在VC6.0中，cout不支持64位长整型。 </li>
</ol>
<p>表中最后一行输出方式中的printint64()是我自己写的一个函数，可以看出，它的兼容性要好于其他所有的输出方式，它是一段这样的代码：</p>
<div class=wp_codebox_msgheader twffan="done" jQuery1261639340203="2"><span class=right twffan="done"><sup><a title="WP-CodeBox HowTo?" href="http://www.ericbess.com/ericblog/2008/03/03/wp-codebox/#examples" target=_blank><span style="COLOR: #99cc00" twffan="done"><strong>?</strong></span></a></sup><a onclick="copycode('p1244code1');" href="javascript:;"><strong><font color=#666666>[Copy to clipboard]</font></strong></a></span><span class=left twffan="done"><a onclick="javascript:showCodeTxt('p1244code1'); return false;" href="javascript:;"><strong><font color=#666666>View Code</font></strong></a> CPP</span>
<div class=codebox_clear twffan="done"></div>
</div>
<div class=wp_codebox twffan="done" sizset="11" sizcache="3">
<table sizset="11" sizcache="2">
    <tbody sizset="11" sizcache="2">
        <tr class=alt id=p12441 jQuery1261639340203="134">
            <td class=code id=p1244code1>
            <pre class=cpp style="FONT-FAMILY: monospace"><span style="COLOR: #0000ff" twffan="done">void</span> printint64<span style="COLOR: #008000" twffan="done">(</span><span style="COLOR: #0000ff" twffan="done">long</span> <span style="COLOR: #0000ff" twffan="done">long</span> a<span style="COLOR: #008000" twffan="done">)</span>
            <span style="COLOR: #008000" twffan="done">{</span>
            <span style="COLOR: #0000ff" twffan="done">if</span> <span style="COLOR: #008000" twffan="done">(</span>a<span style="COLOR: #000080" twffan="done">&lt;=</span>100000000<span style="COLOR: #008000" twffan="done">)</span>
            <span style="COLOR: #0000dd" twffan="done">printf</span><span style="COLOR: #008000" twffan="done">(</span><span style="COLOR: #ff0000" twffan="done">"%d<span style="FONT-WEIGHT: bold; COLOR: #000099" twffan="done">\n</span>"</span>,a<span style="COLOR: #008000" twffan="done">)</span><span style="COLOR: #008080" twffan="done">;</span>
            <span style="COLOR: #0000ff" twffan="done">else</span>
            <span style="COLOR: #008000" twffan="done">{</span>
            <span style="COLOR: #0000dd" twffan="done">printf</span><span style="COLOR: #008000" twffan="done">(</span><span style="COLOR: #ff0000" twffan="done">"%d"</span>,a<span style="COLOR: #000040" twffan="done">/</span>100000000<span style="COLOR: #008000" twffan="done">)</span><span style="COLOR: #008080" twffan="done">;</span>
            <span style="COLOR: #0000dd" twffan="done">printf</span><span style="COLOR: #008000" twffan="done">(</span><span style="COLOR: #ff0000" twffan="done">"%08d<span style="FONT-WEIGHT: bold; COLOR: #000099" twffan="done">\n</span>"</span>,a<span style="COLOR: #000040" twffan="done">%</span>100000000<span style="COLOR: #008000" twffan="done">)</span><span style="COLOR: #008080" twffan="done">;</span>
            <span style="COLOR: #008000" twffan="done">}</span>
            <span style="COLOR: #008000" twffan="done">}</span></pre>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p>这种写法的本质是把较大的64位整型拆分为两个32位整型，然后依次输出，低位的部分要补0。看似很笨的写法，效果如何？我把它和cout输出方式做了比较，因为它和cout都是C++支持跨平台的。首先printint64()和cout<span style="TEXT-DECORATION: underline" twffan="done">(不清空缓冲区)</span>的运行结果是完全相同的，不会出现错误。我的试验是分别用两者输出1000000个随机数，实际结果是，printint64()在1.5s内跑完了程序，而cout需要2s。cout要稍慢一些，所以在输出大量数据时，要尽量避免使用。</p>
<img src ="http://www.cppblog.com/totti1006/aggbug/103944.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2009-12-24 15:44 <a href="http://www.cppblog.com/totti1006/archive/2009/12/24/103944.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MMC不能打开文件C:\Program Files\Microsoft SQL Server\80\Tools\Binn\SQL Server Enterprise Manager.MSC 可能是由于文件不存在，不是一个MMC控制台，或者用后来的MMC版本创建。也可能你没有访问此文件的足够权限 解决方法（转载） </title><link>http://www.cppblog.com/totti1006/archive/2009/11/30/102260.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Mon, 30 Nov 2009 01:39:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2009/11/30/102260.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/102260.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2009/11/30/102260.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/102260.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/102260.html</trackback:ping><description><![CDATA[早上开机，打开企业管理器弹出如下错误提示
<p><img height=123 alt="" src="http://images.cnblogs.com/cnblogs_com/noble_herb/error.jpg" width=747 align=left border=0 twffan="done"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>在网上找到了解决方法：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<div class=postText twffan="done">&nbsp;&nbsp;1、 打开运行对话框输入：mmc &nbsp; </div>
<div class=postText twffan="done"><br>&nbsp; 2、控制台--添加/删除管理单元--添加--找到MicrosoftSQL企业管理器--添加--关闭--确定&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp; 3、控制台--选项--控制台模式选择"用户模式完全访问"--将下面的选择全部取消&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp; 4、控制台--另存为--存储为:C:\ProgramFiles\MicrosoftSQLServer\80\Tools\BINN\SQLServerEnterpriseManager.MSC&nbsp;&nbsp; </div>
<div class=postText twffan="done">&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class=postText twffan="done">&nbsp;&nbsp;&nbsp;好了，控制台恢复成功！</div>
<img src ="http://www.cppblog.com/totti1006/aggbug/102260.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2009-11-30 09:39 <a href="http://www.cppblog.com/totti1006/archive/2009/11/30/102260.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>控件句柄和控件ID的区别</title><link>http://www.cppblog.com/totti1006/archive/2009/09/17/96499.html</link><dc:creator>天之骄子</dc:creator><author>天之骄子</author><pubDate>Thu, 17 Sep 2009 02:37:00 GMT</pubDate><guid>http://www.cppblog.com/totti1006/archive/2009/09/17/96499.html</guid><wfw:comment>http://www.cppblog.com/totti1006/comments/96499.html</wfw:comment><comments>http://www.cppblog.com/totti1006/archive/2009/09/17/96499.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/totti1006/comments/commentRss/96499.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/totti1006/services/trackbacks/96499.html</trackback:ping><description><![CDATA[打个比喻：<br>你的家庭现在就好比是一个程序（窗体也是程序的一部分，实际窗体、控件都是由命令语句动态构建的，只是省了你自己写代码而已），控件ID就好像你的名字，一般一个家庭（程序）里的众多成员（控件）之间是不会重名的，每个人在自己家（程序）里的名字（控件ID）都是唯一的，当你的家人（程序代码）需要找你，叫你名字的时候就直接称呼你的名字（控件ID）。<br>但是全国无数个家庭，就可能会有重名了吧，于是政府（系统）就不管你的名字（控件ID）是什么，直接分配给每个人（控件）一个身份证号码（句柄），公安局（其他程序）来找你（一个控件）的时候就使用你的身份证号（句柄）来保证在全国（内存）范围里也不会把你弄错。<br>每个家庭组成以后（运行了一个新程序），生下了小宝宝（构建新的控件）就会得到一个全国（内存）唯一的一个身份证号（句柄），以保证在国内（内存）的唯一性。当一个家庭的成员寿终正寝（控件销毁）的时候，身份证号码（句柄）同时被作废（系统收回，以后分配给新产生的控件）。<br>
<img src ="http://www.cppblog.com/totti1006/aggbug/96499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/totti1006/" target="_blank">天之骄子</a> 2009-09-17 10:37 <a href="http://www.cppblog.com/totti1006/archive/2009/09/17/96499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>