博文
time(2007-02-21 21:46:00)
摘要:你想知道你的代码究竟执行了多长时间吗?是的,有的时候我们需要确定我们的代码到底执行了多长时间。或许你会想这有何难,在代码的前后增加GetTickCount来得到时间不就可以了吗?没错,如果系统是线形执行的话,这样做肯定可以得到,但我们怎么能够保证系统是线形的来?
我们这里只讨论WINDOWS,WINDOWS是典型的多线程、抢占式的多任务操作系统。如果你的计算机只有一块CPU的话,那么你的应用程序是这样运行的:一个线程执行一个时间片,然后再由另外一个线程执行一个时间片(这个时间片是20毫秒),然后又可能暂停这个线程回到开始的哪个线程,也有可能执行其他的线程;这就是操作系统的调度算法了,调度算法是复杂的,目的是保证所有的线程都有机会被执行,从而看起来感觉每个线程都在运行,但实际上每个时刻只可能有一个线程在运行,即占用CPU时间(所以线程是分配CPU资源的最小单位了)。上面的是单CPU的,如果在多CPU上就更复杂了,但这个时候就不能保证一个时刻只有一个线程被执行了,因为可能在每个CPU上运行一个线程(WINDOWS2000就可以在多个CPU上运行),从而更好的保证了程序的并行性。
从我们对WINDOWS的分析可以看出,程序不是线性方式执行的。或许你会问:单线程、单CPU的情况下,程序是线性的吧?乍听起来说的有道理,但事实并不是这样,因为你的程序运行在操作系统上,操作系统要能够正确的运行,它本身就有许多线程在运行(EXPLORE.EXE),在必要的时候被唤醒,所以你的程序根本不可能在单线程的理想环境下运行。
好了,既然我们已经了解了上面的本质之后,你就会想,是的真的没有办法来得到我的代码到底执行了多长时间?其实不然,下面我们就几种测试方法来比较一下,究竟该如何计算?
用来计算代码执行时间有三种方法:
1. 使用GetTickCount,得到当前的时间,单位是毫秒
代码示例:
DWORD startTime = GetTickCount();
//被测试的代码
DWORD totalTime = GetTtickCount() – startTime;
2. 使用GetThreadTimes;该函数得到的时间包括两......
Class function callback(2007-01-26 10:20:00)
摘要:这篇文章介绍了如何使类中的非静态成员函数成为线程函数或者使其成为回调函数的方法,颇值得一读:
Introduction
In this article I'll show you how to make a callback to a function within a class that is NOT static. This will make use of functions, pointers and some assembly. It's not possible to my knowledge to do this without the ASM. However, this guide does not require you to know any ASM; instead it will explain what it does. This guide is not intended to teach out ASM, however. There's plenty of other good guides out there on the web for that. This is an advanced technique, however, but even beginners should be able to make use of it.
What do we need? A compiler that can do ASM and a class! In this article I will also show you how I used this technique to forward messages through a window proc. Let's get started then!
First then, we need a base-class. I use MFC in this sample to show you how this technique works. So if you want to follow in this sample, use MFC to create a dialog window. The class is the dialog class of your mai......
Use Pseudoregisters to debug in MSVC++(2006-12-24 23:49:00)
摘要:Introduction
Let's start with the reason why I wrote this article. One day, a colleague asked me to help him debug a problem he had. So I was watching him stepping in his code, when I noticed the following line: int test = GetLastError();
He did this, because he wanted to know the error code, if the previous function failed. He was adding this line every time he wanted to know the error code. I advised him to remove all those lines and use the @ERR pseudoregister in his watch window. He didn't know what it was and asking around in the office, a lot of other people didn't. So I came up with this article for people who have never heard of pseudoregisters.
What is a pseudoregister anyway?
A pseudoregister is not an actual hardware register, but is displayed as though it were a hardware register. With a pseudoregister, you can see and use certain values (error codes, thread information block...) in the debugger.
Let's have a look at the @ERR pseudoregister. Fire up your debugger wit......
谈谈连接器是如何工作的(2006-09-29 15:09:00)
摘要: 许多Visual C++的使用者都碰到过LNK2005:symbol already defined和LNK1169:one or more multiply defined symbols found这样的链接错误,而且通常是在使用第三方库时遇到的。对于这个问题,有的朋友可能不知其然,而有的朋友可能知其然却不知其所以然,那么本文就试图为大家彻底解开关于它的种种疑惑。
大家都知道,从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码,然后由汇编器(assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中;(2)链接器(linker)将一个个的目标文件(或许还会有若干程序库)链接在一起生成一个完整的可执行文件。
编译器编译源文件时会把源文件的全局符号(global symbol)分成强(strong)和弱(weak)两类传给汇编器,而随后汇编器则将强弱信息编码并保存在目标文件的符号表中。那么何谓强弱呢?编译器认为函数与初始化了的全局变量都是强符号,而未初始化的全局变量则成了弱符号。比如有这么个源文件:
extern int errorno;
int buf[2] = {1,2};
int *p;
int main()
{
return 0;
}
其中main、buf是强符号,p是弱符号,而errorno则非强非弱,因为它只是个外部变量的使用声明。
有了强弱符号的概念,链接器(Unix平台)就会按如下规则(参考[1],p549~p550)处理与选择被多次定义的全局符号:
规则1: 不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号);
规则2: 如果一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么选择强符号;
规则3: 如果一个符号在所有目标文件中都是弱符号,那么选择其中任意一个;
虽然上述3条针对的是Unix平台的链接器,但据作者试验,至少VC6.0的linker也......
关于'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 *,哈哈!......