本站首页    管理页面    写新日志    退出


«September 2025»
123456
78910111213
14151617181920
21222324252627
282930


公告

戒除浮躁,读好书,交益友


我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:邢红瑞的blog
日志总数:523
评论数量:1142
留言数量:0
访问次数:9707647
建立时间:2004年12月20日




[c++]编写可移植的程序(一):不要依赖的你编译器 
原创空间,  软件技术,  电脑与网络

邢红瑞 发表于 2006/11/30 12:49:14

最近和微软Microsoft Visual Studio 2005 开发组的一个程序员放牛娃(一般称放牛娃为牛人)讨论可移植程序的问题,我发表一下自己的观点。尽量使用标准c的类库,少使用平台提供的,注意虽然glibc提供了一个各平台的c库,但是各平台的同一版本提供的函数有很大的不同,一般难以保证你可以编译通过,即使编译通过,运行结果未必一样,opendir处理当前目录就是一个例子。尽可能使用标准c++,因为标准c++的兼容性远好于c,当然包括stl,虽然stl在vc borland c和mingw略有差异,但是可以规避的。跨平台线程编程使用boost或者intel TBB,网络编程使用ACE或者ICE。一般编程使用boost或者Loki。第一次先说明编译器的兼容性第一个例子#include <iostream>using namespace std;void main(int argc, char* argv[]){ char str1[] = "abc";    char str2[] = "abc";     const char str3[] = "abc";    const char str4[] = "abc";     const char *str5 = "abc";    const char *str6 = "abc";     char *str7 = "abc";    char *str8 = "abc";     cout << (str1 == str2) << endl;    cout << (str3 == str4) << endl;    cout << (str5 == str6) << endl;    cout << (str7 == str8) << endl;} 注意这里用的是 #include <iostream>后面是using namespace std;不是 #include <iostream.h>, iostream.h不是标准c++的头文件,具体的原因看c++手册,我就不多说了。vc的结果是 0 0 1 1看一下vc的反编译代码,vc的反编译确实不大好用8:        char str1[] = "abc";00401058   mov         eax,[string "abc" (0042801c)]0040105D   mov         dword ptr [ebp-4],eax9:        char str2[] = "abc";00401060   mov         ecx,dword ptr [string "abc" (0042801c)]00401066   mov         dword ptr [ebp-8],ecx10:11:       const char str3[] = "abc";00401069   mov         edx,dword ptr [string "abc" (0042801c)]0040106F   mov         dword ptr [ebp-0Ch],edx12:       const char str4[] = "abc";00401072   mov         eax,[string "abc" (0042801c)]00401077   mov         dword ptr [ebp-10h],eax13:14:       const char *str5 = "abc";0040107A   mov         dword ptr [ebp-14h],offset string "abc" (0042801c)15:       const char *str6 = "abc";00401081   mov         dword ptr [ebp-18h],offset string "abc" (0042801c)16:17:       char *str7 = "abc";00401088   mov         dword ptr [ebp-1Ch],offset string "abc" (0042801c)18:       char *str8 = "abc";0040108F   mov         dword ptr [ebp-20h],offset string "abc" (0042801c)str5 str6 str7和str8使用同一个地址borland cbuilder的结果0 0  0  0,bcc32 -S 反编译的文件 char str1[] = "abc"; ; @1: mov eax,dword ptr [$mecgibia] mov dword ptr [ebp-4],eax ;  ;     char str2[] = "abc"; ;  mov edx,dword ptr [$eicgibia] mov dword ptr [ebp-8],edx ;  ;   ;     const char str3[] = "abc"; ;  mov ecx,dword ptr [$mlcgibia] mov dword ptr [ebp-12],ecx ;  ;     const char str4[] = "abc"; ;  mov eax,dword ptr [$epcgibia] mov dword ptr [ebp-16],eax ;  ;   ;     const char *str5 = "abc"; ;  mov ebx,offset s@ ;  ;     const char *str6 = "abc"; ; ?live16385@96: ; EBX = str5 mov esi,offset s@+4 ;  ;   ;     char *str7 = "abc"; ; ?live16385@112: ; EBX = str5, ESI = str6 mov edi,offset s@+8 ;  ;     char *str8 = "abc"; ; ?live16385@128: ; EBX = str5, ESI = str6, EDI = str7 mov dword ptr [ebp-20],offset s@+12str1-str4使用堆栈,str5-7使用寄存器,str8使用堆栈,所以比较地址是不同的。gcc的结果0 0 1 1,我使用codeblock,不大喜欢devcpp因为这个delphi开发的c++ide确实不咋的,汇编代码0x4013ba: mov    eax,ds:0x43e0000x4013bf: mov    DWORD PTR [ebp-4],eax0x4013c2: mov    eax,ds:0x43e0000x4013c7: mov    DWORD PTR [ebp-8],eax0x4013ca: mov    eax,ds:0x43e0000x4013cf: mov    DWORD PTR [ebp-12],eax0x4013d2: mov    eax,ds:0x43e0000x4013d7: mov    DWORD PTR [ebp-16],eaxstr5-str8 全是一个地址0x4013da: mov    DWORD PTR [ebp-20],0x43e0000x4013e1: mov    DWORD PTR [ebp-24],0x43e0000x4013e8: mov    DWORD PTR [ebp-28],0x43e0000x4013ef: mov    DWORD PTR [ebp-32],0x43e000还有gcc下main函数的返回值必须int,其实这也是c++规定的,但是vc bcb 返回值为void也可以的。第二个例子 j++与++j ,j++是先用后加,++j是先加后用,#include <iostream>using namespace std;int main(int argc, char* argv[]){ int n=0,j=7; n=(++j)+(++j)+(++j); cout <<n <<endl;}如果n=(j++)+(j++)+(j++);大家没有意见是21,可以n=(++j)+(++j)+(++j);呢vc的结果是289:        int n=0,j=7;00401798   mov         dword ptr [ebp-4],00040179F   mov         dword ptr [ebp-8],710:       n=(++j)+(++j)+(++j);004017A6   mov         eax,dword ptr [ebp-8]004017A9   add         eax,1004017AC   mov         dword ptr [ebp-8],eax004017AF   mov         ecx,dword ptr [ebp-8]004017B2   add         ecx,1004017B5   mov         dword ptr [ebp-8],ecx004017B8   mov         edx,dword ptr [ebp-8]004017BB   add         edx,dword ptr [ebp-8]004017BE   mov         eax,dword ptr [ebp-8]004017C1   add         eax,1004017C4   mov         dword ptr [ebp-8],eax004017C7   add         edx,dword ptr [ebp-8]004017CA   mov         dword ptr [ebp-4],edxvc是取出[ebp-8]的值7,放到eax,然后加1,因为内存地址不允许直接加,然后把eax放回[ebp-8],然后取出放到ecx,然后加1,再放回[ebp-8],此时值为9,然后取出放到edx,然后加[ebp-8]的值9,这是edx是18,然后取出[ebp-8]的值9,放到eax,加1在放回[ebp-8]这是[ebp-8]的值是10,然后加上edx的值,这个结果就是28.gcc的结果28 0x4013ba: mov    DWORD PTR [ebp-4],0x00x4013c1: mov    DWORD PTR [ebp-8],0x70x4013c8: lea    eax,[ebp-8]0x4013cb: inc    DWORD PTR [eax]0x4013cd: lea    eax,[ebp-8]0x4013d0: inc    DWORD PTR [eax]0x4013d2: mov    eax,DWORD PTR [ebp-8]0x4013d5: mov    edx,DWORD PTR [ebp-8]0x4013d8: add    edx,eax0x4013da: lea    eax,[ebp-8]0x4013dd: inc    DWORD PTR [eax]0x4013df: mov    eax,edx0x4013e1: add    eax,DWORD PTR [ebp-8]0x4013e4: mov    DWORD PTR [ebp-4],eax0x4013e7: mov    eax,DWORD PTR [ebp-4][ebp-8]的地址给eax,然后直接加1,这是值是8,然后在加1 ,值变为9,然后[ebp-8]放到eax,edx然后相加,edx的值为18,然后[ebp-8]的值加1变为10,然后和edx相加,edx的值是28,然后edx给eax,用于输出。borlandc的结果是30 mov eax,7 ;  ;  n=(++j)+(++j)+(++j); ; ?live16385@32: ; EAX = j inc eax inc eax inc eax lea edx,dword ptr [eax+eax] add eax,edx ;  ;  cout <<n <<endl; ; ?live16385@48: ; EAX = n道理很简单,borland的程序员比较懒,先把所有的值加完了,再计算。第3个例子        for(int i=0;i<3;i++)  printf("%d\n",i); i=9; printf("%d\n",i);学过c的程序员,认识后面的i=9不能编译通过,但是vc6是可以的。vc的汇编代码9:        for(int i=0;i<3;i++)00401268   mov         dword ptr [ebp-4],00040126F   jmp         main+2Ah (0040127a)00401271   mov         eax,dword ptr [ebp-4]00401274   add         eax,100401277   mov         dword ptr [ebp-4],eax0040127A   cmp         dword ptr [ebp-4],30040127E   jge         main+43h (00401293)10:           printf("%d\n",i);00401280   mov         ecx,dword ptr [ebp-4]00401283   push        ecx00401284   push        offset string "%d\n" (0043101c)00401289   call        printf (00401550)0040128E   add         esp,800401291   jmp         main+21h (00401271)11:       i=9;00401293   mov         dword ptr [ebp-4],912:       printf("%d\n",i);0040129A   mov         edx,dword ptr [ebp-4]0040129D   push        edx0040129E   push        offset string "%d\n" (0043101c)004012A3   call        printf (00401550)004012A8   add         esp,8放在[ebp-4]中,当然可以的。for(int i=0;i<3;i++)  printf("%d\n",i);for(int i=9;i<12;i++)  printf("%d\n",i);是正确地,vc编译出现error C2374: 'i' : redefinition; multiple initialization。最后一个例子        int n=4; int a[n]; for(int i=0;i<n;i++) {      a[i]=n; }学过谭浩强的c的人认为他是错的,但是在gcc下他是对的,其实很好理解,int a[n],是在栈上分配的空间,真不理解c的创始人和c标准委员会的人如何想的。


阅读全文(3235) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.063 second(s), page refreshed 144781809 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号