但有个坏消息:在Windows程序中不能使用printf。虽然Windows程序中可以使用大多数C的执行时期函数库。实际上,许多程式写作者更愿意使用C的内存管理和I/O函数而不是Windows中等效的函数-Windows对标准输入和标准输出没有概念。在Windows程序中可使用fprintf,而不是printf。
还有一个好消息,那就是仍然可以使用sprintf 及sprintf 系列中的其他函数来显示文字。这些函数除了将内容格式化输出到第一个参数所提供的字符串缓冲区以外,其功能与printf 相同。然后便可对该字符串进行操作(例如将其传给MessageBox)。
如果您从未使用过sprintf (我第一次开始写Windows程序时也没用过此函数),这里有一个简短的执行实体。
printf 函数说明如下:
int printf (const char * szFormat, ...);
第一个参数是一个格式化字符串,后面是与格式化字符串中的代码相对应的不同类型多个参数。
sprintf 函数定义如下:
int sprintf (char * szBuffer, const char * szFormat, ...);
第一个参数是字符串缓冲区,后面是一个格式字串。sprintf不是将格式化结果标准输出,而是将其存入szBuffer。该函数返回该字符串的长度。
在命令行程序中,
printf ("The sum of %i and %i is %i", 5, 3, 5+3) ;
的功能相同于
char szBuffer [100] ;
sprintf (szBuffer, "The sum of %i and %i is %i", 5, 3, 5+3) ;
puts (szBuffer) ;
在Windows中,使用MessageBox显示结果优于puts。
snprintf
int snprintf(char *restrict buf, size_t n, const char * restrict format, ...);
几乎每个人都经历过,当格式化字符串与被格式化的变数不合时,可能使printf 执行错误并可能造成程序当掉。使用sprintf 时,您不但要担心这些,而且还有一个新的负担:您定义的字符串缓冲区必须足够大以存放结果。Microsoft 专用函数_snprintf 解决了这一问题,此函数引进了另一个参数,表示以字节计算的缓冲区大小
vprintf 函数定义如下:
int vprintf ( const char * format, va_list arg );
vprintf是printf的一个变形,与printf函数实现功能相同,实现标准输出。唯一区别在于,参数只有两个。可以用来封装我们自己的printf函数实现。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void my_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
return;
}
int main(void)
{
my_printf(">>>This is an test program :%s<<<\n", __func__);
return 0;
}
vsprintf 函数定义如下:
int vsprintf(char *string, char *format, va_list param);
vsprintf 是sprintf 的一个变形,它只有三个参数。vsprintf 用于执行有不定数量参数的函数,类似printf 格式。vsprintf的前两个参数与sprintf相同:一个用于保存结果的字符串缓冲区和一个格式化字符串。第三个参数是指向格式化参数队列的指针。实际上,该指针指向在堆栈中供函数调用的变量。va_list、va_start和va_end宏(在STDARG.H中定义)帮助我们处理堆栈指针。本章最后的SCRNSIZE程式展示了使用这些宏的方法。使用vsprintf 函数,sprintf 函数可以这样编写:
int sprintf (char * szBuffer, const char * szFormat, ...)
{
int iReturn ;
va_list pArgs ;
va_start (pArgs, szFormat) ;
iReturn = vsprintf (szBuffer, szFormat, pArgs) ;
va_end (pArgs) ;
return iReturn ;
}
vsnprintf
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
函数说明:最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n 的话,将不会溢出。
函数返回值:若成功则返回欲写入的字符串长度,若出错则返回负值。
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *my_printf(const char *fmt, ...)
{
int n;
int size = 100; /* Guess we need no more than 100 bytes. */
char *p, *np;
va_list ap;
if ((p = malloc(size)) == NULL)
return NULL;
while (1) {
/* Try to print in the allocated space. */
va_start(ap, fmt);
n = vsnprintf(p, size, fmt, ap);
vprintf(fmt, ap);
va_end(ap);
/* If that worked, return the string. */
if (n > -1 && n < size)
return p;
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n+1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
if ((np = realloc (p, size)) == NULL) {
free(p);
return NULL;
} else {
p = np;
}
}
}
int main(void)
{
char *p = NULL;
p = make_message("This is a test program: %s\n", __func__);
return 0;
}
va_start宏将pArg设置为指向一个堆栈变量,该变量位址在堆栈参数szFormat的上面。
由於许多Windows早期程序使用了sprintf 和vsprintf,最终导致Microsoft 向Windows API中增添了两个相似的函数。Windows的wsprintf 和wvsprintf 函数在功能上与sprintf 和vsprintf 相同,但它们不能处理浮点格式。
当然,随著宽字符的发表,sprintf 类型的函数增加了许多,使得函数名称变得极为混乱。下面列出了Microsoft 的C执行时期函数库和Windows支持的所有sprintf 函数。
ASCII 宽字符 常规
参数的变量个数
标准版 sprintf swprintf _stprintf
最大长度版 _snprintf _snwprintf _sntprintf
Windows版 wsprintfA wsprintfW wsprintf
参数队列的指针
标准版 vsprintf vswprintf _vstprintf
最大长度版 _vsnprintf _vsnwprintf _vsntprintf
Windows版 wvsprintfA wvsprintfW wvsprintf
在宽字符版的sprintf 函数中,将字符串缓冲区定义为宽字符串。在宽字符版的所有这些函数中,格式化字符串必须是宽字符串。不过,您必须确保传递给这些函数的其他字符串也必须由宽字符组成。