分类 C++ 下的文章

Windows 7中系统服务进程无法使用cuda进行GPU并行计算的解决方案

最近测试提了一个Bug,原来一直正常的服务进程在新的系统中无法正常工作,现象就是显卡GPU没有正常工作。

这两天跟踪了一下问题,找到了原因,这里把具体的解决办法贴出来,方便有同样问题的朋友快速解决问题^_^。

原因分析:
Vista之后,操作系统引入了Session 0 Isolation的机制,将系统服务全部隔离到会话0中执行,但是会话0中的系统服务无法使用显卡设备,导致cuda在加载显卡驱动时失败,而无法启动GPU进行并行计算。

具体的会话0隔离机制可以参考MSDN文章:
http://msdn.microsoft.com/en-us/library/bb756986.aspx

解决方案:
找到了原因一切就好办了,从分析来看,主要是会话0中无法加载显卡驱动,所以这里需要将使用显卡的进程越狱到用户会话中,比如会话1等。

这个办法在CUDA的论坛中已经有过讨论了,这里有兄台给出了一些示例代码,可以参考:
http://forums.nvidia.com/index.php?showtopic=93450

这里说说我的具体解决方案:

  1. 在服务进程中,循环枚举可用用户会话ID
  2. 获取有效用户会话之后,获取对应用户的会话令牌
  3. 以该用户会话启动新的进程

涉及到的API如下:
通过WTSEnumerateSessions 获取所有有效会话,通过判断WTS_SESSION_INFO的State字段,获取活动会话ID
记得使用完之后,通过WTSFreeMemory释放相关内存
通过WTSQueryUserToken获取会话ID对应的会话Token
最后调用CreateProcessAsUser将进程启动到用户会话中,就可以了

总结:
其实上面的方法,相当于是延时启动,当有用户登陆之后,才启动真正的工作进程。
上述方案,需要对工程进行进程分离,将实际工作的进程独立起来,以启动到用户会话中。
有一个小问题,目前没有找到相关解决方案,有待进一步学习,有知道的朋友请分享一下^_^:
当开启UAC功能时,通过CreateProcessAsUser启动的进程无法获取管理员权限,如果工作进程需要管理员权限,可能需要关闭UAC功能才行。

(转)Windows 7 下无法使用DevPartner BoundsChecker 9.1进行调试的问题

问题描述如下:

点击Start with Error Detection时回出现错误提示,显示无法打开dpinjsvc服务

进一步的错误提示如下:

CRunManager::InjectNow() - BCAX::StartRecording() FAILED: hr = 0x80004005

解决方法:

添加localhost到127.0.0.1的本地DNS映射

在%WinDir%/System32/Drivers/Etc目录中,编辑hosts文件,在末尾添加两行

127.0.0.1 localhost

::1 localhost

然后重新启动计算机即可

这是官方给出的解决方案,经测试,能够解决问题,看来BoundsChecker的机制也是基于远程调试的。。。

(转)C++为什么不用delete代替delete[]?

总结:一直想不通c++为什么多此一举,呵呵,前几天给Bjarne Stroustrup大师写了一份信,第二天就收到回复了,自己再仔细琢磨了一下,终于好像弄明白了:-)

我的理解是这样的,无论new还是new[ ],C++的确知道返回的这个指针它指向多大的内存块,否则它就不可能正确地释放掉这块内存了。但是delete需要知道的不仅仅是指针指向多大的内存,而更重要的是要知道指针指向的数组中有多少个对象,知道了对象数量才能依次一一调用它们的析构函数。那么,下面的代码会产生什么样的结果??

int * pArray = new int[100];

……

delete pArray; //本来应该是 delete [ ] pArray;

根据上面的解释,我们不难想象,上述代码错误的原因并不是因为它只释放掉第一个元素的空间而没有能完全释放整个pArray所指的内存。 也就是说,在内存释放上不存在问题。问题出在在这里只对第一个元素调用了析构函数,而其他99个元素并没有被妥善地析构掉。呵呵,这才是真正的问题所在!! 当然对于整数类型来说并不会导致什么问题,但假如在每个对象中都分配了额外的资源,那么不调用对象的析构函数可就会导致严重的后果了……

