2006年04月27日

UTF8并不算是一种电脑编码,而是一种储存和传送的格式,如前所述,每个Unicode/UCS字符都以 2或4个bytes来储存,看看以下的比较:

  以"I am Chinese"为例
   用ANSI储存:12 Bytes
   用Unicode/UCS2储存:24 Bytes + 2 Bytes(header)
   用UCS4储存:48 Bytes + 4 Bytes(header)

  以"我是中国人"为例
   用ANSI储存:10 Bytes
   用Unicode/UCS2储存:10 Bytes + 2 Bytes(header)
   用UCS4储存:20 Bytes + 4 Bytes(header)

  由此可见直接以Unicode/UCS的原始形式来储存是一种极大的浪费,而且也不利于互联网的传输(中文稍为合算一点^_^)。

  有见及此,Unicode/UCS的压缩形式--UTF8出现了,套用官方网站的首句话『UTF-8 stands for Unicode Transformation Format-8. It is an octet (8-bit) lossless encoding of Unicode characters.』,由于UTF也适用于编码UCS,故亦可称为『UCS transformation formats (UTF)』

  UTF8是以8bits即1Bytes为编码的最基本单位,当然也可以有基于16bits和32bits的形式,分别称为UTF16和UTF32,但目前用得不多,而UTF8则被广泛应用在文件储存和网络传输中。


编码原理

先看这个模板:

UCS-4 range (hex.) UTF-8 octet sequence (binary)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx

0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0400 0000-7FFF FFFF 1111110x 10xxxxxx … 10xxxxxx

编码步骤:
1) 首先确定需要多少个8bits(octets)
2) 按照上述模板填充每个octets的高位bits
3) 把字符的bits填充至x中,字符顺序:低位→高位,UTF8顺序:最后一个octet的最末位x→第一个octet最高位x
4) 解码的原理一样。

实例:(留意每个bit的颜色,粗体字为模板内容)

UCS-4 UTF-8
HEX BIN Bytes BIN HEX Bytes
0000 000A 00001010 4 00001010 0A 1
0000 0099 10011001 4 11000010 10011001 C2 99 2
0000 8D99 10001101 10011001 4 11101000 10110110 10011001 E8 B6 99 3

  不知大家看懂了没有,其实不懂也无所谓,反正又不用自己算,程式可以完全代劳。

  以UTF8格式储存的文件档首标识为EF BB BF。


效率

  从上述编码原理中得出的结论是:
   1.每个英文字母、数字所占的空间为1 Byte;
   2.泛欧语系、斯拉夫语字母占2 Bytes;
   3.汉字占3 Bytes。

  由此可见UTF8对英文来说是个非常诱人的方案,但对中文来说则不太合算,无论用ANSI还是 Unicode/UCS2来编码都只用2 Bytes,但用UTF8则需要3 Bytes。

  以下是一些统计资料,显示用UTF8来储存文件每个字符所需的平均字节:
   1.拉丁语系平均用1.1 Bytes;
   2.希腊文、俄文、阿拉伯文和希伯莱文平均用1.7 Bytes;
   3.其他大部份文字如中文、日文、韩文、Hindi(北印度语)用约3 Bytes;
   4.用超过4 Bytes的都是些非常少用的文字符号。

2006年04月19日

1. 县长讲完以后,主持人说:“咸菜请香肠酱瓜!”

2. 乡长说:“兔子们,虾米们,猪尾巴!不要酱瓜,咸菜太贵了!”

3. 乡长说:“不要酱瓜,我捡个狗屎让你们舔舔!”

4. 乡长说:“兔子们!今天的饭狗吃了!大家都是大王八!”

普通话翻译

(1) 现在请乡长讲话!

(2) 同志们,乡民们,注意吧!不要讲话,现在开会了!

(3) 不要讲话,我讲个故事让你们听听!

(4) 同志们!今天的饭够吃了!大家都使大腕吧!

2006年04月14日

这次延边队在郑州跟河南队碰头了。

河南队这场是第一个主场比赛,肯定非常重视。我们延边队可要加把劲儿啊!

这场比赛由郑州电视台二套进行直播。

我在网上搜了网上直播地址,效果不是很好,但总比没有好啊!

延边队,加油!

2006年04月06日

http://publishblog.blogdriver.com/blog/tb.b?diaryID=793127

