博文
关于'ab'的类型(2006-09-26 16:59:00)
摘要:今天看MSDN偶然间发现其中有这么一句:
int mbch = 'ab';
对'ab'这种表示方法还是第一次见到(别见笑啊,呵呵):P
并且它的类型是const int,这更让我感到迷惑。仔细研究了一番,稍微弄懂了一些,
特地写下这篇文章记录一下。以备日后察看(VC6)。
关于'ab'等字符常量的类型,周星星(bruceteen)说C++标准文档中是有规定的(具体我没有去翻阅文档),他的类型是const int 型的。
后来慢慢发现了mbch在内存中是的值是0x6162(十进制就是24930了)。
'\13a'这样的字符常量,如果:
int i = '\13a';
cout<< i << endl ;
会发现输出了:2913
为什么呢?
原来'\13a'表示八进制,因此'\34' = 3*8 + 4 = 28;
可是'\13a'中的a又代表了什么呢?八进制只能用0~7,a 又怎么解释呢?
原来对于0~7以外的字符,编译器解释为:
'\13'和'a'的结合。
'\13'是0x0B, 'a'是0x61;
那么i的值其实就是0x0B61了。......
UNICODE字符输出???(2006-09-25 16:33:00)
摘要:
这几天研究Unicode,勤快些,把看到的东西觉得有用的都copy了下来,文章是拼凑出来的,所以看起来会觉得有些乱 :)。
1. wprintf
Q : sizeof(wchar_t) = ?
A : 随编译器不同。(所以:在需要跨平台的时候尽量不用wchar_t) vc : sizeof(wchar_t) = 2;
Q: 在vc中,为什么直接使用wprintf(L"测试1234")会没有结果
A: 没有设置好locale,这样做
setlocale(LC_ALL ,"chs");
wprintf(L"%s",L"测试1234");
或者(假设当前活动codepage为chs)
char scp[16];
int cp = GetACP();
sprintf(scp,".%d",cp);
setlocale( LC_ALL, scp );
wprintf(L"测试1234");
2. wcout
一样,不过设定locale,请用std::locale
locale loc("chs");
wcout.imbue(loc);
wcout << L"测试1234" << endl;
这篇文章应该是[netsin]的成果,我勤快,记下来。
注:wprintf是C的标准库函数,但wcout不是C++的标准成员,C++中的 L"……" 是宽字符,却未必是unicode字符,这与编译器实现相关。
[乾坤一笑]说:为什么 C/C++ 语言把 L"xx" 定义为由实现决定的呢?这显然是为了 C/C++ 的普适性、可移植性。Bjarne 的观点认为,C++ 的方式是允许程序员使用任何字符集作为串的字符类型。另外,unicode 编码已经发展了若干版本了,是否能永久适合下去也不得而知......
C++流格式控制(2006-09-25 15:25:00)
摘要:格式控制
当输入/输出的数据没有指定格式,它们都按缺省的格式输入/输出。然而,有时需要对数据格式进行控制。这时需利用ios类中定义的格式控制成员函数,通过调用它们来完成格式的设置。ios类的格式控制函数如下所示:
long flags( ) const
返回当前的格式标志。
long flays(long newflag)
设置格式标志为newflag,返回旧的格式标志。
long setf(long bits)
设置指定的格式标志位,返回旧的格式标志。
long setf(long bits,long field)
将field指定的格式标志位置为bits,返回旧的格式标志。
long unsetf(long bits)
清除bits指定的格式标志位,返回旧的格式标志。
long fill(char c)
设置填充字符,缺省条件下是空格。
char fill( )
返回当前填充字符。
int precision(int val)
设置精确度为val,控制输出浮点数的有效位,返回旧值。
int precision( )
返回旧的精确度值。
int width(int val)
设置显示数据的宽度(域宽),返回旧的域宽。
int width( )
只返回当前域宽,缺省宽度为0。这时插入操作能按表示数据的最小宽度显示数据。
预定义的操纵算子
使用成员函数控制格式化输入输出时,每个函数调用需要写一条语句,尤其是它不能用在插入或提取运算符的表达式中,而使用操纵算子,则可以在插入和提取运算符的表达式中控制格式化输入和输出。在程序中使用操纵算字必须嵌入头文件iomanip.h
dec
十进制的输入输出
hex
十六进制的输入输出
oct
八进制的输入输出
ws
提取空白字符
ends &nbs......
完整ASCII字符表(2006-09-22 11:42:00)
摘要:
ASCII(美国信息交换标准编码)表
字符
ASCII代码
字符
ASCII代码
字符
ASCII代码
二进制
十进制
十六进制
二进制
十进制
十六进制
二进制
十进制
十六进制
回车
ESC
空格
!
"
#
$
0001101
0011011
0100000
0100001
0100010
0100011
0100100
13
27
32
33
34
35
36
0D
1B
20
21
22
23
24
?
@
A
B
C
D
E
0111111
1000000
1000001
1000010
1000011
1000100
1000101
63
64
65
66
67
68
69
3F
40
41
42
43
44
45
a
b
c
d
e
f
g
1100001
1100010
1100011
1100100
1100101
1100110
1100111
97
98
99
100
101
102
调用规范与可变参数表(2006-07-21 08:59:00)
摘要: 语言调用规范是指进行一次函数调用所采用的传递参数的方法,返回值的处理以及调用堆栈的清理。Microsoft C/C++ 语言中采用了五种调用规范,分别是__cdecl, __stdcall, __fastcall,thiscall和nake每一中调用规范都是利用eax作为返回值,如果函数返回值是64位的,则利用edx:eax对来返回值。Nake调用规范非常的灵活,足以独立的一篇文章描述,这里就不再描述nake调用规范。下表列出了前面四种规范调用的特点:
关键字
堆栈清理者
参数传递顺序
__cdecl
调用者
从右至左
__stdcall
被调用者
从右至左
__fastcall
被调用者
从右至左,前两个参数由寄存器ecx,edx传递
thiscall
被调用者或者调用者
从右至左
__cdecl 最大好处在于由于是调用者清理栈,它可以处理可变参数,缺点则在于它增加了程序的大小,因为在每个调用返回的时候,需要多执行一条清理栈的指令。
__stdcall 是在windows程序设计中出现的最多的调用规则,所有的不可变参数的API调用都使用这个规则。
__fastcall 在windows内核设计中被广泛的使用,由于两个参数由寄存器直接传递,采用这种规则的函数效率要比以上两种规则高。
thiscall是C++成员函数的默认调用规范,编译期间,这种调用会根据函数是否支持可变参数表来决定采用什么方式清理堆栈。如果成员函数不支持可变参数,那么它就是用参数入栈,ecx保存this指针的方式进行调用,如果成员函数支持可变参数,那么它的调用和__cdecl类似,唯一不同的是将this指针最后压入栈中进行传递。
调用者和被调用者必须采用同样的规则才能保证程序的正常执行,曾经看到很多程序员犯的错误就是由于调用规范的不一样,致使程序异常,比如:DWORD ThreadFunc(LPVOID lpParam)
{
//…
}
CreateThread(..,(LPTHREAD_START_ROUTINE)ThreadFunc, …);
如果在编译期间没有指定编译选项/Gz......
为什么 char** 不能自动转化为 const char** (2006-05-23 20:41:00)
摘要:
声明文章来源: http://blog.vckbase.com/bruceteen/archive/2005/12/07/15691.html
一次偶然的情况下我发现以下代码竟然无法被编译通过(如果你的编译器,比如VC6或VC2003,允许它编译通过,我想你首先应该换个编译器,比如GCC或VC2005):
void foo( const char* [] ) { }
int main( void )
{
char* s[2];
foo( s );
}
简化成更一般的形式是:
char** p1 = 0;
const char** p2 = p1;
错误是:invalid conversion from `char**' to `const char**'.
lostpencil更加仔细,使用C编译器给出的是一个警告:
initialization from incompatible pointer type.
随后hpho给出了合理的解释,同时comp.lang.c++.moderated上的Ulrich Eckhardt也用代码进行了说明。
用代码来说明最直观了:
const char* s = "abc";
int main( void )
{
char* p0 = 0;
char** p1 = &p0;
const char** p2 = p1; // 先假设这一句是合法的 ( 测试时,可以先强制类型转化一下 )
*p2 = s;
*p0 = 'A'; // 通过p0在修改不应该被修改的s,这显然和const相违背,其运行结果不可知。
}
实际上 const char** 不是 char** 的const形式,char** 的const形式应该是 char* const *,哈哈!......
Data Alignment(2006-05-22 12:18:00)
摘要:#pragma pack
Preface
很多 CPU ,如基于 Alpha, IA-64, MIPS, 和 SuperH 体系的,拒绝读取未对齐数据。当一个程序要求其中之一的 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。举个例子,在 ARM, MIPS, 和 SH 硬件平台上,当操作系统被要求存取一个未对齐数据时默认通知应用程序一个异常。
对齐性
对齐性是一种内存地址的特性,表现为内存地址模上 2 的幂。例如,内存地址 0x0001103F 模 4 结果为 3 ;这个地址就叫做与 4n + 3 对齐, 4 指出了所选用的 2 的幂的值。内存地址的对齐性取决于所选择的关于 2 的幂值。同样的地址模 8 结果为 7 。
一个内存地址符合表达式 Xn + 0 ,那么就说该地址对齐于 X 。
CPU 执行指令就是对存储于内存上的数据进行操作,这些数据在内存上是以地址为标记的。对于地址来说,单独的数据会有其占用内存的字节数。如果它的地址对齐于它的字节数,那么就称作该数据自然对齐,否则称为未对齐。例如,一个标记 8 字节的浮点数据的地址对齐于 8 ,那么这个数据就自然对齐。
数据对齐性的编译处理
设备编译器以一种防止造成数据未对齐的方式来对数据进行地址分配。
对于单个的数据类型,编译器为其分配的地址是数据类型字节数的倍数。因此,编译器分配给 long 型变量的地址为 4 的倍数,就是说以 2 进制表示地址的话,最后两位为 0 。
另外,编译器以一种自然对齐每个结构成员的方式来填充结构体。参看下面的代码里面的结构体 struct x_ 。......
关于CString(2005-12-12 15:37:00)
摘要:通过阅读本文你可以学习如何有效地使用 CString。
CString 是一种很有用的数据类型。它们很大程度上简化了MFC中的许多操作,使得MFC在做字符串操作的时候方便了很多。不管怎样,使用CString有很多特殊的技巧,特别是对于纯C背景下走出来的程序员来说有点难以学习。这篇文章就来讨论这些技巧。
使用CString可以让你对字符串的操作更加直截了当。这篇文章不是CString的完全手册,但囊括了大部分常见基本问题。
这篇文章包括以下内容:
CString 对象的连接
格式化字符串(包括 int 型转化为 CString )
CString 型转化成 int 型
CString 型和 char* 类型的相互转化
char* 转化成 CString
CString 转化成 char* 之一:使用LPCTSTR强制转化
CString 转化成 char* 之二:使用CString对象的GetBuffer方法
CString 转化成 char* 之三: 和控件的接口
CString 型转化成 BSTR 型;
BSTR 型转化成 CString 型;
VARIANT 型转化成 CString 型;
载入字符串表资源;
CString 和临时对象;
CString 的效率;
总结
下面我分别讨论。
1、CString 对象的连接
能体现出 CString 类型方便性特点的一个方面就字符串的连接,使用 CString 类型,你能很方便地连接两个字符串,正如下面的例子:
CString gray("Gray");
CString cat("Cat");
CString graycat = gray + cat;
要比用下面的方法好得多:
char gray[] = "Gray";
char cat[] = "Cat";
char * graycat = malloc(strlen(gray) + strlen(cat) + 1);
......
确定一个对象在使用前已经被初始化(2005-11-23 12:39:00)
摘要:C++ 看上去在对象的初始化方面变化无常。例如,如果你这样做,
int x;
在某些情形下,x 会被初始化(0),但是在其它情形下,也可能没有。如果你这样做,
class Point {
int x, y;
};
...
Point p;
p 的数据成员有时会被初始化(0),但有时没有。如果你从一个不存在未初始化对象的语言来到 C++,请注意这个问题,因为它非常重要。
读取一个未初始化的值会引起未定义行为。在一些平台上,读一个未初始化的值会引起程序中止,更可能的情况是得到一个你所读的那个位置上的随机值,最终导致不可预测的程序行为和恼人的调试。
现在,有一些关于什么时候进行对象的初始化什么时候不进行的规则的描述。不幸的是,这些规则很复杂——我觉得它复杂得无法记住。通常,如果你使用 C++ 的 C 部分(参见 Item 1),而且初始化可能会花费一些运行时间,它就不一定会进行。如果你使用 C++ 的 non-C 部分,事情会有些变化。这就是为什么一个数组(C++ 的 C 部分)不能确保它的元素被初始化,但是一个 vector(C++ 的 STL 部分)就能够确保。
处理这种表面上看起来不确定的事情的最好方法就是总是在使用之前初始化你的对象。对于内建类型的非成员对象,需要你手动做这件事。例如:
int x = 0; // manual initialization of an int
const char * text = "A C-style string"; // manual initialization......
利用C++模板技术支持多种计算策略(2005-11-17 15:27:00)
摘要:任何有经验的程序员和软件设计师都知道,在软件开发中最常见的困境的并不是在问题面前束手无策,而是在一大堆好像都可行的解决策略中选择一个。软件设计就是选择的过程,其困难之处也就在于你得不断地、小心翼翼地做出各种选择。在传统的程序设计模式下,每一次选择都是不可逆转的,你做出了选择,那么随后的开发都将会受到这次选择的剧烈影响。当你发现自己当初的选择错误的时候,可能一切都已经为时太晚。这有点像是走迷宫,而走迷宫时如果你发觉走进了死胡同,还可以掉头换一条路线。但在软件开发中,更换计算策略的成本太高,几乎是无法承受的。如果能够有一种软件设计方法,使更换计算策略的成本大大降低,使你有更多的“悔棋”的机会,那么软件设计的难度就可以大大降低,质量就会得到很大的提高。
另一个常常出现而且令人烦恼的问题是这样的,我们在软件的开发中经常会遇到同类型的问题反复地以不同面貌出现,每次都让你在直觉上感到这几个问题好像差不多。但是当你满怀信心的企图实现一个可重用组件,一劳永逸地解决问题的时候,你就会沮丧地发现,一些细节上的不同使你很难、甚至根本没有办法实现这样一个可重用的组件。于是你不得不一次次地重写类似的代码,使得程序冗长,难于维护。如果我们有一种高度抽象的软件开发技术,把所有细节上的不同都抽象起来,包装在一个统一的接口背后,那么我们就有能力来达成更高程度的代码可重用性,写出适用性更强的软件组件。显然,在这些细节里,类型是一个重要方面,而另一个重要方面就是计算策略。
综合这两点,我们可以看到,把计算策略从一个软件组件中抽象出来将会非常有益。这当然不是一件容易的事情,不光需要在设计上做大量分析,还需要一些特别的编程技术。本文试图用C++提供的模板技术,来给出解决这个问题的一种思路。
1. 一个实际例子
我拿一个手头的例子来说明,在施工进度控制系统里,有一个这样的功能:在已知各种必要信息的情况下,需要估算进行某项工作需要花费的时间。在这里,我的角色不是应用程序的开发者,而是支持组件的开发者。也就是说,我来提供组件,由客户程序员具体使用,完成特定的任务。在这样一个简单的任务里,充满了各种选择,我们把这些选择简述如下:
基本计算模型:可以采用经验估计法,也可以用公式计算法,用户还可能有其他更贴切的计算模型。
精确......