上海今天下雨了,湿湿漉漉的相当的犯淫,哥今天又木带伞,一般的小雨根本阻挡不了我们山东纯爷们,看到街上的女人穿着暴露的扭来扭去,哥真想过去给她们取取暖,大冷天也出来骚扰老爷们的眼球,要腚不要命。
第二章最后就两个内容,第一个,sprintf及其扩展,第二个,一段稍微增强点的MessageBox代码。
第一个,sprintf及其扩展。
知道怎么用是一码事,知道为什么用是另外一码事,这不是广大老爷们泡妞,追求关上灯一般都行的境界,一个大型软件在编写的时候一般要求严格,比如我所在Team开发的软件,持续不断30年,历经无数淫手修改添加,所以不知道为什么用是很苦逼的事情,就算他娘的自己觉得这样写很牛B,但只要跟先前的标准不一样,review的时候肯定会被追问为何这样写,他娘的,每次想到这,蛋都凉了一半,当然,如果不矫情,就写个“hello world”类似的操蛋程序,你随便巴拉脚丫子都没人管。
sorry,上一段我意淫太多了,sprintf,是个c语言函数,用来格式化字符串,有哥们可能说,驴踢了啊,直接printf不就好了啊,按李云龙的说法你丫真是个乡下人.
比如现在int numF=1; int numS=2;要你在控制台输出"numF + numS = 3", 你直接用
printf("numF + numS = %d",numF+numS);操,这样写完全很浪荡很完美。但如果现在老子不在控制台输出,毕竟那玩意在黑框框里鼠标标闪闪滴没人喜欢,我就要求你组成一个字符串char*numSum="numF + numS = 3",其中3要用numF + numS得出,你丫怎么搞?你咋搞?咋搞嘛?这就是sprintf的拿手菜。
sprintf,功能是把格式化的数据写入某个字符串中,参数说白了有三部分,第一部分,字符存储部分,第二部分,格式化部分,第三部分,替换第二部分中参数的数据部分。
char numSum[15];
sprintf(numSum, "numF + numS = %d",numF+numS);
就是这个叼样,sprintf还有个表妹,专门格式化时间的strftime,这以后用到再说。
好了,问题来了,咱们讲的是啥?windows编程,丫的上半部分都是说的sprintf,当然这在windows中也能用,不然你咋在你丫盗版的Visual Studio2005,2008。。。中写C码子,但windows毕竟有自己与之对应的函数,除了这些,上一次也说了,还他娘的分ASCII和UNICODE版本,都不同,要是这样也就算了,还他娘的有通用版本,要我说操这些蛋干嘛,直接一个通用版不就完事了嘛,哎。。
下面是个对照,其实我建议,只记住通用版就好了,两不得罪。
C语言(ASCII) C语言(UNICODE) C语言(通用)
sprintf swprintf stprintf
windows(ASCII) windows(UNICODE) windows(通用)
wsprintfA wsprintfW wsprintf
我觉得能记住sprintf, stprintf 和wsprintf就不错了.
作为一个吊死程序员,我深信人无完人,码无完码,sprintf是不错,但是第一个参数,不完美啊,容易越界,于是又制造出来一个也是操蛋的不是完美的函数int snprintf(char *str, size_t size, const char *format, ...);
这个函数跟sprintf长的只有一个区别,多了一个size_t size,这个函数比sprintf强在哪呢?
(1) 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');
snprintf snwprintf sntprintf
这个函数也有windows版_snprintf,但这个跟上面的不太一样,比如>=size的是时候后面不自动添加('\0'),这里就不多说了,反正C语言在winows也能用,所以用上面三个比较好。
第二个内容,下面再看一段码子。
#include <windows.h>
#include <TCHAR.H>
int CDECL MessageBoxPrintf(TCHAR* szCaption, TCHAR* szFormat,...)
{
TCHAR szBuffer[1024];
va_list pArgList;
va_start(pArgList, szFormat);
_vsntprintf(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), szFormat, pArgList);
va_end(pArgList);
return MessageBox(NULL, szBuffer, szCaption, 0);
}
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
PSTR szCmdLine,
int iCmdShow)
{
int iScreenSizeX = GetSystemMetrics(SM_CXSCREEN);
int iScreenSizeY = GetSystemMetrics(SM_CYSCREEN);
MessageBoxPrintf(TEXT("Screen Size"), TEXT("The Screen is %d pixels wide by %d pixels high."), iScreenSizeX, iScreenSizeY);
return 0;
}
#include <windows.h>,据说是windows编程头文件中的扛把子。
#include <TCHAR.H>,你可以不加,只要你能把编译时候_vsntprintf未定义错误解决掉。
int CDECL MessageBoxPrintf(TCHAR* szCaption, TCHAR* szFormat,...) ,函数定义,CDECL,含义就是c-declaration,就是个函数调用约定,上次我们说了WINAPI也是个调用约定,跟这个的相同及区别就是:
相同点:在调用时,参数列表中的参数都是从右向左进入堆栈。
不同点:CDECL函数是调用者负责清理堆栈,WINAPI是函数自己清理堆栈。
拿MessageBoxPrintf(TCHAR* szCaption, TCHAR* szFormat,...) 来说,最后面的是不定形参,在调用之前MessageBoxPrintf连自己的参数类型数量类型都不知道,它处理个毛啊,所以就需要调用它的函数负责给处理了。
MessageBoxPrintf函数包含两个知识点,第一,不定形参的处理,第二,_vsntprintf
第一,不定形参的处理,抛开这个程序,不定形参处理就需要四个宏,记住,是宏,不是函数,长得象函数,就像在大街上看到一亮妞,从后面看迷死一万,从前面看杀死9千,剩下的一千疯了。
这四个宏可以用C语言对文件的操作中的4部分对比,FILE ,fopen,fseek,fclose。
第一个va_list ,就是类型定义,定义个变量说明这个变量是用来处理不定形参的,作用相当于FILE,操,别误会啊,别以为也是处理文件的,我说的不是功能类似,而是在不同的领域做个对比。
变量定义完了需要干嘛?初始化,va_start,有两个参数第一个就是va_list定义的变量,第二个就是函数参数列表中最靠近不定形参的参数,放到MessageBoxPrintf函数里就是 szFormat,这个参数的类型,名字跟不定形参美半毛钱关系,之所以要用它初始化,是应为在参数进栈后,它的地址紧连着不定形参的第一个参数的地址。
初始化完了干嘛?要取值,va_arg,也是两个参数,其作用是获取当前不定形参的值,并且使指针指向下一个不定形参,所以只需要用个循环,自己不需要处理指针,就可以获取所有不定形参的值, 参数第一个是va_list定义的变量,第二个是当前要获取的不定形参的类型,这个说起来别扭,真他娘的设计的操蛋,它自己没法判断类型是什么,必须自己指定,这我就不多说了。
处理完了要干啥?擦屁股,va_end,不要干完事就走人,一个参数,关闭va_list定义的变量。
第二个内容,_vsntprintf,这个跟上半篇说过的sntprintf差不多,不同的是_vsntprintf最后面的参数是一个可变参数,也就是va_list类型,这个程序中没有使用va_arg,因为在_vsntprintf函数中自己处理了。
靠,说到这里,MessageBoxPrintf(TCHAR* szCaption, TCHAR* szFormat,...)中的TCHAR*,上次说可以用啥代替了?LPTSTR,PTSTR等,你可以换上,保证不出错,除非我拼错了,丫的,举一反三呀,别死脑筋。
WinMain里面就一个内容GetSystemMetrics,函数,这个函数都不用百度是干嘛的,看拼音,Get + system + metrics,擦,我可能说错了,有可能爱国者们需要百度这三单词是啥意思,“获取系统度量值" , 获取什么度量值?那要看你在参数里用的啥, 比如这里的SM_CXSCREEN,获取的就是你当前电脑屏幕的像素宽,有很多度量值可以获取,没必要记住,用到的时候一搜就找到了,但是要是你丫连GetSystemMetrics都记不住,你搜了其它的有个蛋用,中国人执行命令的时候就是会打折。