公司还真看的起我。。。。。。。。对于非计算机专业毕业的我,一开始就做错误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
头文件:
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函数的实现前有注释说明。
| 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
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
阅读全文....
继续为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 函数 __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.
这个函数也是一个双前置下划线的函数,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
阅读全文....