那使用delete[ ]有什么用?这要从new[ ]说起,在new一个数组时,编译器会悄悄地在内存中保存一个整数用来表示数组中元素的个数。这样在使用delete[ ]时,编译器就会生成读取这个整数的代码,然后逐个调用析构函数。如果使用delete,则编译器只当作指针所指的是单个对象。再说了,new单个对象时,编译器也不会悄悄记录数组中元素的个数。呵呵,从这也可以感觉出C++的设计风格和宗旨,决不多费一点手脚,单个对象我就不记录长度,只有是数组时我才记录!:-)

下面是我给Bjarne Stroustrup的邮件及他的回信。在此对Bjarne Stroustrup大师平易近人的风范再次表示钦佩!!

Hi, Dr. Stroustrup

Firstly,Thank you for your great invention, the c++ programming language is
really a great job, it gives me a lot of help in my study, it makes my work much
more convenient!

Thanks

Well, today ,I run into a confused problem about delete and delete[], so I
wonder if you could give me some explanation or tips. I simply can't understand
why c++ don't treat them in the same way. In other words, I think there is no
need for delete[].

For example, "delete [] ps", many textbooks just say that the operator
delete[] is used to tell the compiler that the pointer "ps" refers to an array,
but not a single object. Of course, I know that, but the thing is that I think
the c++ compiler is totally clever enough to figure out whether "ps" points to a
single object or an array. There is no need to bother the programmer to point it
out explicitly.

No. The compiler cannot know.

void f(int* p)
{
delete p; // or maybe delete [] p?
}

void g()
{
int* q = new int[70];
f(q);
q = new int;;
f(q);
}

separately compile those two functions an the compiler had no way of
knowing how many ints p points to.

It's no doubt that the compiler knows the size of storage to which is pointed
by "ps". Otherwise, it's impossible for c++ to deallocate the memory properly.
OK, now that the compiler exactly knows how many bytes is allocated there and
which type the object is, it can compute how many object are there, I mean,
" number of object = number of bytes allocated / sizeof(object type) "
Ok, after that, c++ may call destructor iteratively to destroy each object in
the array and then deallocates the memory.

It is a bit trickier than that. Many allocators allocates space for at
least N ints when you ask for new int[N]. It is not uncommon for an
allocator to allocate more to minimize fragmentation.

I don't know if I have made my question clear:-) In conclusion, I think a
single object is just a special case of an array, so c++ is supposed to deal
with them in the same way. There is no need for us to differentiate delete and
delete[].

The snag is that many allocators (at least traditionally) just keep
track of the number of bytes allocated (rounding the allocation up to
something convenient). With destructors, the compiler has to know how
many objects are there (not just how much space is allocated). Some
implementers use (or used) two different layouts for individual objects
and arrays - the array layout (only) included an object count.

In fact, I know certainly this is my misunderstanding. if not that, the c++
committe should have changed the c++ syntax:-) I just hope to get some
convincing explanation.

Thank you, really appreciate for your help!

Supermonkey,China,Nankai University
2007-09-03

(转)GCC中__attribute__属性的使用

  在学习linux内核代码及一些开源软件的源码(如:DirectFB),经常可以看到有关__attribute__的相关使用。
  本文结合自己的学习经历,较为详细的介绍了__attribute__相关语法及其使用。

  GNU C的一大特色(却不被初学者所知)就是__attribute__机制。
  __attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。

__attribute__书写特征是:

  • __attribute__前后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的
  • __attribute__参数。
  • __attribute__语法格式为:
  • __attribute__ ((attribute-list))

其位置约束为:
放于声明的尾部“;”之前。

函数属性(Function Attribute)

  函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。__attribute__机制也很容易同非GNU应用程序做到兼容之功效。
  GNU CC需要使用 –Wall编译器来激活该功能,这是控制警告信息的一个很好的方式。

下面介绍几个常见的属性参数。

