作者:zyl910
C++标准为C++标准IO库设计了十分完善的国际化文本处理机制。但在实际使用中,却发现各种编译器对它的支持性存在较大的差异,很多时候无法正确的输出字符。于是我对此进行了深入的调查。
一、说明
1.1 测试程序
下面有一段很简单的程序,分别利用cout、wcout、printf输出字符串。具体代码为——
#include <locale.h>
#include <wchar.h>
#include <string>
#include <iostream>
using namespace std;
const char* psa = "A汉字ABC";
const wchar_t* psw = L"W汉字ABC";
int main(int argc, char* argv[])
{
// init.
//ios::sync_with_stdio(false); // Linux gcc.
locale::global(locale(""));
//setlocale(LC_CTYPE, ""); // MinGW gcc.
wcout.imbue(locale(""));
// C++
cout << psa; cout.clear(); cout<<endl;
wcout << psw; wcout.clear(); wcout<<endl;
// C
printf("\nC:\n");
printf("\t%s\n", psa);
printf("\t%ls\n", psw);
return 0;
}
大家猜一猜这段程序的运行结果是什么?
1.2 理论结果
先根据C++标准,分析一下这段程序的理论结果。
在main函数中,首先执行了这两行代码对地区环境进行了初始化——
wcout.imbue(locale(""));
细节解释——
1. locale(""):调用构造函数创建一个local,其中的空字符串具有特殊含义:使用客户环境中缺省的locale(《C++标准程序库—自修教程与参考手册》P697)。例如在简体中文系统上,会返回简体中文的locale。
2. locale::global(locale("")):将“C++标准IO库的全局locale”设为“客户环境中缺省的locale”。注意它还会设置C标准库的locale环境,造成与“setlocale(LC_ALL, "")”类似的效果(《C++标准程序库—自修教程与参考手册》P698)。
3. wcout.imbue(locale("")):使wcout使用“客户环境中缺省的locale”。
就这样,使C标准库、C++标准IO库(尤其是wcout)均正确的设置了地区环境,与客户环境中缺省环境完全匹配。
随后,使用C++标准IO库的cout、wcout分别输出窄字符串和宽字符串——
cout << psa; cout.clear(); cout<<endl;
wcout << psw; wcout.clear(); wcout<<endl;
细节解释——
1. 调用cout、wcout的clear成员函数是为了清除错误状态,使后续输出能正常运行。
2. 使用“cout<<endl”或“wcout<<endl”时,不仅会使输出文本换行,而且还会执行flush成员函数,提交缓冲区中的数据。使得cout、wcout的输出文本不会发生冲突。
最后,使用C标准库的printf函数输出窄字符串和宽字符串——
printf("\nC:\n");
printf("\t%s\n", psa);
printf("\t%ls\n", psw);
所以,测试程序的运行结果应当为——
W汉字ABC
C:
A汉字ABC
W汉字ABC
注意为了更好区分C++标准IO库与C标准库的输出结果,这里给printf加了个TAB字符。
二、测试VC2005
因VC2005是VC系列中第一个对C++03标准支持性较好的编译器,先来测测它。
2.1 Debug版
在VC2005中以Debug模式编译测试程序,执行结果为——
W
C:
A汉字ABC
W汉字ABC
可见C++的cout、wcout均无法正常输出中文字符。
而C的printf都能正常输出含中文字符的窄字符串与宽字符串。
2.2 Release版
将编译配置改为“Release”模式,再编译运行,神奇的事情发生了。执行结果为——
W汉字ABC
C:
A汉字ABC
W汉字ABC
Release版下全部通过,cout、wcout、printf均能正常输出。
三、测试VC2008及更高版本的VC
在VC2008中编译测试程序,执行结果为——
W汉字ABC
C:
A汉字ABC
W汉字ABC
全部通过,cout、wcout、printf均能正常输出。然后测试了Release版,也是全部通过。看来VC2005的Bug已经被修正了。
随后又测试了VC2010、VC2012,均是全部通过。
四、测试Windows中的MinGW
4.1 测试
使用GCC 4.6.2(MinGW(20120426))编译测试程序,执行结果为——
W
C:
A汉字ABC
W
窄字符串都能正常输出,但宽字符串都不能正常输出。
4.2 修改代码,使MinGW能正常显示
将初始化代码增加一行——
- 相同点:
指针指向一块内存,它的内容是所指内存的地址;
引用是某块内存的别名。
- 不同点:
int a=3; int &ra = a;
(1)&在此不是求地址运算,而是起标识作用
(2)类型标识符是指目标变量的类型
(3)声明引用时,必须同时对其进行初始化
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名(int b;int &ra=b 是不可以的)
(5)声明一个引用,不是新定义一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。所以对引用求地址,就是对目标变量求地址(&ra == &a)
(6)不能建立数组的引用,因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
(7)引用中是没有const,指针有const ,const的指针不可改变
(8)引用不能为空,指针可以为空
小总结:引用只能在定义时被初始化一次,之后不可变;指针是可以改变的,引用从一而终;
#include <string>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int &rn = a;
cout <<"rn="<< rn << " | " <<"a="<<a <<endl;
cout << "&rn="<< &rn<< " | " <<"&a="<<&a<<" | " <<"&b="<<&b <<endl;
cout <<"-------------------------------"<<endl;
rn = b;
cout <<"rn="<< rn <<" | "<<"a="<<a<<" | "<<"b="<<b<<endl;
cout << "&rn="<< &rn<< " | " <<"&a="<<&a <<" | " <<"&b="<<&b <<endl;
cout <<"-------------------------------"<<endl;
rn = 100;
cout <<"rn="<< rn <<" | "<<"a="<<a<<" | "<<"b="<<b<<endl;
cin.get();
return 0;
}
运行结果如下:
说明:由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。
引用的实例应用讲解
下面我们通过以下三个例子对照来说明引用:
1〉
#include <string>
using namespace std;
float c;
float test(float a,float b)
{
c = a*b;
return c;
}
int main()
{
float pn = test(3.0f,1.2f);
cout <<pn;
cin.get();
return 0;
}
说明:上面的代码中我们可能以为函数返回的就是变量c,这么想可能就错了,在一般情况下,通过函数的调用返回的值其实会在内存栈空间内自动产生一个临时变量temp,因此函数在return时,返回的是一个临时产生的副本而已!!
2〉我们把上面的例子加以修改
#include <string>
using namespace std;
float c;
float test(float a,float b)
{
c = a*b;
return c;
}
int main()
{
float &pn = test(3.0f,1.2f);
cout <<pn;
cin.get();
return 0;
}
说明:你会发现, 在vc++6.0编译器下会报错,出现error C2440: 'initializing' : cannot convert from 'float' to 'float &' A reference that is not to 'const' cannot be bound to a non-lvalue! 原因:一旦test()执行产生的临时变量在调用之后就会在栈空间内消失,这样pn也就成为一个没有明确目标的引用,严重的时候会出现内存错误!!
3〉我们再将以上代码进行修改!!希望大家把这个例子好好理解!
#include <string>
using namespace std;
float c;
float &test(float a,float b)
{
c = a*b;
return c;
}
int main()
{
float pn = test(3.0f,1.2f);
cout <<pn;
cin.get();
return 0;
}
说明:这种返回引用给变量的情况,在内存中,test()所在栈空间内并没有产生临时变量,而是直接将全局变量c赋值给pn,这种方式是我们最为推荐的操作方式,因为不产生临时变量直接赋值的方式可以节省内存空间提高效率,而且程序的可读性也好!!
1、引用作为参数:
引用作为参数讲,我们就会想起指针,最为典型的例子就是交换两个变量的值!!
a、针交换两个变量的值
using namespace std;
void swap1(int *m,int *n)
{
int t;
t=*m;
*m = *n;
*n=t;
}
int main()
{
int a=3,b=2;
int *pa = &a;
int *pb = &b;
cout <<"a = "<<a <<"--front--"<<"b ="<<b<<endl;
swap1(pa,pb);
cout <<"a = "<<a <<"--after--"<<"b ="<<b<<endl;
return 0;
}
b、用交换两个变量的值
using namespace std;
void swap1(int &m,int &n)
{
int t;
t=m;
m = n;
n=t;
}
int main()
{
int a=3,b=2;
cout <<"a = "<<a <<"--front--"<<"b ="<<b<<endl;
swap1(a,b);
cout <<"a = "<<a <<"--after--"<<"b ="<<b<<endl;
return 0;
}
从以上两段代码可以看出:1〉传递引用给函数与传递指针的效果是一样的!!,这时,被调用函数的形参就成为原来主调函数中的是残变量或对象的一个别名来使用,所以在被调用函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
2〉使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。
总结:如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。
(后续--------)
本文链接
作者:zyl910
C++标准为C++标准IO库设计了十分完善的国际化文本处理机制。但在实际使用中,却发现各种编译器对它的支持性存在较大的差异,很多时候无法正确的输出字符。于是我对此进行了深入的调查。
一、说明
1.1 测试程序
下面有一段很简单的程序,分别利用cout、wcout、printf输出字符串。具体代码为——
#include <locale.h>
#include <wchar.h>
#include <string>
#include <iostream>
using namespace std;
const char* psa = "A汉字ABC";
const wchar_t* psw = L"W汉字ABC";
int main(int argc, char* argv[])
{
// init.
//ios::sync_with_stdio(false); // Linux gcc.
locale::global(locale(""));
//setlocale(LC_CTYPE, ""); // MinGW gcc.
wcout.imbue(locale(""));
// C++
cout << psa; cout.clear(); cout<<endl;
wcout << psw; wcout.clear(); wcout<<endl;
// C
printf("\nC:\n");
printf("\t%s\n", psa);
printf("\t%ls\n", psw);
return 0;
}
大家猜一猜这段程序的运行结果是什么?
1.2 理论结果
先根据C++标准,分析一下这段程序的理论结果。
在main函数中,首先执行了这两行代码对地区环境进行了初始化——
wcout.imbue(locale(""));
细节解释——
1. locale(""):调用构造函数创建一个local,其中的空字符串具有特殊含义:使用客户环境中缺省的locale(《C++标准程序库—自修教程与参考手册》P697)。例如在简体中文系统上,会返回简体中文的locale。
2. locale::global(locale("")):将“C++标准IO库的全局locale”设为“客户环境中缺省的locale”。注意它还会设置C标准库的locale环境,造成与“setlocale(LC_ALL, "")”类似的效果(《C++标准程序库—自修教程与参考手册》P698)。
3. wcout.imbue(locale("")):使wcout使用“客户环境中缺省的locale”。
就这样,使C标准库、C++标准IO库(尤其是wcout)均正确的设置了地区环境,与客户环境中缺省环境完全匹配。
随后,使用C++标准IO库的cout、wcout分别输出窄字符串和宽字符串——
cout << psa; cout.clear(); cout<<endl;
wcout << psw; wcout.clear(); wcout<<endl;
细节解释——
1. 调用cout、wcout的clear成员函数是为了清除错误状态,使后续输出能正常运行。
2. 使用“cout<<endl”或“wcout<<endl”时,不仅会使输出文本换行,而且还会执行flush成员函数,提交缓冲区中的数据。使得cout、wcout的输出文本不会发生冲突。
最后,使用C标准库的printf函数输出窄字符串和宽字符串——
printf("\nC:\n");
printf("\t%s\n", psa);
printf("\t%ls\n", psw);
所以,测试程序的运行结果应当为——
W汉字ABC
C:
A汉字ABC
W汉字ABC
注意为了更好区分C++标准IO库与C标准库的输出结果,这里给printf加了个TAB字符。
二、测试VC2005
因VC2005是VC系列中第一个对C++03标准支持性较好的编译器,先来测测它。
2.1 Debug版
在VC2005中以Debug模式编译测试程序,执行结果为——
W
C:
A汉字ABC
W汉字ABC
可见C++的cout、wcout均无法正常输出中文字符。
而C的printf都能正常输出含中文字符的窄字符串与宽字符串。
2.2 Release版
将编译配置改为“Release”模式,再编译运行,神奇的事情发生了。执行结果为——
W汉字ABC
C:
A汉字ABC
W汉字ABC
Release版下全部通过,cout、wcout、printf均能正常输出。
三、测试VC2008及更高版本的VC
在VC2008中编译测试程序,执行结果为——
W汉字ABC
C:
A汉字ABC
W汉字ABC
全部通过,cout、wcout、printf均能正常输出。然后测试了Release版,也是全部通过。看来VC2005的Bug已经被修正了。
随后又测试了VC2010、VC2012,均是全部通过。
四、测试Windows中的MinGW
4.1 测试
使用GCC 4.6.2(MinGW(20120426))编译测试程序,执行结果为——
W
C:
A汉字ABC
W
窄字符串都能正常输出,但宽字符串都不能正常输出。
4.2 修改代码,使MinGW能正常显示
将初始化代码增加一行——