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

今天开始研究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下都可以用,最后却变成了函数的使用说明和实现代码分析。。。。。。。。花费时间,其实也没有实际价值,因为用法可以查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

阅读全文....

偶有会意,导致现在还没有睡觉。。。。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;

}

再次验证了一件事情,那就是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

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

 

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

 

头文件:

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相关的知识可以写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函数的实现前有注释说明。

sigvalue 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或者一书。

另外,说明一下的是,假如在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

一天一个C Run-Time Library函数(2)__max & __min

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

头文件

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::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

阅读全文....

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

阅读全文....

一天一个C Run-Time Library 函数(1) __isascii & __iswascii & __toascii

一天一个C Run-Time Library 函数  __isascii & iswascii & __toascii

 

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

 

msdn:

Determines whether a particular character is an ASCII character.

int __isascii(

    int c

);

int iswascii(

    wint_t c

);

测试程序:

#include "stdafx.h"
#include "ctype.h"
#include "locale.h"
#include "stdio.h"

void CheckCharAndPrint(char acChar)
{
    if(__isascii(acChar))
    {
       printf("char %c is a ascii char./n",acChar);
    }
    else
    {
        // 此处无法正常输出中文,没有深入研究了
       printf("char %c is not a ascii char./n",acChar);
    }
}

void CheckWCharAndPrint(wchar_t awcChar)
{
    if(iswascii(awcChar))
    {
       wprintf(L"wchar %c is a ascii char./n",awcChar);
    }
    else
    {
       setlocale(LC_ALL,"");
       wprintf(L"wchar %c is not a ascii char./n",awcChar);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    char lcC = 'a';
    char lcD = '中';

    CheckCharAndPrint(lcC);
    CheckCharAndPrint(lcD);

    wchar_t lwcC = L'a';
    wchar_t lwcD = L'中';

    CheckWCharAndPrint(lwcC);
    CheckWCharAndPrint(lwcD);

    return 0;
}

 

说明:

__isascii是一个比较特殊的函数,因为它以两个前置下划线开头。这在C语言中并不多见。(起码我看到的比较少)

此函数应该不属于标准库函数,《TCPL》中,《C语言参考》中并没有描述,但是gcc中有此函数。也就是说linux下也能正常使用此函数。

iswascii这个__isascii函数的宽字节版本,如同很多宽字节版本的函数一样,这个函数属于MS自己扩的,于是。。linux下无法使用此函数,要使用,只能自己实现罗。

 

实现:

MS:

#define __isascii(_Char)   ( (unsigned)(_Char) < 0x80 )
inline int __cdecl iswascii(wint_t _C) {return ((unsigned)(_C) < 0x80); }

gcc:

#define __isascii(c)  (((c) & ~0x7f) == 0)  /* if C is a 7 bit value*/

__isascii都是一个简单的宏。MS的iswascii原理和其__isascii都一样,仅仅是一个内联的函数。

微软的实现是依赖于字符小于128(0x80),这里还做了一次强转,不是太理解,因为实际char可以直接作为整数来比较,也许仅仅是为了屏蔽warning?

gcc的实现是依赖于字符除低七位外无任何其他值。即先将127(0x7f)取反,再与字符位与。实际就是取得字符c除了低七位以外的值。再比较此值是否为零。

想不到一个这样简单的函数,MS,gcc的实现差别都这么大,相对而言MS的实现自然是比较浅显易懂的,但是gcc用这么复杂的实现,应该有更好的效率。

就分析而言,强转+小于操作 运行时间大于 一次取反一次位与一次等于操作。还真不容易知道谁的效率真的更高。那么就测试一下吧。

效率测试:

#include "jtianling.h"
#define __isasciims(_Char)   ( (unsigned)(_Char) < 0x80 )
#define __isasciigcc(c)  (((c) & ~0x7f) == 0)  /* if C is a 7 bit value*/

const int  DEF_TEST_TIMES = 1000000000;

void CheckMS(char ac)
{
    double ldTimeLast = jtianling::GetTime();
    for (int i=0; i<DEF_TEST_TIMES ; ++i)
    {

       __isasciims(ac);

    }
    double ldTimePast = jtianling::GetTime() - ldTimeLast;

    printf("__isasciims %c run %d times cost %lf secs./n", ac, DEF_TEST_TIMES, ldTimePast);
}

void Checkgcc(char ac)
{
    double ldTimeLast = jtianling::GetTime();
    for (int i=0; i<DEF_TEST_TIMES ; ++i)
    {

       __isasciigcc(ac);

    }
    double ldTimePast = jtianling::GetTime() - ldTimeLast;


    printf("__isasciigcc %c run %d times cost %lf secs./n", ac, DEF_TEST_TIMES, ldTimePast);
}

int _tmain(int argc, _TCHAR* argv[])
{
    char lc = 'a';
    char lc2 = '中';
    CheckMS(lc);
    Checkgcc(lc);
    CheckMS(lc);
    Checkgcc(lc2);

    return 0;
}

至于GetTime函数的意义,请参考我以前写的库,无非就是获取当前时间,不知道也没有关系。你可以用time(NULL)来替代,只不过精度没有这个函数高而已。

实际的测试结果很让人失望,在测试了几乎无数次以后,MS和gcc的实现效率都几乎相同,在10亿这个级别,gcc也不过有时快0.1秒而已,而且多次运行,还不是太稳定。看来并不是复杂的实现就一定好。。。

 

相关函数:

msdn:

Converts characters.

int __toascii(
   int c 
);

这个函数也是一个双前置下划线的函数,MS,gcc中都有实现。而且在此时,实现都是一样的。

#define __toascii(_Char)   ( (_Char) & 0x7f )

gcc注释到 “mask off high bits.”

这里和gcc中__isascii函数实现的前一部分很像,一个是去除低七位,一个是保留低七位。看了这个以后才知道gcc为什么想到这样实现__isascii了。

 

个人想法:

这两个函数在实际中我从来没有用到过,假如不是我工作范围太窄那就是这两个函数的使用性并不强了,的确,我没有事去把一个值转为ascii?是ascii的话就没有意义,不是的话,原来的含义还能保留吗?至于__isascii函数可能还在某些情况下有用吧,只不过我没有用到过,谁有实际中使用此两个函数的代码可以告诉我一下。

 

另外,总结的是,虽然C Runtime库MS也有源码,但是完全没有任何注释。相对而言gcc的注释就算是很丰富和详细了,呵呵,毕竟开源代码就是不一样啊,做来就是给人看的,想想这样分析下去,光是看源代码收获都不会太小。

 

最后。。。。。。。。。。。这样一个最最简单的函数,宏定义的函数。用了我整整一个没有加班的晚上的,晕掉了。当然,有在家里的VS中编程已经不太习惯的因素,但是还是太过了,下次不能这样太过了。。。点到为止就好了,不然这一辈子都分析不完了。

 

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

阅读全文....