关于这个问题,,自己一直没模糊的。。一直的做法是把字符串全部转成宽字节。然后再去查找,但是想了下。。有时有可能。不能转。。比如我需要得到一个char字符串中汉字的位置,,,如果转成宽字节。。位置是不对的。。去网上查了一下相关的资料。 记录下一段代码,方便以后
int main(int argc, char* argv[])
{
char *str="abc一个问题def";
char *p=str,*p1;
unsigned short ch;
while(strcmp(p,"")!=0)
{
p1=CharNextExA(936,p,0);
int i=p1-p;
if(i==1)
{
ch=(unsigned char)*p;
}else if(i==2)
{
ch=(*p)*255+*(p+1);
}
p=p1;
}
return 0;
}
今天继续编写,基于框架的效果编辑器,修正了部分BUG后。。就想把。渲染窗口挂到MFC的窗口上。。。做为整个编辑器的一个子窗口打开。。这次。。。也比较方便。。也便于以后修改。以前挂接渲染窗口时。是使用settime来进行定时刷新渲染窗口,原来就感觉到刷新比较慢。但是因为查看MSDN。。。上讲。。SetTime是毫秒级。。。最低精确到1毫秒。。。1秒=1000毫秒。我为了保证窗口UI的正常使用。一般设置为20毫秒。。为什么不用线程咧。因为渲染窗口一般会与其它信息存在交互。。单纯渲染线程。。在处理互斥时。。。效率会慢到无法用言语来表达。。。1000/20=50。。每秒50的fps。。足可以应付简单的渲染需求了。但是在今天的测试中。。动态调整动画的FPS时。。。确发现。当FPS超过每秒20的范围后。。动画就不再有任何变化了,郁闷。。。然后。。就轻易的推翻了自己写的一切。。。使用线程。。渲染。。。。因为渲染纹理是从一个模板LIST中。。。获得。。。立刻就开始徘徊在。。加锁。。。。。巨慢。。。不加锁。。。异常的围城中。。。。在多方思索无果后。。。。忽然觉得SetTime不应该会如此慢的。。。。然后。。。放弃线程。。。去实际测试了一下。。。发现了问题:
SetTime虽然MSDN上说可以最低设置到一毫秒,但实际上测试只能精确到53毫秒左右,然后google上查询。。。有了答案。windows 以经限定了Settimer()的精度是55ms。。。就是所谓的1000/55==18.2 ..因为98系统是根据时间中断反馈,而时间中段每秒最多18.2次,虽然2000的系统据说没有此限制。。但在我XP的系统下测试。。。精度依旧。应该是为了保证系统兼容。。所以将SetTime精度设定在最高的范围。。如果这样换算。。。也就和我的问题基本吻合。。。。SetTime(1)的情况下。。实现是55毫秒触发一次,也就是每秒18.2帧。。。。18的FPS。。。啥都玩不转了。而且WM-TIME消息 在系统的优先级为极低。很容易被抛弃。。渲染窗口最少要保证在20毫秒触发一次。。也就是FPS50的情况下。。才可以保证动画等效果的连续性。。怎么办。。。怎么办。。。。继续google。。。毫秒级记时器。。。。。终于被我找到了。。。。。好几种解决的方法,对了。。。sleep的精度是30MS。。。以后要注意。。一下:
一.GetTickCount()函数,该函数的返回值是
DWORD型,表示以ms为单位的计算机启动后经历的时间间隔。精度比WM_TIMER消息映射高,在较 短的定时中其计时误差为15ms,在较长的定时中其计时误差较低,如果定时时间太长,就好象死机一样,CPU占用率非常高,只能用于要求不高的延时程序中.
下列代码可以实现50ms的精确定时:
DWORD dwStart = GetTickCount();
DWORD dwEnd = dwStart;
do
{
//为了在延迟时间不阻塞。可在此将消息传出,但是会降低精度
MSG msg;
GetMessage(&msg,NULL,0,0);
TranslateMessage(&msg);
DispatchMessage(&msg);
dwEnd = GetTickCount()-dwStart;
}while(dwEnd <50);
二,这就是我现在使用的方法,也决定以后都使用。。哈哈
使用多媒体定时器timeSetEvent()函数,该函数定时精度为ms级(精度基本准确,由于挂接窗口一般是在工具中实现,内部人员使用,所以不需要控制机器配置)。
MMRESULT timeSetEvent( UINT uDelay,
UINT uResolution,
LPTIMECALLBACK lpTimeProc,
WORD dwUser,
UINT fuEvent )
该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回NULL
参数说明:
uDelay:以毫秒指定事件的周期。
Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
LpTimeProc:指向一个回调函数。
DwUser:存放用户提供的回调数据。
FuEvent:指定定时器事件类型:
TIME_ONESHOT:uDelay毫秒后只产生一次事件
TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。
需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。
虽然还有更加精确的计时方法,不过对于我的需求这个以经够用了
我的实现代码
UINT uTimerID; //定义定时器句柄
void CALLBACK TimerCallProc(UINT TimerID, UINT msg,DWORD dwUser, DWORD dwa,DWORD dwb)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
Sleep(1);
}
//当前设置为1ms...
uTimerID=timeSetEvent(1,1,&TimerCallProc,0,TIME_PERIODIC);
//退出时
timeKillEvent(uTimerID); //删除定时器事件
timeEndPeriod(1); //清除定时器分辨率
1、使用 new 或者 malloc 分配后的内存,其内容被填充为 0xCD,CRT 中称处于这种状态的内存区为 Clean Land,即干净区,由此推测 0xCD 的含义可能为 Clean Data。在此数据的前后(不包含于此区域内),各有一个守卫用的 DWORD,其内容为 0xFDFDFDFD。
2、使用 delete 或者 free 释放后的内存,其内容被清空为 0xDD,CRT 中称处于这种状态的内存区为 Dead Land,即死区,由此推测 0xDD 的含义可能为 Dead Data。守卫用 DWORD 也同时被清除。
3、使用 HeapAlloc 分配的内存,其内容被填充为 0xBAADF00D,我推测为 Bad Food 的变体,经过 HeadFree 释放的内存,其大部分内容会被填充为 0xFEEE,但起始的两个 DWORD 不是,在我的机器上这两个 DWORD 的值均为 0x00140238,含义不详。
在简单的测试中,delete/free 最终总会调用到 HeapFree,因而看到的释放后的内存中是 0xFEEE,而 new/malloc 虽然调用了 HeapAlloc,但之后又填充了 0xCD,故而看到的是 0xCD。
记忆中有过释放之后看到 0xDD 的情况,而且 CRT 的代码里也的确有并不立刻调用 HeapFree 来真正释放内存的分支,但测试代码并未达到。
另外,0xCC 也是有的,好像是编译器做的事情之一,用于填充栈上的自动变量。
上述 CRT 的行为均为 Debug 版本的表现。
上述的系统行为(即 HeapXXX 函数族的行为)均为在 Windows Xp SP2 上的表现。
Microsoft Visual C++ Runtime library
C runtime library provides it own debug codes:
0xCD, 0xCDCDCDCD - New objects. New objects are filled with 0xCD when they are allocated.
0xFD, 0xFDFDFDFD - No-man's land memory. Extra bytes that belong to the internal block allocated, but not the block you requested. They are placed before and after requested blocks and used for data bound checking.
0xDD, 0xDDDDDDDD - Freed blocks. The freed blocks kept unused in the debug heap's linked list when the _CRTDBG_DELAY_FREE_MEM_DF flag is set are currently filled with 0xDD. Although in some cases you won't see magic 0xDDDDDDDD value, as it will be overwritten by another debug function (e.g. 0xFEEEFEEE for HeapFree).
These constants are defined in
DbgHeap.c file as
static unsigned char _bNoMansLandFill = 0xFD; /* fill no-man's land with this */
static unsigned char _bDeadLandFill = 0xDD; /* fill free objects with this */
static unsigned char _bCleanLandFill = 0xCD; /* fill new objects with this */
Compiler initialisations
0xCC, 0xCCCCCCCC - The /GX Microsoft Visual C++ compiler option initialises all local variables not explicitly initialised by the program. It fills all memory used by these variables with 0xCC, 0xCCCCCCCC.
Windows NT memory codes
0xABABABAB - Memory following a block allocated by LocalAlloc().
0xBAADF00D - "Bad Food". This is memory allocated via LocalAlloc( LMEM_FIXED, ... ). It is memory that has been allocated but not yet written to.
0xFEEEFEEE - OS fill heap memory, which was marked for usage, but wasn't allocated by HeapAlloc() or LocalAlloc(). Or that memory just has been freed by HeapFree().
今天Ben忽然问我。。。LPVOID中的L是什么意思,一下把我问住了。不可能是长的意思是。查了下。。竟然是远指针的含义,都没学过这个东东。学习了下。。以前都没听说指针还有远、近、巨之分的,从没听说过什么近指针、远指针和巨指针。
char near *p; /*定义一个字符型“近”指针*/
char far *p; /*定义一个字符型“远”指针*/
char huge *p; /*定义一个字符型“巨”指针*/
首先要从8086处理器体系结构和汇编渊源学起。8086是一个16位处理器,它设定了四个段寄存器,专门用来保存段地址:CS(Code Segment):代码段寄存器;DS(Data Segment):数据段寄存器;SS(Stack Segment):堆栈段寄存器;ES(Extra Segment):附加段寄存器。8086采用段式访问,访问本段(64K范围内)的数据或指令时,不需要变更段地址(意味着段地址寄存器不需修改),而访问本段范围以外的数据或指令时,则需要变更段地址(意味着段地址寄存器需要修改)。
因此,在16位处理器环境下,如果访问本段内地址的值,用一个16位的指针(表示段内偏移)就可以访问到;而要访问本段以外地址的值,则需要用16位的段内偏移+16位的段地址,总共32位的指针。
这样,就可以知道远、近指针的区别:
近指针是只能访问本段、只包含本段偏移的、位宽为16位的指针;
远指针是能访问非本段、包含段偏移和段地址的、位宽为32位的指针。
近指针只能对64k字节数据段内的地址进行存取,如:
char near *p;
p=(char near *)0xffff;
远指针是32位指针,它表示段地址:偏移地址,远指针可以进行跨段寻址,可以访问整个内存的地址。如定义远程指针p指向0x1000段的0x2号地址,即1000:0002,则可写作:
char far *p;
p=(char far *)0x10000002;
除了远指针和近指针外,还有一个巨指针的概念。
和远指针一样,巨指针也是32位的指针,指针也表示为16位段:16位偏移,也可以寻址任何地址。它和远指针的区别在于进行了规格化处理。远指针没有规格化,可能存在两个远指针实际指向同一个物理地址,但是它们的段地址和偏移地址不一样,如23B0:0004和23A1:00F4都指向同一个物理地址23604!巨指针通过特定的例程保证:每次操作完成后其偏移量均小于10h,即只有最低4位有数值,其余数值都被进位到段地址上去了,这样就可以避免Far指针在64K边界时出乎意料的回绕的行为。当然,一次操作必须小于64K。下面的函数可以将远指针转换为巨指针:
void normalize(void far ** p)
{
*p=(void far *)(((long)*p&0xffff000f)+(((long)*p&0x0000fff00<<12));
}
从上面的函数中 可以看到指针之指针的使用,这个函数要修改指针的值,因此必须传给它的指针的指针作为参数。
看到最后。。。。学习到了一点。。让人很郁闷的事实。。。
近指针、远指针、巨指针是段寻址的16bit处理器的产物(如果处理器是16位的,但是不采用段寻址的话,也不存在近指针、远指针、巨指针的概念),当前普通PC所使用的32bit处理器(80386以上)一般运行在保护模式下的,指针都是32位的,可平滑地址,已经不分远、近指针了。但是在嵌入式系统领域下,8086的处理器仍然有比较广泛的市场,如AMD公司的AM186ED、AM186ER等处理器,开发这些系统的程序时,我们还是有必要弄清楚指针的寻址范围。
一个多小的学习。。。。。。。白学了。。。。。。-_______________________________-
想想自己也注册很多天了。也不是没时间写。。只是忽然不觉得有什么系统的东西可以写上来的。后来想想。也没什么啦。哪有那么多系统的东西可以写啊。所以。。就把每天code上遇到的一些问题。。以及自己学习的一些心得记下来吧。以便日后忘了的话。。可以看下,呵呵。。。以后要坚持啦。
今天下午权哥忽然发过来一道题,后来说明是网上流传的一道关于字节对齐的题,很久没做了。尝试着想啊想啊做了下。也算做出来了
#pragma pack(8)
struct s1{
short a;
long b;
};
struct s2{
char c;
s1 d;
long long e;
};
#pragma pack()
问
1.sizeof(s2) = ?
2.s2的s1中的a后面空了几个字节接着是b?
回忆中的思路是:
s1 short(2) long(4)
0 1 2 3 4 5 6 7 8 9 10
short long 算错了
对齐的原则是取小 当前类型与pack(n)的字节数取小的那一个
short占两个字节后当前地址为2 long为四字节。可以被整除2 所以不用添字节
所以 sizeof(s1) = 6 算错了这个地方算错了。。非常对不起大家。。重写的那天。。头晕。。按1字节算了
也多谢朋友的提醒,以后一定注意.
重新修正为
s1 short(2) long(4)
0 1 2 3 4 5 6 7
short 补 long
对齐的原则是取小 当前类型与pack(n)的字节数取小的那一个
short占两个字节 long是占四个字节。所以需要当前地址可以被4整除 所以。。补两个字节
所以 sizeof(s1) =8然后来算s2
char(1) s1被拆分 (short(2) long(4) 结构体拆分后按最大字节对齐) longlong(8)
0 1 2 3 4567 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
char 补 short long 补 longlong
所以sizeof(s2)=24
a后面空了2个字节接着是b
应该是没有问题的 还有一点。。如果最后位数不够的话。。。是按packer和结构体两个最小的字节数补齐