谁让纸版图书利润那么高 让人买不起那
谁让电子版图书 得来不费功夫那
不过电子版图书 费的是你的眼睛 这个代价不小呀
我在网上申请了一个网络硬盘, 里面有一些软考教材及辅导书
如果 你还需要 什么图书 可以留言 我尽量满足  

方法一:

终于有了自己的免费FTP(不用担心被人删除或占为己有了),大家快去下载吧

地址:ftp://f769chzg991@769.cc

方法二:

另外,我有个邮箱,chzg99@56.com,容量2G,速度快,还可以共享,我把书也都放在共享文件夹了,不过你必须有自己的56.com信箱,才可以看我的共享文件夹。你也去申请吧,地址:
http://www.56.com/index.html

最近更新(11月9日)

2005软件设计师教程(终于做成了最新版的考试指定书)
陈省生先生在南大的讲课纪录
健康的钥匙在你自己手中
大道至简??软件工程实践者的思想

最近更新(9月18日)
DD省某银行扣税系统需求说明书
C案例分析-开发综合程序
软件工程??实践者的研究方法
软件工程知识体系指南
东软管理文档
together6.0学习指南
实战C++9个别具特色的实验
实战JAVA9个别具特色的实验
C++ 语言命令详解(第二版)

最近更新(7月21号): 

  UML参考手册.doc   2669KB    
  系统设计师(高级程序员)级考试辅导书.pdf  
  系统设计师(高级程序员)教程.pdf   10995KB  
  系统设计师(高级程序员)教程同步辅导.pdf   9851KB  
  TCP详解.doc   417KB  
  Word 排版??.rar   1899KB   
  人月神话.doc   33KB   
  软件水平软件设计师训练与测试系统.exe   24758KB   
  软件设计师训练与测试系统补丁.exe   2808KB   
  04年11月软件设计师上午试题详解.rar   372KB  
  UML基础案例应用.pdf   243KB  
  东软管理文档(软件工程).rar   2093KB     
  高级程序员考试试题分类精解(第二版).pdf   9633KB  
  算 法 设 计 题 集.zip   58KB    
  实用算法的分析与程序设计.pdf   10991KB 

   UML学习
   UML用户指南
   数据结构与算法综合资料库

   实用数据结构(徐士良)
  实用数据结构题解

2006年03月27日

 

编译参数的设置

 

       主要通过VC的菜单项Project->Settings->C/C++页来完成。我们可以看到这一页的最下面Project Options中的内容,一般如下:

 

/nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fp"Debug/WritingDlgTest.pch" /Yu"stdafx.h" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c

 

  各个参数代表的意义,可以参考Msdn。比如/nologo表示编译时不在输出窗口显示这些设置(我们可以把这个参数去掉来看看效果)等等。一般我们不会直接修改这些设置,而是通过这一页最上面的Category中的各项来完成。

 

1) General:一些总体设置

  • Warning level用来控制警告信息,其中Level 1是最严重的级别;
  • Warnings as errors将警告信息当作错误处理;
  • Optimizations是代码优化,可以在CategoryOptimizations项中进行更细的设置;
  • Generate browse info用以生成.sbr文件,记录类、变量等符号信息,可以在CategoryListing Files项中进行更多的设置。
  • Debug info,生成调试信息:
    • None,不产生任何调试信息(编译比较快)
    • Line Numbers Only,仅生成全局的和外部符号的调试信息到.obj文件或.exe文件,减小目标文件的尺寸;
    • C 7.0- Compatible,记录调试器用到的所有符号信息到.obj文件和.exe文件;
    • Program Database,创建.pdb文件记录所有调试信息;
    • Program Database for "Edit & Continue",创建.pdb文件记录所有调试信息,并且支持调试时编辑。

 

2) C++ Language

  • pointer_to_member representation用来设置类定义/引用的先后关系,一般为Best-Case Always表示在引用类之前该类肯定已经定义了;
  • Enable Exception Handling,进行同步的异常处理;
  • Enable Run-Time Type Information迫使编译器增加代码在运行时进行对象类型检查;
  • Disable Construction Displacements,设置类构造/析构函数调用虚函数问题。

 

