代码改变世界

C语言Windows程序设计->第三天->变参与Windows版printf

2012-10-08 12:44  wid  阅读(7035)  评论(9编辑  收藏  举报

在Windows中使用printf

==============

 

一、可变参函数

  在Windows程序设计中, 由于Windows不存在标准输入输出的概念, 这就意味这以前我们学习的printf函数将不再适用, 我们知道, C语言标准输入输出函数printf一个常用的功能就是输出格式化后的文字, 例如:

 

int iAge = 18 ;

printf( "Hello, I am %d years old.\n", iAge );

 

是的, printf函数的使用确实十分方便, 今天上午的学习我们要做的就是完成一个Windows版的printf。

 

 

关于sprintf:

  看一个C语言的函数, sprintf函数, 这个函数在stdio.h头文件中有声明, sprintf函数的主要功能是把格式化的数据写入某个字符串中, 函数的原型如下:

 

int sprintf( char *buffer, const char *format, [ argument] … );

 

sprintf的第一个参数为字符缓冲区, 后面的参数就像printf一样, 是一个格式化字符串, 函数的返回值为缓冲区buffer内的有效字符串长度, 我们尝试使用一下:

 

#include <stdio.h>

int main()
{
    char szBuffer[50] ;   //定义缓冲区大小为50字节
    sprintf( szBuffer, "today: %d-%d-%d", 2012, 10, 8 ) ;   //将今天的日期格式化输出到缓冲区
    puts( szBuffer ) ;   //输出缓冲区szBuffer中的字符

    return 0 ;
}

 

先在我们已经能看到Windows版的printf的影子了, 在Windows版的printf中, 我们可以使用MessageBox函数来替代C语言的标准输入输出函数puts, 但是在这之前我们还有一个重要的问题要解决。

 

sprintf的安全版

  当我们使用sprintf时, 我们需要首先定义一个缓冲区, 这个缓冲区我们必须定义的足够大以至于能容纳我们格式化输出的字符串, 如果超过了这个长度, 程序将会出错, 这样是不安全的, 这里, 使用_snprintf()函数来解决这个问题, _snprintf的原型为:

int _snprintf(char *szBuffer, size_t size,  const char *format, ...); 

_snprintf相对于sprintf函数多出了个参数, size_t size, 这个参数的功能是:

  当格式化后的字符串长度 < size,则将此字符串全部复制到szBuffer中,并给其后添加一个字符串结束符('\0');当格式化后的字符串长度 => size,则仅复制( size - 1 )个字符到缓冲区szBuffer内, 并在其后添加一个字符串结束符('\0') 。

 

我们自己的可变参函数

sprintf有个变形函数, 叫vsprintf, 首先看下这个函数的原型:

int vsprintf(char *szBuffer, const char *format, va_list param);

vsprintf的前两个参数与sprintf相同, 第三个参数为一个va_list宏, va_list宏就是解决C语言变参问题的。

 

va_list宏的用法:

我们将用到valist的以下几个成员:

va_list、va_start、va_end;

在些宏在STDARG.H头文件中有定义打开STDARG.H可以看到其中有句:

typedef char *  va_list;

从这句我们可以看出va_list其实就是char*型类型, 一个字符型指针;

 

再来看看va_start宏:

va_start(va_list ap, char*format);

 format为函数的参数, 可以用来指明参数的类型

可以看出, ap为va_list类型, 也就是可变参数列表; 通过va_start后, 就能使得变参列表与format中的类型一一对应起来;

 

va_end的作用是销毁释放一个va_list型的参数列表, 例如:

va_list ap
va_end( ap );

下面我们就可以实现我们自己的变参了代码如下:

 

#include <stdio.h>
#include <stdarg.h>

int mySprintf( char *szBuffer, const char *szFormat, ... )
{
    int iLength ;             //存储字符串的长度信息
    va_list pArgs ;              //声明一个va_list型变量

    va_start( pArgs, szFormat ) ;  //让pArgs指向变参
    iLength = vsprintf( szBuffer, szFormat, pArgs ) ;  //输出到缓冲区szBuffer中
    va_end(pArgs);              //释放pArgs

    return iLength ;             //返回字符串长度
}

int main()
{
    int i;
    char szBuffer[128] ;

    i = mySprintf( szBuffer, "today: %d-%d-%d", 2012, 10, 8 ) ;
    puts( szBuffer ) ;

    return 0 ;
}            

 

运行后的结果和sprintf是相同的, 说明我们已经实现了自己的可变参函数。

 

二、实现Windows版的printf

在上面的介绍中, 我们所使用的字符串处理函数都是针对于char型的, 下面我们将这些转换为wchar_t型来实现Windwos的格式化消息框。

 

vsprintf和sprintf一样, 也有其相对应的安全版(最大长度版), 为_vsnprintf, 为了程序的安全性, 这里我们使用vsprintf的安全版, _vsnprintf。

 

myMessageBox的实现代码如下:

 

#include <stdio.h>
#include <windows.h>

int CDECL myMessageBox( TCHAR * szCaption, size_t iStyle, TCHAR * szFormat, ... )

{  //myMessageBox函数参数: 标题, 样式, 格式化输出内容
  //CDECL为调用规则, 在WINDEF.H定义为: #define CDECL _cdecl
TCHAR szBuffer [1024] ; va_list pArgs ; va_start (pArgs, szFormat) ; _vsnprintf( szBuffer, sizeof(szBuffer) / sizeof (TCHAR), szFormat, pArgs ) ;   //sizeof(szBuffer) / sizeof (TCHAR)得到最大能容下的字符个数 va_end (pArgs) ;
return MessageBox(NULL, szBuffer, szCaption, iStyle) ; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { int cxScreen, cyScreen ;
cxScreen
= GetSystemMetrics (SM_CXSCREEN) ; //获取显示器x方向像素 cyScreen = GetSystemMetrics (SM_CYSCREEN) ; //获取显示器y方向像素 myMessageBox( TEXT ("显示器分辨率"), MB_OKCANCEL, TEXT ("显示器当前分辨率为:%dx%d。"), TEXT(cxScreen), TEXT(cyScreen) ) ; return 0 ; }

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