__attribute__ format

  该__attribute__属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。该功能十分有用,尤其是处理一些很难发现的bug。

format的语法格式为:
format (archetype, string-index, first-to-check)

  format属性告诉编译器,按照printf, scanf, strftime或strfmon的参数表格式规则对该函数的参数进行检查。“archetype”指定是哪种风格;“string-index”指定传入函数的第几个参数是格式化字符串;“first-to-check”指定从函数的第几个参数开始按上述规则进行检查。

具体使用格式如下:
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))

其中参数m与n的含义为:

  • m:第几个参数为格式化字符串(format string);
  • n:参数集合中的第一个,即参数“…”里的第一个参数在函数参数总数排在第几,注意,有时函数参数里还有“隐身”的呢,后面会提到;

  在使用上,__attribute__((format(printf,m,n)))是常用的,而另一种却很少见到。下面举例说明,其中myprint为自己定义的一个带有可变参数的函数,其功能类似于printf:

//m=1;n=2
extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));
//m=2;n=3
extern void myprint(int l,const char *format,...) __attribute__((format(printf,2,3)));

需要特别注意的是,如果myprint是一个函数的成员函数,那么m和n的值可有点“悬乎”了,例如:
//m=3;n=4
extern void myprint(int l,const char *format,...) __attribute__((format(printf,3,4)));

其原因是,类成员函数的第一个参数实际上一个“隐身”的“this”指针。(有点C++基础的都知道点this指针,不知道你在这里还知道吗?)

这里给出测试用例:attribute.c,代码如下:

extern void myprint(const char *format,...) __attribute__((format(printf,1,2)));

void test()
{
    myprint("i=%d\n",6);
    myprint("i=%s\n",6);
    myprint("i=%s\n","abc");
    myprint("%s,%d,%d\n",1,2);
}

运行$gcc –Wall –c attribute.c attribute后,输出结果为:

attribute.c: In function `test':
attribute.c:7: warning: format argument is not a pointer (arg 2)
attribute.c:9: warning: format argument is not a pointer (arg 2)
attribute.c:9: warning: too few arguments for format

  如果在attribute.c中的函数声明去掉__attribute__((format(printf,1,2))),再重新编译,既运行$gcc –Wall –c attribute.c attribute后,则并不会输出任何警告信息。
  注意,默认情况下,编译器是能识别类似printf的“标准”库函数。

__attribute__ noreturn

  该属性通知编译器函数从不返回值,当遇到类似函数需要返回值而却不可能运行到返回值处就已经退出来的情况,该属性可以避免出现错误信息。C库函数中的abort()和exit()的声明格式就采用了这种格式,如下所示:

extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));

  为了方便理解,大家可以参考如下的例子:

//name: noreturn.c  ;测试__attribute__((noreturn))
extern void myexit();

int test(int n)
{
       if ( n > 0 )
       {
              myexit();
              /* 程序不可能到达这里*/
       }
       else
              return 0;
}

编译显示的输出信息为:

$gcc –Wall –c noreturn.c
noreturn.c: In function `test':
noreturn.c:12: warning: control reaches end of non-void function

  警告信息也很好理解,因为你定义了一个有返回值的函数test却有可能没有返回值,程序当然不知道怎么办了!
  加上__attribute__((noreturn))则可以很好的处理类似这种问题。把
extern void myexit();
  修改为:
extern void myexit() __attribute__((noreturn));
之后,编译不会再出现警告信息。

__attribute__ const

  该属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外,其它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态(static state)和副作用的一些函数,并且返回值仅仅依赖输入的参数。
  为了说明问题,下面举个非常“糟糕”的例子,该例子将重复调用一个带有相同参数值的函数,具体如下:

extern int square(int n) `__attribute__`((const));
...
    for (i = 0; i < 100; i++ )
    {
        total += square(5) + i;
    }

  通过添加__attribute__((const))声明,编译器只调用了函数一次,以后只是直接得到了相同的一个返回值。
  事实上,const参数不能用在带有指针类型参数的函数中,因为该属性不但影响函数的参数值,同样也影响到了参数指向的数据,它可能会对代码本身产生严重甚至是不可恢复的严重后果。
  并且,带有该属性的函数不能有任何副作用或者是静态的状态,所以,类似getchar()或time()的函数是不适合使用该属性的。