3) Code Generation

  • Processor表示代码指令优化,可以为8038680486PentiumPentium Pro,或者Blend表示混合以上各种优化。
  • Use run-time library用以指定程序运行时使用的运行时库(单线程或多线程,Debug版本或Release版本),有一个原则就是,一个进程不要同时使用几个版本的运行时库。
    • Single-Threaded,静态连接libc.lib库;
    • Debug Single-Threaded,静态连接libcd.lib库;
    • Multithreaded,静态连接libcmt.lib库;
    • Debug Multithreaded,静态连接libcmtd.lib库;
    • Multithreaded DLL,动态连接msvcrt.dll库;
    • Debug Multithreaded DLL,动态连接msvcrtd.dll库。
    • 连接了单线程库就不支持多线程调用,连接了多线程库就要求创建多线程的应用程序。
  • Calling convention可以用来设定调用约定。
    • __cdecl:函数调用时,函数的参数是从左到右压入堆栈还是从右到左压入堆栈;
    • __fastcall:在函数返回时,由函数的调用者来清理压入堆栈的参数还是由函数本身来清理;
    • __stdcall:在编译时对函数名进行的命名修饰(可以通过Listing Files看到各种命名修饰方式)
  • Struct member alignment用以指定数据结构中的成员变量在内存中是按几字节对齐的,根据计算机数据总线的位数,不同的对齐方式存取数据的速度不一样。这个参数对数据包网络传输等应用尤为重要,不是存取速度问题,而是数据位的精确定义问题,一般在程序中使用#pragma pack来指定。

 

4) Customize

  • Disable Language Extensions,表示不使用微软为标准C做的语言扩展;
  • Eliminate Duplicate Strings,主要用于字符串优化(将字符串放到缓充池里以节省空间),使用这个参数,使得

char *sBuffer = "This is a character buffer";

char *tBuffer = "This is a character buffer";

sBuffertBuffer指向的是同一块内存空间;

  • Enable Function-Level Linking ,告诉编译器将各个函数按打包格式编译;
  • Enables minimal rebuild,通过保存关联信息到.idb文件,使编译器只对最新类定义改动过的源文件进行重编译,提高编译速度;
  • Enable Incremental Compilation,同样通过.idb文件保存的信息,只重编译最新改动过的函数;
  • Suppress Startup Banner and Information Messages,用以控制参数是否在output窗口输出。

 

5) Listing Files

  • Generate browse info的功能上面已经提到过。这里可以进行更多的设置。
  • Exclude Local Variables from Browse Info表示是否将局部变量的信息放到.sbr文件中。
  • Listing file type可以设置生成的列表信息文件的内容:
    • Assembly-Only Listing仅生成汇编代码文件(.asm扩展名)
    • Assembly With Machine Code生成机器代码和汇编代码文件(.cod扩展名)
    • Assembly With Source Code生成源代码和汇编代码文件(.asm扩展名)
    • Assembly, Machine Code, and Source生成机器码、源代码和汇编代码文件(.cod扩展名)
  • Listing file name为生成的信息文件的路径,一般为DebugRelease目录下,生成的文件名自动取源文件的文件名。

 

6) Optimizations:代码优化设置。

  • 可以选择Maximize Speed生成最快速的代码,或Minimize Size生成最小尺寸的程序,或者Customize定制优化。定制的内容包括:
  • Assume No Aliasing,不使用别名(提高速度)
  • Assume Aliasing Across Function Calls,仅函数内部不使用别名;
  • Global Optimizations,全局优化,比如经常用到的变量使用寄存器保存,或者循环内的计算优化,如

i = -100;

while( i < 0 ){ i += x + y;}

会被优化为

i = -100;

t = x + y;

while( i < 0 ){i += t;}

  • Generate Intrinsic Functions,使用内部函数替换一些函数调用(提高速度)
  • Improve Float Consistency,浮点运算方面的优化;
  • Favor Small Code,程序(exedll)尺寸优化优先于代码速度优化;
  • Favor Fast Code,程序(exedll)代码速度优化优先于尺寸优化;
  • Frame-Pointer Omission,不使用帧指针,以提高函数调用速度;
  • Full Optimization,组合了几种参数,以生成最快的程序代码。
  • Inline function expansion,内联函数扩展的三种优化(使用内联可以节省函数调用的开销,加快程序速度)
    • Disable不使用内联;
    • Only __inline,仅函数定义前有inline__inline标记使用内联;
    • Any Suitable,除了inline__inline标记的函数外,编译器“觉得”应该使用内联的函数,都使用内联。

 

