专栏导航
下一篇:无
本节前言
对于本节所讲解的知识,有可能,你会需要时不时地参考本专栏的其它文章。真的遇到了需要参考之前的文章的知识点,请你自行查阅。
我呢,也会提到一部分的参考课节。但是呢,你不应该依赖于我的主动提及。最好呢,你自己能够多去了解和查看本专栏目录。
本节内容,来自对佩措尔德教材内容的大段抄录。
我们开始。
一. 本节正文
Windows 运行时,系统字体不会变化。因此,应用程序只需要调用一次 GetTextMetrics 函数。最好的时机,是在窗口过程处理 WM_CREATE 消息时。 WM_CREATE 消息是窗口过程收到的第一条消息。当应用程序在 WinMain 中调用 CreateWindow 时,Windows 将发送一条 WM_CREATE 消息给窗口过程。
假设你的 Windows 程序需要在客户区内显示几行文本,你需要获取字符的宽度和高度。在窗口过程中,可以定义两个变量来保存字符的平均宽度和总高度。
static int cxChar, cyChar;
变量名的前缀 c 代表 "count",也就是 "个数" ,在这里代表像素数。和 x 与 y 结合,分别表示宽度和高度。它们被定义为静态变量,因为它们必须在 Windows 处理其它消息(比如 WM_PRINT)时还有效。它们也可以定义为全局变量,也就是定义在所有函数之外。
忘了 static 语法的,我们可以来略微复习一下。请看下面的函数。
func_01(int * py)
{
int x = 3;
x++;
*py = 3 * x + 1;
}
这段代码,其实还是不难的。假定,我们在主函数中调用了 func_01 函数三次,则两次调用的结果是一样的,每一次调用结束以后,*py 的值都是 13 。
然后呢,我们将上面的代码修改一下,修改为下面的样子。
func_01(int * py)
{
static int x = 3;
x++;
*py = 3 * x + 1;
}
修改之后的代码,相比之前的版本,我们只是将 x 的声明加上了 static 关键字。可是,加上了关键字之后,执行机制就会有所不同了。
加上了 static 之后,变量 x 成了一个局部静态变量。它在程序运行的整个周期里面都有效,但只能在 func_01 内部引用它。
我们依然假定,我们在主函数中调用 func_01 函数三次。
第一次调用之后,*py 的值为 13 。第二次调用之后,*py 的值为 16 。第三次调用之后,*py 的值为 19 。
这里,我简单地领大家复习了一下,在函数体中,用 static 关键字来静态声明一个局部变量的用法。我相信,你能够理解我的意思,尽管我的概念表述不精确。
我们接着讲。
以下的 WM_CREATE 代码显示了怎样获取系统字体的宽度和高度。
case WM_CREATE:
hdc = GetDC (hwnd);
GetTextMetrics (hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC (hwnd, hdc);
return 0;
注意,这里在 cyChar 中包括了 tmExternalLeading 。尽管在我前面用到的系统字体中,这一项通常是 0,但考虑到它可能不是 0,所以还是要包括它,因为这会使行与行之间更加清晰易读。在窗口中,每一行文本显示在上一行文本下方 cyChar 个像素处。
通常需要显示的是格式化的数字和字符串。如第 2 章所述,不能用传统的工具(例如 print 函数),但是可以使用 Windows 版的字符处理函数 ------ wsprintf 。这个函数与 printf 函数功能类似,区别在于格式化之后的字符保存在一个字符串里。这个字符串可以通过 TextOut 函数输出到窗口中。方便的是,wsprintf 函数的返回值是字符串中包含的字符的个数。
注意哦,是字符个数,而不是字符串所包含的字节数。而且,这一个数,并不包含字符串结尾的 NULL 字符。
可以将 wsprintf 的返回值传递给 TextOut 函数,作为 iLength 参数。下面的代码是典型的 wsprintf 和 TextOut 的组合。
int iLength;
TCHAR szBuffer[40];
[其他程序行]
iLength = wsprintf (szBuffer, TEXT("the sum of %d and %d is %d"),
iA, iB, iA + iB);
TextOut (hdc, x, y, szBuffer, iLength);
对于上述代码,wsprintf 的返回值会由 iLength 来接收,而 iLength 又会作为 TextOut 的参数,因此,我们可以省略 iLength 变量的定义,而将 TextOut 函数写作下面的模样。
iLength = ;
TextOut (hdc, x, y, szBuffer,
wsprintf (szBuffer, TEXT("the sum of %d and %d is %d"),
iA, iB, iA + iB));
这么写,看上去并不直观,而功能是一样的。
我个人更喜欢分着写的方式,因为这样子写,逻辑上更为清楚。合起来写的方式,虽说代码简略了一点,但是,读起来也会更为费力一些。
我个人,还是更喜欢让代码容易阅读一些。不过,你还是需要能够理解这样的代码。
在调用一个函数的时候,首先呢,会将此被调函数的各个实参压入栈中,参数入栈的顺序,是从右到左。也就是,在参数入栈之时,会先将右侧的参数压入栈中,然后再将左边的参数压入栈中。全部参数入栈以后,才是调用此被掉函数,进入被调函数的函数体之中。
对于上面的代码块的代码,本来应该是从右到左,依次将参数给压入栈中的。然而,当想要将最右边的参数压入栈中的时候,却发现,最右边的参数部分,不是一个已确定的数值,而是一个函数调用,此函数调用尚未有返回值。因此,计算机将先去执行 wsprintf 函数,从 wsprintf 函数返回之后,返回值就有了。有了返回值以后,计算机会将 wsprintf 的返回值压入栈中,然后依次将 szBuffer,y,x 和 hdc 压入栈中,最后才是去执行 TextOut 函数。
我这样子的表述,不知道,你能否理解。多看几遍吧。此处,务必理解。
这算是一个小的攻坚战,硬骨头,务必将其啃下来。
在我的专栏里面,如果呢,我所采用的代码,是佩措尔德的代码,那么,佩措尔德使用的合写的、逻辑复杂一些的代码,那么,我也就这么来写,不会对这种合写的风格作出修改。
但是呢,如果是说,我自己编写某一个程序的话,那么,我尽量采用分着写,容易理解的风格,来书写代码。
你呢,其实最好还是说,你能够把这种复杂的、合写的、不太好理解的代码风格,给啃下来。但是呢,当前阶段,我还是希望,尽量地,分解难度,希望尽量地,能够让你学得会,看得懂。所以呢,我自己写的代码,我会尽量地采用容易理解的、分着写的写法。
如果你只能看懂分着写的、容易理解的代码,而看不懂合写的、不好理解的代码,那么,你还是需要多锻炼。编程学习的路上,难免会有人喜欢用复杂风格的代码。自己的能力上来了,难的,我们可以应付,简单的,也能够应付,那不是很好吗?
总 结
本节的主体内容,是对佩措尔德教材的大段抄录。我自己呢,还是在写教程的时候,加上了我自己的一点讲解,以便让你好理解一些。
以后,有机会,我应该还是会尽量地去修改内容,以便,我以我的讲法,分解难度,让学员更容易学。
专栏导航
下一篇:无