九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

H.264的学习结束了,工具的学习又开始了。。。。。。。比想的还快

当时就感觉可能没有太多时间(尽管应该会需要很久)来给我学习h.264,但是也太快了,甚至我买的书都还在路上呢,就要换方向了。现在开始做工具。老总是肯定准备把我打造成通吃天地人(服务器,客户端,工具,做网络游戏程序的还有其他的一块吗?)三界的全能打工者了。。。。。
作为打工者,自然是有什么工作做什么贝,又没得选择。
虽然我的兴趣实际在网络,linux和系统的底层,不过Windows SDK, MFC学得本来又不差,工作也不是不能做好。现在来看,只能是工作上老老实实用MFC了,工作之余我再来自己学习linux和网络吧。。。。。。。。。。向自己适应环境的能力(其实是向环境妥协的能力)致敬。

阅读全文....

今天开始研究h.264..........

 公司还真看的起我。。。。。。。。对于非计算机专业毕业的我,一开始就做错误dump,文件系统,然后写了几个服务器,现在安排我看公司以前写的h264的编解码实现。。。。。不知道这个过程能够持续多久,不知道公司能给我多久时间去了解和消化h.264的东西,不过,谁都知道,这不是一两天就可以完成的任务,希望不要像为游戏加lua的脚本模块一样半途而废。。。。。
视频牵涉到的东西比lua脚本模块牵涉到的东西实在是多了太多太多,还有那么多算法。。。。看到离散余弦变换的时候还是感觉挺熟悉的:)我的数字图像处理当年可是89分:)可是其他的东西嘛。。。。完全不懂。。。。。
多给我点时间啊,让我吃透它。。。。。。

阅读全文....

一天一个C Run-Time Library 函数 (7) asctime(时间函数)


一天一个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(&ltNow);

 

    const char*
lpszNow = asctime(lptmNow);

 

   

    printf("%s",lpszNow);

 

 

    return 0;

}

 

 

说明:

asctime是非常使用的函数,与time(),localtimegmtimemktime,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) 三角函数


一天一个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


一天一个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下都可以用,最后却变成了函数的使用说明和实现代码分析。。。。。。。。花费时间,其实也没有实际价值,因为用法可以查msdnman,我贴一下也没有什么用,还是把时间用在更值得用过的地方吧。

实现:

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

 

阅读全文....

偶有会意,导致现在还没有睡觉。。。。winscp让我彻底remove了samba

以前读大学的时候就捣鼓过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


一天一个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;

}

再次验证了一件事情,那就是gccMS的势不两立,(突然觉得把gccMS并称很奇怪,因为根本不是同一类的事物。。。。就以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)就调用glibclabs来完成,而abs(long long)libstdc++中也有实现。

另外,不知道为什么这里即便是C语言的实现MS,GCC也都没有用习惯的宏,因此在这里,c++的函数调用会比C语言的版本快一点,因为C++还有inline

今天对我来说是周末,偷懒了:)其实这么一个简单的函数也没有太多好说的。想用就用吧。

 

write
by
九天雁翎(JTianLing)
-- www.jtianling.com

阅读全文....

一天一个C Run-Time Library 函数(3) abort


一天一个C Run-Time Library 函数3  abort

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

 

头文件: <stdlib.h>

msdn:

Aborts the current process and returns an error
code.

void
abort( void );

 

测试程序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相关的知识可以写23天。

首先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的标准,但是MSsignal函数的实现前有注释说明。

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%d22linux下为6,不知道为什么windows要特意做的和别人不一样,然后特意声明一个

#define SIGABRT_COMPAT  6      
/* SIGABRT compatible with other platforms,
same as SIGABRT */

这一点我比较不解。

另外,通过对signal使用的例子可以看出来,响应的函数的参数实际就是消息定义的值。这样的好处是你可以为所有的消息定义一个函数,然后通过参数来判断到底是哪个消息。就类似与windows下可以SetTimer多次,而只OnTimer一个函数中响应,通过TimerID来判断到底是哪个时间到了。

再次说明一下,我并不是来说明函数的用法的。。。所以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下多的就是一个文件的flushclose还有用static stage的方式来允许递归的abort调用。glibc中详尽的注释可帮了我不少忙。以前还真没有见到过类似的用法,今天算是长见识了。说实话,这样一个简单的函数,我并没有完全看懂,可能需要有时间一步一步跟,并创造递归abort调用的情况才能很好的理解。

windows这点可能是由操作系统自己做了,所以没有在abort函数中有体现。

另外,我怎么测试返回值都是134......但是很明显调用的是_exit(127),原因和windows老是返回0一样不明。

效率测试:

相关函数:

raisesignal

 

个人想法:

虽然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

一天一个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++我常常迫使自己忘记宏,有的时候感觉CC++的区别还是挺大的,特别是C99以后。。。。。。。

对了,对于所有还不习惯C语言大面积用宏的兄弟说一句,这样的宏引进了这么多麻烦,其实还是有一个好处的,那就是省略了一次函数的调用。函数的调用在C /C++中开销不算太大,但是在高效率的要求下也不算小,可以看看以前我分析c++函数调用的文章。

http://www.jtianling.com/archive/2008/06/01/2501238.aspx

作为C语言来说,很多时候自然都是效率之上了。

 

效率测试:

略。

 

相关函数:

无。

 

个人想法:

虽然感觉这两个函数是会很常用到的,但是实际工作中还真没有用过一次。。。说实话,并不是没有这两个函数适用的场合,而是这样的场合自己用?:实现也很简单,所以常常根本想不到还有这样的两个函数可以实现,我更加是没有准备使用windows.hmax,min宏的想法,很简单就可以做到的事情,为什么要使用以导致移植性的降低了,没有理由。

 

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

一天一个C Run-Time Library 函数(绪)

继续为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的程序,但是因为公司用的是这个,个人沿用此习惯)

 

阅读全文....