7) Precompiled Headers

  • 预编译头文件的设置。使用预编译可以提高重复编译的速度。VC一般将一些公共的、不大变动的头文件(比如afxwin.h)集中放到stdafx.h中,这一部分代码就不必每次都重新编译(除非是Rebuild All)

 

8) Preprocessor:预编译处理。可以定义/解除定义一些常量。

  • Additional include directories,可以指定额外的包含目录,一般是相对于本项目的目录,如..\Include

 

 

连接参数的设置

 

       主要通过VC的菜单项Project->Settings->Link页来完成。我们可以看到这一页的最下面Project Options中的内容,一般如下:

/nologo /subsystem:windows /incremental:yes /pdb:"Debug/WritingDlgTest.pdb" /debug /machine:I386 /out:"Debug/WritingDlgTest.exe" /pdbtype:sept

 

  下面我们分别来看一下Category中的各项设置。

 

1) General:一些总体设置。

  • 可以设置生成的文件路径、文件名;连接的库文件;
  • Generate debug info,生成Debug信息到.pdb文件(具体格式可以在Category->Debug中设置)
  • Ignore All Default Libraries,放弃所有默认的库连接;
  • Link Incrementally,通过生成. ilk文件实现递增式连接以提高后续连接速度,但一般这种方式下生成的文件(exedll)较大;
  • Generate Mapfile,生成.map文件记录模块相关信息;
  • Enable Profiling,这个参数通常与Generate Mapfile参数同时使用,而且如果产生Debug信息的话,不能用.PDB文件,而且必须用Microsoft Format

 

2) Customize:这里可以进行使用程序数据库文件的设置。

  • Force File Output ,强制产生输出文件(exedll)
  • Print Progress Messages,可以将连接过程中的进度信息输出到Output窗口。

 

3) Debug

  • 设置是否生成调试信息,以及调试信息的格式。格式可以有Microsoft FormatCOFF Format(Common Object File Format)Both Formats三种选择;
  • Separate Types,表示将Debug格式信息以独立的.pdb文件存放,还是直接放在各个源文件的.pdb文件中。选中的话,表示采用后者的方式,这种方式调试启动比较快。

 

4) Input

  • 这里可以指定要连接的库文件,放弃连接的库文件。还可以增加额外的库文件目录,一般是相对于本项目的目录,如..\Lib
  • Force Symbol References,可以指定连接特定符号定义的库。

 

5) Output

  • Base Address可以改变程序默认的基地址(exe文件默认为0×400000dll默认为x10000000),操作系统装载一个程序时总是试着先从这个基地址开始。
  • Entry-Point Symbol可以指定程序的入口地址,一般为一个函数名(且必须采用__stdcall调用约定)。一般Win32的程序,exe的入口为WinMaindll的入口为DllEntryPoint;最好让连接器自动设置程序的入口点。默认情况下,通过一个C的运行时库函数来实现:
    • 控制台程序采用mainCRTStartup(wmainCRTStartup)去调用程序的main(wmain)函数;
    • Windows程序采用WinMainCRTStartup(wWinMainCRTStartup)调用程序的WinMain(wWinMain,必须采用__stdcall调用约定)
    • dll采用_DllMainCRTStartup调用DllMain函数(必须采用__stdcall调用约定)
  • Stack allocations,用以设置程序使用的堆栈大小(请使用十进制),默认为1兆字节。
  • Version Information告诉连接器在exedll文件的开始部分放上版本号。

  

       值得注意的是,上面各个参数是大小写敏感的;在参数后加上“-”表示该参数无效;各个参数值选项

有“*”的表示为该参数的默认值;可以使用页右上角的“Reset”按钮来恢复该页的所有默认设置。

  

其它一些参数设置

1) Project->Settings->General,可以设置连接MFC库的方式(静态或动态)。如果是动态连接,在你的软件发布时不要忘了带上MFCdll

2) Project->Settings->Debug,可以设置调试时运行的可执行文件,以及命令行参数等。