-finstrument-functions

  该参数可以使程序在编译时,在函数的入口和出口处生成instrumentation调用。恰好在函数入口之后并恰好在函数出口之前,将使用当前函数的地址和调用地址来调用下面的 profiling 函数。(在一些平台上,__builtin_return_address不能在超过当前函数范围之外正常工作,所以调用地址信息可能对profiling函数是无效的。)

void __cyg_profile_func_enter(void *this_fn, void *call_site);
void __cyg_profile_func_exit(void *this_fn, void *call_site);

  其中,第一个参数this_fn是当前函数的起始地址,可在符号表中找到;第二个参数call_site是指调用处地址。
  instrumentation 也可用于在其它函数中展开的内联函数。从概念上来说,profiling调用将指出在哪里进入和退出内联函数。这就意味着这种函数必须具有可寻址形式。如果函数包含内联,而所有使用到该函数的程序都要把该内联展开,这会额外地增加代码长度。如果要在C 代码中使用extern inline声明,必须提供这种函数的可寻址形式。
  可对函数指定no_instrument_function属性,在这种情况下不会进行instrumentation操作。例如,可以在以下情况下使用no_instrument_function属性:上面列出的profiling函数、高优先级的中断例程以及任何不能保证profiling正常调用的函数。

no_instrument_function

  如果使用了-finstrument-functions ,将在绝大多数用户编译的函数的入口和出口点调用profiling函数。使用该属性,将不进行instrument操作。

constructor/destructor

  若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动的执行。类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行。拥有此类属性的函数经常隐式的用在程序的初始化数据方面。
  这两个属性还没有在面向对象C中实现。

同时使用多个属性

  可以在同一个函数声明里使用多个__attribute__,并且实际应用中这种情况是十分常见的。使用方式上,你可以选择两个单独的__attribute__,或者把它们写在一起,可以参考下面的例子:

/* 把类似printf的消息传递给stderr 并退出 */
extern void die(const char *format, ...)
__attribute__((noreturn))
__attribute__((format(printf, 1, 2)));
或者写成

extern void die(const char *format, ...)
__attribute__((noreturn, format(printf, 1, 2)));

  如果带有该属性的自定义函数追加到库的头文件里,那么所以调用该函数的程序都要做相应的检查。

和非GNU编译器的兼容性

  庆幸的是,__attribute__设计的非常巧妙,很容易作到和其它编译器保持兼容,也就是说,如果工作在其它的非GNU编译器上,可以很容易的忽略该属性。即使__attribute__使用了多个参数,也可以很容易的使用一对圆括弧进行处理,例如:

/* 如果使用的是非GNU C, 那么就忽略__attribute__ */
#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif

  需要说明的是,__attribute__适用于函数的声明而不是函数的定义。所以,当需要使用该属性的函数时,必须在同一个文件里进行声明,例如:

/* 函数声明 */
void die(const char *format, ...) `__attribute__`((noreturn))
                                  `__attribute__`((format(printf,1,2)));
void die(const char *format, ...)
{
               /* 函数定义 */
}

更多的属性含义参考:
http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Function-Attributes.html

变量属性(Variable Attributes)

  关键字__attribute__也可以对变量(variable)或结构体成员(structure field)进行属性设置。这里给出几个常用的参数的解释,更多的参数可参考本文给出的连接。
  在使用__attribute__参数时,你也可以在参数的前后都加上“”(两个下划线),例如,使用__aligned而不是aligned,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。

aligned (alignment)

  该属性规定变量或结构体成员的最小的对齐格式,以字节为单位。例如:

int x __attribute__ ((aligned (16))) = 0;

  编译器将以16字节(注意是字节byte不是位bit)对齐的方式分配一个变量。也可以对结构体成员变量设置该属性,例如,创建一个双字对齐的int对,可以这么写:

struct foo { int x[2] __attribute__ ((aligned (8))); };

  如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果aligned后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。例如:

short array[3] __attribute__ ((aligned));

  选择针对目标机器最大的对齐方式,可以提高拷贝操作的效率。
  aligned属性使被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。
需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16字节对齐,那么你此时定义32字节对齐也是无济于事的。

packed

  使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。
下面的例子中,x成员变量使用了该属性,则其值将紧放置在a的后面:
```cpp
struct test
{
char a;
int x[2] __attribute__ ((packed));
};


其它可选的属性值还可以是:cleanup,common,nocommon,deprecated,mode,section,shared,tls_model,transparent_union,unused,vector_size,weak,dllimport,dlexport等, 详细信息可参考: http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes ##类型属性(Type Attribute)   关键字`__attribute__`也可以对结构体(struct)或共用体(union)进行属性设置。大致有六个参数值可以被设定,即:aligned, packed, transparent_union, unused, deprecated 和 may_alias。   在使用`__attribute__`参数时,你也可以在参数的前后都加上“__”(两个下划线),例如,使用__aligned__而不是aligned,这样,你就可以在相应的头文件里使用它而不用关心头文件里是否有重名的宏定义。 ###aligned (alignment) 该属性设定一个指定大小的对齐格式(以字节为单位),例如: struct S { short f[3]; } `__attribute__` ((aligned (8))); typedef int more_aligned_int `__attribute__` ((aligned (8)));   该声明将强制编译器确保(尽它所能)变量类型为struct S或者more-aligned-int的变量在分配空间时采用8字节对齐方式。   如上所述,你可以手动指定对齐的格式,同样,你也可以使用默认的对齐方式。如果aligned后面不紧跟一个指定的数字值,那么编译器将依据你的目标机器情况使用最大最有益的对齐方式。例如: struct S { short f[3]; } `__attribute__` ((aligned));   这里,如果sizeof(short)的大小为2(byte),那么,S的大小就为6。取一个2的次方值,使得该值大于等于6,则该值为8,所以编译器将设置S类型的对齐方式为8字节。   aligned属性使被设置的对象占用更多的空间,相反的,使用packed可以减小对象占用的空间。   需要注意的是,attribute属性的效力与你的连接器也有关,如果你的连接器最大只支持16字节对齐,那么你此时定义32字节对齐也是无济于事的。 ###packed 使用该属性对struct或者union类型进行定义,设定其类型的每一个变量的内存约束。当用在enum类型定义时,暗示了应该使用最小完整的类型(it indicates that the smallest integral type should be used)。 下面的例子中,my-packed-struct类型的变量数组中的值将会紧紧的靠在一起,但内部的成员变量s不会被“pack”,如果希望内部的成员变量也被packed的话,my-unpacked-struct也需要使用packed进行相应的约束。 struct my_unpacked_struct { char c; int i; }; struct my_packed_struct { char c; int i; struct my_unpacked_struct s; }`__attribute__` ((__packed__)); 其它属性的含义见: http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Type-Attributes.html#Type-Attributes ###变量属性与类型属性举例   下面的例子中使用`__attribute__`属性定义了一些结构体及其变量,并给出了输出结果和对结果的分析。 程序代码为: ```cpp struct p { int a; char b; char c; }`__attribute__`((aligned(4))) pp; struct q { int a; char b; struct n qn; char c; }`__attribute__`((aligned(8))) qq; int main() { printf("sizeof(int)=%d,sizeof(short)=%d.sizeof(char)=%d\n",sizeof(int),sizeof(short),sizeof(char)); printf("pp=%d,qq=%d \n", sizeof(pp),sizeof(qq)); return 0; }

输出结果:

sizeof(int)=4,sizeof(short)=2.sizeof(char)=1
pp=8,qq=24

