当时就感觉可能没有太多时间(尽管应该会需要很久)来给我学习h.264,但是也太快了,甚至我买的书都还在路上呢,就要换方向了。现在开始做工具。老总是肯定准备把我打造成通吃天地人(服务器,客户端,工具,做网络游戏程序的还有其他的一块吗?)三界的全能打工者了。。。。。 作为打工者,自然是有什么工作做什么贝,又没得选择。 虽然我的兴趣实际在网络,linux和系统的底层,不过Windows SDK, MFC学得本来又不差,工作也不是不能做好。现在来看,只能是工作上老老实实用MFC了,工作之余我再来自己学习linux和网络吧。。。。。。。。。。向自己适应环境的能力(其实是向环境妥协的能力)致敬。
阅读全文....
公司还真看的起我。。。。。。。。对于非计算机专业毕业的我,一开始就做错误dump,文件系统,然后写了几个服务器,现在安排我看公司以前写的h264的编解码实现。。。。。不知道这个过程能够持续多久,不知道公司能给我多久时间去了解和消化h.264的东西,不过,谁都知道,这不是一两天就可以完成的任务,希望不要像为游戏加lua的脚本模块一样半途而废。。。。。 视频牵涉到的东西比lua脚本模块牵涉到的东西实在是多了太多太多,还有那么多算法。。。。看到离散余弦变换的时候还是感觉挺熟悉的:)我的数字图像处理当年可是89分:)可是其他的东西嘛。。。。完全不懂。。。。。 多给我点时间啊,让我吃透它。。。。。。
阅读全文....
一天一个 C Run-Time Library 函数 (7) asctime(时间函数)
write by 九天雁翎 (JTianLing) -- www.jtianling.com
msdn:
Convert a tm time structure to a character
string. These functions are deprecated because more secure versions are
available; see asctime_s,
char
*asctime(
const struct tm *timeptr
);
测试程序:
#include <stdio.h>
#include <time.h>
int main ( void )
{
time_t ltNow = time (NULL );
struct tm *
lptmNow = localtime (<Now );
const char *
lpszNow = asctime (lptmNow );
printf ("%s" ,lpszNow );
return 0;
}
说明:
asctime 是非常使用的函数,与 time(),localtime , gmtime , mktime ,ctime 一起构成了 C 语言世界的时间概念。 C 中的格林威治时间体系就是由这几个函数来完成的。 time_t 类型是用来沟通这些函数的桥梁。说明一下, localtime 在转换的时候是根据本地 locale 来转换的, gmtime 就是转换标准的格林威治时间,(似乎从结果上来看就是转换成了伦敦时间)。顺便说一下,以 _t 结尾的类型定义虽然你在 Windows 中看到是可能是简单的 int 型,但是即使这样,作为可移植的程序开发,还是应该用原来的类型定义 , 这样碰到不同实现的时候不至于会导致错误,比如 windows 中现在 time_t 现在默认的就是 int64 了。
与此类似的类型还有 pid_t,win_t 等
实现:
static _TSCHAR * __cdecl store_dt (
REG1 _TSCHAR *p ,
REG2 int val
)
{
*p ++ = (_TSCHAR )(_T ('0' ) + val /
10);
*p ++ = (_TSCHAR )(_T ('0' ) + val %
10);
return (p );
}
REG2 _TSCHAR *p = buffer ;
int day , mon ;
int i ;
day = tb ->tm_wday
* 3; /*
index to correct day string */
mon = tb ->tm_mon *
3; /*
index to correct month string */
for (i =0; i < 3; i ++,p ++) {
*p =
*(__dnames + day
+ i );
*(p +4)
= *(__mnames + mon
+ i );
}
*p = _T (' ' ); /* blank
between day and month */
p += 4;
*p ++ = _T (' ' );
p = store_dt (p , tb ->tm_mday ); /* day of the
month (1-31) */
*p ++ = _T (' ' );
p = store_dt (p , tb ->tm_hour ); /* hours (0-23) */
*p ++ = _T (':' );
p = store_dt (p , tb ->tm_min ); /* minutes (0-59)
*/
*p ++ = _T (':' );
p = store_dt (p , tb ->tm_sec ); /* seconds (0-59)
*/
*p ++ = _T (' ' );
p = store_dt (p , 19
+ (tb ->tm_year /100));
/* year (after 1900) */
p = store_dt (p , tb ->tm_year %100);
*p ++ = _T ('/n' );
*p = _T ('/0' );
MS:
这个实现是我此系列开始写以后看的源代码中最有意思的源代码了。所以不惜编程源代码的分析都要讲一讲。
很有意思的代码,淋漓尽致的体现了 C 语言指针的灵活。
for (i =0; i < 3; i ++,p ++) {
*p =
*(__dnames + day
+ i );
*(p +4)
= *(__mnames + mon
+ i );
}
通过对月和星期的 buf, 用这个循环完成的 6 个字符的拷贝更是很让我欣赏:)
通过 val/10 的加上 ’0’ 的 asc 值的方式完成的默认前置 0 的赋值也是很有意思的技巧。
*p ++ = (_TSCHAR )(_T ('0' ) + val /
10);
*p ++ = (_TSCHAR )(_T ('0' ) + val %
10);
gcc:
仅仅通过一个简单的 snprintf 来实现,这估计也是大部分人的实现方式。。。。我要是真的准备实现这样一个代码也会这样实现。
效率测试:
很想知道到底哪个会赢。。。。不过太晚了。。。。
相关函数:
gmtime, localtime, ctime, time,
个人想法:
除了宽字节版本不能使用之外,其他函数的使用自然没有任何问题。并且函数名完全一致。对于这么古老和标准的函数假如还能出现问题,估计任何做可移植程序的人都会崩溃的。放心用吧。
write by 九天雁翎 (JTianLing) -- www.jtianling.com
阅读全文....
一天一个 C Run-Time Library 函数 (6 ) 三角函数
write by 九天雁翎 (JTianLing) -- www.jtianling.com
msdn:
太多,不列举了。包括 acos,cos,asin,sin,atan,tan 等等
测试程序:
说明:
都没有太多需要解释的,三角运算时需要的函数。
实现:
MS:
我只看了 acos 的实现,完全是汇编实现的。并且可以看到 sse2 的指令集, mmx 指令集都有响应的优化。 MS 也会判断你的机器是否有此指令集。有的话就是用优化后的指令。
效率测试:
这里效率测试很有意思,可以看看两个编译器对于汇编代码的优化到什么地步。。。。但是由于我懒嘛。。。所以没有进行。
相关函数:
无
个人想法:
对于这么简单的函数自然可以自由使用了。并且在 C++ 下的话通过重载可以更简单的使用。不然就只能记得使用 f 后缀的使用方式了。比如 cosf 。。。表示浮点类型的余弦函数。
write by 九天雁翎 (JTianLing) -- www.jtianling.com
阅读全文....
一天一个 C Run-Time Library 函数(5) access
write by 九天雁翎 (JTianLing) -- www.jtianling.com
msdn:
Determines if a file
is read-only or not. More secure versions are available; see _access_s, _waccess_s .
int
_access(
const char *path ,
int mode
);
int
_waccess(
const wchar_t *path ,
int mode
);
测试程序:
说明:
access 是个很有用的函数,非常之有用。特别是在 linux 下。对于 windows 中,似乎都没有不可读和不可执行的概念,所以相对来说使用率相对低一些。另外,最标准的测试一个文件是否存在的方式可能就是用 access 函数了,虽然真正的可以达到目的方式有很多。
在 linux 下此函数的使用需求很大,因为 linux 的权限设置更为丰富。很多时候是否存在,可读,可写,可执行都需要判断,这样你能更容易知道你下一步该做什么,而不是真的等到某个文件打不开,写不了,执行失败时才报告错误。
对于这么重要的函数,我没有给出示例。。。只能说我是越来越懒了,不过总是发现明明我仅仅是想看看哪些函数在 windows/linux 下都可以用,最后却变成了函数的使用说明和实现代码分析。。。。。。。。花费时间,其实也没有实际价值,因为用法可以查 msdn , man ,我贴一下也没有什么用,还是把时间用在更值得用过的地方吧。
实现:
windows 下主要通过 API GetFileAttributes函数来完成。
linux 下的实现我竟然没有找到。。。。。无语
效率测试:
此函数好像通常不太需要考虑效率,因为两个系统完全不一样,所以测试此函数效率没有任何意义。
相关函数:
个人想法:
宽字节的版本照例 linux 下没有实现。其实可以总结一下,起码所有的关于文件的宽字节版本的函数 ,linux 下都没有。原因很简单, linux 虽然支持 unicode ,但是支持的不是通常 windows 下所谓的 unicode 的版本,而是 utf-8 ,所以不需要宽字节。
在此时完全没有办法糅合,也完全没有办法放弃功能。不可能说以后编写的都是不用文件操作的东西吧?那么只能是一方妥协于另一方。我公司的做法是向微软妥协。也就是完全使用 32 位的宽字节版本的 unicode, 这样在 windows 下其实效率是最高的(因为 windows 的核心都是 unicode 了, ansi 版本的 api 需要多进行一次编码转换)。但是所有在 linux 下的文件操作都需要经过 unicode2utf8 的编码转换,还好这个转换是非常快的(起码没有编码映射的过程)。本人更喜欢 linux ,更喜欢让 windows 版本的东西经过编码转换才用。因此 linux 下的操作会更快(这就是我想要的)而 windows 版本的很多操作都需要先转换编码,带来的代价是,所有的字符串在 VS 中调试时将不能看到有意义的输出:)所有的命令行字符串中文都无法正确显示。(实际只能显示 ASCII )。纯粹个人爱好,因为我想强迫自己去熟悉 linux 下的东西。真正的工程应用假如大部分在 windows 下开发,自然是向 windows 妥协的好。
所以,从此以后所有宽字节的文件操作命令我直接忽略。顺便忽略的还有 MS 为增强 C 语言库写的一族 _s 函数,虽然可能真的会更安全一些,但是,可移植性不需要这样特立独行的东西。直接忽略。
最后, linux 下的函数命名遵循的是 POSIX 标准, POSIX 函数的命名没有前缀。 MS 的函数命名遵循的是 ISO 标准(据说), ISO 标准的函数命名都带前置下划线。这点纯属瞎猜。。。。来源于所有的 POSIX 函数在 MSDN 中都会有类似的文字:
This POSIX function is deprecated beginning in
Visual C++ 2005. Use the ISO C++ conformant _access or
security-enhanced _access_s instead.
write by 九天雁翎 (JTianLing) -- www.jtianling.com
阅读全文....
以前读大学的时候就捣鼓过samba,那时候寝室四台电脑,我一个人装个英文的fc4,没有办法和室友们共享文件,特别是想共享他们的电影的时候特别不方便(好像能共享的也就是电影了)。 于是当时找到了samba,当时那个痛苦啊,折腾了起码两天才基本算搞明白怎么回事,基本实现了读取windows文件的功能和让室友访问我电脑的功能。
最近再次需要在台式机与笔记本(ubuntu)共享文件的时候,自然还是想到了samba,我弄ssh只用了一下子,弄samba又用了2个晚上,而且一直是痛苦着用着。。。。因为偶尔能够登上,偶尔又登不上。。。。。痛苦至极,当然这可能是我配置的问题。以前就想过实在不行就直接驾一个ftp就好了,今天晚上正准备好好的解决一下,给我发现了原来ssh协议中有个附带的sftp协议,可以实现类似ftp的功能。那还说什么,本来就像干脆驾个ftp了,看到这样更没有犹豫了。
并且给我找到了winscp这个软件。。。。在windows下实现了类total command的界面。。。这下台式机及笔记本之间的文件共享问题总算是解决了。。。。以后再也不会痛苦了。可惜的是RSA的密钥验证方式我一直还没有弄好,小郁闷。
用winscp传输文件,用putty远程登录,完美编程的解决方案:)另外。。。虽然对于我来说不是那么重要,但是值得一提的一点是,基于ssh2协议的传输还是加过密的。包括使用sftp在内。
阅读全文....
一天一个 C Run-Time Library 函数 (4) abs _abs64
write by 九天雁翎 (JTianLing) -- www.jtianling.com
msdn:
Calculate the absolute value.
int abs(
int n
);
long
abs(
long n
); // C++ only
double
abs(
double n
); // C++ only
long
double abs(
long double n
); // C++ only
float
abs(
float n
); // C++ only
__int64
_abs64(
__int64 n
);
这里要说明的是 c++ 因为有重载的技术,所以都可以用 abs 来表示,而 C 语言里面实际还有 labs 函数,用来表示 long 类型的绝对值。
测试程序:
虽然MSDN 都有example, 但是对于这么简单的函数,就没有必要贴了。
说明:
功能很简单,也很实用的函数。以前学习 c++ 的时候有用到过,可是实际上工作以后竟然一次也没有用过。
实现:
MS:
int __cdecl abs (
int number
)
{
return (
number >=0 ? number
: -number );
}
gcc:
/* Return
the absolute value of I. */
int
abs (int
i )
{
return i
< 0 ? -i : i ;
}
再次验证了一件事情,那就是gcc 和MS 的势不两立,(突然觉得把gcc 与MS 并称很奇怪,因为根本不是同一类的事物。。。。就以MS 代指VC2005 吧。。。)
这么一个简单的函数,实现方式上几乎没有选择,即便是同样的 ?: 结构, MS 选择了判断 >= , gcc 选择了判断 < ,再次发挥我 BT 的精神,决定看一下他们生成的汇编代码。为了公正,先都在 VS2005 看看生成的代码。
MS:
0041140E cmp
dword ptr [number],0
00411412 jl
MSabs+2Fh (41141Fh)
00411414 mov
eax,dword ptr [number]
00411417 mov
dword ptr [ebp-0C4h],eax
0041141D jmp
MSabs+3Ah (41142Ah)
0041141F mov
ecx,dword ptr [number]
00411422 neg
ecx
00411424 mov
dword ptr [ebp-0C4h],ecx
0041142A mov
eax,dword ptr [ebp-0C4h]
gcc:
004113BE cmp
dword ptr [i],0
004113C2 jge
GCCabs+31h (4113D1h)
004113C4 mov
eax,dword ptr [i]
004113C7 neg
eax
004113C9 mov
dword ptr [ebp-0C4h],eax
004113CF jmp
GCCabs+3Ah (4113DAh)
004113D1 mov
ecx,dword ptr [i]
004113D4 mov
dword ptr [ebp-0C4h],ecx
004113DA mov
eax,dword ptr [ebp-0C4h]
DEBUG 下才能看到老实的逐句解析的代码, Release 下简单的 abs 函数调用都直接在编译期间就计算完了。说实话,即使在 DEBUG 版本中我看不出来两者有什么区别。两者在参数为正,或为负的时候都需要一次 jmp 。唯一也许有不同的可能就是 neg eax 的时候也许比 neg ecx 会快一点。(这还是个人猜想,因为毕竟 eax 是最常用也是 CPU 设计时提供最快操作的寄存器)
效率测试:
实在不想再测试效率了,对于这样简单的函数测试一百亿次也看不出什么东西。这也是我为什么去看汇编出来的东西。何况, release 的时候还被优化了呢。。。
相关函数:
无
个人想法:
对于这样简单的函数想怎么用就怎么用吧。假如用的是纯 C ,那么可能需要注意 64 位的实现是由 MS 实现的, linux 下没有。但是 c99 已经定义了新的 long long 类型的 abs 。用 gcc 的人就可以用这个,函数名为 llabs 。
用 c++ 的话根本不需要操心类似的东西,也不需要关心到底使用 labs,llabs 还是其他,因为我们有重载。在 libstdc++ 中 abs(long) 就调用 glibc 的 labs 来完成,而 abs(long long) 在 libstdc++ 中也有实现。
另外,不知道为什么这里即便是 C 语言的实现 MS,GCC 也都没有用习惯的宏,因此在这里, c++ 的函数调用会比 C 语言的版本快一点,因为 C++ 还有 inline 。
今天对我来说是周末,偷懒了:)其实这么一个简单的函数也没有太多好说的。想用就用吧。
write
by 九天雁翎 (JTianLing)
-- www.jtianling.com
阅读全文....
一天一个 C Run-Time Library 函数 ( 3 ) abort
write by 九天雁翎 (JTianLing) -- www.jtianling.com
头文件: <stdlib.h>
msdn:
Aborts the current process and returns an error
code.
测试程序 3.1 :
#include <stdlib.h>
#include <stdio.h>
int main ()
{
printf ("Running/n" );
abort ();
printf ("Still Running/n" );
return 0;
}
以上函数在windows 上输出为Running ,然后弹出对话框。点终止即终止程序,点忽略还会输出类似 “
This
application has requested the Runtime to terminate it in an unusual way.
Please contact the
application's support team for more information
。”的信息。
在
linux
下运行输出
Running
Aborted
说明:
总算碰到一个真正的函数了,可惜一开始真正的函数问题就复杂了,比如关于abort 相关的知识可以写2 ,3 天。
首先abort 函数是一个导致消息发生的函数, 引发的消息为 SIGABRT ,说起来就很复杂了吧,什么是消息呢?在 windows 下编程我还从来没有用过类似的东西。事实上 UNIX/Linux 才是消息用的多的地方。
简而言之,消息是软件中断的一种。 abort 函数和消息的主要(也是最简单的)函数 signal 已经是 ANSI C 标准中的一员了。
signal 函数也在这里附带讲了算了, MSDN 声明如下:
Sets interrupt signal handling.
void
(__cdecl *signal(
int sig ,
void (__cdecl *func ) (int [, int ] )))
(int);
事实上感觉微软实现消息系统似乎仅仅是为了稍微合乎点ANSI C 的标准,因为在众多的消息中,其只实现了六种,而以下的六种其实都是ANSI. 我没有去查ANSI C 的标准,但是MS 在signal 函数的实现前有注释说明。
sig value
Description
SIGABRT
Abnormal termination
SIGFPE
Floating-point error
SIGILL
Illegal instruction
SIGINT
CTRL+C signal
SIGSEGV
Illegal storage access
SIGTERM
Termination request
SIGABRT 这个由abort 函数引发的消息是其中之一。
要知道,在一般的UNIX 系统中,消息起码有五十种以上。这也是在windows 中很少有人使用消息,而在Unix/linux 中使用的很多的原因吧。
另外,MSDN 中虽然MS 明确说明调用abort 函数的返回码是3 ,事实上经过我的实际测试,测试方法为用CreateProcess 运行一个新的用abort 函数结束的进程,然后调用GetExitCodeProcess 函数获得返回值,返回值一直为0. 我也很纳闷,觉得我不对的请自己测试一下,我也希望你们能指出我方式的错误。因为MS 一般不会犯这样的错误,所以我都怀疑自己的正确性。(好啰嗦啊。。。)
在linux 下( 未有说明仅仅实在我的系统环境中测试) 测试结果为返回134.
然后,顺便也给出一个signal 函数的用法,这样才能理解abort 函数的作用和消息的作用。
例子3.2
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
void Abort (int ai )
{
printf ("catch the SIGABRT and agrument is %d" , ai );
}
int main ()
{
printf ("Running/n" );
signal (SIGABRT , Abort );
abort ();
printf ("Still Running/n" );
return 0;
}
此例子在windows 下和linux 下效果一致,都是在调用abort 函数后引发SIGABRT 消息,因为先用signal 捕获了此消息并指定此时调用Abort 函数,所以最后的输出都是
Running
catch the SIGABRT and agrument is %d
有所不同的是,在windows 下%d 为22 ,linux 下为6 ,不知道为什么windows 要特意做的和别人不一样,然后特意声明一个
#define SIGABRT_COMPAT 6
/* SIGABRT compatible with other platforms,
same as SIGABRT */
这一点我比较不解。
另外,通过对 signal 使用的例子可以看出来,响应的函数的参数实际就是消息定义的值。这样的好处是你可以为所有的消息定义一个函数,然后通过参数来判断到底是哪个消息。就类似与 windows 下可以 SetTimer 多次,而只 OnTimer 一个函数中响应,通过 Timer 的 ID 来判断到底是哪个时间到了。
再次说明一下,我并不是来说明函数的用法的。。。所以 signal 函数的参数什么的都省略了,请参考 MSDN 或者 <advanced programming in the UNIX
environment> 一书。
另外,说明一下的是,假如在 Abort 这个响应函数中提前用 exit 函数退出程序,那么在 linux 下就不会再次触发系统的默认响应,并且程序的退出代码也由 exit 函数指定了。
在 windows 下,总是会触发系统的默认响应并弹出对话框。。。。。
另外,试图忽略 SIGABORT 消息(在 signal 函数的第二参数用 SIG_IGN ),我总是没有成功输出过 Still running 的语句,并且总是会触发系统默认响应,无论在 windows 还是 linux 下都是这样,希望有人可以告诉我为什么。
最后,需要注意的是 Aborted 这个 linux 下的输出实际实在错误流中输出的,比如以上例子编译为 test 程序,通过 echo `./test` 你可以看到,实际只有 Running 输出到标准输出。
实现:
MS: (删除次要部分)
void __cdecl abort (
void
)
{
_PHNDLR sigabrt_act = SIG_DFL ;
if (__abort_behavior & _WRITE_ABORT_MSG )
{
/* write the
abort message */
_NMSG_WRITE (_RT_ABORT );
}
................
/* Check if the
user installed a handler for SIGABRT.
* We need to read the user handler
atomically in the case
* another thread is aborting while we
change the signal
* handler.
*/
sigabrt_act
= __get_sigabrt ();
if (sigabrt_act != SIG_DFL )
{
raise (SIGABRT );
}
_exit (3);
}
先调用 _NMSG_WRITE 函数输出abort 的错误信息,此时即弹出了对话框,无论你怎么设置忽略或者响应函数都没有用。
当没有忽略此消息时调用 raise (SIGABRT ) 产生消息。
最后调用_exit 返回错误代码3 作为返回值。这里明明返回了3 ,但是我怎么得到程序的返回值总是0 ?奇了怪了。
gcc:
/* We must
avoid to run in circles. Therefore we
remember how far we
already
got. */
static int stage ;
/* We
should be prepared for multiple threads trying to run abort. */
__libc_lock_define_initialized_recursive (static ,
lock );
/* Cause an abnormal program
termination with core-dump. */
void abort (void )
{
struct sigaction
act ;
sigset_t sigs ;
/* First acquire the lock. */
__libc_lock_lock_recursive (lock );
/* Now it's for sure we are alone. But recursive calls are possible. */
/* Unlock SIGABRT. */
if (stage
== 0)
{
++stage ;
if (__sigemptyset
(&sigs ) == 0 &&
__sigaddset (&sigs , SIGABRT )
== 0)
__sigprocmask (SIG_UNBLOCK , &sigs ,
(sigset_t *) NULL );
}
/* Flush all streams. We cannot close them now because the user
might have registered a handler for
SIGABRT. */
if (stage
== 1)
{
++stage ;
fflush (NULL );
}
/* Send signal which possibly calls a
user handler. */
if (stage
== 2)
{
/*
This stage is special: we must allow repeated calls of
`abort' when a user defined handler for
SIGABRT is installed.
This is risky since the `raise'
implementation might also
fail but I don't see another
possibility. */
int save_stage
= stage ;
stage = 0;
__libc_lock_unlock_recursive
(lock );
raise (SIGABRT );
__libc_lock_lock_recursive
(lock );
stage = save_stage + 1;
}
/* There was a handler installed. Now remove it. */
if (stage
== 3)
{
++stage ;
memset (&act , '/0' , sizeof (struct sigaction ));
act .sa_handler = SIG_DFL ;
__sigfillset (&act .sa_mask );
act .sa_flags = 0;
__sigaction (SIGABRT , &act ,
NULL );
}
/* Now close the streams which also
flushes the output the user
defined handler might has produced. */
if (stage
== 4)
{
++stage ;
__fcloseall ();
}
/* Try again. */
if (stage
== 5)
{
++stage ;
raise (SIGABRT );
}
/* Now try to abort using the system
specific command. */
if (stage
== 6)
{
++stage ;
ABORT_INSTRUCTION ;
}
/* If we can't signal ourselves and the
abort instruction failed, exit. */
if (stage
== 7)
{
++stage ;
_exit (127);
}
/* If even this fails try to use the
provided instruction to crash
or otherwise make sure we never return. */
while (1)
/* Try for ever and ever. */
ABORT_INSTRUCTION ;
}
做的工作差不多,实现手段差好远啊, linux 下多的就是一个文件的 flush 和 close 还有用 static stage 的方式来允许递归的 abort 调用。 glibc 中详尽的注释可帮了我不少忙。以前还真没有见到过类似的用法,今天算是长见识了。说实话,这样一个简单的函数,我并没有完全看懂,可能需要有时间一步一步跟,并创造递归 abort 调用的情况才能很好的理解。
windows 这点可能是由操作系统自己做了,所以没有在 abort 函数中有体现。
另外,我怎么测试返回值都是 134...... 但是很明显调用的是 _exit(127) ,原因和 windows 老是返回 0 一样不明。
效率测试:
略
相关函数:
raise ,signal
个人想法:
虽然 windows 也包含消息,虽然那 6 个消息是 ANSI 的标准,但是个人推荐,对于可移植的东西来说,最好是不要用消息了,除非你确定只用 windows 的那 6 个消息。即使你只用这六个消息,你都会发现 windows 运行的特性与 linux 不同。不然,对于消息系统来说,要实现 windows 中没有实现的消息不是太容易的事情。。。。。所以,强大的消息系统只能在 windows 中找替代方案了。对于 windows/linux 可移植的东西来说,这也是最大最大的一个缺憾,那就是你不能用太多平台相关的东西,而偏偏很多这样的东西就是这个平台中最好的东西。比如 Windows 的核心对象系统, linux 下的消息系统等等等等,真是缺憾啊。。。
本来因为有宏嘛,可以在所有类似的系统上再封一层,以达到虽然使用不同的东西也可以保持很好的移植性。但是对于消息系统或者 Windows SEH 这样彻底的破坏程序流程的东西,根本是没有办法通过宏来做到的。宏毕竟不是万能的。
在实在没有办法的时候,只能把你所封的那一层尽量的调高了(可以到类)。那样,代码量的增加几乎是一倍,除非必要,并不是太值得。(在封装网络模块的时候只能用到)
在非必要的时候,尽量做到最简单的封装(在简单的函数这一层),这样统一的程序流程和逻辑,也更加容易理解,代码量也小。
而对于消息系统这样破坏程序流程的东西,甚至想封在一个子类里面,然后使用共同的接口都不是太容易。
总而言之,非必要,个人认为,可移植程序,少用消息系统。
write
by 九天雁翎 (JTianLing)
-- www.jtianling.com
阅读全文....
一天一个 C Run-Time Library 函数 (2) __max & __min
write by 九天雁翎 (JTianLing) -- www.jtianling.com
头文件 <stdlib.h>
msdn:
Returns the larger of two values.
type __max(
type a ,
type b
);
Returns the smaller of two values.
type __min(
type a,
type b
);
测试程序:
#include <stdlib.h>
#include <stdio.h>
int main( void )
{
int a = 10;
int b = 21;
printf( "The larger of %d and %d is %d/n", a, b, __max( a, b ) );
printf( "The smaller of %d and %d is %d/n", a, b, __min( a, b ) );
}
说明:
这次的测试程序因为MSDN 中有sample ,就使用了他们原来的程序,起码能够说明问题吧,其实作为一个本意是用来说明移植性的文章系列中,没有samlple 也无所谓的。
又是两个双前置下划线的函数,事实上C 语言参考和TCPL 中也都没有说明,估计还是属于MS 自己加的。
gcc 无此函数,鉴于此函数的实用性,自然也需要实现一份。
对于这么简单的函数也就不多说明了,不然又太过了。
实现:
MS:
#define __max ( a , b ) ((( a ) > ( b )) ? ( a ) : ( b ))
#define __min ( a , b ) ((( a ) < ( b )) ? ( a ) : ( b ))
还是简单的宏,对于一个 C++ 程序员来说,我不是太习惯,可能我属于原教义派,看多了 Bjarne Stroustrup 的书,心中总是对于宏有所排斥,我以前就因为被 windows 一个简单的宏折磨的够呛。那个宏也是关于最大,最小的 , 我在 http://www.jtianling.com/archive/2007/07/06/1680398.aspx
一文中有所描述。
简而言之,就是 windows.h 中(实际定义在 windef.h 中,但是 windows.h 包含了 windef.h ,而 windows.h 才是经常包含的文件)
不知道新的 C 语言中有没有 inline ,实话说,那样要好的多。
#ifndef NOMINMAX
#ifndef max
#define max ( a , b ) ((( a ) > ( b )) ? ( a ) : ( b ))
#endif
#ifndef min
#define min ( a , b ) ((( a ) < ( b )) ? ( a ) : ( b ))
#endif
#endif /* NOMINMAX */
这样的效果是,所有的 min,max 都会被宏替换,甚至是
std::numeric_limits<T>::min()
或者
#include "windows.h"
class max
{
};
int main ( void )
{
max * lpmax = new max ();
return 0;
}
这样的形式都会导致编译通不过,而第一种情况那可是C++ 标准库使用numeric_limits 特性必须的。因为MS 也给出了解决的方式。预先定义一个NOMINMAX 的宏就能解决问题。
不知道新的 C 语言中有没有 inline ,实话说,那样要好的多。 C 语言运行库中再次实现了最大最小的函数,不过以双前置下划线的方式可以极大的防止宏替换的意外情况。
用 C 语言就得习惯宏,用 C++ 我常常迫使自己忘记宏,有的时候感觉 C 与 C++ 的区别还是挺大的,特别是 C99 以后。。。。。。。
对了,对于所有还不习惯 C 语言大面积用宏的兄弟说一句,这样的宏引进了这么多麻烦,其实还是有一个好处的,那就是省略了一次函数的调用。函数的调用在 C /C++ 中开销不算太大,但是在高效率的要求下也不算小,可以看看以前我分析 c++ 函数调用的文章。
http://www.jtianling.com/archive/2008/06/01/2501238.aspx
作为 C 语言来说,很多时候自然都是效率之上了。
效率测试:
略。
相关函数:
无。
个人想法:
虽然感觉这两个函数是会很常用到的,但是实际工作中还真没有用过一次。。。说实话,并不是没有这两个函数适用的场合,而是这样的场合自己用 ?: 实现也很简单,所以常常根本想不到还有这样的两个函数可以实现,我更加是没有准备使用 windows.h 中 max,min 宏的想法,很简单就可以做到的事情,为什么要使用以导致移植性的降低了,没有理由。
write by 九天雁翎 (JTianLing) -- www.jtianling.com
阅读全文....
继续为windows/linux通用服务器框架做研究工作,从C语言运行时库开始。
最好的可移植编程方式是什么?除了java。。。还有C。。。。,标准C是可以在几乎任何有C语言编译器的机器上运行的,这是lua作者只用标准C开发lua的原因,并谈到了lua可移植性好的理由。他说除了动态链接的模块,他用的几乎都是标准C。
其实说起来移植性最好的可能是C++,C++的标准化工作在今天看来是非常成功的,C++的标准库移植性也是非常的好,可是其实C++中大部分底层的操作还是靠C语言部分来完成,所以研究C++的库好像也没有意义,毕竟我的目的就是考察移植性而已,不是来考察库的用法。。。
作为一个服务器的框架,光用标准C好像有点难度。。。比如IOCP,epoll这些最好的服务器模型都是典型的操作系统相关。但是,用C++语言编写时,总会用到C语言的运行时库(在windows下叫CRT -- C Run-Time Library)。并且因为MS为C语言的库做了很多扩展工作,并且将很多POSIX的东西标了前置的下划线,并且都放在这个库中,使得很多时候我自己都不知道哪些是标准库的,哪些是MS扩展的了(都是常查MSDN不翻书的恶果,但是查MSDN的确快了很多)。今天又一次的碰到这个问题。
实际程序编写的时候,使用这些库函数,比使用windows API的可移植性(也许仅仅是移植的代价)要好的多。
因为没有直接的类似资料可以查看,所以我下决心,直接一个一个通过实践,决定哪个是windows/linux都可用的,哪些是新扩展,linux下不能用的。仿照很久以前看到某人写的一天一个windows API来写。我就一天一个C Run-Time Library的函数吧:)
呵呵,最近刚刚看完了一本bash的书,正好不想再学太多语言语法的东西,想好好的多编点东西,这个正好是个机会。另外。。。个人好像老是计划的多。。。实际做下去的少,希望这个计划能够对自己的计划能力做出考验,然后做一个完整的PDF/CHM,方便大家,也方便自己以后的查看:)
另外还可以将MS那些前置的下划线通过宏取消掉,最后还有一个非常非常有必要的事情也可以顺便做了,那就是为linux下的库建立一套asc/Unicode的自适应库(类似TCHAR系统)。
顺便,完整性,再贴一次我的开发及测试环境。
windows XP VS2005 VA vimemu boost 1.36
ubuntu 8.10 gcc version 4.2.4 vim+gdb boost 1.36
源代码控制统一在 windows XP 中用 VSS.( 个人习惯问题 , 公司用的就是这个 )
另外 :
其他工具包括 .IBM Rational Rose Realtime( 又是习惯问题 , 虽然不是 Realtime 的程序 , 但是因为公司用的是这个 , 个人沿用此习惯 )
阅读全文....