3) Project->Settings->Custom Build,可以设置编译/连接成功后自动执行一些操作。比较有用的是,写COM时希望VC对编译通过的COM文件自动注册,可以如下设置:

   Description: Register COM

   Commands: regsvr32 /s /c $(TargetPath)

   echo regsvr32 exe.time > $(TargetDir)\$(TargetName).trg

   Outputs: $(TargetDir)\$(TargetName).trg

4) Tools->Options->Directories,设置系统的IncludeLibrary路径。

 

一些小窍门

1) 有时候,你可能在编译的时候,计算机突然非法关机了(可能某人不小心碰了电源或你的内存不稳定等原因)。当你重启机器后打开刚才的项目,重新进行编译,发现VC会崩掉。你或许以为你的VC编译器坏了,其实不然(你试试编译其它项目,还是好的!),你只要将项目的.ncb.opt.aps.clw文件以及DebugRelease目录下的所有文件都删掉,然后重新编译就行了。

2) 如果你想与别人共享你的源代码项目,但是把整个项目做拷贝又太大。你完全可以删掉以下文件:.dsw.ncb.opt.aps.clw. plg文件以及DebugRelease目录下的所有文件。

3) 当你的Workspace中包含多个Project的时候,你可能不能直观地、一眼看出来哪个是当前项目。可以如下设置:Tools->Options->Format,然后在Category中选择Workspace window,改变其默认的字体(比如设成Fixedsys)就行了。

4) 如何给已有的Project改名字?将该Project关掉。然后以文本格式打开.dsp文件,替换原来的Project名字即可。

5) VC6对类成员的智能提示功能很有用,但有时候会失灵。你可以先关掉项目,将.clw.ncb删掉,然后重新打开项目,点击菜单项View->ClassWizard,在弹出的对话框中按一下“Add All”按钮;重新Rebuild All。应该可以解决问题。

 

源文档 <http://www.yesky.com/20030320/1658358_1.shtml>

 

 

表示数据的方向性。

  • IN参数:从外部添加数据后,再传递给函数的参数。
  • OUT参数:只传递变量的指针,函数得到指针后,添加数据。
  • IN, OUT参数:给变量添加数据后,传递给函数,在函数中再添加数据。
2006年03月24日

1.堆内存

  堆(heap)是内存空间。堆是区别于栈区、全局数据区和代码区的另一个内存区域。堆允许程序在运行时(而不是在编译时),申请某个大小的内存空间。
  在通常情况,一旦定义了一个数组, 那么不管这个数组是局部的(在栈中分配)还是全局的(在全局数据区分配),它的大小在程序编译时即是已知的,因为必须用一个常数对数组的大小进行声明:
    int i=10;
    //….
    int a[i];  //error: 定义时不允许数组元素个数为变量
    int b[20]; //ok

  但是,在编写程序时不是总能知道数组应该定义成多大,如果定义数组元素多了就浪费内存了,更何况有时候根本不知道需要使用多少个数组元素。因此,需要在程序运行时从系 统中获取内存。
  程序在编译和连接时不予确定这种在运行中获取的内存空间,这种内存环境随着程序运行的进展而时大时小,这种内存就是堆内存,所以堆内存是动态的。堆内存也称动态内存