分析:
sizeof(pp):
sizeof(a)+ sizeof(b)+ sizeof(c)=4+1+1=6<23=8= sizeof(pp)
sizeof(qq):
sizeof(a)+ sizeof(b)=4+1=5
sizeof(qn)=8;即qn是采用8字节对齐的,所以要在a,b后面添3个空余字节,然后才能存储qn,
4+1+(3)+8+1=17
  因为qq采用的对齐是8字节对齐,所以qq的大小必定是8的整数倍,即qq的大小是一个比17大又是8的倍数的一个最小值,由此得到
17<24+8=24= sizeof(qq)

更详细的介绍见:
http://gcc.gnu.org

  下面是一些便捷的连接:GCC 4.0 Function Attributes;GCC 4.0 Variable Attributes ;GCC 4.0 Type Attributes ;GCC 3.2 Function Attributes ;GCC 3.2 Variable Attributes ;GCC 3.2 Type Attributes ;GCC 3.1 Function Attributes ;GCC 3.1 Variable Attributes

Reference:
1.有关__attribute__的相对简单的介绍:http://www.unixwiz.net/techtips/gnu-c-attributes.html
2.__attribute__详细介绍:http://gcc.gnu.org

(转)字符串编码的那点事

  最近总感觉心里空落落的,今天就写点东西踏实踏实吧,而且博客很久没有加入新的博文了,得赶紧补充点新东西了^_^
  
  今天就谈谈一些和字符串编码相关的事吧,公司有个新同事对这个问题一直很纠结,我想很多开发人员对此不一定搞得很明白,因为字符串编码这些东西太过杂乱,让大家总感觉摸不到头脑,我这里简单总结一下一些和字符串编码相关的小经验,拿出来大家一起讨论讨论。
  
  1. 理清楚ANSI和Unicode
  
  做过Win32开发的人,对这些应该很熟悉,VC中的CStringA,CStringW,_T宏,Windows API中一堆带A或带W的函数就和这有关。
  
  这需要慢慢理出头绪来。
  
  我们现在的计算机能够直接输入的只有ASCII码,这样的字符很少,ASCII码标准中定义了128个,这些字符的存储只需要一个字节就可以全部涵盖了,对应C语言就是char类型,就是我们所说的字符类型,其实实际意义上的ANSI标准就是这里的ASCII编码标准。
  
  早期的计算机程序在处理字符型数据时都是以这样的思路来开发的,即一个字节存储一个字符。当非英语语系的国家需要使用计算机时,他们该如何表示自己的文字?像我们的汉字,何止成千上万,一个字节根本无法满足,所以这些研发人员就需要思考如何对自己国家的文字进行编码以便让计算机处理,这里对应我们的简体中文汉字就是常见的GB2312,GBK,GB18030等,繁体中文像BIG5等。这些编码是以国家为界限的,有的是以语言种类为界限的,比如当一个程序既要处理中文简体,中文繁体,韩文等,它怎么知道一个非ASCII的编码到底是简体中文还是繁体中文,亦或是韩文?这里没有任何标志!(顺便提一下,各个国家为了区分本国字符和ASCII字符的区别,基本都将编码字节的最高位,置为1,因为ASCII码只占用了一个字节中的低7位,最高位是0)
  
  上述这些编码我们一般统称为MBCS编码,即多字节编码,也有叫窄字节编码。其实Win32中A本意指ANSI,实际上处理的是MBCS编码。
  
  MBCS的缺点就是不统一,各自为政,给应用程序的跨语言应用带来不小的麻烦,所以全球统一编码就诞生了,也就是Unicode。
  
  Unicode编码为涵盖全球所有的字符而设计,在Unicode字符集中,所有编码的存储长度都是统一的,大家一视同仁,并且同类语言都是分区存放的,能够很好的根据一个字符的编码确定该字符是哪国语言,这就为跨语言的应用开发带来了极大的便利。
  
  2. UTF8编码:为了兼容ANSI的Unicode
  
  凡事有利也有弊,在这里就很好的体现了出来。在Unicode概念提出来之前,N多的应用程序都是基于ANSI开发的,这类程序在处理字符串时很多都是以\0来判断字符串结尾的,尤其是C语系的语言开发的程序,这在Unicode中就玩不转了,比如Win32下的Unicode编码存储都是双字节的,ASCII字符对应的Unicode编码的高字节都是\0,所以这类程序在处理Unicode编码的代码就必须要大改,而且很多标准库函数都不能用了。导致最初Unicode的发展受到了很大程度的抵制,因为乌托邦的到来是以重度自残为前提的。所以急需一套编码标准能够更好的兼容ANSI编码,所以就有了UTF8编码
  
  其实UTF8的主要目的就是让基于ANSI开发的程序能够以最小的修改来达到兼容Unicode编码的目的。UTF8的一个特点就是字符串的结尾仍然是\0,但是其他非ASCII的编码是不等长的,可能是2个字节,也可能是3个字节,4个字节等等。像strcmp,strlen,strcpy等标准库函数还是可以用的,而不至于错的那么离谱。
  
  这里有必要理一理UTF8和Unicode、ANSI的关系
  
  首先,UTF8是为了兼容ANSI而设计的。可以将UTF8认为是一种MBCS编码,只是这种编码和Unicode编码存在一一对应的关系,你能够从UTF8字节流中得到具体字符的边界,并将其对应到Unicode编码中来,这样UTF8也是全球统一的。
  
  其次,从概念上区别UTF8和Unicode。其实Unicode只是一种字符到编码的规范,至于编码如何存储,如何传输,Unicode本身是不做规范的,所以才会有一般说的Win32中每个Unicode字符都是双字节编码,而Linux中的gcc则是每个Unicode编码都是4字节编码。
  
  从UTF8的英文缩写也能够看出来其含义,Universal Transformation Format即统一传输格式,UTF8只是定义了Unicode的传输和存储标准,丝毫不影响Unicode作为统一编码这一特性
  
  3. char还是wchat_t,或者是TCHAR;什么是_T,TEXT,L;CStringA还是CStringW,或者CString;std::string还是std::wstring
  
  说完了编码规范,再来说说编码和编程的关系,首先要强调一点,字符串编码只是和字符串存储有关,和其他的没有关系,包括字符串显示等。
  
  char大家都很熟悉,在C中代表一个字符,存储长度是1个字节。wchar_t有些人可能接触不多,尤其是初学者,在C中其实就是通过typedef定义的unsigned short类型,但是在C++被语言接纳,定义为C++的内置类型,和int,char等是一样的,代表一个存储Unicode编码字符。
  
  现在有个问题,如果我们在程序中使用常量字符串的话,怎么定义Unicode编码的常量字符串?这里就要用到L关键字,以前定义一个ANSI字符串可以这样"abc",当要以Unicode编码存储的话,就需要这样定义为L"abc"。
  
  VC中为了统一ANSI和Unicode的编码定义,通过宏定义了TCHAR类型,在这里可以将_T,TEXT拉进来一起说。
  
  TCHAR类型是一种宏定义类型,当宏变量_UNICODE(Win32定义)或者UNICODE(C标准库定义)有效时,TCHAR等于wchar_t,否则TCHAR代表char类型。
  
  对应的当定义上述变量时,_T,TEXT就是L,否则为空。所以VC初学者经常见到这样的字符串定义_T("abc"),TEXT("abc"),其实就是针对上面两个宏的自适应常量字符串,当定义上述宏变量时,_T("abc")等于L"abc",否则就是"abc"
  
  对应的可能也会见到这样的函数_tcscpy,其实就是wcscpy和strcpy的自适应宏定义。
  
  所以我们常使用的一些Windows API函数,像GetWindowText其实是没有的,有的只是GetWindowTextA和GetWindowTextW,因为这也是自适应宏定义。
  
  CStringA和CStringW是MFC中定义了字符串存储处理,用于管理和处理字符串,有了上面的描述,就不难理解CStringA和CStringW的区别了,其实CStringA就是CStringT,CStringW就是CStringT,其中CStringT是一个模板类。它本质上就是一堆处理字符数组的函数集合体。
  
  std::string和std::wstring是C++标准库中定义的类,对应的就是basic_string和basic_string,应该不难理解