2.获得堆内存

  函数malloc()是C程序获得堆内存的一种方法,它在alloc.h头文件中声明。malloc()函数的原型为:
    void* malloc(size_t size);
    size_t即unsigned long。

  该函数从堆内存中“切下”一块size大小的内存,将指向该内存的地址返回。该内存中的内容是未知的。
  例如,下面的程序从堆中获取一个整数数组,赋值并打印:
    //**********************
    //**     func1.cpp    **
    //**********************

    #include <iostream.h>
    #include <alloc.h>

    void main()
    {
        int arraysize; //元素个数
       int *array;
       cout <<"please input a number of array:\n";
      cin >> arraysize;

       array = (int*)malloc(arraysize * sizeof(int));

      //堆内存分配

       for(int count = 0; count < arraysize; count++)
         array[count] = count * 2;

       for(int count = 0; count < arraysize; count++)
         cout << array[count] << " ";

       cout << endl;
    }


  运行结果为:
    C>func1
    please input a number Of array elements:
    10
    0  2  4  6  8  10  12  14  16  18

  程序编译和连接时,在栈中分配了arraysize整型变量和array整型指针变量空间。程序运行中,调用函数malloc(),并以键盘输入的整数值作为参数。malloc()函数在堆中寻找未被使用的内存,找够所需的字节数后返回该内存的起始地址。因为malloc()函数并不知道用这些内存干什么,所以它返回一个没有类型的指针。但对整数指针array来说,malloc()函数的返回值必须显式转换成整数类型指针才能被接受(ANSIC++标准)。
  一个拥有内存的指针完全可以被看作为一个数组,而且位于堆中的数组和位于栈中的数组结构是一样的。表达式“array[count] = count * 2;”正是这样应用的。
  上例中并没有保证一定可以从堆中获得所需内存。有时,系统能提供的堆空间不够分配,这时系统会返回一个空指针值NULL。这时所有对该指针的访问都是破坏性的,因此调用malloc()函数更完善的代码应该如下:
    if((array = (int *)malloc(arraysize * sizeof(int) == NULL)
    {
       cout << "can’t allocate more memory, terminate. \n";
       exit(1);
    }

3.释放堆内存

  我们把堆看作是可以按要求进行分配的资源或内存池。程序对内存的需求量随时会增大或缩小。程序在运行中可能经常会不再需要由malloc()函数分配的内存,而且程序还未运行结束,这时就需要把先前所占用的内存释放回堆以供程序的其它部分所用。
  函数free()返还由malloc()函数分配的堆内存,其函数原型为:
    void free(void*);
  free()参数是先前调用malloc()函数时返回的地址。把其它值传给free()很可能会造成灾难性的后果。
  例如,下面的程序完善程序mallocFunc.cpp:
    //***********************
    //**     func2.cpp    **
    //***********************

    #include <iostream.h>
    #include <alloc.h>

    void main()
    {
       int arraysize; //元素个数
       int *array;
       cout << "please input a number of array:\n";
      cin >> arraysize;

        if ((array=(int*)malloc(arraysize*sizeof(int))) == NULL)
      {

          cout<<"can’t allocate more memory,terminating.\n";
           return;

}

     for(int count=0; count<arraysize; count++)
       array[count]=count*2;

     for(int count=0; count<arraysize; count++)
       cout <<array[count] <<" ";

     cout <<endl;
     free(array); //释放堆内存
    }

  运行结果为:
    C>ch8_8
    please input a number Of array elements:
    10
    0  2  4  6  8  10  12  14  16  18

4.new与delete

  new和delete是C++专有的操作符,它们不用头文件声明。new类似于函数malloc(),分配堆内存,但比malloc()更简练。new的操作数为数据类型,它可以带初始化值表或单元个数。new返回一个具有操作数之数据类型的指针。返回delete类似于函数free(),释放堆内存。delete的操作数是new返回的指针,当返回的是new分配的数组时,应该带[]。
  例如,下面的程序是程序ch8_8.cpp的新版:
    //*********************
    //** ch8_9.cpp **
    //*********************

    #include <iostream.h>
    //#include <alloc.h> //无须头文件

    void main()
    {
     int arraysize; //元素个数
     int *array;
     cout <<"please input a number of array:\n";
      cin >>arraysize;

     if((array=new int[arraysize])==NULL){ //分配堆内存
      cout<<"can’t allocate more memory,terminating.\n";
      return ;
     }

     for(int count=0; count<arraysize; count++)
       array[count]=count*2;

     for(int count=0; count<arraysize; count++)
       cout <<array[count] <<" ";

     cout <<endl;
     delete[]array; //释放堆内存
    }

  运行结果为:
    C>ch8_9
    please input a number Of array elements:
    10
    0  2  4 6  8  10  12  14  16  18

  上例中可以看到,Hew的返回值无须显式转换类型, 直接赋给整数指针array;new的操作数是int[arraysize],它只要指明什么类型和要几个元素就可以了,它比函数malloc()更简洁。事实上,new和delete比函数malloc()和free()具有更丰富的功能。在第14章将进一步展开new和delete的讨论。

2006年03月23日

 

  1. 引言
    • C++语言的创建初衷为"a better C",这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。
    • C++保留了一部分过程式语言的特点,可以定义不属于任何类的全局变量和函数。
    • 为了支持函数的重载,C++对全局函数的处理方式与C有明显的不同。
  2. 从标准头文件说起
    • 某企业面试题

为什么标准头文件有类似以下的结构?

#ifndef __INCvxWorksh

#define __INCvxWorksh

#ifdef __cplusplus

extern "C"

{

#endif

/* … */

#ifdef __cplusplus

}

#endif

#endif /* __INCvxWorksh */

 

分析:防止该头文件被重复引用。

那么,extern "C"是由什么作用?

  1. 深层揭秘extern "C"
    • 双重含义:
      • 被它修饰的目标是"extern"
      • 被它修饰的目标是"C"
    • extern
      • externC/C++语言中表明函数和全局变量作用范围(可见性)的关键字。
      • extern告诉编译器,其声明的函数和变量可以在本模块或其他模块中使用。
      • 通常在模块的头文件中对本模块提供给其他模块引用的函数和全局变量以关键字extern声明。
        • 例如:如模块B欲引用模块A中所定义的全局变量和函数时,只需包含模块A的头文件即可。模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在link阶段从模块A编译生成的目标代码中找到此函数。
      • extern对应的关键字:static
        • static修饰的全局变量和函数只能在本模块中使用。
        • 一个函数或变量值可能被本模块使用时,其不可能被extern "C"修饰。
    • 按照C语言方式CompileLink
      • C++中对类似C的函数的编译:C++支持函数重载,而C不支持,函数被C++编译后在符号库中的名字与C语言的不同。
      • void foo(int x, int y);
        • C编译器编译后在符号库中的名字:_foo
        • C++编译器编译后的名字(mangled name)_foo_int_int
      • C++中的变量
        • 除了支持局部变量外,还支持类成员变量和全局变量。
        • 用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。
        • 编译器进行编译时,味蕾中的变量取一个独一无二的名字,这与用户程序中同名的全局变量名字不同。
      • 未加extern "C"声明时的连接方式
        • // 模块A头文件 moduleA.h

#ifndef MODULE_A_H

#define MODULE_A_H

int foo(int x, int y);

#endif

  • // 模块B实现文件 mobuleB.cpp

#include "moduleA.h"

Foo(2, 3);

  • Link阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号。
  • extern "C"声明后的编译和连接方式
    • // 模块A头文件 moduleA.h

#ifndef MODULE_A_H

#define MODULE_A_H

extern "C" int foo(int x, int y);

#endif // MODULE_A_H

  • 模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
  • 连接器在为模块B的目标代码寻找foo(2, 3)调用时,寻找的是未经修改的符号名_foo
  • 如果在模块A中函数声明了fooextern "C"类型,而模块B中包含的是extern int foo(int x, int y),则模块B找不到模块A中的函数,反之亦然。
  • extern "C"声明的真实目的:实现C++C及其它语言的混合编程。
  1. extern "C"的惯用法
    • C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)

Extern "C"

{

#include "cExample.h"

}

  • C语言的头文件中,对其外部函数只能指定为extern类型,C语言不支持extern "C"声明。
  • 实例:

/* c语言头文件:cExample.h */

#ifndef C_EXAMPLE_H

#define C_EXAMPLE_H

extern int add(int x, int y);

#endif

 

/* c语言实现文件:cExample.c */

#include "cExample.h"

int add(int x, int y)

{

    return x + y;

}

 

// C++实现文件,调用add:cppFile.cpp

extern "C"

{

#include "cExample.h"

}

int main()

{

    add(2, 3);

    return 0;

}

  • 如果C++调用一个C语言编写的.dll文件时,当包括.dll的头文件或声明接口函数时,应加extern "C"{ }
  • C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接应用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。

// C++头文件:cppExample.h

#ifndef CPP_EXAMPLE_H

#define CPP_EXAMPLE_H

extern "C" int add(int x, int y);

#endif

 

// C++实现文件:cppExample.cpp

#include "cppExample.h"

int add(int x, int y)

{

    return x + y;

}

 

/* C实现文件:cFile.c */

/* 这样会编译出错:#include "cppExample.h" */

extern int add(int x, int y);

Int main()

{

    add(2, 3);

    return 0;

}

  • 如深入理解了extern "C"在编译和连接阶段发挥的作用,就能真正理解从C++引用C函数和C引用C++函数的惯用法。

 

2006年03月21日

在创建MFC项目时, 不使用MFC AppWizard向导, 如果没有设置好项目参数, 就会在编译时产生很多连接错误, 如error LNK2001错误, 典型的错误提示有:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex

下面介绍解决的方法:

1. Windows子系统设置错误, 提示:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main

Windows项目要使用Windows子系统, 而不是Console, 可以这样设置:

[Project] –> [Settings] –> 选择"Link"属性页,
在Project Options中将/subsystem:console改成/subsystem:windows

2. Console子系统设置错误, 提示:
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16

控制台项目要使用Console子系统, 而不是Windows, 设置:

[Project] –> [Settings] –> 选择"Link"属性页,
在Project Options中将/subsystem:windows改成/subsystem:console

3. 程序入口设置错误, 提示:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16

通常, MFC项目的程序入口函数是WinMain, 如果编译项目的Unicode版本, 程序入口必须改为wWinMainCRTStartup, 所以需要重新设置程序入口:

[Project] –> [Settings] –> 选择"C/C++"属性页,
在Category中选择Output,
再在Entry-point symbol中填入wWinMainCRTStartup, 即可

4. 线程运行时库设置错误, 提示:
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex

这是因为MFC要使用多线程时库, 需要更改设置:

[Project] –> [Settings] –> 选择"C/C++"属性页,
在Category中选择Code Generation,
再在Use run-time library中选择Debug Multithreaded或者multithreaded

其中,
Single-Threaded 单线程静态链接库(release版本)
Multithreaded 多线程静态链接库(release版本)
multithreaded DLL 多线程动态链接库(release版本)
Debug Single-Threaded 单线程静态链接库(debug版本)
Debug Multithreaded 多线程静态链接库(debug版本)
Debug Multithreaded DLL 多线程动态链接库(debug版本)

单线程: 不需要多线程调用时, 多用在DOS环境下
多线程: 可以并发运行
静态库: 直接将库与程序Link, 可以脱离MFC库运行
动态库: 需要相应的DLL动态库, 程序才能运行
release版本: 正式发布时使用
debug版本: 调试阶段使用

2006年03月10日

요즘 어느날부터인가 갑자기 돈 없는 생활을 시작하게 되였다.

돈 없는 생활이라… 

회사 다니기전까지는 계속 돈 없이 살아왔었지만 취직후부턴 여태까지 이렇게 한푼이라도 쪼개면서 살아보기는 첨인것 같다.

지난달 이런 저런 비용이 많이 나갔기에 할수없이 신용카드 현금서비스를 받으며 살아야 했었다. (월급 나오기전까지만.)

근데 어느날 지갑에 돈이 얼마 남지 않아 ATM에서 현금을 뽑으려고 은행에 갔었는데 현금서비스가 안된다고 한다.

억!!! 이런~~~

유일한 내 명줄인 신용카드가 현금을 뽑지 못한다면 난 어떻게 하라고??? 몇번이나 카드를 집어넣고 그리운 100원짜리 몇장 뽑으려 했으나…(후에 알아본건데 현금서비스를 받을수 있는 금액이 제한되여 있다고 공상은행에서 설명해줬음. 그걸 내가 여태까지 모르고 있었던것이다…)

지갑에 몇십원은 있으니깐 퇴근할수는 있겠건만 월급 나올때까지는 어떻게 해야지?

집에 돌아온후 집안을 샅샅이 뒤졌다. 돈이 나올만한 곳은 전부 뒤져서 겨우 몇원 모았다.

꿀꿀이 저금통에 있는 동전이 생각났다. 1원짜리, 50전짜리 합쳐서 몇원은 되는것 같았다. 출퇴근 뻐스요금은 되겠군. 쩝!

이때에야 지난번에 피자 먹지 말았을걸 하는 생각이 들었다. 그뿐이 아니다. 주말에 놀러 갔으때 택시도 타지 말았을걸…….

돈이 있을때는 아낄려고 해도 잘 안되는것 같다. 어떻게 하든 무슨 이유가 꼭 나온다. 그래서 그 돈은 꼭 써야 하는걸로 되고 만다. 지금 다시 생각해보면 그렇지도 않은데…

돈이 없을때는 이상하게도 돈 쓸 일이 더 많아 지는것 같다.

이것 저것… 일일이 설명하지는 않겠다.

음…. 월급이 빨리 나와야 하는뎅…