??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久久人妻精品一区二区三区,www.久久99,久久综合久久鬼色http://www.shnenglu.com/zzh/Xiao.Zhu C++zh-cnThu, 08 May 2025 22:27:48 GMTThu, 08 May 2025 22:27:48 GMT60嵌入式程序员应该知道?6个问?/title><link>http://www.shnenglu.com/zzh/archive/2008/11/28/68049.html</link><dc:creator>Xiao.Zhu</dc:creator><author>Xiao.Zhu</author><pubDate>Fri, 28 Nov 2008 00:41:00 GMT</pubDate><guid>http://www.shnenglu.com/zzh/archive/2008/11/28/68049.html</guid><wfw:comment>http://www.shnenglu.com/zzh/comments/68049.html</wfw:comment><comments>http://www.shnenglu.com/zzh/archive/2008/11/28/68049.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zzh/comments/commentRss/68049.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zzh/services/trackbacks/68049.html</trackback:ping><description><![CDATA[<div class="2kgugai" id="art" style="margin: 15px;"> <div>---Sailor_forever分析整理Q?a href="mailto:sailing_9806@163.com">sailing_9806@163.com</a><br>1、预处理器(PreprocessorQ?.. 1<br>2、如何定义宏... 2<br>3、预处理器标?error的目的是什么?... 4<br>4、死循环QInfinite loopsQ?.. 4<br>5、数据声明(Data declarationsQ?.. 5<br>6、关键字static的作用是什么?... 6<br>7、关键字const有什么含意?... 7<br>8、Volatile的?.. 9<br>9、位操作QBit manipulationQ?.. 12<br>10、访问固定的内存位置QAccessing fixed memory locationsQ?.. 13<br>11、中断(InterruptsQ?.. 13<br>12、符h展的代码例子QCode examplesQ?.. 15<br>13、处理器字长D的数据扩展问?.. 16<br>14、动态内存分配(Dynamic memory allocationQ?.. 16<br>15、用Typedef构造复合类?.. 17<br>16、晦涩的语法及代码风?.. 18<br><br>C语言试是招聘嵌入式pȝE序员过E中必须而且有效的方法。这些年Q我既参加也l织了许多这U测试,在这q程中我意识到这些测试能为面试者和被面试者提供许多有用信息,此外Q撇开面试的压力不谈,q种试也是相当有趣的?<br><br>? 被面试者的角度来讲Q你能了解许多关于出题者或监考者的情况。这个测试只是出题者ؓ昄其对ANSI标准l节的知识而不是技术技巧而设计吗Q这是个愚蠢? 问题吗?如要你答出某个字W的ASCII倹{这些问题着重考察你的pȝ调用和内存分配策略方面的能力吗?q标志着出题者也许花旉在微Z而不是在嵌入? pȝ上。如果上qCQ何问题的{案??的话Q那么我知道我得认真考虑我是否应该去做这份工作?<br><br>从面试者的角度来讲Q一个测试也许能 从多斚w揭示应试者的素质Q最基本的,你能了解应试者C语言的水q뀂不怎么P看一下这人如何回{他不会的问题也是满有趣。应试者是以好的直觉做出明? 的选择Q还是只是瞎蒙呢Q当应试者在某个问题上卡住时是找借口呢,q是表现出对问题的真正的好奇心,把这看成学习的机会呢Q我发现q些信息与他们的试? l一h用?<br><br>有了q些xQ我军_Z些真正针对嵌入式pȝ的考题Q希望这些o人头痛的考题能给正在扑ַ作的Z点帮助。这些问题都是我q些q实际碰到的。其中有些题很难Q但它们应该都能l你一点启q?<br><br>q个试适于不同水^的应试者,大多数初U水q的应试者的成W会很差,l验丰富的程序员应该有很好的成W。ؓ了让你能自己军_某些问题的偏好,每个问题没有分配分数Q如果选择q些考题Z所用,误行按你的意思分配分数?<br><br><br><br>1、预处理器(PreprocessorQ?<br>用预处理指o#define 声明一个常敎ͼ用以表明1q中有多秒Q忽略闰q问题) <br><br>#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)ULQ大写都行Q常量后面可以加此标志,宏的命名风格要大写,多个之间用下划线Q?<br><br>我在q想看到几g事情Q?<br><br>1) #define 语法的基本知识(例如Q不能以分号l束Q括L使用Q表辑ּ、参数等要括hQ,{等Q?<br><br>2)懂得预处理器ؓ你计常数表辑ּ的|N不是替换么,先算再替Q会常数合qӞQ因此,直接写出你是如何计算一q中有多秒而不是计出实际的|是更清晰而没有代L?<br><br>3) 意识到这个表辑ּ一?6位机的整型数溢出-因此要用到长整型W号L,告诉~译器这个常数是的长整型数?<br><br>4) 如果你在你的表达式中用到ULQ表C无W号长整型)Q那么你有了一个好的v炏V记住,W一印象很重要?br><br>2、如何定义宏<br>写一?标准"宏MIN Q这个宏输入两个参数q返回较的一个?br><br>考点Q(表达式、参数等要括hQ?<br><br>#define MIN(A,B) Q(AQ?<= (B) ? (A) : (B)) <br><br>q个试是ؓ下面的目的而设的: <br><br>1) 标识#define在宏中应用的基本知识。这是很重要的。因为在嵌入(inline)操作W变为标准C的一部分之前Q宏是方便生嵌入代码的唯一ҎQ对 于嵌入式pȝ来说Qؓ了能辑ֈ要求的性能Q当然主要是实时性哦Q牺牲代码空间换取时间效率)Q嵌入代码经常是必须的方法?<br><br>2)三重条g操作W的知识。这个操作符存在C语言中的原因是它使得~译器能产生比if-then-elseQ存在条件{UM中断指o水U)更优化的代码Q了解这个用法是很重要的?<br><br>3) 懂得在宏中小心地把参数用括号括v?<br><br>4) 我也用这个问题开始讨论宏的副作用Q例如:当你写下面的代码时会发生什么事Q?<br><br>least = MIN(*p++, b); <br><br><br><br>此处考点Qinline函数和宏的区?br><br>? 只是参数完全替换,即MIN(*p++, b)q行宏展开后ؓQ(*p++Q?<= (b) ? (*p++) : (b))Q如果(*p++Q?<= (b)成立Q则表达式的gؓ(*p++)Q但׃在(*p++Q?lt;= (b)判断q程中改变了p的|使得此时? (*p++)非(*p++Q?lt;= (b)中的gQ违背了Q号表达式的原意?br><br>但是内联inline函数进行参数检查,求出参数的值后再将此值带入函CQ因此(QAQ?<= (B) ? (A) : (B))中的A是一致的?br><br><br><br>W一部分Q宏<br>Z么要使用宏呢Q?br>? 为函数的调用必须要将E序执行的顺序{Ud函数所存放在内存中的某个地址Q将函数的程序内Ҏ行完后,再返回到转去执行该函数前的地斏V这U{UL作要? 在{L行前要保存现场ƈ记忆执行的地址Q{回后要恢复现场,q按原来保存地址l箋执行。因此,函数调用要有一定的旉和空间方面的开销Q于是将影响其效 率?br>而宏只是在预处理的地Ҏ代码展开Q不需要额外的I间和时间方面的开销Q所以调用一个宏比调用一个函数更有效率?br>但是宏也有很多的不尽人意的地斏V?br>1、宏不能讉K对象的私有成员?br>2、宏的定义很Ҏ产生二意性?br><br>3、宏定义的常量在代码区,很多调试器不能够对其调试<br>我们举个例子Q?br>#define square(x) (x*x)<br><br>避免q些错误的方法,一是给宏的参数都加上括受?br>#define square(x) ((x)*(x))<br><br>W二部分Q内联函?br>从上面的阐述Q可以看到宏有一些难以避免的问题Q怎么解决呢?<br>内联函数是代码被插入到调用者代码处的函数。如?#define 宏,内联函数通过避免被调用的开销来提高执行效率,其是它能够通过调用Q?#8220;q程化集?#8221;Q被~译器优化?br><br><br>? 联函数和宏很cMQ而本质区别在于,宏是由预处理器对宏进行替代,而内联函数是通过~译器控制来实现的。而且内联函数是真正的函数Q只是在需要用到的? 候,内联函数像宏一L展开Q所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函Ch调用内联函数Q而不必担心会产生于处理宏的一些问题?br>声明内联函数看上d普通函数非常相|<br>void f(int i, char c);<br>当你定义一个内联函数时Q在函数定义前加?inline 关键字,q且定义放入头文gQ?br>inline void f(int i, char c)<br>{<br>// ...<br>}<br>内联函数必须是和函数体的定义x在一P才有效?br>像这Lxinline function(int i)是没有效果的Q编译器只是把函C为普通的函数xQ我们必d义函C?br>inline int function(int i) {return i*i;}<br>q样我们才算定义了一个内联函数。我们可以把它作Z般的函数一栯用。但是执行速度比一般函数的执行速度要快?br><br><br>当然Q内联函C有一定的局限性。就是函C的执行代码不能太多了Q如果,内联函数的函Cq大Q一般的~译器会攑ּ内联方式Q而采用普通的方式调用函数。这P内联函数和普通函数执行效率一样了?br>有上面的两者的Ҏ,我们可以用内联函数完全取代预处理宏?br><br><br><br>3、预处理器标?error的目的是什么? <br>如果你不知道{案Q请看参考文?。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去扑և象这U问题的{案。当然如果你不是在找一个书呆子Q那么应试者最好希望自׃要知道答案?br><br><br><br>4、死循环QInfinite loopsQ?<br>嵌入式系l中l常要用到无限@环,你怎么LC~写d@环呢Q?q个问题用几个解x案?<br><br>我首选的Ҏ是: <br><br>while(1) <br><br>{ <br><br><br><br>} <br><br>一些程序员更喜Ƣ如下方案: <br><br>for(;;) Q此处的判断效率要低的多Q在汇编代码中看看?Q?Q?br><br>{ <br><br><br><br>} <br><br>q? 个实现方式让我ؓ难,因ؓq个语法没有切表达到底怎么回事。如果一个应试者给个作为方案,我将用这个作Z个机会去探究他们q样做的基本原理。如? 他们的基本答案是Q?我被教着q样做,但从没有惛_qؓ什么?q会l我留下一个坏印象? Q很多时候面试官x你思考问题的方式Q是否留意某些东西善于思考,可能q没有对错,只是偏好而已Q比如memset和memcopy以及strcpy? 能拷贝字W串Q到底有什么区别呢Q看你是否善于比较是否关注细节)<br><br><br><br>W三个方案是?goto Qgoto语句在C中是应该量避免的,只在处理错误代码时用Q?br><br>Loop: <br><br>... <br><br>goto Loop; <br><br>应试者如l出上面的方案,q说明或者他是一个汇~语aE序员(q也许是好事Q或者他是一个想q入新领域的BASIC/FORTRANE序员?<br><br><br><br>5、数据声明(Data declarationsQ?<br>用变量al出下面的定?<br><br>a) 一个整型数QAn integerQ?<br><br>b)一个指向整型数的指针( A pointer to an integerQ?<br><br>c)一个指向指针的的指针,它指向的指针是指向一个整型数Q?A pointer to a pointer to an integersQ?<br><br>d)一个有10个整型数的数l( An array of 10 integersQ?<br><br>e) 一个有10个指针的数组Q该指针是指向一个整型数的。(An array of 10 pointers to integersQ?<br><br>f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integersQ?<br><br>g) 一个指向函数的指针Q该函数有一个整型参数ƈq回一个整型数QA pointer to a function that takes an integer as an argument and returns an integerQ?<br><br>h) 一个有10个指针的数组Q该指针指向一个函敎ͼ该函数有一个整型参数ƈq回一个整型数Q?An array of ten pointers to functions that take an integer argument and return an integer Q?<br><br>{案是: <br><br>a) int a; // An integer <br><br>b) int *a; // A pointer to an integer <br><br>c) int **a; // A pointer to a pointer to an integer <br><br>d) int a[10]; // An array of 10 integers <br><br>e) int *a[10]; // An array of 10 pointers to integers <br><br>f) int (*a)[10]; // A pointer to an array of 10 integers <br><br>g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer // 不是Qint xQ,不需要具体的参数<br><br>h) int (*a[10])(int)Q可以从e、gcL得到Q? // An array of 10 pointers to functions that take an integer argument and return an integer <br><br>? 们经常声U这里有几个问题是U要M下书才能回答的问题,我同意这U说法。当我写q篇文章ӞZ定语法的正性,我的查了一下书。但是当我被面试 的时候,我期望被问到q个问题Q或者相q的问题Q。因为在被面试的q段旉里,我确定我知道q个问题的答案。应试者如果不知道所有的{案Q或臛_大部分答 案)Q那么也没有ؓq次面试做准备,如果该面试者没有ؓq次面试做准备,那么他又能ؓ什么做准备呢? <br><br><br><br>6、关键字static的作用是什么? <br>q个单的问题很少有h能回{完全。在C语言中,关键字static有三个明昄作用Q?<br><br>1)在函C内,一个被声明为静态的变量在这一函数被调用过E中l持其g变(该变量存攑֜静态变量区Q?<br><br>2) 在模块内Q但在函C外)Q一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量?<br><br>3) 在模块内Q一个被声明为静态的函数只可被这一模块内的其它函数调用。那是Q这个函数被限制在声明它的模块的本地范围内用?<br><br>大多数应试者能正确回答W一部分Q一部分能正回{第二部分,但是很少的h能懂得第三部分。这是一个应试者的严重的缺点,因ؓ他显然不懂得本地化数据和代码范围的好处和重要性?<br><br><br><br>考点Q在嵌入式系l中Q要时刻懂得UL的重要性,E序可能是很多程序员共同协作同时完成Q在定义变量及函数的q程Q可能会重名Q这l系l的集成带来ȝQ因此保证不冲突的办法是昄的表C此变量或者函数是本地的,static卛_?br><br>在Linux的模块编E中Q这一条很明显Q所有的函数和全局变量都要用static关键字声明,其作用域限制在本模块内部,与其他模块共享的函数或者变量要EXPORT到内怸?br><br><br><br>static关键字至有下列n个作用:<br>Q?Q设|变量的存储域,函数体内static变量的作用范围ؓ该函CQ不同于auto变量Q该变量的内存只被分配一ơ,因此其值在下次调用时仍l持上次的|<br><br>Q?Q限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;<br><br>Q?Q限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用Q这个函数的使用范围被限制在声明它的模块内;<br>Q?Q在cM的static成员变量意味着它ؓ该类的所有实例所׃nQ也是说当某个cȝ实例修改了该静态成员变量,其修改gؓ该类的其它所有实例所见;<br>Q?Q在cM的static成员函数属于整个cL拥有Q这个函C接收this指针Q因而只能访问类的static成员变量?br><br><br><br>7、关键字const有什么含意? <br>? 只要一听到被面试者说Q?const意味着常数"Q不是常敎ͼ可以是变量,只是你不能修改它Q,我就知道我正在和一个业余者打交道。去qDan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什?如果你从没有d那篇文章Q只要能说出const意味着"只读"可 以了。尽这个答案不是完全的{案Q但我接受它作ؓ一个正的{案。(如果你想知道更详l的{案Q仔l读一下Saks的文章吧。) <br><br>如果应试者能正确回答q个问题Q我问他一个附加的问题Q下面的声明都是什么意思? <br><br>Const只是一个修饰符Q不怎么样a仍然是一个int型的变量<br><br>const int a; <br><br>int const a; <br><br>const int *a; <br><br>int * const a; <br><br>int const * a const; <br><br>本质Qconst在谁后面谁就不可修改Qconst在最前面则将其后UM位即可,二者等?br><br><br><br>前两个的作用是一Pa是一个常整型数。第三个意味着a是一个指向常整型数的指针Q也是Q指向的整型数是不可修改的,但指针可以,此最常见于函数的参数Q当你只引用传进来指针所指向的值时应该加上const修饰W,E序中修改编译就不通过Q可以减程序的bugQ?br><br><br><br>W四个意思a是一个指向整型数的常指针Q也是_指针指向的整型数是可以修改的Q但指针是不可修改的Q。最后一个意味着a是一个指向常整型数的常指针(也就是说Q指针指向的整型数是不可修改的,同时指针也是不可修改的)?br><br><br><br>如果应试者能正确回答q些问题Q那么他q我留下了一个好印象。顺带提一句,也许你可能会问,即不用关键?Q也q是能很Ҏ写出功能正确的程序,那么我ؓ什么还要如此看重关键字const呢?我也如下的几下理由: <br><br>1) 关键字const的作用是为给M代码的h传达非常有用的信息,实际上,声明一个参Cؓ帔R是ؓ了告诉了用户q个参数的应用目的。如果你曾花很多旉清理 其它人留下的垃圾Q你׃很快学会感谢q点多余的信息。(当然Q懂得用const的程序员很少会留下的垃圾让别人来清理的。) <br><br>2) 通过l优化器一些附加的信息Q用关键字const也许能生更紧凑的代码?<br><br>3) 合理C用关键字const可以使编译器很自然地保护那些不希望被改变的参敎ͼ防止其被无意的代码修攏V简而言之,q样可以减少bug的出现?br><br><br><br>const关键字至有下列n个作用:<br><br>Q?Q欲L一个变量被改变Q可以用const关键字。在定义该const变量Ӟ通常需要对它进行初始化Q因Z后就没有Z再去改变它了Q?br>Q?Q对指针来说Q可以指定指针本wؓconstQ也可以指定指针所指的数据为constQ或二者同时指定ؓconstQ?br>Q?Q在一个函数声明中Qconst可以修饰形参Q表明它是一个输入参敎ͼ在函数内部不能改变其|<br>Q?Q对于类的成员函敎ͼ若指定其为constcdQ则表明其是一个常函数Q不能修改类的成员变量;<br>Q?Q对于类的成员函敎ͼ有时候必L定其q回gؓconstcdQ以使得其返回g?#8220;左?#8221;。例如:<br>const classA operator*(const classA& a1,const classA& a2); <br>  operator*的返回结果必L一个const对象。如果不是,q样的变态代码也不会~译出错Q?br>classA a, b, c;<br>(a * b) = c; // 对a*b的结果赋?<br>  操作(a * b) = c昄不符合编E者的初衷Q也没有M意义?br><br><br><br>8、Volatile的?br>关键字volatile有什么含?q给Z个不同的例子?<br><br>一 个定义ؓvolatile的变量是说这变量可能会被意想不到地改变,q样Q编译器׃会去假设q个变量的g。精地说就是,优化器在用到q个变量时必? 每次都小心地重新dq个变量的|而不是用保存在寄存器里的备份(׃讉K寄存器的速度要快qRAMQ所以编译器一般都会作减少存取外部RAM的优 化)。下面是volatile变量的几个例子: <br><br>1) q行讑֤的硬件寄存器Q如Q状态寄存器Q通常在头文g中将g寄存器地址define为某个意义明的表达式) <br><br>2) 一个中断服务子E序中会讉K到的非自动变?Non-automatic variablesQ即static变量) Q在中断服务E序中修改的供其他程序检用的变量需要加volatile声明Q否则编译器可能对变量更Cơ后每次都用缓存g再立x斎ͼ<br><br>3) 多线E应用中被几个Q务共享的变量Q可能被多个U程随时修改Q?<br><br><br><br>? {不个问题的人是不会被雇佣的。我认ؓq是区分CE序员和嵌入式系l程序员的最基本的问题。搞嵌入式的家伙们经常同g、中断、RTOS{等打交道, 所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正地回答了这是问题(嗯,怀疑是否会是这PQ我? E微q一下,看一下这家伙是不是直正懂得volatile完全的重要性?<br><br>1)一个参数既可以是constq可以是volatile吗?解释Z么?<br><br>2); 一个指针可以是volatile 吗?解释Z么?<br><br>3); 下面的函数有什么错误: <br><br>int square(volatile int *ptr) <br><br>{ <br><br>return *ptr * *ptr; <br><br>} <br><br>下面是答案: <br><br>1)是的。一个例子是只读的状态寄存器。它是volatile因ؓ它可能被意想不到地改变。它是const因ؓE序不应该试囑֎修改它?<br><br>2); 是的。尽这q不很常见。一个例子是当一个中断服务子E序修改一个指向一个buffer的指针时?<br><br>3) q段代码有点变态。这D代码的目的是用来返回指?ptr指向值的qxQ但是,׃*ptr指向一个volatile型参敎ͼ~译器将产生cM下面的代码: <br><br>int square(volatile int *ptr) <br><br>{ <br><br>int a,b; <br><br>a = *ptr; <br><br>b = *ptr; <br><br>return a * b; <br><br>} <br><br>׃*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,q段代码可能q不是你所期望的^方|正确的代码如下: <br><br>long square(volatile int *ptr) <br><br>{ <br><br>int a; <br><br>a = *ptr; <br><br>return a * a; <br><br>} <br><br><br><br>关于volatile关键字在中断函数中的影响实例<br><br><br><br>串口发送数据,中断中对其检,当中断生后Q置接收标志Q主循环中检此L志,未用valotile修饰Ӟ~译l果如下Q?br><br><br><br>[0xe59f41bc] ldr r4Q?x30203378 ; = #0x302096f0<br><br>0x302031b8 [0xe5d40000] ldrb r0Q[r4Q?0]<br><br>while(!uart1_rxFlag); //uart1_rxFlag为全局变量Q在串口接收中断中置1<br><br>0x302031bc [0xe3500000] cmp r0Q?0<br><br>0x302031c0 [0x0afffffd] beq 0x302031bc; (Can_Int_Test + 0x17c)<br><br>? ~译器对其进行了优化Q读取一ơuart1_rxFlag的g后,其存放在寄存器r0中,比较后,条g不满Il箋{待Q但未重新取存储器中 uart1_rxFlag的|此时即中断服务函数中修改了uart1_rxFlag的|比较处仍然不能发玎ͼ出C无论如何E序停在此处的? 题?br><br><br><br>// 加了volatile关键字后Q编译的l果<br><br>302031b4 ldr r4Q?x30203378 ; = #0x302096f0<br><br>while(uart1_rxFlag == 0);<br><br>302031b8 [0xe5d40000] ldrb r0Q[r4Q?0]<br><br>302031bc [0xe3500000] cmp r0Q?0<br><br>302031c0 [0x0afffffc] beq 0x302031b8 ; (Can_Int_Test + 0x288)<br><br>d了关键字后,比较不等Q蟩转到重新取存储器中的uart1_rxFlagQ因此Q何时候uart1_rxFlag的值都是最新的?br><br>一定程度的优化Q去掉了duart1_rxFlag地址的语句?br><br><br><br>? 义一个易失性变量,~译器有一U技术叫数据分析,分析E序中的变量在哪里被赋倹{在哪里使用、在哪里失效Q分析结果可以用于常量合qӞ帔R传播{优化? 当编译器查到代码没有修改字段的|有可能在你讉K字段时提供上ơ访问的~存|q能够提高程序的效率Q但有时q些优化会带来问题,不是我们E序所需 要的Q特Ҏ对硬件寄存器操作的程序,q时可以用volatile关键字禁止做q些优化?br><br><br><br>多Q务环境下各Q务间׃n的标志应该加voatile关键字:在多U程讉K某字D|Q代码希望这些访问能够操作(dQ到字段的最新|同时写到变量的操作能立即更新Q对字段加上volatile关键字,那么对该字段的Q何请求(?写)都会立刻得到执行?br><br><br><br>9、位操作QBit manipulationQ?<br>嵌入式系lL要用户对变量或寄存器q行位操作。给定一个整型变量aQ写两段代码Q第一个设|a的bit 3Q第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变?对这个问题有三种基本的反?<br><br>1)不知道如何下手。该被面者从没做qQ何嵌入式pȝ的工作?<br><br>2) 用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同~译器之间是不可UL的,同时也保证了的你的代码是不可重用的?br><br>3) ?#defines ?bit masks 操作。这是一个有极高可移植性的ҎQ是应该被用到的Ҏ。最佳的解决Ҏ如下Q?<br><br>#define BIT3 (0x1 << 3) Q采用宏数字定义ؓ有意义的BIT3Q明,不易出错Q改h方便Q?br><br>static int a; <br><br>void set_bit3(void) <br><br>{ <br><br>a |= BIT3; <br><br>} <br><br>void clear_bit3(void) <br><br>{ <br><br>a &= ~BIT3; <br><br>} <br><br><br><br>一 些h喜欢|和清除D定义一个掩码(待操作位?Q其余位?的数Q对于某个意义靠多位同时表示的最好带上掩码,隔离其他位的影响Q同时定义一些说? 常数Q这也是可以接受的。我希望看到几个要点Q说明常数、|=?amp;=~操作Q先取反?amp;是对某位?的最好操作?<br><br><br><br>考点Q?br><br>在嵌入式pȝ中,时刻要关注移植性,具体的程序中不要出现具体的数字,q些数字都应该define成某个有意义的符P可读性可UL性都很强Q比?br><br>#define BIT(x) (0x1 << (x))<br><br>X作ؓ参数可以很方便的对Q意位q行操作Q意义明,更改替换方便<br><br><br><br>10、访问固定的内存位置QAccessing fixed memory locationsQ?<br>嵌入式系l经常具有要求程序员去访问某特定的内存位|的特点?<br><br>? 某工E中Q要求设|一l对地址?x67a9的整型变量的gؓ0xaa66。编译器是一个纯_的ANSI~译器。写代码d成这一d。这一问题试你是 否知道ؓ了访问一l对地址把一个整型数强制转换QtypecastQؓ一指针是合法的。这一问题的实现方式随着个h风格不同而不同。典型的cM代码如下Q? <br><br>int *ptr; <br><br>ptr = (int *)0x67a9; <br><br>*ptr = 0xaa55; <br><br>A more obscure approach is: ( 一个较晦ӆ的方法是)Q?<br><br>*(int * const)(0x67a9) = 0xaa55; <br><br>即你的品味更接q第二种ҎQ但我徏议你在面试时使用W一U方案?<br><br><br><br>在嵌入式pȝ中,对于大量此类型数据如g寄存器应该采用如下方?br><br>typedef volatile unsigned int HARD_REG;<br><br>#define REG_NAME (*(HARD_REG *)ADDR)<br><br>卛_ADDR强制转换Z个指向HARD_REGcd数据的指针,*HARD_REG为volatile的无W号整型?br><br><br><br>11、中断(InterruptsQ?<br>? 断是嵌入式系l中重要的组成部分,q导致了很多~译开发商提供一U扩?让标准C支持中断。其代表事实是,产生了一个新的关键字 __interruptQ?1卛_此)。下面的代码׃用了__interrupt关键字去定义了一个中断服务子E序(ISR)Q请评论一下这D代码的? <br><br>__interrupt double compute_area (double radius) <br><br>{ <br><br>double area = PI * radius * radius; <br><br>printf("\nArea = %f", area); <br><br>return area; <br><br>} <br><br>q个函数有太多的错误了,以至让h不知从何说v了(前提是非操作pȝ下的中断服务函数Q: <br><br>1)ISR 不能q回一个|都应该ؓvoidcdQ。如果你不懂q个Q那么你不会被雇用的?<br><br>2)ISR 不能传递参数。如果你没有看到q一点,你被雇用的机会等同第一V?<br><br>3)在许多的处理?~译器中QQ点一般都是不可重入的。有些处理器/~译器需要让额外的寄存器入栈Q有些处理器/~译器就是不允许在ISR中做点q算。此外,ISR应该是短而有效率的,在ISR中做点q算是不明智的?<br><br>///////////////////////////////<br><br>另外中断服务E序是运行在内核态的QlinuxQ,内核通常是不支持点q算的?br><br><a target="_blank"><font color="#0000ff">http://access911.net/n/doc1.asp?mode=a&aid=4750647</font></a><br><br>内核中的printk和标准库的printf不一P前者因为由内核直接实现Q不能支持Q炏V?br><br>在<linux内核设计与实玎ͼ的第一章中内核开发的特点一节里就有比较了内核开发与应用开发的差异。其中一点就是内核编E时点数的问题Q书中有一句话是:内核~程时QҎ很难使用<br><br>因ؓ没有点单元,内核要支持Q点必L内核以soft-float 方式重新~译,其连接所有的库也都要用soft-float 方式~译.<br><br>否则另外一U方式用整数定义Q点类型加点预算库完成你的工?<br><br><br><br><a target="_blank"><font color="#0000ff">http://topic.csdn.net/u/20070417/16/a4b56569-228c-4b70-b5ab-30ee61c99a3d.html</font></a><br><br>如果你的内核里编译进了QҎ持,那么是可以的。要不内核或是模块不能用float或是double内型的变量或函数<br><br><br><br>在配|内核的时候把点模拟器选上Q应该是可以支持的,但是速度非常慢?<br>我曾l遇到过Q硬件明明支持Q点运的FPUQ但是编译内核的时候选上了QҎ拟器Q结果所有的应用E序的Q点运速度都非常慢。所以我怀疑要支持点只要~译内核的时候选上Q对于应用程序不需要怎么兛_?br><br>///////////////////////////////<br><br><br><br>4) 与第三点一脉相承,printf()l常有重入和性能上的问题。如果你丢掉了第三和W四点,我不会太为难你的。不用说Q如果你能得到后两点Q那么你的被雇用前景来光明了?<br><br><br><br>12、符h展的代码例子QCode examplesQ?<br>下面的代码输出是什么,Z么? <br><br>void foo(void) <br><br>{ <br><br>unsigned int a = 6; <br><br>int b = -20; <br><br>(a+b > 6) ? puts("> 6") : puts("<= 6"); <br><br>} <br><br>Vc6.0试情况<br><br>void main(void)<br><br>{<br><br>unsigned int a = 6; <br><br>int b = -20; <br><br>printf("unsigned int a + int b = %x\n", (a + b));<br><br><br><br>}<br><br>/*unsigned int a + int b = fffffff2*/<br><br>q? 个问题测试你是否懂得C语言中的整数自动转换原则Q我发现有些开发者懂得极这些东ѝ不如何,q无W号整型问题的答案是输出? ">6"。原因是当表辑ּ中存在有W号cd和无W号cd时所有的操作数都自动转换为无W号cd。因?20变成了一个非常大的正整数Q所以该表达? 计算出的l果大于6。这一点对于频J用到无W号数据cd的嵌入式pȝQ硬件寄存器的值全部是无符LQ来说是丰常重要的。如果你{错了这个问题,你也到 了得不到q䆾工作的边~?<br><br><br><br>13、处理器字长D的数据扩展问?br>评h下面的代码片断: <br><br>unsigned int zero = 0; <br><br>unsigned int compzero = 0xFFFF; <br><br>/*1's complement of zero */ 0的补码ؓ?的数<br><br>对于一个int型不?6位的处理器ؓ_上面的代码是不正的。应~写如下Q?<br><br>unsigned int compzero = ~0; <br><br>q一问题真正能揭露出应试者是否懂得处理器字长的重要性(嵌入式^台可能是8?6?2的,UL的角度来说写出固定的0xFFFF是不对的Q。在我的l验里,好的嵌入式程序员非常准确地明白硬件的l节和它的局限,然而PC机程序往往把硬件作Z个无法避免的烦恼?<br><br><br><br>? 了这个阶D,应试者或者完全垂头气了或者信心满满志在必得。如果显然应试者不是很好,那么q个试在q里l束了。但如果昄应试者做得不错,那么我就 扔出下面的追加问题,q些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提些问题,我希望更多看到应试者应付问题的ҎQ很重要哦,面试者关 注的是你思考问题解决问题的q程Q当你不知道{案时千万千万不要猜一个答案给他,因ؓ现在不是选择题,面试官要的是q程Q你只需要将你考虑问题的过E说? 白就OK了)Q而不是答案。不如何,你就当是q个׃?.. <br><br><br><br>14、动态内存分配(Dynamic memory allocationQ?<br>? 不像非嵌入式计机那么常见Q嵌入式pȝq是有从堆(heapQ中动态分配内存的q程的。那么嵌入式pȝ中,动态分配内存可能发生的问题是什么?q里Q? 我期望应试者能提到内存片Q碎片收集的问题Q变量的持行旉{等。这个主题已l在ESP杂志中被q泛地讨了(主要?P.J. Plauger, 他的解释q远过我这里能提到的Q何解释)Q所有回q头看一下这些杂志吧Q让应试者进入一U虚假的安全感觉后,我拿么一个小节目Q下面的代码片段的输 出是什么,Z么? <br><br>char *ptr; <br><br>if ((ptr = (char *)malloc(0)) == NULL) <br><br>puts("Got a null pointer"); <br><br>else <br><br>puts("Got a valid pointer"); <br><br>q? 是一个有的问题。最q在我的一个同事不l意?gl了函数mallocQ得C一个合法的指针之后Q我才想到这个问题。这是上面的代码,该代码的? 出是"Got a valid pointer"。我用这个来开始讨L一问题Q看看被面试者是否想到库例程q样做是正确Q因为如果申请失败,则程序处理认为内存不了Q一般会l止 E序Q是很严重的问题Q)。得到正的{案固然重要Q但解决问题的方法和你做军_的基本原理更重要些?<br><br>q回一個控指針還是指向 0 字節的指針甚x向一个可以操作的指针Q?br><br>Q取决于pȝq_的实玎ͼC99及其他标准规定可以不同的Q?br><br>malloc(0) in glibc returns a valid pointer to something(!?!?) while in uClibc calling malloc(0) returns a NULL. The behavior of malloc(0) is listed as implementation-defined by SuSv3, so both libraries are equally correct. This difference also applies to realloc(NULL, 0). I personally feel glibc's behavior is not particularly safe. To enable glibc behavior, one has to explicitly enable the MALLOC_GLIBC_COMPAT option.<br><br><br><br>15、用Typedef构造复合类?<br>在C语言中频J用以声明一个已l存在的数据cd的同义字。也可以用预处理器做cM的事。例如,思考一下下面的例子Q?<br><br>#define dPS struct s * <br><br>typedef struct s * tPS; <br><br>以上两种情况的意N是要定义dPS ?tPS 作ؓ一个指向结构s指针。哪U方法更好呢Q(如果有的话)Z么? <br><br>q是一个非常微妙的问题QQ何h{对q个问题Q正当的原因哦,而不是猜Q如果你没有原因Q说不会比猜一个答案要好的多,C啊,说话是要讲根据的Q是应当被恭喜的。答案是Qtypedef更好。思考下面的例子Q?<br><br>dPS p1,p2; <br><br>tPS p3,p4; <br><br>W一个扩展ؓ <br><br>struct s * p1, p2; <br><br>上面的代码定义p1Z个指向结构的指,p2Z个实际的l构Q这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针?<br><br><br><br>16、晦涩的语法及代码风?<br>C语言同意一些o人震惊的l构,下面的结构是合法的吗Q如果是它做些什么? <br><br>int a = 5, b = 7, c; <br><br>c = a+++b; <br><br>q个问题做个测验的一个愉快的l尾。不你怸怿Q上面的例子是完全合乎语法的。问题是~译器如何处理它Q水q不高的~译作者实际上会争个问题,~译器应可能多的从左至叛_若干个字W组成一个运符。因此,上面的代码被处理成:c = a++ + b; <br><br>逗号表达式依ơ对每个表达式计,最后的l果为最后一个表辑ּ的?br><br>因此, q段代码执行后a = 6, b = 7, c = 12?<br><br>如果你知道答案,或猜出正答案,做得好。如果你不知道答案,我也不把q个当作问题。我发现q个问题的最大好处是q是一个关于代码编写风|要明的加上括号Q避免歧义或者编译器不同带来的差异)Q代码的可读性,代码的可修改性的好的话题?<br><br><br><br>注:引出代码风格的问题正是作者问此问题的目的Q这告诉我们要揣摩面试管每个问题背后隐藏的考查点,能够机发挥下就大功告成了!<br><br><br><br>好了Q伙计们Q你现在已经做完所有的试了。这是我出的C语言试题,我怀着愉快的心情写完它Q希望你以同L心情d它。如果是认ؓq是一个好的测试,那么量都用C的找工作的过E中d。天知道也许q个一两年Q我׃做现在的工作Q也需要找一个?<br><br><br><br>以下Zq?6个问题的英文表述Q熟悉下相关的专业词汇对于英文面试的单表q很重要?br><br><a target="_blank"><font color="#0000ff">http://www.yuanma.org/data/2007/0509/article_2585.htm</font></a><br><br>An obligatory and significant part of the recruitment process for embedded systems programmers seems to be the "C test." Over the years, I have had to both take and prepare such tests and, in doing so, have realized that these tests can be informative for both the interviewer and interviewee. Furthermore, when given outside the pressure of an interview situation, these tests can also be quite entertaining. <br><br>From the interviewee's perspective, you can learn a lot about the person who has written or administered the test. Is the test designed to show off the writer's knowledge of the minutiae of the ANSI standard rather than to test practical know-how? Does it test ludicrous knowledge, such as the ASCII values of certain characters? Are the questions heavily slanted towards your knowledge of system calls and memory allocation strategies, indicating that the writer may spend his time programming computers instead of embedded systems? If any of these are true, then I know I would seriously doubt whether I want the job in question. <br><br>From the interviewer's perspective, a test can reveal several things about the candidate. Primarily, you can determine the level of the candidate's knowledge of C. However, it's also interesting to see how the person responds to questions to which they don't know the answers. Do they make intelligent choices backed up with good intuition, or do they just guess? Are they defensive when they are stumped, or do they exhibit a real curiosity about the problem and see it as an opportunity to learn something? I find this information as useful as their raw performance on the test. <br><br>With these ideas in mind, I have attempted to construct a test that is heavily slanted towards the requirements of embedded systems. This is a lousy test to give to someone seeking a job writing compilers! The questions are almost all drawn from situations I have encountered over the years. Some of them are tough; however, they should all be informative. <br><br>This test may be given to a wide range of candidates. Most entry-level applicants will do poorly on this test, while seasoned veterans should do very well. Points are not assigned to each question, as this tends to arbitrarily weight certain questions. However, if you choose to adapt this test for your own uses, feel free to assign scores. <br><br><br><br>Preprocessor <br>1. Using the #define statement, how would you declare a manifest constant that returns the number of seconds in a year? Disregard leap years in your answer. <br><br>#define SECONDS_PER_YEAR <br><br>(60 * 60 * 24 * 365)UL<br><br><br><br>I'm looking for several things here: <br><br>Basic knowledge of the #define syntax (for example, no semi-colon at the end, the need to parenthesize, and so on) <br><br>An understanding that the pre-processor will evaluate constant expressions for you. Thus, it is clearer, and penalty-free, to spell out how you are calculating the number of seconds in a year, rather than actually doing the calculation yourself <br><br>A realization that the expression will overflow an integer argument on a 16-bit machine-hence the need for the L, telling the compiler to treat the variable as a Long <br><br>As a bonus, if you modified the expression with a UL (indicating unsigned long), then you are off to a great start. And remember, first impressions count! <br><br><br><br>2. Write the "standard" MIN macro-that is, a macro that takes two arguments and returns the smaller of the two arguments. <br><br>#define MIN(A,B) <br><br>((A) <br><br>< <br><br>= (B) ? (A) : (B))<br><br><br><br>The purpose of this question is to test the following: <br><br>Basic knowledge of the #define directive as used in macros. This is important because until the inline operator becomes part of standard C, macros are the only portable way of generating inline code. Inline code is often necessary in embedded systems in order to achieve the required performance level <br><br>Knowledge of the ternary conditional operator. This operator exists in C because it allows the compiler to produce more optimal code than an if-then-else sequence. Given that performance is normally an issue in embedded systems, knowledge and use of this construct is important <br><br>Understanding of the need to very carefully parenthesize arguments to macros <br><br>I also use this question to start a discussion on the side effects of macros, for example, what happens when you write code such as: <br><br>least = MIN(*p++, b);<br><br><br><br>3. What is the purpose of the preprocessor directive #error? <br><br>Either you know the answer to this, or you don't. If you don't, see Reference 1. This question is useful for differentiating between normal folks and the nerds. Only the nerds actually read the appendices of C textbooks to find out about such things. Of course, if you aren't looking for a nerd, the candidate better hope she doesn't know the answer. <br><br><br><br>Infinite loops <br>4. Infinite loops often arise in embedded systems. How does you code an infinite loop in C? <br><br>There are several solutions to this question. My preferred solution is: <br><br>while(1)<br><br>{<br><br>?<br><br>}<br><br><br><br>Many programmers seem to prefer: <br><br>for(;;)<br><br>{<br><br>?<br><br>}<br><br><br><br>This construct puzzles me because the syntax doesn't exactly spell out what's going on. Thus, if a candidate gives this as a solution, I'll use it as an opportunity to explore their rationale for doing so. If their answer is basically, "I was taught to do it this way and I haven't thought about it since," it tells me something (bad) about them. <br><br>A third solution is to use a goto : <br><br><br><br>Loop:<br><br>...<br><br>goto Loop;<br><br><br><br><br><br><br><br>Candidates who propose this are either assembly language programmers (which is probably good), or else they are closet BASIC/FORTRAN programmers looking to get into a new field. <br><br><br><br>Data declarations <br>5. Using the variable a, give definitions for the following: <br>a) An integer <br>b) A pointer to an integer <br>c) A pointer to a pointer to an integer <br>d) An array of 10 integers <br>e) An array of 10 pointers to integers <br>f) A pointer to an array of 10 integers <br>g) A pointer to a function that takes an integer as an argument and returns an integer <br>h) An array of ten pointers to functions that take an integer argument and return an integer <br><br>The answers are: <br>a) int a; // An integer <br>b) int *a; // A pointer to an integer <br>c) int **a; // A pointer to a pointer to an integer <br>d) int a[10]; // An array of 10 integers <br>e) int *a[10]; // An array of 10 pointers to integers <br>f) int (*a)[10]; // A pointer to an array of 10 integers <br>g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer <br>h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer <br><br>People often claim that a couple of these are the sorts of thing that one looks up in textbooks-and I agree. While writing this article, I consulted textbooks to ensure the syntax was correct. However, I expect to be asked this question (or something close to it) when I'm being interviewed. Consequently, I make sure I know the answers, at least for the few hours of the interview. Candidates who don't know all the answers (or at least most of them) are simply unprepared for the interview. If they can't be prepared for the interview, what will they be prepared for? <br><br><br><br>Static <br>6. What are the uses of the keyword static? <br><br>This simple question is rarely answered completely. Static has three distinct uses in C: <br><br>A variable declared static within the body of a function maintains its value between function invocations <br><br>A variable declared static within a module, (but outside the body of a function) is accessible by all functions within that module. It is not accessible by functions within any other module. That is, it is a localized global <br><br>Functions declared static within a module may only be called by other functions within that module. That is, the scope of the function is localized to the module within which it is declared <br><br>Most candidates get the first part correct. A reasonable number get the second part correct, while a pitiful number understand the third answer. This is a serious weakness in a candidate, since he obviously doesn't understand the importance and benefits of localizing the scope of both data and code. <br><br>Const <br><br><br>7. What does the keyword const mean? <br><br>As soon as the interviewee says "const means constant," I know I'm dealing with an amateur. Dan Saks has exhaustively covered const in the last year, such that every reader of ESP should be extremely familiar with what const can and cannot do for you. If you haven't been reading that column, suffice it to say that const means "read-only." Although this answer doesn't really do the subject justice, I'd accept it as a correct answer. (If you want the detailed answer, read Saks' columns-carefully!) <br><br>If the candidate gets the answer correct, I'll ask him these supplemental questions: <br><br>What do the following declarations mean? <br><br><br><br>const int a;<br><br>int const a;<br><br>const int *a;<br><br>int * const a;<br><br>int const * a const;<br><br><br><br>The first two mean the same thing, namely a is a const (read-only) integer. The third means a is a pointer to a const integer (that is, the integer isn't modifiable, but the pointer is). The fourth declares a to be a const pointer to an integer (that is, the integer pointed to by a is modifiable, but the pointer is not). The final declaration declares a to be a const pointer to a const integer (that is, neither the integer pointed to by a, nor the pointer itself may be modified). If the candidate correctly answers these questions, I'll be impressed. Incidentally, you might wonder why I put so much emphasis on const, since it is easy to write a correctly functioning program without ever using it. I have several reasons: <br><br>The use of const conveys some very useful information to someone reading your code. In effect, declaring a parameter const tells the user about its intended usage. If you spend a lot of time cleaning up the mess left by other people, you'll quickly learn to appreciate this extra piece of information. (Of course, programmers who use const , rarely leave a mess for others to clean up.) <br><br>const has the potential for generating tighter code by giving the optimizer some additional information <br><br>Code that uses const liberally is inherently protected by the compiler against inadvertent coding constructs that result in parameters being changed that should not be. In short, they tend to have fewer bugs <br><br><br><br>Volatile <br>8. What does the keyword volatile mean? Give three different examples of its use. <br><br>A volatile variable is one that can change unexpectedly. Consequently, the compiler can make no assumptions about the value of the variable. In particular, the optimizer must be careful to reload the variable every time it is used instead of holding a copy in a register. Examples of volatile variables are: <br><br>Hardware registers in peripherals (for example, status registers) <br><br>Non-automatic variables referenced within an interrupt service routine <br><br>Variables shared by multiple tasks in a multi-threaded application <br><br>Candidates who don't know the answer to this question aren't hired. I consider this the most fundamental question that distinguishes between a C programmer and an embedded systems programmer. Embedded folks deal with hardware, interrupts, RTOSes, and the like. All of these require volatile variables. Failure to understand the concept of volatile will lead to disaster. <br><br>On the (dubious) assumption that the interviewee gets this question correct, I like to probe a little deeper to see if they really understand the full significance of volatile . In particular, I'll ask them the following additional questions: <br><br>Can a parameter be both const and volatile ? Explain. <br><br>Can a pointer be volatile ? Explain. <br><br>What's wrong with the following function?: <br><br><br><br>int square(volatile int *ptr)<br><br>{<br><br>return *ptr * *ptr;<br><br>}<br><br><br><br>The answers are as follows: <br><br>Yes. An example is a read-only status register. It is volatile because it can change unexpectedly. It is const because the program should not attempt to modify it <br><br>Yes, although this is not very common. An example is when an interrupt service routine modifies a pointer to a buffer <br><br>This one is wicked. The intent of the code is to return the square of the value pointed to by *ptr . However, since *ptr points to a volatile parameter, the compiler will generate code that looks something like this: <br><br><br><br>int square(volatile int *ptr) <br><br>{<br><br>int a,b;<br><br>a = *ptr;<br><br>b = *ptr;<br><br>return a * b;<br><br>}<br><br><br><br>Because it's possible for the value of *ptr to change unexpectedly, it is possible for a and b to be different. Consequently, this code could return a number that is not a square! The correct way to code this is: <br><br><br><br>long square(volatile int *ptr) <br><br>{<br><br>int a;<br><br>a = *ptr;<br><br>return a * a;<br><br>}<br><br><br><br>Bit manipulation <br>9. Embedded systems always require the user to manipulate bits in registers or variables. Given an integer variable a, write two code fragments. The first should set bit 3 of a. The second should clear bit 3 of a. In both cases, the remaining bits should be unmodified. <br><br>These are the three basic responses to this question: <br><br>No idea. The interviewee cannot have done any embedded systems work <br><br>Use bit fields. Bit fields are right up there with trigraphs as the most brain-dead portion of C. Bit fields are inherently non-portable across compilers, and as such guarantee that your code is not reusable. I recently had the misfortune to look at a driver written by Infineon for one of their more complex communications chips. It used bit fields and was completely useless because my compiler implemented the bit fields the other way around. The moral: never let a non-embedded person anywhere near a real piece of hardware! <br><br>Use #defines and bit masks. This is a highly portable method and is the one that should be used. My optimal solution to this problem would be: <br><br><br><br>#define BIT3 (0x1 << 3)<br><br>static int a;<br><br>void set_bit3(void) {<br><br>a |= BIT3;<br><br>}<br><br>void clear_bit3(void) {<br><br>a &= ~BIT3;<br><br>}<br><br><br><br>Some people prefer to define a mask together with manifest constants for the set and clear values. This is also acceptable. The element that I'm looking for is the use of manifest constants, together with the |= and &= ~ constructs <br><br><br><br>Accessing fixed memory locations <br>10. Embedded systems are often characterized by requiring the programmer to access a specific memory location. On a certain project it is required to set an integer variable at the absolute address 0x67a9 to the value 0xaa55. The compiler is a pure ANSI compiler. Write code to accomplish this task. <br><br>This problem tests whether you know that it is legal to typecast an integer to a pointer in order to access an absolute location. The exact syntax varies depending upon one's style. However, I would typically be looking for something like this: <br><br><br><br>int *ptr;<br><br>ptr = (int *)0x67a9;<br><br>*ptr = 0xaa55;<br><br><br><br>A more obscure approach is: <br><br><br><br>*(int * const)(0x67a9) = 0xaa55;<br><br><br><br>Even if your taste runs more to the second solution, I suggest the first solution when you are in an interview situation. <br><br><br><br>Interrupts <br>11. Interrupts are an important part of embedded systems. Consequently, many compiler vendors offer an extension to standard C to support interrupts. Typically, this new keyword is __interrupt. The following code uses __interrupt to define an interrupt service routine (ISR). Comment on the code. <br><br><br><br>__interrupt double compute_area<br><br>(double<br><br>radius) <br><br>{<br><br>double area = PI * radius * <br><br>radius;<br><br>printf("\nArea = %f", area);<br><br>return area;<br><br>}<br><br><br><br>This function has so much wrong with it, it's hard to know where to start: <br><br>ISRs cannot return a value. If you don't understand this, you aren't hired <br><br>ISRs cannot be passed parameters. See the first item for your employment prospects if you missed this <br><br>On many processors/compilers, floating-point operations are not necessarily re-entrant. In some cases one needs to stack additional registers. In other cases, one simply cannot do floating point in an ISR. Furthermore, given that a general rule of thumb is that ISRs should be short and sweet, one wonders about the wisdom of doing floating-point math here <br><br>In a vein similar to the third point, printf() often has problems with reentrancy and performance. If you missed points three and four, I wouldn't be too hard on you. Needless to say, if you got these last two points, your employment prospects are looking better and better <br><br><br><br>Code examples <br>12. What does the following code output and why? <br><br>void foo(void)<br><br>{<br><br>unsigned int a = 6;<br><br>int b = -20;<br><br>(a+b > 6) ? puts("> 6") : <br><br>puts("<br><br>< <br><br>= 6");<br><br>}<br><br><br><br>This question tests whether you understand the integer promotion rules in C-an area that I find is very poorly understood by many developers. Anyway, the answer is that this outputs "> 6." The reason for this is that expressions involving signed and unsigned types have all operands promoted to unsigned types. Thus ?20 becomes a very large positive integer and the expression evaluates to greater than 6. This is a very important point in embedded systems where unsigned data types should be used frequently (see Reference 2). If you get this one wrong, you are perilously close to not getting the job. <br><br><br><br>13. Comment on the following code fragment. <br><br>unsigned int zero = 0;<br><br>unsigned int compzero = 0xFFFF; <br><br>/*1's complement of zero */<br><br><br><br>On machines where an int is not 16 bits, this will be incorrect. It should be coded: <br><br>unsigned int compzero = ~0;<br><br><br><br>This question really gets to whether the candidate understands the importance of word length on a computer. In my experience, good embedded programmers are critically aware of the underlying hardware and its limitations, whereas computer programmers tend to dismiss the hardware as a necessary annoyance. <br><br>By this stage, candidates are either completely demoralized-or they're on a roll and having a good time. If it's obvious that the candidate isn't very good, then the test is terminated at this point. However, if the candidate is doing well, then I throw in these supplemental questions. These questions are hard, and I expect that only the very best candidates will do well on them. In posing these questions, I'm looking more at the way the candidate tackles the problems, rather than the answers. Anyway, have fun... <br><br><br><br>Dynamic memory allocation <br>14. Although not as common as in non-embedded computers, embedded systems do still dynamically allocate memory from the heap. What are the problems with dynamic memory allocation in embedded systems? <br><br>Here, I expect the user to mention memory fragmentation, problems with garbage collection, variable execution time, and so on. This topic has been covered extensively in ESP , mainly by P.J. Plauger. His explanations are far more insightful than anything I could offer here, so go and read those back issues! Having lulled the candidate into a sense of false security, I then offer up this tidbit: <br><br>What does the following code fragment output and why? <br><br><br><br>char *ptr;<br><br>if ((ptr = (char *)malloc(0)) == <br><br>NULL) <br><br>else<br><br>puts("Got a null pointer");<br><br>puts("Got a valid pointer");<br><br><br><br>This is a fun question. I stumbled across this only recently when a colleague of mine inadvertently passed a value of 0 to malloc and got back a valid pointer! That is, the above code will output "Got a valid pointer." I use this to start a discussion on whether the interviewee thinks this is the correct thing for the library routine to do. Getting the right answer here is not nearly as important as the way you approach the problem and the rationale for your decision. <br><br><br><br>Typedef <br>15. Typedef is frequently used in C to declare synonyms for pre-existing data types. It is also possible to use the preprocessor to do something similar. For instance, consider the following code fragment: <br><br><br><br>#define dPS struct s *<br><br>typedef struct s * tPS;<br><br><br><br>The intent in both cases is to define dPS and tPS to be pointers to structure s. Which method, if any, is preferred and why? <br><br>This is a very subtle question, and anyone who gets it right (for the right reason) is to be congratulated or condemned ("get a life" springs to mind). The answer is the typedef is preferred. Consider the declarations: <br><br>dPS p1,p2;<br><br>tPS p3,p4;<br><br><br><br>The first expands to: <br><br>struct s * p1, p2;<br><br><br><br>which defines p1 to be a pointer to the structure and p2 to be an actual structure, which is probably not what you wanted. The second example correctly defines p3 and p4 to be pointers. <br><br><br><br>Obscure syntax <br>16. C allows some appalling constructs. Is this construct legal, and if so what does this code do? <br><br><br><br>int a = 5, b = 7, c;<br><br>c = a+++b;<br><br><br><br>This question is intended to be a lighthearted end to the quiz, as, believe it or not, this is perfectly legal syntax. The question is how does the compiler treat it? Those poor compiler writers actually debated this issue, and came up with the "maximum munch" rule, which stipulates that the compiler should bite off as big (and legal) a chunk as it can. Hence, this code is treated as: <br><br>c = a++ + b;<br><br>Thus, after this code is executed, a = 6, b = 7, and c = 12. <br><br>If you knew the answer, or guessed correctly, well done. If you didn't know the answer then I wouldn't consider this to be a problem. I find the greatest benefit of this question is that it is good for stimulating questions on coding styles, the value of code reviews, and the benefits of using lint. <br><br>Well folks, there you have it. That was my version of the C test. I hope you had as much fun taking it as I had writing it. If you think the test is a good test, then by all means use it in your recruitment. Who knows, I may get lucky in a year or two and end up being on the receiving end of my own work. <br><br>Nigel Jones is a consultant living in Maryland. When not underwater, he can be found slaving away on a diverse range of embedded projects. He enjoys hearing from readers and can be reached at NAJones@compuserve.com . <br><br>References <br><br>Jones, Nigel, "In Praise of the #error directive," Embedded Systems Programming, September 1999, p. 114. <br><br>Jones, Nigel, " Efficient C Code for Eight-bit MCUs ," Embedded Systems Programming, November 1998, p. 66. </div> </div> <p style="margin: 5px; line-height: 150%;"> </p>  <font color="#000099"><strong>原文地址</strong></font> <a target="_blank">http://blog.csdn.net/sailor_8318/archive/2008/03/25/2215041.aspx</a><img src ="http://www.shnenglu.com/zzh/aggbug/68049.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zzh/" target="_blank">Xiao.Zhu</a> 2008-11-28 08:41 <a href="http://www.shnenglu.com/zzh/archive/2008/11/28/68049.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Visual Studio 6.0 和 Visual Studio 2005 ~辑器配色方?/title><link>http://www.shnenglu.com/zzh/archive/2007/09/18/32402.html</link><dc:creator>Xiao.Zhu</dc:creator><author>Xiao.Zhu</author><pubDate>Tue, 18 Sep 2007 02:12:00 GMT</pubDate><guid>http://www.shnenglu.com/zzh/archive/2007/09/18/32402.html</guid><wfw:comment>http://www.shnenglu.com/zzh/comments/32402.html</wfw:comment><comments>http://www.shnenglu.com/zzh/archive/2007/09/18/32402.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.shnenglu.com/zzh/comments/commentRss/32402.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zzh/services/trackbacks/32402.html</trackback:ping><description><![CDATA[<p>Visual Studio 6.0 ~辑器配色方?br><br>复制以下代码保存?reg文gQ然后导入注册表?/p> <p>--------------------------------------------------------------------------------------------------------------------<br>Windows Registry Editor Version 5.00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format]<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Calls Window]<br>"FontFace"="Fixedsys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:00,00,00,00,ff,ff,ff,00,13,01,b3,00<br>"Text Selection"=hex:ff,ff,ff,00,00,00,00,00,19,00,19,00<br>"Calls Highlight"=hex:00,00,00,00,00,ff,00,00,10,00,10,00<br>"Superceded code"=hex:80,80,80,00,ff,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Disassembly Window]<br>"FontFace"="Fixedsys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:00,00,00,00,ff,ff,ff,00,13,01,b3,00<br>"Text Selection"=hex:ff,ff,ff,00,00,00,00,00,19,00,19,00<br>"Assembly Code"=hex:80,80,80,00,ff,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Memory Window]<br>"FontFace"="Fixedsys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:00,00,00,00,ff,ff,ff,00,13,01,b3,00<br>"Text Selection"=hex:ff,ff,ff,00,00,00,00,00,19,00,19,00<br>"Memory Highlight"=hex:ff,00,00,00,ff,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Output Window]<br>"FontFace"="FixedSys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:c0,c0,c0,00,00,00,00,00,15,00,15,00<br>"Text Selection"=hex:00,00,00,00,c0,c0,c0,00,19,00,19,00<br>"Current Error/Tag"=hex:ff,ff,ff,00,00,00,80,00,55,00,55,00<br>"Bookmark"=hex:00,00,00,00,00,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Registers Window]<br>"FontFace"="Fixedsys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:00,00,00,00,ff,ff,ff,00,13,01,b3,00<br>"Text Selection"=hex:ff,ff,ff,00,00,00,00,00,19,00,19,00<br>"Value Highlight"=hex:ff,00,00,00,ff,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Source Browser]<br>"FontFace"="DejaVu Sans"<br>"FontSize"=dword:0000000c<br>"Text"=hex:c0,c0,c0,00,00,00,00,00,15,00,15,00<br>"Text Selection"=hex:00,00,00,00,c0,c0,c0,00,19,00,19,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Source Window]<br>"FontFace"="DejaVu Sans Mono"<br>"FontSize"=dword:0000000d<br>"Text"=hex:c0,c0,c0,00,00,00,00,00,12,01,b2,00<br>"Text Selection"=hex:00,00,00,00,c0,c0,c0,00,19,00,19,00<br>"Current Error/Tag"=hex:ff,ff,ff,00,00,00,80,00,d2,01,b2,01<br>"Bookmark"=hex:00,00,00,00,00,ff,ff,00,10,00,10,00<br>"Breakpoint"=hex:ff,ff,ff,00,80,00,00,00,10,00,10,00<br>"Current Statement"=hex:00,00,00,00,ff,ff,00,00,70,00,70,00<br>"Selection Margin"=hex:d4,d0,c8,00,d4,d0,c8,00,13,00,13,00<br>"Keyword"=hex:ff,00,ff,00,00,00,00,00,10,00,11,00<br>"Comment"=hex:00,e6,00,00,00,00,00,00,10,00,11,00<br>"Number"=hex:ff,ff,00,00,00,00,00,00,10,00,11,00<br>"String"=hex:ff,80,00,00,00,00,00,00,10,00,11,00<br>"Operator"=hex:c0,c0,c0,00,00,00,00,00,11,00,11,00<br>"Wizard IDL/ODL Code"=hex:80,80,80,00,00,00,00,00,14,00,15,00<br>"HTML Element Name"=hex:80,00,80,00,00,00,00,00,14,00,15,00<br>"HTML Attribute Name"=hex:ff,00,00,00,00,00,00,00,14,00,15,00<br>"HTML Attribute Value"=hex:00,00,ff,00,00,00,00,00,14,00,15,00<br>"HTML Comment"=hex:00,80,00,00,00,00,00,00,14,00,15,00<br>"HTML Entity"=hex:ff,00,00,00,00,00,00,00,14,00,15,00<br>"HTML Tag Delimiter"=hex:00,00,ff,00,00,00,00,00,14,00,15,00<br>"HTML String"=hex:00,00,ff,00,00,00,00,00,14,00,15,00<br>"HTML Tag Text"=hex:ff,00,ff,00,00,00,00,00,14,00,15,00<br>"HTML Operator"=hex:00,00,ff,00,00,00,00,00,14,00,15,00<br>"HTML Server-Side Script"=hex:00,00,00,00,ff,ff,00,00,14,00,14,00<br>"User Defined Keywords"=hex:00,00,ff,00,00,00,00,00,14,00,15,00<br>"Wizard Code"=hex:80,80,80,00,00,00,00,00,14,00,15,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Variables Window]<br>"FontFace"="Fixedsys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:00,00,00,00,ff,ff,ff,00,13,01,b3,00<br>"Text Selection"=hex:ff,ff,ff,00,00,00,00,00,19,00,19,00<br>"Variables Highlight"=hex:ff,00,00,00,ff,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Watch Window]<br>"FontFace"="Fixedsys"<br>"FontSize"=dword:0000000c<br>"Text"=hex:00,00,00,00,ff,ff,ff,00,13,01,b3,00<br>"Text Selection"=hex:ff,ff,ff,00,00,00,00,00,19,00,19,00<br>"Watch Highlight"=hex:ff,00,00,00,ff,ff,ff,00,10,00,10,00<br>[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Format\Workspace Window]<br>"FontFace"="Segoe UI"<br>"FontSize"=dword:0000000c<br>-----------------------------------------------------------------------------------------------------------------------<br><br><br>Visual Studio 2005 <br>复制以下代码保存?reg文gQ然后导入注册表?br>------------------------------------------------------------------------------------------------------------------------<br>Windows Registry Editor Version 5.00</p> <p>[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\FontAndColors\{A27B4E24-A735-4D1D-B8E7-9716E1E3D8E0}]<br>"Colorable item format version"=dword:00000008<br>"FontName"="Courier"<br>"FontPointSize"=dword:0000000a<br>"FontCharSet"=dword:00000000<br>"Plain Text Foreground"=dword:00e5e5e5<br>"Plain Text Background"=dword:00000000<br>"Plain Text FontFlags"=dword:00000000<br>"Selected Text Foreground"=dword:00000000<br>"Selected Text Background"=dword:00ebe8e7<br>"Selected Text FontFlags"=dword:80000000<br>"Line Numbers Foreground"=dword:00804000<br>"Line Numbers Background"=dword:02000000<br>"Line Numbers FontFlags"=dword:80000000<br>"Visible White Space Foreground"=dword:0080ffff<br>"Visible White Space Background"=dword:02000000<br>"Visible White Space FontFlags"=dword:80000000<br>"Collapsible Text Foreground"=dword:00c9c9c9<br>"Collapsible Text Background"=dword:00000000<br>"Collapsible Text FontFlags"=dword:00000000<br>"Comment Foreground"=dword:00c4c4c4<br>"Comment Background"=dword:001f1f1f<br>"Comment FontFlags"=dword:00000000<br>"CSS Property Name Foreground"=dword:0000ffff<br>"CSS Property Name Background"=dword:02000000<br>"CSS Property Name FontFlags"=dword:00000000<br>"CSS Property Value Foreground"=dword:0000ff00<br>"CSS Property Value Background"=dword:02000000<br>"CSS Property Value FontFlags"=dword:00000000<br>"CSS Selector Foreground"=dword:00ffff00<br>"CSS Selector Background"=dword:02000000<br>"CSS Selector FontFlags"=dword:00000000<br>"HTML Attribute Value Foreground"=dword:0000ff00<br>"HTML Attribute Value Background"=dword:02000000<br>"HTML Attribute Value FontFlags"=dword:00000000<br>"HTML Comment Foreground"=dword:0080ff80<br>"HTML Comment Background"=dword:02000000<br>"HTML Comment FontFlags"=dword:00000000<br>"HTML Element Name Foreground"=dword:0000ffff<br>"HTML Element Name Background"=dword:02000000<br>"HTML Element Name FontFlags"=dword:00000000<br>"HTML Entity Foreground"=dword:00ffb66c<br>"HTML Entity Background"=dword:02000000<br>"HTML Entity FontFlags"=dword:00000000<br>"HTML Operator Foreground"=dword:00ffffff<br>"HTML Operator Background"=dword:02000000<br>"HTML Operator FontFlags"=dword:00000000<br>"HTML Server-Side Script Foreground"=dword:00000000<br>"HTML Server-Side Script Background"=dword:00007777<br>"HTML Server-Side Script FontFlags"=dword:00000000<br>"HTML Tag Delimiter Foreground"=dword:00ffff00<br>"HTML Tag Delimiter Background"=dword:02000000<br>"HTML Tag Delimiter FontFlags"=dword:00000000<br>"Identifier Foreground"=dword:00eaeaea<br>"Identifier Background"=dword:02000000<br>"Identifier FontFlags"=dword:00000000<br>"Keyword Foreground"=dword:00ffff00<br>"Keyword Background"=dword:02000000<br>"Keyword FontFlags"=dword:00000000<br>"Number Foreground"=dword:0000a8a8<br>"Number Background"=dword:02000000<br>"Number FontFlags"=dword:00000000<br>"Operator Foreground"=dword:00bf80ff<br>"Operator Background"=dword:02000000<br>"Operator FontFlags"=dword:00000000<br>"Preprocessor Keyword Foreground"=dword:00ff6f6f<br>"Preprocessor Keyword Background"=dword:02000000<br>"Preprocessor Keyword FontFlags"=dword:00000000<br>"Read-Only Region Foreground"=dword:02000000<br>"Read-Only Region Background"=dword:00c0c0c0<br>"Read-Only Region FontFlags"=dword:00000000<br>"Register Data Foreground"=dword:02000000<br>"Register Data Background"=dword:02000000<br>"Register Data FontFlags"=dword:00000000<br>"Register NAT Foreground"=dword:00ffffff<br>"Register NAT Background"=dword:02000000<br>"Register NAT FontFlags"=dword:00000000<br>"String Foreground"=dword:0081c2fe<br>"String Background"=dword:02000000<br>"String FontFlags"=dword:00000000<br>"XML Doc Comment Foreground"=dword:00b7ffb7<br>"XML Doc Comment Background"=dword:001f1f1f<br>"XML Doc Comment FontFlags"=dword:00000000<br>------------------------------------------------------------------------------------------------------------------------<br></p> <img src ="http://www.shnenglu.com/zzh/aggbug/32402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zzh/" target="_blank">Xiao.Zhu</a> 2007-09-18 10:12 <a href="http://www.shnenglu.com/zzh/archive/2007/09/18/32402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>杂7?技术资?/title><link>http://www.shnenglu.com/zzh/archive/2007/08/13/29864.html</link><dc:creator>Xiao.Zhu</dc:creator><author>Xiao.Zhu</author><pubDate>Mon, 13 Aug 2007 01:22:00 GMT</pubDate><guid>http://www.shnenglu.com/zzh/archive/2007/08/13/29864.html</guid><wfw:comment>http://www.shnenglu.com/zzh/comments/29864.html</wfw:comment><comments>http://www.shnenglu.com/zzh/archive/2007/08/13/29864.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/zzh/comments/commentRss/29864.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zzh/services/trackbacks/29864.html</trackback:ping><description><![CDATA[<p>一般全局变量应该用比较长Q详l的名称Q而本地变量则单明了ؓ宜?br>GetSystemMetrics可以获得很多pȝ信息?/p> <p>Qi nclude <>先搜?I~译选项的\径,然后搜烦环境变量INCLUDEQ?"先搜索父文g的\径,然后才是前面两个路径?/p> <p>用C设计一个动态增长的数组Q需要用到MALLOC和REALLOCQ对与同一个指针,如果内存不够存储数据Q就用REALLOC重新分配多一倍的内存。ƈ且返回的指针不能直接l原来的指针Q因为如果分配失败,那么原来的数据就会丢失?/p> <p>传递函数地址作ؓ参数</p> <p>首先定义以函数地址作ؓ参数的函?/p> <p>                   <br>fun1(q回cd(*fun2)(参数))<br>{<br>   ...=(*fun2)(参数);</p> <p>}<br>fun2正常定义<br>调用的时候  fun1(函数?;<br>int fun2(int n)<br>{<br> return n+1;</p> <p>}</p> <p>int fun1(int n,int(*f)(int b))<br>{<br> return (*f)(n);</p> <p> </p> <p>}<br>int main(int argc, char* argv[])<br>{<br> int a(B),b;<br>    b=fun1(a,fun2);<br>    cout<<b;<br> return 0;<br>}</p> <p>实现散列表:使用一个散列函敎ͼ项散列C个数l里面,每个数组元素是一个链表,记录q个散列值的所有项?/p> <p>CMemoryState cd用于查内存泄霌Ӏ?/p> <p> </p> <p> </p> <p>构造函数和析构函数都没有返回?/p> <p>cȝ成员~省是私有的.<br>如果定义了构造函数和析构函数,必须它们设|ؓPUBLIC,否则无法讉K.<br>(事实上也可以是private,但只能访问类的静态成?q时不能生成对象,如果要生成对?需要一定的技?</p> <p>重蝲依靠参数不同而不是返回值的不同,因ؓ我们可以在调用函数的时候忽略返回?q个时候编译器无法定该调用那个函?</p> <p>构造函数可以根据参数类型不同区?也可以根据参CC同区?但如果一个构造函数的参数有缺省D且前面的参C其它构造函数相?那么如果在调用的时候用C~省?~译器无法分辨该调用那个函数,会报?</p> <p>可以在定义函数的时?让某个参数只有类型名而没有标识符,q样做的目的是ؓ了将来可能需要插入一个参?在调用的时候随便给q个位置一个值就可以?</p> <p>int temp(int a,int,int b)<br>{}</p> <p>~按位求反</p> <p>const 存放在符可?不会被分配内?如果使用&W号,׃~译器ؓ帔R分配地址,const数组也会被分配内?</p> <p>const int* pQint const* p;指向帔R的指针,它的内容(*p)不能被改变,也不能将它的Dint * const p,常指针,g变。给非常量指针?br>对时对象要使用常值引用接Ӟ因ؓ临时对象是常量?/p> <p>对于const成员变量Q必d构造函数前(构造函数初始化?对它赋初?莄onst也可以在q里赋?但没有必?可以转到构造函C?虽然在初始化表里效率更高)。Q何类型的成员变量都不能在声明的时候赋倹{?/p> <p>cM的常量const是对某个具体对象而言的,如果需要对整个cȝ常|使用enum.</p> <p>char *p="123456";<br>cout<<*p++<<*p++;<br>*p=3;<br>char *t;<br>t=p;<br>输出l果?21,因ؓ求值顺序是从右到左,W三个语句是错的,*p的内存是不可写的,gp应该Z个常量指?但确实可以将p赋给一个非常指?语句5不报?</p> <p>如果成员函数被声明ؓconst,那么其中不能含有改变成员变量值的语句(但可以改变被mutable修饰的成员变?),也不能调用非const成员函数,如果对象被声明ؓconst,那么它只能调用const成员函数.</p> <p>volatile的用法同const,甚至可以使用const volatile做修饰没,volatile标识数据可能被别的进E改?因此有必要在每次使用的时候重读这个数?q在优化期间特别重要,防止~译器做一些假?</p> <p>l宏的参数最好是单变量,如果不是Q如a++,那么变量在宏中出现几ơ,a׃被加多少ơ。最好不用宏做类似函数的事情Q在cMQ用内联函数代替宏,一样可以得到高效率?/p> <p>在类中定义的函数自动成ؓ内联函数Q在cdQ用inline关键字?/p> <p>不应该用public成员变量Q而是应该使用内联函数存取q些变量Q用重载可以只用一个函数名字完成存取?/p> <p>一个程序的所有文Ӟ所有的名字Q不在函数或cMQ缺省都是外部连接,q意味着不同文g中相同的名字Q不在函数或cMQ会引v冲突Q如果对q些名字用static修饰Q就会变成内部连接,名字仅在~译单元内可见。extern是static的反义词Q表C外部连接,同缺省的意义相同。两U连接的名字都会存储在静态存储区?/p> <p>一旦用于修饰局部变量,static׃再表C可见性,而只改变变量的存贮类型?/p> <p>register表示希望q个变量被放在寄存器里,因ؓ它经常被用到Q应该避免用这个关键字Q因斚w通常机器比h更擅ѝ?/p> <p>cM的static变量Q包括用static修饰的constQ必dcd函数外的全局位置初始化,初始化的语法是:static cd c?:变量?|</p> <p>存在嵌套cd局部类:cM定义的类和函C定义的类,后者不能有静态成员变?昄Q没有办法初始化q种static变量).</p> <p>调用c?在声明的时候要使用extern "C" 函数声明,指明q是一个cq接.因ؓc++同c的编译器不同,会ؓ函数产生不同的内部名,按照c++的方式连接c函数,会找不到库中的函C.当然,通常情况下库的开发上已经为我们做好了q些.</p> <p>引用必须被初始化,且引用的对象不能改变.不能引用null.</p> <p>int f(const int&)<br>上面是一个常量引?使用帔R引用是ؓ了保证外部变量不被修?另外,如果传入的是帔R或者时对?不用常量引用的参数出?因ؓ二者都是常?</p> <p>无返回值的函数是voidcd.</p> <p>void inf(int*&i){i++;}  调用:int *i=0; inf(i); <br>上面的函数是以指针引用做参数,改变指针的?q可以用指向指针的指针,要麻烦一?不过表达更明?void inf(int **i){(*i)++}; 调用?int *i=0; inf(&i); </p> <p>通过g递给函数Q或者函数返回一个对?是用位拯建立对象,q种情况下编译器会调用拷贝构造函?如果没有~译器会建立一个缺省的),但对象销毁时会调用析构函?</p> <p>如果想禁止通过g递某个对?只要声明一个私有的拯构造函?此时~译器认为用h了q项工作,不会建立~省的拷贝构造函?而用户徏立的函数是私有的,没法调用,~译器就会报?</p> <p>可以定义指向cȝ成员变量和成员函数的指针Q程序不必用函数的名字可以调用它Q想起了高通的CDMAE序框架.c++~程思想W?0章?/p> <p>一个指向函数的指针:<br>void inf(int *&i){i++;}<br>int main(int argc, char* argv[])<br>{<br>int *i=0;<br>cout<<i<<endl;<br>void (*pf)(int *&);<br>pf=&inf;<br>(*pf)(i);<br>cout<<i<<endl;<br>}</p> <p><br>q算W重?br>重蝲仅是对用L型的数据来说的,对内|的数据cd是不可以重蝲q算W的?br>.?*都不能重?可以运符重蝲看作另外一UŞ式的函数调用Q函数的名字是operator@,@代表q算W,参数的个数取决于两个因素Q?br>1 q算W是一元还是二?br>2 q算W是全局函数(一元是一个参?二元是两个参?,q是成员函数(一元没有参?二元一个参?---对象变ؓ左侧参数)</p> <p>可以重蝲几乎所有的q算W?但对于现在c中没有意义的q算W是不能重蝲?也不能改变运符的参C数和优先U?</p> <p>重蝲q算W的q回?如果需要返回对象本w?Ҏ需要返?br>对象的指针或者引?如果是返回时生成的对象,那么q回对象.</p> <p>重蝲q算W的q回值是否常?当返回的是一个时值得时??%,&,>>,q些q算W得到的l果要赋l另外一个变?q时q回值是const,如果q回值直接用于变??=,+=,q是q回g要加const.</p> <p>函数q回对象的时?q回一个时对象比新徏一个对象在q回效率要高很多,因ؓq时调用的是普通构造函数而不是拷贝构造函?而且不需要调用析构函?虽然新徏一个对象再q回q回的也是一个时对?</p> <p>指针(smart pointer):对象,包容?q代?</p> <p>自动cd转换:可以~程实现自动cd转换.如需要从对象one到two,那么只需要ؓtwo定义一个以one&为参数的构造函?当编译器发现需要进行从对象one到two的{换的时?会自动检查two的定?扑ֈq个构造函?构造一个two对象.如果需要显式类型{?在构造函数前加一?explicit</p> <p>q有一U自动类型{换方法是:为需要{换的对象重蝲一个运符,q算W以要{换到的对象的名字命名.无须声明q回?<br>operator one() const{ return one(x);}</p> <p><br>不过q不提倡隐式类型{?q样Ҏ隐藏错误,也会降低调用时的效率.<br> <br>使用全局重蝲q算W而不是成员运符的好处是可以对左x作书都自动作cd转换,而成员运符的操作数左侧的必L正确的对?/p> <p>重蝲赋值操作符"=",q回可以是引用也可以是?前者效率较?但要记得此时q回的引用不能是属于局部对象的.通常q回*this.</p> <p>return String(s1+s2); 与String temp(s1+s2);return temp;的效率是不同的,后者要q行对象拯Q而前者直接将临时对象创徏在函数的q回区。同时也更加z?/p> <p>函数中少用static变量。让相同的输入生相同的输出Q这L代码便于使用和维护?/p> <p>对函数的参数和返回值的有效性进行检查?/p> <p>U极使用断言(ASSERT),同时要加上注释,防止来忘记ASSERT的目的?/p> <p>之所以有了指针还要引入引用,是ؓ了对功能加以限制Q防止发生意外,像对参数加上const限定的目的一栗?/p> <p>动态分配内存的原则:<br>1 分配后要查是否分配成?即if(p==NULL)<br>2 释放内存后要记得令p=NULL,防止产生野指?野指针会让我们在使用指针前的if(p==NULL)查Ş同虚?</p> <p>要申请一块内存复制数lchar a[]的内?应该甌的内存大是sizeof(char)*(strlen(a)+1);</p> <p>如果通过参数传递数l?数组名自动退化ؓ一个指?<br>main()<br>{<br> char a[100];<br>        cout<<sizeof(a);<br> fun(a);<br>}<br>void fun(char a[100])<br>{<br>   cout<<sizeof(a);<br>}<br>输出100 4.</p> <p>对内存分配失败进行处理有两种Ҏ:<br>1 if(p==NULL) 适用于内存分配语句较的情况<br>2 _set_new_handler  _set_new_mode 适用于内存分配语句较多的情况</p> <p>unsigned与没有unsigned cd只是表示范围不同,大小相同.</p> <p> </p> <p>如果不给cd义拷贝构造函数和赋值函敎ͼ如果cM有指针变量,׃D错误Q如果指针指向动态内存区Q那q块内存会丢失,而两个指针相同一个块内存Q导致其值无法判定,而且两个函数的析构函C这块内存释放两ơ,D出错?br>String a("hello");<br>String b("world");<br>String c(a); //调用拯构造函敎ͼq可以写成:String c=a;但风D差?br>c=a;         //调用赋值函?operator =) 赋值函C注意先检查自赋倹{?/p> <p>在承当中,构造函敎ͼ析构函数Q赋值函数都不能被承,在编写子cL要注以下几点Q?br>1子类必须在构造函数的初始化表调用基类的构造函数?br>2父类和子cȝ析构函数都必Lvirtual.//用于多态?br>3子类赋值函数要调用父类的赋值函敎ͼBase::operater=(other);</p> <p><br>对函数参数和q回D行const限定仅对指针和引用有意义Q对g递没有意义,对输出参C定不要用const,不然无法输出参数?/p> <p>重蝲new和delete的原因有两个Q需要反复分配内存,需要亲自做q个工作提高效率Q还有就是减内存碎片,比如可以首先使用静态成员指针保留很大一块内?在静态存储区)Q在其中完成内存的分配,q自己标记分配和释放Q释攄时候,只是标记内存Q而不free释放?br>重蝲的new和delete只完成内存的分配和回收工作。new接受size_t函数Q完成内存的分配Q返回一个void*指针Qdelete接受一个void*指针Q将它释放?br>注意重蝲new和delete有两U不同的形式Q一个用于每ơ创Z个对象,另一个用来创Z个对象数l,需要加上[]。如果重载了前者,那么在创建对象数l的时候,pȝ会调用全局的new和delete.</p> <p>发现一个有的现象Q可以用gؓNULL的指针调用Q意对象的成员函数Q只要先强制转换到这个对象,q且调用的是U代码?/p> <p>成员对象的初始化可以和父cL造函数的调用q排攑֜初始化表?/p> <p>在进入构造函数的左括号前Q所有的成员变量都必被初始化?/p> <p>构造函敎ͼ析构函数Q赋D赋不被l承?/p> <p>cȝ友元能够讉K其private,protected成员Q子c能讉Kcȝprotected成员?/p> <p>不要在析构函C抛出异常Q因为异常处理函数在获得异常后要调用析构函数清理对象Q此时再发生异常会导致程序无法再捕获异常Q只能终止(只能在自定义的set_terminate()中作最后的处理。)?/p> <p>拯字符串的Ҏ<br>char dest[sz];<br>memset(dest,0,sz);<br>strncpy(dest,source,sz-1);<br>q样保证了不会超q缓冲区且结ؓ'\0'.</p> <p>异常处理函数会首先调用所有在try块中创徏了的对象的析构函敎ͼ然后执行异常处理函数Q然后l运行后面的E序。但问题是,如果一个析构函数出C异常Q在析构函数中异常前创徏的堆上的所有对象都无法调用其析构函数正帔R毁。方法是使用模板Qƈ自初始化表创些模板对象?/p> <p>set_unexpceted可以截获没有被函数异常规D明包括得异常Q还可以单的用一个throw;这个异怽为已知异常再ơ抛出,如果有相应的catch语句Q那么就可以捕获q个异常?/p> <p>抛出异常的子c,会被能够捕获其父cd常的处理器捕莗这时会产生切片Q即处理器收到的是一个父c,使用引用而不是传递值可以避免这个问题?br>      try<br>  {<br>   throw(except("got it"));<br>  }<br>  catch(except &t)<br>  {<br>   t.what();<br>  }</p> <p><br>q行时类形识别对void指针无效?/p> <p>dynamic_cast<>用于向下映射?br>base* b=new derived;<br>derived* d=dynamic_cast<derived*>b; 如果dynamic_castp|的话Q将q回NULL,可以以此来试探着判断指针b的类型?/p> <p>RTTIq可以用typeinfo().name()的方法返回对象id。typeinfo()q回typeinfo对象Q用前要包含头文gtypeinfo.h.</p> <p>class B<br>class D:public B</p> <p>B* p=new D;<br>B& r=*p;</p> <p>typeid(p)==typeid(B*)<br>typeid(r)!==typeid(D)<br>typeid(*p)==typeid(D)<br>typeid(&r)==typeid(B*)</p> <p>对引用的动态映也要制定到一个引用上Q如果失败不是返回NULL,因ؓ应用不许为空Q而是产生一个异常,因此对引用的动态映必M用异常处理?/p> <p>对空指针使用typeid()也会产生异常Q可以在使用之前查指针是否ؓNULL来避免这个问题?/p> <p>在对重承的情况下,传统的强制类型{换可能无法正常工作,但动态映和typeid工作的很好?/p> <p>要是动态类型类型{换,需要基cd含virtual成员函数Qƈ且vc~译器有/GR选项。经q动态类型{换,qc{换而来的子cL针可以调用子cM新添加而父cM没有的方法?/p> <p>static_cast 通常不是必需的,但它会让cd转换更加醒目?/p> <p>const_cast用于常量和volatile映射l普通指针?/p> <p>reinterpret_cast是危险ƈ且可UL性很差的转换Q它对象看作二q制数进行{换。最好不要用?/p> <p>C++中,结构名直接作ؓcd名用,而不需要象c中那样用typedef  struct l构名{} cd?</p> <p>                    WINDOWS核心~程<br>内核对象Q每个内核对象都是一个内存块Q由内核l护Q进E在创徏了一个内核对象后获得一个句柄,通常一个进E的句柄对另外一个进E是没有意义的,但可以通过一定措施在q程间共享内核对象。当q程l止后,它创建的内核对象不一定消失,内核l护每个内核对象的引用计数?/p> <p><br>GDI对象不是内核对象Q区分内核对象和GDI对象的方法是内核对象的创建函数的参数中有安全属性,而GDI对象没有?/p> <p>内核对象的安全属性通常在创建服务器E序的时候用刎ͼ传递一个NULL可以获得~省的安全属性?/p> <p>当不再用某个内核对象的时候,可以使用BOOL CloseHandle(HANDLE)关闭句柄Q系l会自动内核对象信息清除出q程的句柄表Q此句柄表保存且仅保存该q程使用的所有内核对象信息。)Qƈ自动为内核对象的引用计数减一。如果忘记关闭句柄也不要紧,在进E推出后Q系l会自动查进E的句柄表,清理没有释放的句柄,因此忘记关闭句不一定会造成内存泄漏?/p> <p>E序的进入点WinMain的第一个参数时q程的实例句柄,也是q程映射到虚拟地址I间的v始地址Qvc++默认?x00400000.可以用GetModuleHandle()得到q个?br>PTSTR GetCommandLine()获得命o?br>PWSTR CommandLineToArgvW()分解命o?/p> <p>每个q程都有一个与他相关的环境块?br>VarName1=VarValue1\0<br>VarName2=VarValue2\0<br>...............<br>\0</p> <p>GetEnvironmentVariable()  //获得环境变量?br>ExpandEnvironmentStrings()//展开%包裹的环境变量?br>SetEnvironmentVariable()  //讑֮环境变量</p> <p>q程的亲~性:q程的线E被再CPU的子集上q行?/p> <p>子进E默认承父q程的错误标志?br>SetErrorMode(UINT)  //讑֮错误模式</p> <p>q程l护当前驱动器和目录信息<br>GetCurrentDirectory()<br>SetCurrentDirectory()</p> <p>获得pȝ版本Q?br>GetVersion()<br>GetVersionEx()<br>VeryfyVersionInfo()</p> <p><br>GetExitCodeProcess(),对于q在q行的进E,可以得到0x103QSTILL_ACTIVE),对于l止的进E,如果q没有CloseHandle(pi.hProcess),可以得到它的退出码,否则得到的是q?/p> <p>windows2000支持作业理Q?通过进E加入作业,可以对进E的q行权限Q用的资源q行限制。方法如下:<br>HANDLE hjob=CreateJobObject(NULL,NULL);//创徏一个作业对象?br>SetInformationJobObject();//讑֮作业对象的参敎ͼ包括对进E的各种限制?br>CreateProcess(NULL,"CMD",NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);//创徏新进E?br>AssignProcessToJobObject(hjob,pi.hProcess); //进E加入作业。可加入多个?br>ResumeThread(pi.hThread);<br>CloseHandle(pi.hThread);<br>HANDLE h[2];<br>h[0]=pi.hProcess;<br>h[1]=hjob;<br>DWORD dw=WaitForMultipleObject(2,h,false,INFINITE);<br>switch(dw-WAIT_OBJECT_0Q?br>  case 0://the process has terminated..<br>  case 1://all of the job's allotted cpu time was used.<br>}<br>CloseHandle(pi.hProcess);<br>CloseHandle(hjob);</p> <p>l止作业中所有进E的q行<br>TerminateJobObject(hjob,UINT uExitCode)</p> <p>查询作业l计信息<br>QueryInformationJobObject();</p> <p>监视作业的运行:<br>JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;//创徏一个I/O完成端口对象<br>SetInformationJobObject(hjob.JobObjectAssociateCompletionPortInformation,&joacp,sizeof(joacp)<br>//作业同完成端口对相兌?br>GetQueuedCompletionStatus()  //监控I/O端口?/p> <p>q程׃部分l成Q进E内核对象, 地址I间。进E是不活泼的Q它的执行依赖于U程?br>U程׃部分l成Q线E内核对象,U程堆栈?/p> <p><br>创徏新线E:<br>DWORD WINAPI FUNC(PVOID pvParam)<br>int Param;<br>DWORD dwThreadID;<br>CreateThread(NULL,0,FUNC,(PVOID)&Param,0,&dwThreadID);</p> <p>查线E是否退出:<br>BOOL GetExitCodeThread(HANDLE hThread,PDWORD pdwExitCode);//如果q未l止Q得?x103.</p> <p><br>获得伪句柄:<br>GetCurrentProcess()<br>GetCurrentThread()</p> <p>获得q行旉Q?br>GetProcessTimes()<br>GetThreadTimes()</p> <p>U程或进E的伪句柄{化ؓ实句柄:<br>DuplicatgeHandle();//此函C增加内核对象的引用计数?/p> <p>伪句柄用于本U程Q获得这个句q不会媄响内核对象的计数Q而实句柄用于传递给子进E?/p> <p>U程的暂停和q行Q?br>ResumeThread(HANDLE)<br>SuspendThread(HANDLE)  //使用此函数要心死锁?/p> <p>U程休眠Q?br>Sleep(DWORD dwMilliseconds);<br>自动退出当前时间片Q?br>SwitchtoThread();</p> <p>可以获得和修改线E的上下文,使用之前要SuspendThread()<br>GetThreadContext()<br>SetThreadContext()</p> <p>改变q程的优先://Cq程是不可以调度的,调度的单位是U程?br>BOOL SetPriorityClass();<br>DWORD GetPriorityClass();</p> <p>讑֮U程的相对优先Q?br>int GetThreadPriority(HANDLE hThread);<br>BOOL SetThreadPriority(Handle hThread,int nPriority);</p> <p>Microsoft保留了随时修改调度算法的权利Q因此用相对优先Q可以保证程序在来的系l上也可以正常运行?br>l合q程优先U和U程的相对优先Q就可以得到U程的基本优先。线E的当前优先U不可以低于基本优先U,<br>也就是说Q系l会提高U程的优先Qƈ随着执行旉片的逝降低优先Q但降到基本优先U后׃再降了?br>优先U?-15成ؓ动态优先范围Q高?5是实时范_pȝ不会调度实时范围U程的优先Q也不会把动态优先范围?br>U程提高?5以上?/p> <p>亲缘性是对多处理器系l来说的Qؓ了能利用保留在cpu高速缓存和NUMA(非统一内存讉KQ结构计机本插件板上内存中的数据,pȝ量U程上次q行使用的CPU来运行线E,包括软亲~性(WIN2000默认Q和亲~性(用户可以选择CPU)<br>相关的函数有Q?br>BOOL SetProcessAffinityMask(HANDLE hProcess,DWORD_PTR dwProcessAffinityMask);<br>BOOL GetProcessAffinityMask(Handle hProcess,PDWORD_PTR pdwProcessAffinityMask,PDWORD_PTR pdwSystemAffinityMask);<br>DWORD_PTR SetThreadAffinityMask(HANDLE hThread,DWORD_PTR dwThreadAffinityMask);<br>DWORD_PTR SetThreadIdealProcessor(HANDLE hThread,DWORD dwIdealProcessor);</p> <p>临界Z证其中的资源Q通常是各U共享变量)被原子的讉KQ当q入临界区后Q其他访问这些资源的U程不会被调度?/p> <p>U程同步包括用户方式和内核方式,用户方式包括原子操作和界区Q它的特Ҏ速度快,但功能有限。内核方式利用内核对象的通知状态来同步U程Q由于需要由用户方式切换到内核方式(q种切换很废旉Q,且系l要q行很多操作Q效率较低,但功能强大(能够讑֮时值等,可以同步多个q程的线E)?/p> <p>内核方式同步的原理:U程使自p入休眠状态,{待内核对象由未通知状态变为已通知状态?/p> <p>可处于未通知状态变和已通知状态的内核对象Q进E,U程Q作业,文g修改通知Q时_可等待定时器Q文Ӟ控制台输入,信号量,互斥体?/p> <p>q程和线E在建立时处于未通知状态,在退出时变ؓ已通知状态?/p> <p>{待函数Q?br>DWORD WaitForSingleObject(HANDLE hObject,DWORD dwMilliseconds);<br>DWORD WaitForMultipleObject(DWORD dwCount,CONST HANDLE* phObjects,BOOL fWaitALL,DWORD dwMilliseconds);其中Q?br>0<dwCount<WAIT_OBJECTS(windows头文件中定义?4Q,如果讑֮fWaitALl为TRUE,那么函数会知道左叛_象变为已通知状态才会返回,如果传递FALSEQ那么只要有一个对象变为已通知状态,函数׃q回?br>q回值的含义Q?br>HANDLE h[3];<br>h[0]=hProcess1;<br>h[1]=hProcess2;<br>h[2]=hProcess3;<br>DWORD dw=WaitForMultipleObject(3,h,FALSE,5000);<br>switch(dw)<br>{<br>    case WAIT_FAILED://Bad call to function(invalid handle?)<br>         break;<br>    case WAIT_TIMEOUTQ?/None of the object became signaled within 5000 milliseconds.<br>         break;<br>    case WAIT_OBJECT_0+0:The process identified by h[0] terminated.<br>         break;<br>    case WAIT_OBJECT_0+1:<br>         break;<br>    case WAIT_OBJECT_0+2:<br>         break;<br>}<br>//WaitForSingleObject()的返回值只有前三种情况。如果给WaitForMutipleObject()的fWaitAll参数传递TRUE,那么其返回g只有前三U?/p> <p>事g内核对象Q有两种Qh工事件对象:当它得到通知的时候,所有等待的U程都变为可调度U程Q自动重|的事gQ当事g得到通知的时候,只有一个等待线E变为可调度的线E。创Z件内核对象:<br>HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa,BOOL fManualReset,BOOL fInitialState,PCTSTR pszName);<br>事件改为通知状态:<br>BOOL SetEvent(HANDLE hEvent);<br>事件改为未通知状态:<br>BOOL ResetEvent(HANDLE hEvent);<br>如果事g是自动重|事Ӟ那么成功{待会生副作用Q即事件自动置为未通知状态。如果是人工事g对象Q则没有副作用?/p> <p>{待定时器内核对象:是在某个旉或按规定的间隔时间发q信号通知的内核对象?br>HANDLE CreateWaitableTimer(PSECURITY_ATTRIBUTES psa,BOOL fManualReset,PCSTR pszName);<br>初始L未通知状态?br>BOOL SetWaitableTimer(<br>HANDLE hTimer,<br>const LARGE_INTEGER *pDueTime,<br>LONG lPeriod,<br>PTIMERAPCROUTINE pfnCompletionRoutine,<br>PVOID pvArgToCompletionRoutine,<br>BOOL fResume);<br>取消定时器:<br>BOOL CancelWaitableTimer(HANDLE hTimer);<br>如果仅想改变报时条gQ不用调用这个函数暂停报时器Q直接调用SetWaitableTimer()可以了?/p> <p>信号量内核对?br>如果当前资源的数量大?Q发Z?br>如果当前资源数量{于0Q不发出信号<br>决不允许资源数量倹{?br>创徏信号量:<br>HANDLE CreateSemaphore(PSECURITY_ATTRIBUTE psa,<br>LONG lInitialCount,<br>LONG lMaximumCount,<br>PCSTR pszName);<br>递增资源Q?br>BOOL ReleaseSemaphore(HANDLE hsem,<br>LONG lReleaseCount,<br>PLONG plPreviousCount);</p> <p>互斥体内核对象:互斥体确保对单个资源的互斥访问。它包含一个用数量,一个线EID,一个递归计数?br>与界区的区别:能够同步多个q程中的U程Q可以设定超时倹{?br>如果ID?Q那么表C没有线E占用互斥体Q互斥体发出信号?br>如果ID不ؓ0Q表C占用资源的U程IDQ不发出信号?/p> <p>HANDLE CreateMutex(PSECURITY_ATTRIBUTES psa,<br>BOOL fInitialOwner,<br>PCTSTR pszName);</p> <p>释放资源Q?br>BOOL ReleaseMutex(HANDLE hMutex);</p> <p>额外的函敎ͼ<br>DWORD SingalObjectAndWait(<br>HANDLE hObjectToSignal,<br>HANDLE hObjectToWaitOn,<br>DWORD dwMilliseconds,<br>BOOL fAlertable);<br>发出一个通知信号q等待另一个通知Q效率比分别操作提高很多?/p> <p>windows2000提供了如下几U线E池函数用于U程理Q?br>一、异步调用函?<br>BOOL QueueUserWorkItem(<br>PTHREAD_START_ROUTINE pfnCallback,<br>PVOID pvContextQ?br>ULONG dwFlags);<br>该函数将“工作目”攑օU程池ƈ且立卌回。工作项目是指一个用pfnCallback参数标识的函数。它被调用ƈ且传递单个参数pvContext.工作目函数原型如下Q?br>DWORD WINAPI WorkItemFunc(PVOID pvContext);<br>dwFlags参数QWT_EXECUTEDEFAULT  工作目攑օ非I/Olg得线E中<br>             WT_EXECUTEINIOTHREAD 工作目攑օI/Olg的线E中Q这LU程在I/Oh没有完成之前不会被终止运?nbsp;                                 Q防止因为线E被l止DI/Oh丢失?br>             WT_EXECUTEINPERSISTENTTHREAD 攑օ怹U程池,<br>             WT_EXECUTELONGFUNCTION  工作目需要长旉的工作,pȝ会据此安排更多的U程?/p> <p>U程池不能设|线E个数的上限Q否则排队个数超q线E个C限的时候,会导致所有的U程都被中断?/p> <p>工作目函数如果讉K了已l被卸蝲的DLL,会生违规访问?/p> <p><br>二、按规定的时间间隔调用函?br>创徏定时器队列:<br>HANDLE CreateTimerQueue();<br>在队列中创徏定时器:<br>BOOL CreateTimerQueueTimer(<br>PHANDLE phNewTimer,<br>HANDLE hTimerQueue,<br>WAITORTIMERCALLBACK pfnCallback,<br>PVOID pvContext,<br>DWORD dwDueTime,<br>DWORD dwPeriod,<br>ULONG dwFlags);<br>工作回调函数原型如下Q?br>VOID WINAPI WaitOrTimerCallback(<br>PVOID pvContext,<br>BOOL fTimerOrWaitFired);<br>dwFlags比前面的多了一个标志:WT_EXECUTEINTIMERTHREAD,表示q件的定时器线E(定时器组件只有一个线E)q行q个<br>工作函数Q此时的工作函数必须是很快返回的Q否则定时器lg无法处理其他的h?/p> <p>删除定时器:<br>BOOL DeleteTimerQueueTimer(<br>HANDLE hTimerQueue,<br>HANDLE hTimer,<br>HANDLE hCompletionEvent);<br>在定时器U程中删除定时器会造成死锁。设定hCompletionEvent为INVALID_HANDLE_VALUEQ那么在定时器的所有排队工作项目没有完成之前,DeleteTimerQueueTimer不会q回Q也是说在工作目中对定时器进行中断删除会死锁。可以给hCompletionEvent传递事件句柄,函数会立卌回,在排队工作完成之后,会设|该事g?/p> <p>重新讑֮定时器://不能修改已经触发的单步定时器?br>BOOL ChangeTimerQueueTimer(<br>HANDLE hTimerQueue,<br>HANDLE hTimer,<br>ULONG dwDueTime,<br>ULONG dwPeriod;</p> <p>删除定时器队列:<br>BOOL DeleteTimerQueueEx(<br>HANDLE hTimerQueue,<br>HANDLE hCompletionEvent);</p> <p>三、当单个内核对象变ؓ已通知状态时调用函数<br>BOOL RegisterWaitForSIngleObject(<br>PHANDLE phNewWaitObject,<br>HANDLE hObject,<br>WAITORTIMERCALLBACK pfnCallback,<br>PVOID pvContext,<br>ULONG dwMilliseconds,<br>ULONG dwFlags);<br>pfnCallBack原型Q?br>VOID WINAPI WaitOrTimerCallbadkFunc(<br>PVOID pvContext,<br>BOOLEAN fTimerorWaitFired);<br>如果{待时QfTimerorWaitFired==TRUE,如果是已通知状态,则ؓFALSE.</p> <p>dwFlags可以传递参敎ͼWT_EXECUTEINWAITTHREAD,它让{待lg得线E之一q行工作目函数。注意项同前?/p> <p>如果{待的内核对象是自动重置的,那么会导致工作函数被反复调用Q传递WT_EXECUTEONLYONCE会避免这U情c?/p> <p>取消{待lg的注册状态:<br>BOOL UnregisterWaitEx(<br>HANDLE hWaitHandle,<br>HANDLE hCompletionEvent);</p> <p>四、当异步I/Oh完成时调用函?br>设备和U程池的非I/Olg兌<br>BOOL BindIoCompletionCallback(<br>HANDLE hDevice,<br>POVERLAPPED_COMPLETION_ROUTINE pfnCallback,<br>ULONG dwFlags//始终?);</p> <p>工作函数原型Q?br>VOID WINAPI OverlappedCompletionRoutine(<br>DWORD dwErrorCode,<br>DWORD dwNumberOfBytesTransferred,<br>                         ,<br>POVERLAPPED pOverlapped);</p> <p>Windows的内存结?/p> <p>?8Q?000Q到64位的windows,内存理方式都是不同的,32位的win2000用户内存是从0x10000?x7fffffffQ?4kB-2G),2000 Advanced server可以辑ֈQ?4kB-3G),其中最?4kB也是止q入的。再往上则ql用?8则是?x400000-0x7fffffff(4M-2G),2G-3G是系l用来存?2位共享数据的地方Q如很多pȝ动态连接库?-4M是ؓ了兼?6位程序保留的?G-4Gql自w用?8的内核区是不受保护的Q?000受保护?br>对虚拟地址I间的分配称作保留,使用虚拟内存分配函数(VirtualAlloc),释放使用VirtualFree(),目前Q所有cpuq_的分配粒度都?4kB,面大小则不同,x86?kB,Alpha?kB,pȝ在保留内存的时候规定要从分配粒度边界开始,q且是页面的整数倍,用户使用VirtualAlloc都遵守这个规定,但系l不是,它是从页面边界开始分配的?/p> <p>物理存储器映射C留的内存区域的过E称为提交物理存储器Q提交是以页面ؓ单位q行的,也用VirtualAlloc函数?/p> <p>物理存储器是由内存和Q硬盘上的)|件组成的Q如果访问的数据是在|件中Q则UCؓ面失效Qcpu会把讉K通知操作pȝQ操作系l负责将数据调入内存Qƈ指导cpu再次q行上次失效的指令?/p> <p>当启动一个程序的时候,pȝq不是将整个文gd内存或者页文gQ而是这个文件直接映到虚拟内存I间Qƈ需要的数据d内存Q即硬盘上的文件本w当作页文gQ虽然不是)。当盘上的一个程序的文g映像Q这是个exe文g或者dll文gQ用作地址I间的物理存储器Q它UCؓ内存映射文g。当一?exe或者dll文g被加载时Q系l将自动保留一个地址I间的区域,q将该文件映到该区域中。但pȝ也提供了一l函敎ͼ用于数据文件映到一个地址I间的区域中?/p> <p><br>物理存储器的面h不同的保护属性:<br>PAGE_NOACESS<br>PAGE_READONLY<br>PAGE_READWRITE<br>PAGE_EXECUTE<br>PAGE_EXECUTE_READ<br>PAGE_EXECUTE_READWRITE<br>PAGE_WRITECOPY<br>PAGE_EXECUTE_WRITECOPY<br>后两个属性是配合׃n面机制使用的。WINDOWS支持多个q程׃n单个内存块,比如q行notepad?0个实例,可以让他们共享应用程序的代码和数据,q样可以大大提高性能Q但要求该内存块是不可写的。于是系l在调入.exe或者dll的时候,会计那些页面是可以写入的,些页面分配虚拟内存。然后同其他的页面一h到一块虚拟内存,但赋PAGE_WRITECOPY或者PAGE_EXECUTE_WRITECOPY属?通常包含代码的块是PAGE_EXECUTE_READQ包含数据的块是PAGE_READWRITE)。当一个进E试囑ְ数据写入׃n内存块时Q系l会q行如下操作Q寻N先分配的一个空闲页面,试图修改的面拯到这个空闲页面,赋予PAGE_READWRITE或者PAGE_EXECUTE_READWRITE属性,然后更新q程的页面表Q得用户可以对新的面q行写入?/p> <p>q有三个Ҏ的保护属性:PAGE_NOCACHE PAGE_WRITECOMBINE PAGE_GUARD,前两个用于驱动程序开发,最后一个可以让应用E序在页面被写入的时候获得一个异常?/p> <p>块的意思是一l相ȝ面Q它们具有相同的保护属性,q且受相同类型的物理存储器支持?br>赋予虚拟内存面保护属性的意义是ؓ了提高效率,而且q个属性M被物理存储器的保护属性取代?/p> <p>如果数据在内存中没有寚wQ那么cpu要多ơ访问才能得到数据,效率很低?/p> <p>内存理函数Q?/p> <p>获得pȝ信息Q?br>VOID GetSystemInfo(LPSYSTEM_INFO psinf);//可以得到面大小Q分配粒度,最大内存地址Q最内存地址?/p> <p>获得内存状态:<br>VOID GlobalMemoryStatus(LPMEMORYSTATUS pmst);</p> <p>获得内存地址的某些信息:<br>DWORD VirtualQuery(<br>LPVOID pvAddress,<br>PMEMORY_BASIC_INFORMATION pmbi,<br>DWORD dwLength);</p> <p>DWORD VirtualQuery(<br>HANDLE hProcess,<br>LPVOID pvAddress,<br>PMEMORY_BASIC_INFORMATION pmbi,<br>DWORD dwLength);</p> <p>内存映射文g的优点:<br>1 节省面文g;<br>2 加快E序启动Q?br>3 在多个进E间׃n数据?/p> <p>q程的启动过E:<br>pȝ首先?exe文g映射到地址I间Q缺省基地址?x400000,然后查询.exe的输入表Q将其用的所?dll也映到地址I间Q基地址在每?dll文g中,如果不能满Q需要重定位Q,然后执?exe的启动代码。此?exe文gq在盘上。每ơ代码蟩C个尚未加载到内存的指令地址Q就会出C个错误,pȝ会发现这个错误,q将代码加再到内存中?br>如果再创?exe文g的一个实例。那么直接将原来的地址I间中的内容映射到新的地址I间可以了。这样多个实例就可以׃n相同的代码和数据。如果某个实例要改变׃n内容Q系l就更改的页面申请一个新的页面,内Ҏ贝一份,然后用新的页面代替地址I间中原来页面的映射可以了?8?000不同Q它不待修改便立即ؓ所有的实例分配新的面?/p> <p>使用内存映射文gQ?br>1 创徏或打开一个文件内核对象:<br>HANDLE CreateFile(<br>PCSTR pszFileName,<br>DWORD dwDesiredAccess,<br>DWORD dwShareMode,<br>PSECURITY_ATTRIBUTES psa,<br>DWORD dwCreationDisposition,<br>DWORD dwFlagsAndAttributes,<br>HANDLE hTemplateFile);<br>p|的返回值是INVALID_HANDLE_VALUE<br>2 创徏一个文件映内核对象:<br>HANDLE CreateFileMapping(<br>HANDLE hFile,<br>PSECURITY_ATTRIBUTES psa,<br>DWORD fdwProtect,<br>DWORD dwMaximumSizeHigh,<br>DWORD dwMaximumSizeLow,<br>PCTSTR pszName);<br>如果l函数的fdwProtect传递PAGE_READWRITE标志Q那么磁盘上文g的大会变ؓ同映像文件相同大?br>p|的返回值是NULL?/p> <p>3 文件映到q程的地址I间Q?br>PVOID MapViewOfFile(<br>HANDLE hFileMappingObject,<br>DWORD dwDesiredAccess,<br>DWORD dwFileOffsetHigh,<br>DWORD dwFileOffsetLow,<br>SIZE_T dwNumberOfBytesToMap);<br>windows2000会根据要求将部分文g映射到地址I间Q而win98L把全部内Ҏ到地址I间Qƈ且仅能映到2G-3GI间Q此I间为共享空_所有的q程如果映射相同的文Ӟ那么都会映射到相同的地址Q一个进E甚至不必映就可以讉Kq个I间里其他进E的映射文gQwin2000多个q程映射同一个文件返回的地址通常是不同的?br>4 从进E的地址I间中撤销文g数句的映?br>BOOL UnmapViewOfFile(PVOID pvBaseAddress);</p> <p>文件映像写入磁盘:<br>BOOL FlushViewOfFile(<br>PVOID pvAddress,<br>SIZE_T dwNumberOfBytesToFlush);</p> <p>windows保证单个文g映射对象的多个视囑օ有相x。但不保证但个文件的多个映射对象有相x?/p> <p>使用MapViewOfFileEx代替MapViewOfFile可以讑֮文g映射的基地址Q?br>PVOID MapViewOfFileEx(<br>HANDLE hFileMappingObject,<br>DWORD dwDesiredAccess,<br>DWORD dwFileOffsetHigh,<br>DWORD dwFileOffsetLow,<br>SIZE_T dwNumberOfBytesToMap,<br>PVOID pvBaseAddress);</p> <p>使用内存映射文g在进E间׃n数据<br>׃n机制QRPC ,COM,OLE,DDE,H口消息QWM_COPYDATA),剪脓板,邮箱Q管道,套接字?br>在单ZQ它们的底层实现Ҏ都是内存映射文g?/p> <p>可以在页文g中直接创建文件映对象,Ҏ是给CreateFileMapping函数的hFile参数传逺NVALID_HANDLE_VALUE.注意Q?br>CreateFile()函数q行p|也会q回q个参数Q因此一定要查CreateFileQ)的返回倹{记住,文g函数q行p|的可能性太大了?/p> <p>W三章:多个q程׃n对象?/p> <p>堆栈Q优点:可以不考虑分配_度和页面边界之cȝ问题Q集中精力处理手头的dQ缺ҎQ分配和释放内存块的速度比其他机制慢Qƈ且无法直接控制物理存储器的提交和回收?/p> <p>q程的默认堆栈是1MB,可以使用/HEAP链接开兌整大,DLL没有相关的堆栈?/p> <p>堆栈的问题在于:很多windows函数要用时内存块Q进E的多个U程要分配内存块Q这些内存都是在默认堆栈上分配的Q但规定旉内,每次只能׃个线E能够分配和释放默认堆栈的内存块Q其他想要处理内存块的线E必ȝ待。这U方法对速度又媄响。可以ؓq程的线E创助堆栈,但windows函数只能使用默认堆栈?/p> <p>获取q程默认堆栈句柄Q?br>HANDLE GetProcessHeap();</p> <p>创徏辅助堆栈的理?br>1 保护lgQ?br>多个lg的数据؜合交叉的存放在一块内存里Q那么一个组件的错误操作很容易媄响到另外一个组件。而要定位错误的来源将十分困难?br>2 更有效的内存理<br>通过在堆栈中分配同样大小的对象,可以更加有效的管理内存,减少内存片?br>3 q行本地讉KQ?br>同U数据集中到一定的内存块,可以在操作的时候访问较的面Q这减了RAM和硬盘对换的可能.<br>4 减少U程同步的开销Q?br>通过告诉pȝ只有一个线E用堆?创徏堆栈时用HEAP_NO_SERIALIZE标志lfdwOptions)Q可以避免堆栈函数执行额外的用于保证堆栈安全性的代码Q提高效率,但此时用户必自q护线E的安全性,pȝ不再Ҏ负责?br>5 q速释攑֠栈?br>因ؓ数据单一Q因此释攄时候只要释攑֠栈即可,不必昄的释放每个内存块?/p> <p>创徏辅助堆栈Q?br>HANDLE HeapCreate(<br>DWORD fdwOptions,<br>SIZE_T dwInitialSize,<br>SIZE_T dwMaximumSize);</p> <p>从堆栈中分配内存Q?br>PVOID HeapAlloc(<br>HANDLE hHeap,<br>DWORD fdwFlags,<br>SIZE_T dwBytes);注意Q当分配过Q?MB)内存块的时候,最好用VirtualAllocQ)Q?/p> <p>改变内存块的大小Q?br>PVOID HeapReAlloc(<br>HANDLE hHeap,<br>DWORD fdwFlags,<br>PVOID pvMem,<br>SIZE_T dwBytes);</p> <p>索内存块的大:<br>SIZE_T HeapSize(<br>HANDLE hHeap,<br>DWORD fdwFlags,<br>LPVOID pvMem);</p> <p>释放内存?<br>BOOL HeapFree(<br>HANDLE hHeap,<br>DWORD fdwFlags,<br>PVOID pvMem);</p> <p>撤销堆栈Q?br>BOOL HeapDestroy(HANDLE hHeap);</p> <p>使用辅助堆栈的方法:重蝲对象的new操作W,在辅助堆上分配内存,q给对象d一个静态变量用于保存堆句柄?/p> <p>其它堆栈函数Q?br>获取q程中所有堆栈得句柄Q?br>DWORD GetProcessHeaps(DWORD dwNumHeaps,PHANDLE pHeaps);<br>验证堆栈完整性:<br>BOOL HeapValidate(<br>HANDLE hHeap,<br>DWORD fdwFlags,<br>LPCVOID pvMem);<br>合ƈ地址中的I闲?br>UINT HeapCompact(<br>HANDLE hHeapQ?br>DWORD fdwFlags);</p> <p>BOOL HeapLock(HANDLE hHeap);<br>BOOL HeapUnlock(HANDLE </p> <p>遍历堆栈Q?br>BOOL HeapWalk(<br>HANDLE hHeap,<br>PProcess_HEAP_ENTRY pHeapEntry);</p> <p>各个dll也可以有自己的输入表?/p> <p>如何~写DLL:<br>在DLL的头文g中,有如下代码:<br>#ifdef MYLIB<br>#else<br>    #define MYLIB extern "C" __declspec(dllimport)    <br>#endif<br>在每个输出变量和输出函数的声明前Q用MYLIB修饰?br>在DLL的实现文件中Q有如下代码Q?br>Qi nclude "windows.h"<br>#define MYLIB extern "C" __declspec(dllexport)<br>Qi nclude "Mylib.h"</p> <p>其它的同~写普通C++E序完全相同?"C" 表示按C方式链接和调用函数。C++~译器缺省按照__stdcall方式~译和调用,q种方式会改变函数的内部名字。此处如果把"C"都去掉也可以Q但CE序无法调用。另外,使用GetProcAddress函数时也会发生困难,因ؓ<br>~译E序已经把函数名字改变了Q无法用原来的名字得到函数地址。(核心~程说的不明白,没想到这本书错误q么多)<br>发行的时候,头文g?lib文g和DLL文gl用户就可以了。lib文g的作用是说明了头文g中函数所在的DLL文gQ如果没有lib文gQ编译器在链接q程中提C错误:unresolved external symbol 函数名?br>事实上,调用DLL有两U方式,W一U是比较常用Q即包含DLL的头文gQƈ在链接的时候将动态链接库同exe文g像连接,建立输入表。这个时候需?lib文g。第二种Ҏexe文g中没有输入表Q程序用LoadLibrary(Ex)和GetProcAddress()昑ּ的加载DLL文gQ卸载用FreeLibrary())Q这个时候不需?lib文g?br>HINSTANCE LoadLibrary(PCTSTR pszDLLpathName);<br>HINSTANCE LoadLibraryEx(PCTSTR pszDLLpathName,NULL,0);</p> <p>两次调用LoardLibraryq不会装载两ơdll文gQ只是将dll映射q进E的地址I间。系l会自动为每个进E维护一个dll的计数。FreeLiabray会计数减一Q如果计Cؓ0Q系l就会将dll从进E的地址I间卸蝲?/p> <p><br>HINSTANCE GetModuleHandle(PCTSTR pszModuleName);//定dll是否已经被映进地址I间?br>HINSTANCE hinstDll=GetModuleHandle("MyLib");<br>if(hinstDll==NULL)<br>{<br>    hinstDll=LoadLibrary("MyLib");<br>}</p> <p>DWORD GetModuleFileName(<br>    HINSTANCE hinstModule,<br>    PTSTR pszPathName,<br>    DWORD cchPath<br>}<br>可以获得某个模块Q?exe或者dll)的全路径名?/p> <p>几个函数的用法:Q注意GetProcAddress()函数的用法,如何定义和用一个函数指针)<br>typedef int (*MYPROC)(int,int);</p> <p>int main()<br>{<br> HINSTANCE t;<br> t=LoadLibraryEx(TEXT("tt.dll"),NULL,0);<br> if(t)<br> {<br>  cout<<TEXT("load success")<<endl;</p> <p> }<br> HINSTANCE hinstDll=GetModuleHandle("tt.dll");<br> if(hinstDll==NULL)<br> {<br>  cout<<TEXT("first load failed")<<endl;<br>  hinstDll=LoadLibrary("MyLib");<br> }<br> size_t sz=100;<br> PTCHAR str=new TCHAR[sz];<br> GetModuleFileName(t,str,sz);<br> cout<<str<<endl;<br> delete str;<br> MYPROC add=NULL;<br> add=(MYPROC)GetProcAddress(t,"add");<br>    if(NULL!=add)<br>    {<br>  cout<<(*add)(1,2)<<endl;<br> }<br> FreeLibrary(t);</p> <p> return 0;<br>}</p> <p><br>UNICODE<br>ANSI/UNICODE通用的定义方法(转换只需要在~译的时候用_UNICODE和UNICODEQ:<br>TCHAR _TEXT("success") PTSTR PCTSTR _tcscpy(),_tcscat();<br>使用BYTE PBYTE定义字节Q字节指针和数据~冲?br>传递给函数的缓存大:sizeof(szBuffer)/sizeof(TCHAR)<br>l字W串分配内存Qmalloc(nCharacters*sizeof(TCHAR));<br>其它的字W串函数Q?br>PTSTR CharLower(PTSTR pszString);<br>PTSTR CharUpper(PTSTR pszString);<br>转换单个字符Q?br>TCHAR c=CharLower((PTSTR)szString[0]);<br>转换~存中的字符Ԍ不必?l尾Q:<br>DWORD CharLowerBuff(<br>PTSTR pszString,<br>DWORD cchString);<br>DWORD CharUpperBuff(<br>PTSTR pszString,<br>DWORD cchString);</p> <p>BOOL IsCharAlpha(TCHAR ch);<br>BOOL IsCharAlpahNumeric(TCHAR ch);<br>BOOL IsCharLower(TCHAR ch);<br>BOOL IsCharUpper(TCHAR ch);</p> <p>U程本地存储(TLS):E的每个U程存储U有数据。用于那些一ơ传递参数后多次调用的函敎ͼ函数会保存上ơ调用的数据Q?br>实现ҎQ进E中有一个位标志树组Qwin2000的这个数l大超q?000Q。在每个U程中有一个对应的PVOID数组。通过讑֮位标志树l的某个位来分配每个U程中的PVOID数组得相应单元。函数需要每ơ检索线E的PVOID数组Q获得该U程的相应数据?/p> <p>DWORD TlsAlloc();  //为每个线E分配一个空的PVOID数组单元?/p> <p>BOOL TlsSetValue(  //U程讑֮自己的PVOID数组单元?br>DWORD dwTlsIndex,<br>PVOID pvTlsValue);</p> <p>PVOID TlsGetValue(<br>DWORD dwTlsIndex);  //索PVOID数组?/p> <p>BOOL TLSFree(<br>DWORD dwTlsIndex);  //释放PVOID数组单元</p> <p>静态TLS:__declspec(thread) DWORD gt_dwStartTime=0;//只能修饰全局或者静态变量?/p> <p>DLL挂接Q进E注入)Q让自己的DLL插入到其他进E的地址I间?/p> <p>1 使用注册表插入DLL<br>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs<br>你的DLL路径攑օq个关键字下面。当User32.dll被映到q程中的时候,它会加在q个关键字下的每个库?br>注意Q(1Q对win98无效<br>      Q?Q由于加载事间比较早Q你的DLL可能无法调用kernel32以外的dll.<br>       (3) 如果q程没有使用user32.dllQ这个方法无效?br>       (4) 需要重新启动?br>      Q?Quser32不会查每个库是否加蝲成功?br>2 使用windows钩子<br>HOOK hHook=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hinstDll,0);<br>BOOL UnhookWindowsHookEx(HHOOK hhook);<br>具体q程如下Q?br>      Q?Q进EB的一个线E准备发送消息给一个窗口?br>      Q?Q系l察看线E上是否已经安装了WH_GETMESSAGE钩子?br>      Q?Q系l察看GetMsgProc的DLL是否已经映射Cq程B的地址I间Q如果没有,pȝ把DLL映射到B的地址I间Qƈ自动增加引用计数?br>      Q?Q调用GetMsgProc函数Q返回时Q系l会自动为DLL的引用计数减一?/p> <p>3 使用q程U程来插入DLL<br>      (1)使用VirtualAllocEx,分配q程q程的地址I间的内存?br>      (2)使用WriteProcessMemory,Dll的\径名拯到第一个步骤中已经分配的内存中?br>      (3)使用GetProcAddress,获得LoadLibrary的实际地址?br>      (4)使用CreateRemoteThread,在远E进E中创徏一个线E?br>      退出:  <br>      (5)使用VirtualFreeExQ释攑ֆ?br>      (6)使用GetProcAddress,获得FreeLiabary的地址?br>      (7)使用CreateRemoteThread,在远E进E中创徏一个线E,调用FreeLiabary函数?br>4 使用Ҏ伊DLL插入<br>  替换dll.<br>5 DLL作ؓ调试E序插入<br>6 win98内存映射文gQcreatprocess </p> <p>l构化异常处理:<br>l束处理E序Q__try{} __finally{}<br>除非__try执行中进E或者线E结束,否则M执行__finallyQƈ且__finally中的return会替代__try中的return;好的习惯是将return ,continue,break,goto语句拿到l构化异常处理语句外面,可以节省开销。将__try中的return 换成__leaveQ可以节省开销。在__finallyȝ定时正常q入q是展开q入Q?br>BOOL AbnormalTermination();//正常q入q回FALSE,局部展开或者全局展开q回TRUE;</p> <p>异常处理E序Q__try{}__exception(异常qo表达?{}</p> <p>EXCEPTION_EXECUTE_HANDLE<br>表示处理异常Q处理后转到exception块后面的代码l箋执行?br>EXCEPTION_CONTINUE_EXECUTION<br>EXCEPTION_CONTINUE_SEARCH</p> <p>可以对异常过滤表辑ּq行编码,也可以用一个调用一个函数来军_qo表达式,函数的返回值是LONG.例如Q可以进行一定处理,然后q回EXCEPTION_CONTINUE_EXECUTIONQ再ơ执行出错语句。但可能再次出错Q因此这U方法必d心,防止生成d@环?/p> <p>DWORD GetExceptionCode() 可以获得异常U类。它只能在__except后的括号或者异常处理程序中调用?/p> <p>发生异常后,操作pȝ会像引v异常的线E的栈里压入三个l构:EXCEPTION_RECORD CONTEXT EXCEPTION_POINTERS,其中W三个结构包含两个成员指针,分别指向前两个结构,使用函数可以获得W三个结构的指针Q?br>PEXCEPTION_POINTERS GetExceptionInformation();//仅可以在异常qo器中调用,既__exception后面的小括号?/p> <p>逗号表达式:从左到右Ҏ有的表达式求|q返回最有面的表辑ּ的倹{?/p> <p>引发软g异常Q?br>VOID RaiseException(<br>DWORD dwExceptionCode,<br>DWORD dwExceptionFlags,<br>DWORD nNumberOfArguments,<br>CONST ULONG_PTR *pArguments);</p> <p>~省调试器所在注册表Q?br>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug  Debugger<br>win98是存攑֜win.ini?/p> <p>调试器挂接到被调试进E?br>BOOL DebugActiveProcess(DWORD dwProcessID);</p> <p><br>while(*str++!='\0');<br>应该注意的是在不满条g后,str仍然会自??/p> <p>位操作符>>?lt;<不会做@环位U,即不会把Ud的位攑ֈ另一头?/p> <p>多态性和动态联~的实现q程分析</p> <p>  一、基Q?/p> <p>  1、多态性:使用基础cȝ指针动态调用其zcM函数的特性?/p> <p>  2、动态联~:在运行阶D,才将函数的调用与对应的函Cq行q接的方式,又叫q行时联~或晚捆l?/p> <p>  二、过E描qͼ</p> <p>  1、编译器发现一个类中有虚函敎ͼ~译器会立即为此cȝ成虚拟函数表 vtableQ后面有对vtable的分析)。虚拟函数表的各表项为指向对应虚拟函数的指针?/p> <p>  2、编译器在此cM隐含插入一个指针vptrQ对vc~译器来_它插在类的第一个位|上Q?/p> <p>  有一个办法可以让你感知这个隐含指针的存在Q虽然你不能在类中直接看到它Q但你可以比较一下含有虚拟函数时的类的尺寸和没有虚拟函数时的cȝ寸Q你能够发现Q这个指针确实存在?</p> <p>  3、在调用此类的构造函数时Q在cȝ构造函CQ编译器会隐含执行vptr与vtable的关联代码,vptr指向对应的vtable。这将cM此类的vtable联系了v来?/p> <p>  4、在调用cȝ构造函数时Q指向基cȝ指针此时已经变成指向具体的类的this指针Q这样依靠此this指针卛_得到正确的vtableQ从而实C多态性。在此时才能真正与函Cq行q接Q这是动态联~?/p> <p>定义U虚函数ҎQvirtual returntype function()= 0;</p> <p>所有的cd都可以用new动态创建,包括c,l构Q内|数据类型?/p> <p>exit()函数包含?cstdlib"?/p> <p>泛型~程Q用STL?br>d有近75个泛型算?/p> <p>所有容器的共通操作:<br>== != = empty() size() clear(),begin(),end(),以及insert和erase,不过后者随容器的的不同而不?/p> <p>序列式容器:<br>vector(数组):插入和删除的效率较低Q但存取效率高?br>listQ双向链表):与前者相反,插入和删除的效率较高Q但存取效率低。每个元素包含三个字D:value back front.<br>deque(队列):在前端和末尾操作效率高?/p> <p>生成序列式容器的五种ҎQ?br>1 产生I的容器Q?br>list<string> slist;<br>vector<int> vtor;<br>2 产生特定大小的容器,容器中的每个元素都以光认gؓ初|发现VC中的int double{没有默认|?br>list<int> ilist(1024);<br>vector<string> svec(24);<br>3 产生特定大小的容器,qؓ每个元素指定初|<br>list<int ilist(1024,0);<br>vector<string> svec(24,"default");<br>4 通过一对P代器产生容器Q这对P代器用来表示数组作ؓ初值的区间Q?br>int ia[10]={1,2,3,4,5,6,7,8,9,0};<br>vector<int>  iv(ia+2,ia+8);<br>5 复制某个现有的容器的|<br>vector<int> ivec1;<br>//填充ivec1;<br>vector<int> ivec2(ivec1);</p> <p>?个方法用于操作开始和末尾的元素:push_front() pop_front() push_back() pop_back(),׃pop操作仅删除元素而不q回元素Q因此还需要front() back()Ҏ取开始和末尾的元素,另外Qvector不包括push_front() pop_frontҎQ很昄Q无法实现?/p> <p>intert的四U变形:<br>iterator insert(iterator position,elemType value):value插到position前。返回值指向被插入的元素?br>void insert(iterator position,int count,elemType value):在position前插入count个元素,每个元素都是value.<br>void insert(iterator1 position,iterator2 first,iterator2 last):first,last之间的元素插到position?<br>iterator insert( iterator position):在position前插入元素,初gؓ所属类型的默认倹{?/p> <p>erase的两U变形:<br>1  iterator erase(iterator posit):删除posit指向的元素?br>list<string>::iterator it=find(slist.begin(),slist,end(),str);<br>slist.erase(it);<br>2  iterator erase(iterator first,iterator last):删除first,last间的元素?/p> <p>list不支持iterator的偏U运?/p> <p>对于常值容器,使用常P代器Q?br>const vector<string> cs_vec;<br>vector<string::const_iterator iter_cs_vec.begin();</p> <p>iterator可以当作指针用,可以?取内容,也可以用->调用对象的成员?/p> <p>使用泛型法<br>Qi nclude <algorithm></p> <p>find():U性搜索无序集?br>binary_search():二分搜烦有序集合?br>count()Q返回元素个数?br>search():搜烦序列Q如果存在返回的iterator指向序列首部Q否则指向容器末?br>max_element(begin,end):q回区间内的最大倹{?br>copy(begin,end,begin):元素复制?br>sort(begin,end):排序?/p> <p>function objects:Qi nclude <functional><br>术q算Q?br>plus<type> minus<type> negate<type> multiplies<type> divides<type> modules<type><br>关系q算Q?br>less<type> less equal<type> greater<type greater equal<type> equal_to<type> not_equal_to<type><br>逻辑q算Q?br>logical_and<type> logical_or<type> logical_not<type></p> <p>adapter:适配器?br>bind1st:数值绑定到function object的第一个参数?br>bind2nd:数值绑定到function object的第二个参数?/p> <p>使用map:<br>Qi nclude <map><br>Qi nclude <string><br>map<string,int> words;</p> <p>words["vermeer"]=1;</p> <p>map<string,int>::iterator it=words.begin();<br>for(;it!=words.end();++it)<br>cout<<"key:"<<it->first<<"value:"<<it->second<<endl;</p> <p>查找map元素的方?<br>words.find("vermeer");//q回iterator,指向扑ֈ的元素,找不到返回end();<br>q可以:<br>if(words.count(search_word))<br>count=words[search_word];</p> <p>使用set:<br>Qi nclude <set><br>Qi nclude <string><br>set<string> word_exclusion;<br>//判断是否存在某个元素<br>if(word_exclusion.count(tword))<br>//默认情况下,所有元素按less-thanq算排列</p> <p>//加入元素<br>iset.insert(ival);<br>iset.insert(vec.begin(),vec.end());</p> <p>与set相关的算?br>set_intersection() set_union() set_difference() set_symmetric_difference()</p> <p>使用insertion adapters:<br>Qi nclude <iterator><br>back_inserter()<br>inserter()<br>front_inserter()</p> <p>使用STL通常会有很多警告Qؓ了避免在调试模式Qdebug modeQ出现恼人的警告Q用下面的~译器命令:</p> <p>#pragma warning(disable: 4786) </p> <p>strncpy(dest,source,count) if(count?strlen(source)),那么nulll尾不会被加在dest的尾部,如果count>strlen(source),那么不的部分会用null填充?/p> <p><br>windows内存是由高地址向底地址分配的,但变量的存储是从底地址到高地址的,如INTcd的四个字节,数组的每个元素?br> <br>内存复制的时候不能用字符串拷贝函敎ͼ因ؓ即使用strncpy指定了复制的长度Q拷贝函C会遇?\0'自动l止Q要使用MEMSET?/p> <p>׃寚w的关p,下面两个l构使用sizeofQ前者是12Q后者是16?br>struct DNSAnswer<br>{<br> unsigned short name;<br> unsigned short type;<br> unsigned short cla; <br> unsigned short length;<br> unsigned int   ttl;<br>};<br>struct DNSAnswer<br>{<br> unsigned short name;<br> unsigned short type;<br> unsigned short cla;<br> unsigned int   ttl;<br>        unsigned short length;<br>};</p> <p>子类可以使用父类的保护成员,而友元比子类的权限还大,可以使用cȝU有和保护成员?/p> <p>在内存分配失败的情况下,pȝ只有在出错处理函CؓI的情况下,才会抛出异常Qstd::bad_alloc(),否则会反复调用处理函数ƈ再次试分配内存?/p> <p>如果重蝲了NEWQ那么在l承的时候要心Q如果子cL有覆盖NEWQ那么它会去使用父类的NEW Q因此应该在newQdelete中做?br>if (size != sizeof(base))             // 如果数量“错误”Q让标准operator newQbase为类?br>    return ::operator new(size);        // d理这个请?/p> <p>  if (size != sizeof(base)) {      // 如果size"错误"Q?br>    ::operator delete(rawmemory);  // 让标准operator来处理请?br>    return;                        <br>  }</p> <p>c++标准规定Q要支持0内存hQ分配一个字节)Qƈ且可以删除NULL指针Q直接返回)?/p> <p>在创建线E的时候,传递的变量一定要是全局或者静态的变量Q因Z递的是变量的地址Q如果是局部变量地址很快׃失效?/p> <p>ȝE退出后Q其子线E自动结束?/p> <p>指针Q它可以避免内存泄露Q因为智能指针是在栈上创建的Q还可以避免堆上内存的重复释N误,因ؓ它保证只有一个指针拥有这块内存的所有权?/p> <img src ="http://www.shnenglu.com/zzh/aggbug/29864.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zzh/" target="_blank">Xiao.Zhu</a> 2007-08-13 09:22 <a href="http://www.shnenglu.com/zzh/archive/2007/08/13/29864.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下基于jrtplib库的实时传送实?http://www.shnenglu.com/zzh/archive/2007/08/08/29578.htmlXiao.ZhuXiao.ZhuWed, 08 Aug 2007 08:39:00 GMThttp://www.shnenglu.com/zzh/archive/2007/08/08/29578.htmlhttp://www.shnenglu.com/zzh/comments/29578.htmlhttp://www.shnenglu.com/zzh/archive/2007/08/08/29578.html#Feedback0http://www.shnenglu.com/zzh/comments/commentRss/29578.htmlhttp://www.shnenglu.com/zzh/services/trackbacks/29578.html一、RTP 是进行实时流媒体传输的标准协议和关键技?br> 实时传输协议QReal-time Transport ProtocolQPRTQ是?nbsp;Internet 上处理多媒体数据的一U网l协议,利用它能够在一对一QunicastQ单播)或者一对多QmulticastQ多播)的网l环境中实现传流媒体数据的实时传输。RTP 通常使用 UDP 来进行多媒体数据的传输,但如果需要的话可以?nbsp;TCP 或?nbsp;ATM {其它协议?br> 协议分析 Q每一个RTP数据报都由头部(HeaderQ和负蝲QPayloadQ两个部分组成,其中头部?nbsp;12 个字节的含义是固定的Q而负载则可以是音频或者视频数据?br>
      RTP 是目前解x媒体实时传输问题的最好办法,要在 Linux q_上进行实时传送编E,可以考虑使用一些开放源代码?nbsp;RTP 库,?nbsp;LIBRTP、JRTPLIB {。JRTPLIB 是一个面向对象的 RTP 库,它完全遵?nbsp;RFC 1889 设计Q在很多场合下是一个非怸错的选择。JRTPLIB 是一个用 C++ 语言实现?nbsp;RTP 库,q个库用socket 机制实现|络通讯 因此可以q行?nbsp;Windows、Linux、FreeBSD、Solaris、Unix和VxWorks {多U操作系l上?br>二、JRTPLIB 库的使用Ҏ及程序实?br> (1)JRTPLIB  函数 的?br> a、在使用 JRTPLIB q行实时媒体数据传输之前,首先应该生成 RTPSession cȝ一个实例来表示此次 RTP 会话Q然后调?nbsp;Create() Ҏ来对其进行初始化操作。RTPSession cȝ Create() Ҏ只有一个参敎ͼ用来指明此次 RTP 会话所采用的端口号?br> RTPSession sess;  sess.Create(5000); 

 b、设|恰当的时戳单元Q是 RTP 会话初始化过E所要进行的另外一w要工作,q是通过调用 RTPSession cȝ SetTimestampUnit() Ҏ来实现的Q该Ҏ同样也只有一个参敎ͼ表示的是以秒为单元的时戳单元?br> sess.SetTimestampUnit(1.0/8000.0);

 c、当 RTP 会话成功建立h之后Q接下去可以开始进行流媒体数据的实时传输了。首先需要设|好数据发送的目标地址QRTP 协议允许同一会话存在多个目标地址Q这可以通过调用 RTPSession cȝ AddDestination()、DeleteDestination() ?nbsp;ClearDestinations() Ҏ来完成。例如,下面的语句表C的是让 RTP 会话数据发送到本地L?nbsp;6000 端口Q?nbsp;

 unsigned long addr = ntohl(inet_addr("127.0.0.1")); 
 sess.AddDestination(addr, 6000);
 
 d、目标地址全部指定之后Q接着可以调?nbsp;RTPSession cȝ SendPacket() ҎQ向所有的目标地址发送流媒体数据。SendPacket() ?nbsp;RTPSession cL供的一个重载函?br>对于同一?nbsp;RTP 会话来讲Q负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB 允许它们设|ؓ会话的默认参敎ͼq是通过调用 RTPSession cȝ SetDefaultPayloadType()、SetDefaultMark() ?nbsp;SetDefaultTimeStampIncrement() Ҏ来完成的。ؓ RTP 会话讄q些默认参数的好处是可以化数据的发送,例如Q如果ؓ RTP 会话讄了默认参敎ͼ 

 sess.SetDefaultPayloadType(0);
  sess.SetDefaultMark(false);  
 sess.SetDefaultTimeStampIncrement(10);
 


之后在进行数据发送时只需指明要发送的数据及其长度可以了Q?nbsp;

 sess.SendPacket(buffer, 5); 


 e、对于流媒体数据的接收端Q首先需要调?nbsp;RTPSession cȝ PollData() Ҏ来接收发送过来的 RTP 或?nbsp;RTCP 数据报。由于同一?nbsp;RTP 会话中允许有多个参与者(源)Q你既可以通过调用 RTPSession cȝ GotoFirstSource() ?nbsp;GotoNextSource() Ҏ来遍历所有的源,也可以通过调用 RTPSession cȝ GotoFirstSourceWithData() ?nbsp;GotoNextSourceWithData() Ҏ来遍历那些携带有数据的源。在?nbsp;RTP 会话中检出有效的数据源之后Q接下去可以调?nbsp;RTPSession cȝ GetNextPacket() Ҏ从中抽取 RTP 数据报,当接收到?nbsp;RTP 数据报处理完之后Q一定要记得及时释放?br>
JRTPLIB ?nbsp;RTP 数据报定义了三种接收模式Q其中每U接收模式都具体规定了哪些到辄 RTP 数据报将会被接受Q而哪些到辄 RTP 数据报将会被拒绝。通过调用 RTPSession cȝ SetReceiveMode() Ҏ可以讄下列q些接收模式Q?nbsp;
? RECEIVEMODE_ALL  ~省的接收模式,所有到辄 RTP 数据报都被接受Q?nbsp;
? RECEIVEMODE_IGNORESOME  除了某些特定的发送者之外,所有到辄 RTP 数据报都被接受Q而被拒绝的发送者列表可以通过调用 AddToIgnoreList()、DeleteFromIgnoreList() ?nbsp;ClearIgnoreList() Ҏ来进行设|; 
? RECEIVEMODE_ACCEPTSOME  除了某些特定的发送者之外,所有到辄 RTP 数据报都被拒绝Q而被接受的发送者列表可以通过调用 AddToAcceptList ()、DeleteFromAcceptList ?nbsp;ClearAcceptList () Ҏ来进行设|?nbsp;下面是采用第三种接收模式的程序示例?br> if (sess.GotoFirstSourceWithData()) {   
  do {   
   sess.AddToAcceptList(remoteIP, allports,portbase);
          sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);
 
    RTPPacket *pack;         
    pack = sess.GetNextPacket();            // 处理接收到的数据    
    delete pack;   } 
  while (sess.GotoNextSourceWithData()); 
  }


  Q?Q程序流E图
发送:获得接收端的 IP 地址和端口号        创徏 RTP 会话        指定 RTP 数据接收?nbsp;讄 RTP 会话默认参数   发送流媒体数据
接收Q获得用h定的端口?nbsp; 创徏RTP会话  讄接收模式  接受RTP数据  索RTP数据?nbsp; 获取RTP数据?nbsp; 删除RTP数据?br>

三、环境搭建及~译Ҏ
Q?QToolchain的安?br> 首先扑ֈxscale-arm-toolchain.tgz文gQ假设该文g包放?tmp/?br> #cd /
 #tar -zxvf /tmp/xscale-arm-toolchain.tgz
 再设|环境变?br> #export PATH=/usr/local/arm-linux/bin:$PATH
 最后检查一下交叉编译工h否安装成?br> #arm-linux-g++ --version
 看是否显Carm-linux-g++的版本,如有则安装成功?br>Q?QJRTPLIB 库的交叉~译及安?br> 首先?nbsp;JRTPLIB 的网站(http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmllQ?nbsp;下蝲最新的源码包,此处使用的是jrtplib-2.8.tarQ假设下载后的源码包攑֜/tmp下,?nbsp;行下面的命o对其解压~:
 #cd /tmp
 #tar -zxvf jrtplib-2.8.tar
 然后要对jrtplibq行配置和编?br> #cd jrtplib-2.8
 #./configure CC=arm-linux-g++ cross-compile=yes
 修改Makefile文g
 链接命令ld 和ar改ؓarm-linux-ld?nbsp;arm-linux-ar
 #make
 最后再执行如下命o可以完?nbsp;JRTPLIB 的安装:
 #make install
(3)E序~译
 a、配|编译环?br> 可以用export来配|,也可以用~写Makefile的方法。这里采用Makefile?br> ~写Makefile&:
INCL = -I/usr/local/include
CFLAGS = -pipe -O2 -fno-strength-reduce
LFLAGS = /usr/local/lib/libjrtp.a -L/usr/X11R6/lib
LIBS = -LX11 -LXext /usr/local/lib/libjrtp.a
CC = arm-linux-g++

main:main.o
 $(CC) $(LFLAGS) $(INCL) -o main main.o $(LIBS)
main.o:main.cpp

clean:
 rm -f main
 rm -f *.o
 
.SUFFIXES:.cpp
.cpp.o:
 $(CC) -c $(CFLAGS) $(INCL) -o $@ $<         /*  $@表示目标的完整名?nbsp;     */
          /* $<表示W一个依赖文件的名字 */
 b、编?br> 假设发送和接收E序分别攑֜/tmp/send?tmp/receive目录?br> #cd /tmp/send
 #make
 #cd /tmp/receive
 #make

四、易出错误及注意问题
 1、找不到一些标准的最 基本的一些头文g?br>  主要是因为Toolchain路径没安装对Q要 严格按照步骤安装?br> 2、找不到使用的jrtplib库中的一些头文g?br>  ?nbsp;jrtplib的安装目录下Qinclude路径下不能再有别的目录?br> 3、recieve函数接收数据包不能正提出所要数据?br>  ׃每一个RTP数据报都由头部(HeaderQ和负蝲QPayloadQ两个部分组成,若用getrawdata()是返回整个数据包的数据,包含传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息。getpayload()函数是返回所发送的数据。两者一定要分清?br> 4、设|RECEIVEMODE_ACCEPTSOME  接收模式后,q行E序接收端不能接包?br>  IP地址格式Z问题。iner_addr()与ntohl()函数要用对,否则参数传不q去Q接受列表中无|当然接收不了数据包?br> 5、编译通过Q但试时接收端不能接收到数据?br>  可能是接收机防火墙未关闭。运行:
  #iptables -F
  也可能是IP地址没有讄好。运行:
  #ifocnfig eth0  *.*.*.*  netmask *.*.*.*
 6、用jrtolib库时Q在E序中include 后最好加上库所在的路径?br>五、程?br>
send:

#include <stdio.h>
#include <string.h>
#include "rtpsession.h"

// 错误处理函数
void checkerror(int err)
{
  if (err < 0) {
    char* errstr = RTPGetErrorString(err);
    printf("Error:%s\\n", errstr);
    exit(-1);
  }
}

int main(int argc, char** argv)
{
  RTPSession sess;
  unsigned long destip;
  int destport;
  int portbase = 6000;
  int status, index;
  char buffer[128];

  if (argc != 3) {
    printf("Usage: ./sender destip destport\\n");
    return -1;
  }

  // 获得接收端的IP地址和端口号
  destip = inet_addr(argv[1]);
  if (destip == INADDR_NONE) {
    printf("Bad IP address specified.\\n");
    return -1;
  }
  destip = ntohl(destip);
  destport = atoi(argv[2]);

  // 创徏RTP会话
  status = sess.Create(portbase);
  checkerror(status);

  // 指定RTP数据接收?br>  status = sess.AddDestination(destip, destport);
  checkerror(status);

  // 讄RTP会话默认参数
  sess.SetDefaultPayloadType(0);
  sess.SetDefaultMark(false);
  sess.SetDefaultTimeStampIncrement(10);

  // 发送流媒体数据
  index = 1;
  do {
    sprintf(buffer, "%d: RTP packet", index ++);
    sess.SendPacket(buffer, strlen(buffer));
    printf("Send packet !\\n");
  } while(1);

  return 0;
}






receive:

#include <stdio.h>
#include "rtpsession.h"
#include "rtppacket.h"

// 错误处理函数
void checkerror(int err)
{
  if (err < 0) {
    char* errstr = RTPGetErrorString(err);
    printf("Error:%s\\n", errstr);
    exit(-1);
  }
}

int main(int argc, char** argv)
{
  RTPSession sess;
  int localport,portbase;
  int status;
  unsigned long remoteIP;
  if (argc != 4) {
    printf("Usage: ./sender localport\\n");
    return -1;
  }

   // 获得用户指定的端口号
   
  remoteIP = inet_addr(argv[1]);
  localport = atoi(argv[2]);
  portbase = atoi(argv[3]);
  // 创徏RTP会话
  status = sess.Create(localport);
  checkerror(status);
  
  //RTPHeader *rtphdr;
  unsigned long timestamp1;
  unsigned char * RawData;
  unsigned char temp[30];
  int lengh ,i;
  bool allports = 1;
  
  sess.AddToAcceptList(remoteIP, allports,portbase);
  
     do {
 //讄接收模式
        sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);
   sess.AddToAcceptList(remoteIP, allports,portbase);

    // 接受RTP数据
    status = sess.PollData();

    
 // 索RTP数据?br>    if (sess.GotoFirstSourceWithData()) {
      do {
        
        RTPPacket* packet;
        // 获取RTP数据?br>        while ((packet = sess.GetNextPacket()) != NULL) {
          printf("Got packet !\n");

   timestamp1 = packet->GetTimeStamp();
   lengh=packet->GetPayloadLength();
   RawData=packet->GetPayload();
   
   for(i=0;i<lengh;i++){
      temp[i]=RawData[i];
  printf("%c",temp[i]);
   }
   temp[i]='\0';
   printf("  timestamp: %d lengh=%d data:%s\n",timestamp1,lengh,&temp);
          // 删除RTP数据?br>   
          delete packet;
        }
      } while (sess.GotoNextSourceWithData());
    }
  } while(1);

  return 0;
}



Xiao.Zhu 2007-08-08 16:39 发表评论
]]>
RTPhttp://www.shnenglu.com/zzh/archive/2007/08/08/29577.htmlXiao.ZhuXiao.ZhuWed, 08 Aug 2007 08:38:00 GMThttp://www.shnenglu.com/zzh/archive/2007/08/08/29577.htmlhttp://www.shnenglu.com/zzh/comments/29577.htmlhttp://www.shnenglu.com/zzh/archive/2007/08/08/29577.html#Feedback1http://www.shnenglu.com/zzh/comments/commentRss/29577.htmlhttp://www.shnenglu.com/zzh/services/trackbacks/29577.html媒体指的是在网l中使用技术传输的q箋时基媒体Q其特点是在播放前不需要下载整个文Ӟ而是采用边下载边播放的方式,它是视频会议?span>IP电话{应用场合的技术基?span>RTP是进行实时流媒体传输的标准协议和关键技术,本文介绍如何?span>Linux下利?span>JRTPLIBq行实时媒体编E?nbsp;

  

一、流媒体?/span>
        
随着Internet的日益普及,在网l上传输的数据已l不再局限于文字和图形,而是逐渐向声韛_视频{多媒体格式q渡。目前在|络上传输音?span>/视频Q?span>Audio/VideoQ简U?span>A/VQ等多媒体文件时Q基本上只有下蝲和流式传输两U选择。通常说来Q?span>A/V文g占据的存储空间都比较大,在带宽受限的|络环境中下载可能要耗费数分钟甚x时Q所以这U处理方法的延迟很大。如果换用流式传输的话,声音、媄像、动ȝ多媒体文件将׃门的媒体服务器负责向用戯l、实时地发送,q样用户可以不必{到整个文g全部下蝲完毕Q而只需要经q几U钟的启动g时就可以了,当这些多媒体数据在客h上播放时Q文件的剩余部分l从媒体服务器下蝲?/span>

 

        StreamingQ是q年?span>Internet上出现的新概念,其定义非常广泛,主要是指通过|络传输多媒体数据的技术ȝ。流媒体包含q义和狭义两U内涵:q义上的媒体指的是佉K频和视频形成E_和连l的传输和回放的一pd技术、方法和协议的ȝQ即媒体技术;狭义上的媒体是相对于传l的下蝲-回放方式而言的,指的是一U从Internet上获取音频和视频{多媒体数据的新ҎQ它能够支持多媒体数据流的实时传输和实时播放。通过q用媒体技术,服务器能够向客户机发送稳定和q箋的多媒体数据,客户机在接收数据的同时以一个稳定的速率回放Q而不用等数据全部下蝲完之后再q行回放?/span>

 

׃受网l带宽、计机处理能力和协议规范等斚w的限Ӟ要想?span>Internet上下载大量的音频和视频数据,无论从下载时间和存储I间上来讲都是不太现实的Q而流媒体技术的出现则很好地解决了这一N。目前实现流媒体传输主要有两U方法:序(progressive streamingQ传输和实时(realtime streamingQ传输,它们分别适合于不同的应用场合?/span>

 

序传?/span>

 

        序传输采用顺序下载的方式q行传输Q在下蝲的同时用户可以在U回攑֤媒体数据Q但l定时刻只能观看已经下蝲的部分,不能跛_未下蝲的部分,也不能在传输期间Ҏ|络状况对下载速度q行调整。由于标准的HTTP服务器就可以发送这UŞ式的媒体,而不需要其他特D协议的支持Q因此也常常被称?span>HTTP 式传输。顺序流式传输比较适合于高质量的多媒体片段Q如片头、片或者广告等?/span>

 

实时传?/span>

 

        实时式传输保证媒体信号带宽能够与当前网l状늛匚wQ从而得流媒体数据L被实时地传送,因此特别适合于现Z件。实时流传输支持随机讉KQ即用户可以通过快进或者后退操作来观看前面或者后面的内容。从理论上讲Q实时流媒体一l播攑ְ不会停顿Q但事实上仍有可能发生周期性的暂停现象Q尤其是在网l状冉|化时更是如此。与序传输不同的是,实时传输需要用到特定的媒体服务器Q而且q需要特定网l协议的支持?/span>

 

二、流媒体协议
实时传输协议Q?span>Real-time Transport ProtocolQ?span>PRTQ是?span>Internet上处理多媒体数据的一U网l协议,利用它能够在一对一Q?span>unicastQ单播)或者一对多Q?span>multicastQ多播)的网l环境中实现传流媒体数据的实时传输?span>RTP通常使用UDP来进行多媒体数据的传输,但如果需要的话可以?span>TCP或?span> ATM{其它协议,整个RTP协议׃个密切相关的部分l成Q?span>RTP数据协议?span>RTP控制协议。实时流协议Q?span>Real Time Streaming ProtocolQ?span>RTSPQ最早由Real Networks?span>Netscape公司共同提出Q它位于RTP?span>RTCP之上Q其目的是希望通过IP|络有效C输多媒体数据?/span>

 

2.1 RTP数据协议

 

RTP数据协议负责Ҏ媒体数据q行包q实现媒体流的实时传输,每一?span>RTP数据报都由头部(HeaderQ和负蝲Q?span>PayloadQ两个部分组成,其中头部?span>12个字节的含义是固定的Q而负载则可以是音频或者视频数据?span>RTP数据报的头部格式如图1所C:

 

 
?span>1 RTP头部格式

 

其中比较重要的几个域及其意义如下Q?nbsp;

 

  • CSRC记数Q?span>CCQ?/span>  表示CSRC标识的数目?span>CSRC标识紧跟?span>RTP固定头部之后Q用来表C?span>RTP数据报的来源Q?span>RTP协议允许在同一个会话中存在多个数据源,它们可以通过RTP混合器合qؓ一个数据源。例如,可以产生一?span>CSRC列表来表CZ个电话会议,该会议通过一?span> RTP混合器将所有讲话者的语音数据l合Z?span>RTP数据源?nbsp;
  • 负蝲cdQ?span>PTQ?/span>  标明RTP负蝲的格式,包括所采用的编码算法、采样频率、承载通道{。例如,cd2表明?span>RTP数据包中承蝲的是?span>ITU G.721法~码的语x据,采样频率?span>8000HzQƈ且采用单声道?nbsp;
  • 序列?/span>  用来为接收方提供探测数据丢失的方法,但如何处理丢q数据则是应用E序自己的事情,RTP协议本nq不负责数据的重传?nbsp;
  • 旉?/span>  记录了负载中W一个字节的采样旉Q接收方能够旉戌够确定数据的到达是否受到了gq抖动的影响Q但具体如何来补偿gq抖动则是应用程序自q事情?nbsp;

?span>RTP 数据报的格式不难看出Q它包含了传输媒体的cd、格式、序列号、时间戳以及是否有附加数据等信息Q这些都为实时的媒体传输提供了相应的基?span>RTP协议的目的是提供实时数据Q如交互式的音频和视频)的端到端传输服务Q因此在RTP中没有连接的概念Q它可以建立在底层的面向q接或面向非q接的传输协议之上;RTP也不依赖于特别的|络地址格式Q而仅仅只需要底层传输协议支持组帧(FramingQ和分段Q?span>SegmentationQ就_了;另外RTP 本nq不提供M可靠性机Ӟq些都要׃输协议或者应用程序自己来保证。在典型的应用场合下Q?span>RTP 一般是在传输协议之上作为应用程序的一部分加以实现的,如图2所C:

 


?span>2 RTP与各U网l协议的关系

 

2.2 RTCP控制协议

 

RTCP 控制协议需要与RTP数据协议一起配合用,当应用程序启动一?span>RTP会话时将同时占用两个端口Q分别供RTP ?span>RTCP使用?span>RTP本nq不能ؓ按序传输数据包提供可靠的保证Q也不提供流量控制和拥塞控制Q这些都?span>RTCP来负责完成。通常RTCP会采用与 RTP相同的分发机Ӟ向会话中的所有成员周期性地发送控制信息,应用E序通过接收q些数据Q从中获取会话参与者的相关资料Q以及网l状c分l丢失概率等反馈信息Q从而能够对服务质量q行控制或者对|络状况q行诊断?/span>

 

RTCP协议的功能是通过不同?span>RTCP数据报来实现的,主要有如下几U类型: 

 

  • SR  发送端报告Q所谓发送端是指发出RTP数据报的应用E序或者终端,发送端同时也可以是接收端?nbsp;
  • RR  接收端报告,所谓接收端是指仅接收但不发?span>RTP数据报的应用E序或者终端?nbsp;
  • SDES  源描qͼ主要功能是作Z话成员有x识信息的载体Q如用户名、邮件地址、电话号码等Q此外还h向会话成员传达会话控制信息的功能?nbsp;
  • BYE  通知dQ主要功能是指示某一个或者几个源不再有效Q即通知会话中的其他成员自己退Z话?nbsp;
  • APP  由应用程序自己定义,解决?span>RTCP的扩展性问题,q且为协议的实现者提供了很大的灵zL?nbsp;

RTCP数据报携带有服务质量监控的必要信息,能够Ҏ务质量进行动态的调整Qƈ能够对网l拥塞进行有效的控制。由?span>RTCP数据报采用的是多播方式,因此会话中的所有成员都可以通过RTCP数据报返回的控制信息Q来了解其他参与者的当前情况?/span>

 

在一个典型的应用场合下,发送媒体流的应用程序将周期性地产生发送端报告SRQ该RTCP数据报含有不同媒体流间的同步信息Q以及已l发送的数据报和字节的计敎ͼ接收端根据这些信息可以估计出实际的数据传输速率。另一斚wQ接收端会向所有已知的发送端发送接收端报告RRQ该RTCP数据报含有已接收数据报的最大序列号、丢q数据报数目、g时抖动和旉戳等重要信息Q发送端应用Ҏq些信息可以估计出往q时Ӟq且可以Ҏ数据报丢失概率和时g抖动情况动态调整发送速率Q以改善|络拥塞状况Q或者根据网l状况^滑地调整应用E序的服务质量?/span>

 

2.3 RTSP实时协?/span>

 

作ؓ一个应用层协议Q?span>RTSP提供了一个可供扩展的框架Q它的意义在于得实时流媒体数据的受控和Ҏ变得可能。ȝ说来Q?span>RTSP是一个流媒体表示协议Q主要用来控制具有实时特性的数据发送,但它本nq不传输数据Q而是必须依赖于下层传输协议所提供的某些服务?span>RTSP 可以Ҏ媒体提供诸如播放、暂停、快q等操作Q它负责定义具体的控制消息、操作方法、状态码{,此外q描qC?span>RTP间的交互操作?/span>

 

RTSP 在制定时较多地参考了HTTP/1.1协议Q甚臌多描qCHTTP/1.1完全相同?span>RTSP之所以特意用与HTTP/1.1cM的语法和操作Q在很大E度上是Z兼容现有?span>Web基础l构Q正因如此,HTTP/1.1的扩展机制大都可以直接引入到RTSP 中?/span>

 

?span>RTSP 控制的媒体流集合可以用表C描qͼPresentation DescriptionQ来定义Q所谓表C是指流媒体服务器提供给客户机的一个或者多个媒体流的集合,而表C描q则包含了一个表CZ各个媒体的相关信息Q如数据~码/解码法、网l地址、媒体流的内容等?/span>

 

虽然RTSP服务器同样也使用标识W来区别每一连接会话(SessionQ,?span>RTSPq接q没有被l定C输层q接Q如TCP{)Q也是说在整个 RTSPq接期间Q?span>RTSP用户可打开或者关闭多个对RTSP服务器的可靠传输q接以发?span>RTSP h。此外,RTSPq接也可以基于面向无q接的传输协议(?span>UDP{)?/span>

 

RTSP协议目前支持以下操作Q?nbsp;

 

  • 索媒?/span>  允许用户通过HTTP或者其它方法向媒体服务器提交一个表C描q。如表示是组播的Q则表示描述包含用于该媒体的l播地址和端口号Q如果表C是单播的,Z安全在表C描qC应该只提供目的地址?nbsp;
  • 邀请加?/span>  媒体服务器可以被邀请参加正在进行的会议Q或者在表示中回攑֪体,或者在表示中录制全部媒体或其子集,非常适合于分布式教学?nbsp;
  • d媒体  通知用户新加入的可利用媒体流Q这对现座来讲显得尤其有用。与HTTP/1.1cMQ?span>RTSPh也可以交׃理、通道或者缓存来q行处理?nbsp;

三、流媒体~程 
RTP 
是目前解x媒体实时传输问题的最好办法,如果需要在Linuxq_上进行实时流媒体~程Q可以考虑使用一些开放源代码?span>RTP库,?span>LIBRTP?span> JRTPLIB{?span>JRTPLIB是一个面向对象的RTP库,它完全遵?span>RFC 1889设计Q在很多场合下是一个非怸错的选择Q下面就?span>JRTPLIBZQ讲q如何在Linuxq_上运?span>RTP协议q行实时媒体编E?/span>

 

3.1 环境搭徏

 

JRTPLIB 是一个用C++语言实现?span>RTP库,目前已经可以q行?span>Windows?span>Linux?span>FreeBSD?span> Solaris?span>Unix?span>VxWorks{多U操作系l上。要?span>Linux pȝ安装JRTPLIBQ首先从JRTPLIB的网站(http: //lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmlQ下载最新的源码包,此处使用的是jrtplib- 2.7b.tar.bz2。假设下载后的源码包保存?span>/usr/local/src目录下,执行下面的命令可以对其进行解压羃Q?/span>

 



[root@linuxgam src]# bzip2 -dc jrtplib-2.7b.tar.bz2 | tar xvf -

 



Xiao.Zhu 2007-08-08 16:38 发表评论
]]>
进E变成一个线E执行代?/title><link>http://www.shnenglu.com/zzh/archive/2007/08/08/29574.html</link><dc:creator>Xiao.Zhu</dc:creator><author>Xiao.Zhu</author><pubDate>Wed, 08 Aug 2007 08:23:00 GMT</pubDate><guid>http://www.shnenglu.com/zzh/archive/2007/08/08/29574.html</guid><wfw:comment>http://www.shnenglu.com/zzh/comments/29574.html</wfw:comment><comments>http://www.shnenglu.com/zzh/archive/2007/08/08/29574.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.shnenglu.com/zzh/comments/commentRss/29574.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zzh/services/trackbacks/29574.html</trackback:ping><description><![CDATA[     摘要:     1  2//*******************************************************************************************************   3// loadEXE.cpp : Defines&nb...  <a href='http://www.shnenglu.com/zzh/archive/2007/08/08/29574.html'>阅读全文</a><img src ="http://www.shnenglu.com/zzh/aggbug/29574.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zzh/" target="_blank">Xiao.Zhu</a> 2007-08-08 16:23 <a href="http://www.shnenglu.com/zzh/archive/2007/08/08/29574.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>中国职员的九U劣Ҏ?/title><link>http://www.shnenglu.com/zzh/archive/2007/07/12/27900.html</link><dc:creator>Xiao.Zhu</dc:creator><author>Xiao.Zhu</author><pubDate>Thu, 12 Jul 2007 04:46:00 GMT</pubDate><guid>http://www.shnenglu.com/zzh/archive/2007/07/12/27900.html</guid><wfw:comment>http://www.shnenglu.com/zzh/comments/27900.html</wfw:comment><comments>http://www.shnenglu.com/zzh/archive/2007/07/12/27900.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.shnenglu.com/zzh/comments/commentRss/27900.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/zzh/services/trackbacks/27900.html</trackback:ping><description><![CDATA[     摘要: 中国Z是文人相轻,而是Zh相轻Q只要想轻视别hQL相轻的理由。比如北京h轻视外地人,上v视外ChQ城里h轻视农村人,南方视北方hQ有׃h轻视IhQ开车的轻视走\的,走\的轻视扫路的Q吃饭的轻视做饭的……就是不会相互尊重?nbsp; <a href='http://www.shnenglu.com/zzh/archive/2007/07/12/27900.html'>阅读全文</a><img src ="http://www.shnenglu.com/zzh/aggbug/27900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/zzh/" target="_blank">Xiao.Zhu</a> 2007-07-12 12:46 <a href="http://www.shnenglu.com/zzh/archive/2007/07/12/27900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深圳http://www.shnenglu.com/zzh/archive/2007/06/20/26730.htmlXiao.ZhuXiao.ZhuWed, 20 Jun 2007 15:12:00 GMThttp://www.shnenglu.com/zzh/archive/2007/06/20/26730.htmlhttp://www.shnenglu.com/zzh/comments/26730.htmlhttp://www.shnenglu.com/zzh/archive/2007/06/20/26730.html#Feedback0http://www.shnenglu.com/zzh/comments/commentRss/26730.htmlhttp://www.shnenglu.com/zzh/services/trackbacks/26730.html  个个都说深圳好,个个都往深圳跑;深圳挣钱深圳花,哪有钞票寄回家?
  都说q里工资高,x没钱买牙膏;都说q里伙食好,青菜里面加青草?
  都说q里环境好,蟑螂蚂蚁四处跑;都说q里领班帅,个个q_像锅盖?
  q年打工q年愁,天天加班像只_加班加点无报酬,天天挨骂无理由?
  见老板低着_发了工资摇摇_C月尾发愁,不知何年才出头?
  |湖的美奻I田的汉Q布吉街的痞子满街串Q南q花,西丽的草Q仙湖里的和满街跑?br>         华L城的帅哥Q沙头角的狼Q皇岗到处是氓Q老成都的饭,彭年的床Q岗厦的女生吓死郎?br>         盐田的田Q大鹏湾的湾Q小梅沙的男奛_疯颠。南头关的痴Q梅林关的怨,|湖关的情G香港转?br>         永的夜Ԍ村ֲ的ؕQ公明的奛_没男伴。西乡的土,沙井的苦Q宝安的男h心里c?br>         兛_的偷,兛_的抢深圳的治安没法讲


Xiao.Zhu 2007-06-20 23:12 发表评论
]]>
串口http://www.shnenglu.com/zzh/archive/2007/06/16/26461.htmlXiao.ZhuXiao.ZhuSat, 16 Jun 2007 15:25:00 GMThttp://www.shnenglu.com/zzh/archive/2007/06/16/26461.htmlhttp://www.shnenglu.com/zzh/comments/26461.htmlhttp://www.shnenglu.com/zzh/archive/2007/06/16/26461.html#Feedback0http://www.shnenglu.com/zzh/comments/commentRss/26461.htmlhttp://www.shnenglu.com/zzh/services/trackbacks/26461.html 

无论那种操作方式Q一般都通过四个步骤来完成:
Q?span>1
Q?打开串口

Win32pȝ把文件的概念q行了扩展。无论是文g、通信讑֤、命名管道、邮件槽、磁盘、还是控制台Q都是用API函数CreateFile来打开或创建的。该函数的原型ؓQ?/span>

HANDLE CreateFile( LPCTSTR lpFileName, 
                
DWORD dwDesiredAccess,  
               
DWORD dwShareMode,    
              
LPSECURITY_ATTRIBUTES lpSecurityAttributes, 
              
DWORD dwCreationDistribution,
               DWORD dwFlagsAndAttributes,
               HANDLE hTemplateFile);

·           lpFileNameQ将要打开的串口逻辑名,?span>“COM1”Q?/span>

·           dwDesiredAccessQ指定串口访问的cdQ可以是d、写入或二者ƈ列;

·           dwShareModeQ指定共享属性,׃串口不能׃nQ该参数必须|ؓ0Q?/span>

·           lpSecurityAttributesQ引用安全性属性结构,~省gؓNULLQ?/span>

·           dwCreationDistributionQ创建标志,对串口操作该参数必须|ؓOPEN_EXISTINGQ?/span>

·           dwFlagsAndAttributesQ属性描qͼ用于指定该串口是否进行异步操作,该gؓFILE_FLAG_OVERLAPPEDQ表CZ用异步的I/OQ该gؓ0Q表C同?span>I/O操作Q?/span>

·           hTemplateFileQ对串口而言该参数必ȝ?span>NULLQ?/span>

同步I/O方式打开串口的示例代码:

HANDLE hCom; //全局变量Q串口句?br>hCom=CreateFile("COM1",//COM1?span>         
  
GENERIC_READ|GENERIC_WRITE, //
允许d?span>         
    
0, //独占方式         
 
      NULL,   
    
OPEN_EXISTING, //
打开而不是创?span>             
  
   0, //同步方式       
  
NULL);    
if(hCom==(HANDLE)-1)  
{              
         AfxMessageBox("
打开COMp|!");    
   
     return FALSE; 
}  
    
return TRUE;

重叠I/O打开串口的示例代码:

HANDLE hCom; //全局变量Q串口句?span> 
hCom =CreateFile("COM1", //COM1
?span>          
  
GENERIC_READ|GENERIC_WRITE, //允许d?span>     
       
0, //独占方式            
       
NULL,           
  
   OPEN_EXISTING, //
打开而不是创?span>       
  
    FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式 
     
NULL);
 
if(hCom ==INVALID_HANDLE_VALUE)     
{           
          
AfxMessageBox("
打开COMp|!"); 
          
return FALSE; 
}        
 
return TRUE;

Q?span>2Q?a name=配置串口>配置串口

在打开通讯讑֤句柄后,常常需要对串口q行一些初始化配置工作。这需要通过一?span>DCBl构来进行?span>DCBl构包含了诸如L特率、数据位数、奇偶校验和停止位数{信息。在查询或配|串口的属性时Q都要用DCBl构来作为缓冲区?span>
  一般用CreateFile打开串口后,可以调用GetCommState函数来获取串口的初始配置。要修改串口的配|,应该先修?span>DCBl构Q然后再调用SetCommState函数讄串口?span>
  DCBl构包含了串口的各项参数讄Q下面仅介绍几个该结构常用的变量Q?/span>

typedef struct _DCB{ 
 
………   //
波特率,指定通信讑֤的传输速率。这个成员可以是实际波特率值或者下面的帔Rg一Q?br>   DWORD BaudRate;
CBR_110
Q?br>CBR_300Q?br>CBR_600Q?br>CBR_1200Q?br>CBR_2400Q?span>
CBR_4800
Q?br>CBR_9600Q?br>CBR_19200Q?br>CBR_38400Q?br>CBR_56000Q?br>CBR_57600Q?span>
CBR_115200Q?span>
CBR_128000Q?span>
CBR_256000Q?span>
CBR_14400DWORD fParity; // 指定奇偶校验使能。若此成员ؓ1Q允许奇偶校验检?span>   
…BYTE ByteSize; // 通信字节位数Q?span>4?BYTE
Parity; //指定奇偶校验Ҏ。此成员可以有下列|EVENPARITY 偶校?span>     NOPARITY 无校?span>MARKPARITY 标记校验   ODDPARITY 奇校?span>BYTE
StopBits; //指定停止位的位数?br>此成员可以有下列|
ONESTOPBIT 1位停止位  
TWOSTOPBITS 2
位停止位
ONE5STOPBITS   1.5
位停止位   ……… } DCB;
winbase.h
文g中定义了以上用到的常量?br>如下Q?br>#define NOPARITY            0
#define ODDPARITY           1
#define EVENPARITY          2
#define ONESTOPBIT          0
#define ONE5STOPBITS        1
#define TWOSTOPBITS         2
#define CBR_110             110
#define CBR_300             300
#define CBR_600             600
#define CBR_1200            1200
#define CBR_2400            2400
#define CBR_4800            4800
#define CBR_9600            9600
#define CBR_14400           14400
#define CBR_19200           19200
#define CBR_38400           38400
#define CBR_56000           56000
#define CBR_57600           57600
#define CBR_115200          115200
#define CBR_128000          128000
#define CBR_256000          256000

GetCommState函数可以获得COM口的讑֤控制块,从而获得相兛_敎ͼ

BOOL GetCommState(   HANDLE hFile, //标识通讯端口的句?span>  
LPDCB lpDCB //
指向一个设备控制块Q?span>DCBl构Q的指针 );
SetCommState
函数讄COM口的讑֤控制块:
BOOL SetCommState(   HANDLE hFile,    LPDCB lpDCB   );

除了?span>BCD中的讄外,E序一般还需要设|?span>I/O~冲区的大小和超时?span>Windows?span>I/O~冲区来暂存串口输入和输出的数据。如果通信的速率较高Q则应该讄较大的缓冲区。调?span>SetupComm函数可以讄串行口的输入和输出缓冲区的大?/span>

BOOL SetupComm(    HANDLE hFile,    // 通信讑֤的句?span>    
DWORD dwInQueue, //
输入~冲区的大小Q字节数Q?span>    
DWORD dwOutQueue   // 输出~冲区的大小Q字节数Q?span>   );

在用ReadFile?span>WriteFiled串行口时Q需要考虑时问题。超时的作用是在指定的时间内没有d或发送指定数量的字符Q?span>ReadFile?span>WriteFile的操作仍然会l束?span>
  要查询当前的时讄应调?span>GetCommTimeouts函数Q该函数会填充一?span>COMMTIMEOUTSl构。调?span>SetCommTimeouts可以用某一?span>COMMTIMEOUTSl构的内Ҏ讄时?span>
  d串口的超时有两种Q间隔超时和总超时。间隔超时是指在接收时两个字W之间的最大时延。总超时是指读写操作dp的最大时间。写操作只支持总超Ӟ而读操作两种时均支持。用COMMTIMEOUTSl构可以规定d操作的超时?span>
COMMTIMEOUTSl构的定义ؓQ?/span>

typedef struct _COMMTIMEOUTS {      
DWORD ReadIntervalTimeout; //
读间隔超?span>   
DWORD ReadTotalTimeoutMultiplier; //
L间系?span>   
DWORD ReadTotalTimeoutConstant; //L间常?span>   
DWORD WriteTotalTimeoutMultiplier; // 写时间系?span>   
DWORD WriteTotalTimeoutConstant; //写时间常?span>
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

COMMTIMEOUTSl构的成员都以毫Uؓ单位。总超时的计算公式是:
总超Ӟ旉pL×要求?span>/
写的字符敎ͼ旉帔R
例如Q要d10个字W,那么L作的总超时的计算公式为:
L超ӞReadTotalTimeoutMultiplier×10Q?span>ReadTotalTimeoutConstant
可以看出Q间隔超时和总超时的讄是不相关的,q可以方侉K信E序灉|地设|各U超时?span>

如果所有写时参数均ؓ0Q那么就不用写时。如?span>ReadIntervalTimeout?span>0Q那么就不用读间隔时。如?span>ReadTotalTimeoutMultiplier ?span> ReadTotalTimeoutConstant 都ؓ0Q则不用读总超时。如果读间隔时被设|成MAXDWORDq且L间系数和L间常量都?span>0Q那么在Mơ输入缓冲区的内容后L作就立即q回Q而不是否读入了要求的字W?span>
  在用重叠方式d串口Ӟ虽然ReadFile?span>WriteFile在完成操作以前就可能q回Q但时仍然是v作用的。在q种情况下,时规定的是操作的完成时_而不?span>ReadFile?span>WriteFile的返回时间?span>
配置串口的示例代码:

SetupComm(hCom,1024,1024); //输入~冲区和输出~冲区的大小都是1024
COMMTIMEOUTS TimeOuts;     //
讑֮读超?span>  
TimeOuts.ReadIntervalTimeout=1000;    
TimeOuts.ReadTotalTimeoutMultiplier=500;     
TimeOuts.ReadTotalTimeoutConstant=5000;     //
讑֮写超?span>   T
imeOuts.WriteTotalTimeoutMultiplier=500;    
TimeOuts.WriteTotalTimeoutConstant=2000;     
SetCommTimeouts(hCom,&TimeOuts); //讄时
DCB dcb;      
GetCommState(hCom,&dcb);      
dcb.BaudRate=9600; //
波特率ؓ9600    
dcb.ByteSize=8; //
每个字节?span>8?span>     
dcb.Parity=NOPARITY; //无奇偶校验位    
dcb.StopBits=TWOSTOPBITS; //
两个停止?span>       
SetCommState(hCom,&dcb);    
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

在读写串口之前,q要?span>PurgeComm()函数清空~冲区,该函数原型:

BOOL PurgeComm(    HANDLE hFile,    //串口句柄    
                                    DWORD dwFlags    //
需要完成的操作  
);

参数dwFlags指定要完成的操作Q可以是下列值的l合Q?/span>

PURGE_TXABORT        中断所有写操作q立卌回,即写操作还没有完成?span>PURGE_RXABORT       中断所有读操作q立卌回,即L作还没有完成?span>PURGE_TXCLEAR      清除输出~冲?span>PURGE_RXCLEAR        清除输入~冲?/span>

Q?span>3Q?a name=d串口>d串口

我们使用ReadFile?span>WriteFiled串口Q下面是两个函数的声明:

BOOL ReadFile(    HANDLE hFile,     //串口的句?span>        // d的数据存储的地址Q?span>    // 卌入的数据存储在以该指针的gؓ首地址的一片内存区   
LPVOID lpBuffer,         
DWORD nNumberOfBytesToRead,       //
要读入的数据的字节数        // 指向一?span>DWORD数|该数D回读操作实际d的字节数   
LPDWORD lpNumberOfBytesRead,             //
重叠操作Ӟ该参数指向一?span>OVERLAPPEDl构Q同步操作时Q该参数?span>NULL?span>   
LPOVERLAPPED lpOverlapped        );    
BOOL WriteFile(    HANDLE hFile,      //串口的句?span>            // 即以该指针的gؓ首地址?span>nNumberOfBytesToWrite    // 个字节的数据要写入串口的发送数据缓冲区?span>   
LPCVOID lpBuffer,       // 写入的数据存储的地址Q?/span>       
DWORD nNumberOfBytesToWrite, //
要写入的数据的字节数        // 指向指向一?span>DWORD数|该数D回实际写入的字节?span>   
LPDWORD lpNumberOfBytesWritten,             // 重叠操作Ӟ该参数指向一?span>OVERLAPPEDl构Q?span>    // 同步操作Ӟ该参CؓNULL?span>  
 
LPOVERLAPPED lpOverlapped        );

在用ReadFile?span>WriteFiled串口Ӟ既可以同步执行,也可以重叠执行。在同步执行Ӟ函数直到操作完成后才q回。这意味着同步执行时线E会被阻塞,从而导致效率下降。在重叠执行Ӟ即操作q未完成Q这两个函数也会立即q回Q费时的I/O操作在后台进行?span>
  ReadFile?span>WriteFile函数是同步还是异步由CreateFile函数军_Q如果在调用CreateFile创徏句柄时指定了FILE_FLAG_OVERLAPPED标志Q那么调?span>ReadFile?span>WriteFile对该句柄q行的操作就应该是重叠的Q如果未指定重叠标志Q则d操作应该是同步的?span>ReadFile?span>WriteFile函数的同步或者异步应该和CreateFile函数怸致?span>
  ReadFile函数只要在串口输入缓冲区中读入指定数量的字符Q就完成操作。?span>WriteFile函数不但要把指定数量的字W拷入到输出~冲区,而且要等q些字符从串行口送出d才算完成操作?span>
  如果操作成功Q这两个函数都返?span>TRUE。需要注意的是,?span>ReadFile?span>WriteFileq回FALSEӞ不一定就是操作失败,U程应该调用GetLastError函数分析q回的结果。例如,在重叠操作时如果操作q未完成函数p回,那么函数p?span>FALSEQ而且GetLastError函数q回ERROR_IO_PENDING。这说明重叠操作q未完成?span>

同步方式d串口比较单,下面先例丑֐步方式读写串口的代码Q?/span>

//同步M?span>char str[100];
DWORD wCount;//
d的字节数
BOOL bReadStat;
bReadStat=ReadFile(hCom,str,100,&wCount,NULL);
if(!bReadStat){  
AfxMessageBox("
M口失?span>!");
return FALSE;
}
return TRUE;
//同步写串?span>       
char lpOutBuffer[100];
DWORD dwBytesWrite=100;  
COMSTAT ComStat;      
DWORD dwErrorFlags;   
BOOL bWriteStat;    
ClearCommError(hCom,&dwErrorFlags,&ComStat);    
bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);    
if(!bWriteStat) { 
            
AfxMessageBox("写串口失?span>!");
 }      
PurgeComm(hCom, PURGE_TXABORT|              PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);

在重叠操作时,操作q未完成函数p回?span>

  重叠I/O非常灉|Q它也可以实现阻塞(例如我们可以讄一定要dC个数据才能进行到下一步操作)。有两种Ҏ可以{待操作完成Q一U方法是用象WaitForSingleObjectq样的等待函数来{待OVERLAPPEDl构?span>hEvent成员Q另一U方法是调用GetOverlappedResult函数{待Q后面将演示说明?span>
下面我们先简单说一?span>OVERLAPPEDl构?span>GetOverlappedResult函数Q?span>
OVERLAPPEDl构
OVERLAPPED
l构包含了重?span>I/O的一些信息,定义如下Q?/span>

typedef struct _OVERLAPPED { // o      DWORD Internal;     DWORD InternalHigh;     DWORD Offset;     DWORD OffsetHigh;     HANDLE hEvent; } OVERLAPPED;

在?span>ReadFile?span>WriteFile重叠操作ӞU程需要创?span>OVERLAPPEDl构以供q两个函C用。线E通过OVERLAPPEDl构获得当前的操作状态,该结构最重要的成员是hEvent?span>hEvent是读写事件。当串口使用异步通讯Ӟ函数q回时操作可能还没有完成Q程序可以通过查该事g得知是否d完毕?span>
  当调?span>ReadFile, WriteFile 函数的时候,该成员会自动被置为无信号状态;当重叠操作完成后Q该成员变量会自动被|ؓ有信L态?/span>

GetOverlappedResult函数BOOL GetOverlappedResult(    HANDLE hFile,        // 串口的句?span>          // 指向重叠操作开始时指定?span>OVERLAPPEDl构    LPOVERLAPPED lpOverlapped,         // 指向一?span>32位变量,该变量的D回实际读写操作传输的字节数?span>    LPDWORD lpNumberOfBytesTransferred,             // 该参数用于指定函数是否一直等到重叠操作结束?span>    // 如果该参CؓTRUEQ函数直到操作结束才q回?span>    // 如果该参CؓFALSEQ函数直接返回,q时如果操作没有完成Q?span>    // 通过调用GetLastError()函数会返?span>ERROR_IO_INCOMPLETE?span>    BOOL bWait    );

该函数返回重叠操作的l果Q用来判断异步操作是否完成,它是通过判断OVERLAPPEDl构中的hEvent是否被置位来实现的?span>

异步M口的CZ代码Q?/span>

char lpInBuffer[1024];
DWORD dwBytesRead=1024;
COMSTAT ComStat;
DWORD dwErrorFlags;
OVERLAPPED m_osRead;
memset(&m_osRead,0,sizeof(OVERLAPPED));
m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
ClearCommError(hCom,&dwErrorFlags,&ComStat);
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue);
if(!dwBytesRead)
return FALSE;
BOOL bReadStatus;
bReadStatus=ReadFile(hCom,
lpInBuffer,                               
dwBytesRead,
&dwBytesRead,
&m_osRead);
if(!bReadStatus) //
如果ReadFile函数q回FALSE
{    
if(GetLastError()==ERROR_IO_PENDING) //GetLastError()
函数q回ERROR_IO_PENDING,表明串口正在q行L?span>  
{             
WaitForSingleObject(m_osRead.hEvent,2000);         //
使用WaitForSingleObject函数{待Q直到读操作完成或g时已辑ֈ2U钟        //当串口读操作q行完毕后,m_osRead?span>hEvent事g会变为有信号              
PurgeComm(hCom, PURGE_TXABORT|                      PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);  
 
     return dwBytesRead;   
}      
return 0;
}
PurgeComm(hCom, PURGE_TXABORT|               PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);
return dwBytesRead;

对以上代码再作简要说明:在?span>ReadFile 函数q行L作前Q应先?span>ClearCommError函数清除错误?span>ClearCommError函数的原型如下:

BOOL ClearCommError(    HANDLE hFile,      // 串口句柄   
LPDWORD lpErrors,      //
指向接收错误码的变量   
LPCOMSTAT lpStat //
指向通讯状态缓冲区  
);

该函数获得通信错误q报告串口的当前状态,同时Q该函数清除串口的错误标志以便l输入、输出操作?span>
参数lpStat指向一?span>COMSTATl构Q该l构q回串口状态信息?span> COMSTATl构 COMSTATl构包含串口的信息,l构定义如下Q?/span>

typedef struct _COMSTAT { // cst     
DWORD fCtsHold : 1; // Tx waiting for CTS signal    
DWORD fDsrHold : 1;   // Tx waiting for DSR signal    
DWORD fRlsdHold : 1; // Tx waiting for RLSD signal    
DWORD fXoffHold : 1; // Tx waiting, XOFF char rec''d    
DWORD fXoffSent : 1; // Tx waiting, XOFF char sent    
DWORD fEof : 1;       // EOF character sent    
DWORD fTxim : 1;      // character waiting for Tx    
DWORD fReserved : 25; // reserved    
DWORD cbInQue;        // bytes in input buffer    
DWORD cbOutQue;       // bytes in output buffer
} COMSTAT, *LPCOMSTAT;

本文只用CcbInQue成员变量Q该成员变量的g表输入缓冲区的字节数?span>

  最后用PurgeComm函数清空串口的输入输出缓冲区?/span>

q段代码?span>WaitForSingleObject函数来等?span>OVERLAPPEDl构?span>hEvent成员Q下面我们再演示一D调?span>GetOverlappedResult函数{待的异步读串口CZ代码Q?/span>

char lpInBuffer[1024];
DWORD dwBytesRead=1024;     
BOOL bReadStatus;     
DWORD dwErrorFlags;      
COMSTAT ComStat;
OVERLAPPED m_osRead;    
ClearCommError(hCom,&dwErrorFlags,&ComStat); 
if(!ComStat.cbInQue)           return 0;
dwBytesRead=min(dwBytesRead,(DWORD)ComStat.cbInQue); 
bReadStatus=ReadFile(hCom,
lpInBuffer,
dwBytesRead,            
&dwBytesRead,
&m_osRead);      
if(!bReadStatus) //
如果ReadFile函数q回FALSE     
{             
if(GetLastError()==ERROR_IO_PENDING)         
{             
GetOverlappedResult(hCom,                         
&m_osRead,&dwBytesRead,TRUE);           // GetOverlappedResult
函数的最后一个参数设?span>TRUE
Q?span>           //函数会一直等待,直到L作完成或׃错误而返回?span>                   return dwBytesRead;           
}             
return 0;     
}      
return dwBytesRead;

异步写串口的CZ代码Q?/span>

char buffer[1024];
DWORD dwBytesWritten=1024;      
DWORD dwErrorFlags;   
COMSTAT ComStat;
OVERLAPPED m_osWrite;      
BOOL bWriteStat;    
bWriteStat=WriteFile(hCom,buffer,dwBytesWritten,           &dwBytesWritten,&m_OsWrite);  
if(!bWriteStat) {           
if(GetLastError()==ERROR_IO_PENDING)         
{                  
WaitForSingleObject(m_osWrite.hEvent,1000);                 
return dwBytesWritten; 
}            
return 0;     
}      
return dwBytesWritten;

Q?span>4Q?a name=关闭串口>关闭串口

利用API函数关闭串口非常单,只需使用CreateFile函数q回的句柄作为参数调?span>CloseHandle卛_Q?/span>

BOOL CloseHandle(    HANDLE hObject; //handle to object to close );

串口~程的一个实?/span>

Z让您更好地理解串口编E?span>,下面我们分别~写两个例程Q见附带的源码部分),q两个例E都实现了工控机与百ҎCZA表通过RS485接口q行的串口通信。其中第一个例E采用同步串口操?span>,W二个例E采用异步串口操作?span>
  我们只介lY仉分,RS485接口接线Ҏ不作介绍Q感兴趣的读者可以查阅相兌料?/span>

例程1

打开VC++6.0Q新建基于对话框的工E?span>RS485CommQ在d话框H口IDD_RS485COMM_DIALOG上添加两个按钮,ID分别?span>IDC_SEND?span>IDC_RECEIVEQ标题分别ؓ发?span>”?span>“接收Q添加一个静态文本框IDC_DISPQ用于显CZ口接收到的内宏V?span>

?span>RS485CommDlg.cpp文g中添加全局变量Q?/span>

HANDLE hCom; //全局变量Q串口句?/span>

?span>RS485CommDlg.cpp文g中的OnInitDialog()函数d如下代码Q?/span>

// TODO: Add extra initialization here    
hCom=CreateFile("COM1",//COM1
?span>           
GENERIC_READ|GENERIC_WRITE, //
允许d?span>             
0, //独占方式         
NULL,       
OPEN_EXISTING, //
打开而不是创?span>              
0, //同步方式         
NULL);    
if(hCom==(HANDLE)-1)  
{             
AfxMessageBox("
打开COMp|!");           
return FALSE; 
}      
SetupComm(hCom,100,100); //
输入~冲区和输出~冲区的大小都是1024    
COMMTIMEOUTS TimeOuts; //
讑֮读超?span>  
TimeOuts.ReadIntervalTimeout=MAXDWORD;    
TimeOuts.ReadTotalTimeoutMultiplier=0;       
TimeOuts.ReadTotalTimeoutConstant=0;     //在读一ơ输入缓冲区的内容后L作就立即q回Q?span>        //而不是否读入了要求的字W?span>     //讑֮写超?span>   TimeOuts.WriteTotalTimeoutMultiplier=100;    
TimeOuts.WriteTotalTimeoutConstant=500;      
SetCommTimeouts(hCom,&TimeOuts); //讄时
DCB dcb;      
GetCommState(hCom,&dcb);      
dcb.BaudRate=9600; //
波特率ؓ9600    
dcb.ByteSize=8; //
每个字节?span>8?span>     
dcb.Parity=NOPARITY; //无奇偶校验位    
dcb.StopBits=TWOSTOPBITS; //
两个停止?span>       
SetCommState(hCom,&dcb);    
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

分别双击IDC_SEND按钮?span>IDC_RECEIVE按钮Q添加两个按钮的响应函数Q?/span>

void CRS485CommDlg::OnSend() {     
// TODO: Add your control notification handler code here    
//
在此需要简单介l百特公?span>XMA5000的通讯协议Q?span>      
//该A?span>RS485通讯采用Lq播方式通讯?span>    
//串行半双工,?span>11位,1个v始位(0)Q?span>8个数据位Q?span>2个停止位(1)      
//
如:MA表显C的瞬时|L发送:DC1 AAA BB ETX  
//
其中Q?span>DC1是标?span>ASCII码的一个控制符P码gؓ11H(十进制的17)    
//
?span>XMA5000的通讯协议中,DC1表示ȝ时?span>   
//AAA是从机地址码,也就?span>XMA5000昄仪表的通讯地址 
//BB
为通道Pȝ时值时该gؓ01    
//ETX
也是标准ASCII码的一个控制符P码gؓ03H     
//
?span>XMA5000的通讯协议中,ETX表示Ll束W?span> 
char lpOutBuffer[7];    
memset(lpOutBuffer,''\0'',7); //?span>7个字节先清零     l
pOutBuffer[0]=''\x11''; //
发送缓冲区的第1个字节ؓDC1     
lpOutBuffer[1]=''0''; //
W?span>2个字节ؓ字符0(30H)  
lpOutBuffer[2]=''0''; //
W?span>3个字节ؓ字符0(30H)    
lpOutBuffer[3]=''1''; //
W?span>4个字节ؓ字符1(31H)     
lpOutBuffer[4]=''0''; //
W?span>5个字节ؓ字符0(30H)     
lpOutBuffer[5]=''1''; //
W?span>6个字节ؓ字符1(31H)    
lpOutBuffer[6]=''\x03''; //
W?span>7个字节ؓ字符ETX       //从该D代码可以看出,仪表的通讯地址?span>001          
DWORD dwBytesWrite=7; 
COMSTAT ComStat;      
DWORD dwErrorFlags;    
BOOL bWriteStat;      
ClearCommError(hCom,&dwErrorFlags,&ComStat);    
bWriteStat=WriteFile(hCom,lpOutBuffer,dwBytesWrite,& dwBytesWrite,NULL);    
if(!bWriteStat) {             
AfxMessageBox("写串口失?span>!"); 
}
}
void CRS485CommDlg::OnReceive() {       
// TODO: Add your control notification handler code here    
char str[100];
memset(str,''\0'',100);       
DWORD wCount=100;//d的字节数    
BOOL bReadStat;
bReadStat=ReadFile(hCom,str,wCount,&wCount,NULL);    
if(!bReadStat)     
AfxMessageBox("
M口失?span>!"); 
PurgeComm(hCom, PURGE_TXABORT|      PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);  
m_disp=str;   
UpdateData(FALSE);    
}

您可以观察返回的字符Ԍ其中有和仪表昄值相同的部分Q您可以q行相应的字W串操作取出仪表的显C倹{?span>
打开ClassWizard,为静态文本框IDC_DISPdCStringcd变量m_dispQ同时添?span>WM_CLOSE的相应函敎ͼ

void CRS485CommDlg::OnClose() {    
// TODO: Add your message handler code here and/or call default   
CloseHandle(hCom);       //
E序退出时关闭串口  
CDialog::OnClose();
}

E序的相应部分已l在代码内部作了详细介绍。连接好g部分Q编译运行程序,l心体会串口同步操作部分?/span>

例程2

打开VC++6.0Q新建基于对话框的工E?span>RS485CommQ在d话框H口IDD_RS485COMM_DIALOG上添加两个按钮,ID分别?span>IDC_SEND?span>IDC_RECEIVEQ标题分别ؓ发?span>”?span>“接收Q添加一个静态文本框IDC_DISPQ用于显CZ口接收到的内宏V在RS485CommDlg.cpp文g中添加全局变量Q?/span>

HANDLE hCom; //全局变量Q?/span>

串口句柄?span>RS485CommDlg.cpp文g中的OnInitDialog()函数d如下代码Q?/span>

hCom=CreateFile("COM1",//COM1?span>           
GENERIC_READ|GENERIC_WRITE, //
允许d?span>          
0, //独占方式         
NULL,         
OPEN_EXISTING, //
打开而不是创?span>           
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //重叠方式              
NULL);    
if(hCom==(HANDLE)-1)   {             
AfxMessageBox("
打开COMp|!");           
return FALSE; 
}      
SetupComm(hCom,100,100); //
输入~冲区和输出~冲区的大小都是100    
COMMTIMEOUTS TimeOuts; //
讑֮读超?span>  
TimeOuts.ReadIntervalTimeout=MAXDWORD;    
TimeOuts.ReadTotalTimeoutMultiplier=0;       
TimeOuts.ReadTotalTimeoutConstant=0;     //在读一ơ输入缓冲区的内容后L作就立即q回Q?span>        //而不是否读入了要求的字W?span>     //讑֮写超?span>   TimeOuts.WriteTotalTimeoutMultiplier=100;    
TimeOuts.WriteTotalTimeoutConstant=500;      
SetCommTimeouts(hCom,&TimeOuts); //讄时
DCB dcb;      
GetCommState(hCom,&dcb);      
dcb.BaudRate=9600; //
波特率ؓ9600    
dcb.ByteSize=8; //
每个字节?span>8?span>     
dcb.Parity=NOPARITY; //无奇偶校验位    
dcb.StopBits=TWOSTOPBITS; //
两个停止?span>       
SetCommState(hCom,&dcb);    
PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);

分别双击IDC_SEND按钮?span>IDC_RECEIVE按钮Q添加两个按钮的响应函数Q?/span>

void CRS485CommDlg::OnSend() {     
// TODO: Add your control notification handler code here    
OVERLAPPED m_osWrite;
 memset(&m_osWrite,0,sizeof(OVERLAPPED)); 
   
m_osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);  
char lpOutBuffer[7];    
memset(lpOutBuffer,''\0'',7); 
lpOutBuffer[0]=''\x11'';      
lpOutBuffer[1]=''0'';    
lpOutBuffer[2]=''0''; 
lpOutBuffer[3]=''1''; 
lpOutBuffer[4]=''0'';    
lpOutBuffer[5]=''1''; 
lpOutBuffer[6]=''\x03'';             
DWORD dwBytesWrite=7;    
COMSTAT ComStat;      
DWORD dwErrorFlags;   
BOOL bWriteStat;    
ClearCommError(hCom,&dwErrorFlags,&ComStat); 
bWriteStat=WriteFile(hCom,lpOutBuffer,      dwBytesWrite,& dwBytesWrite,&m_osWrite);     
if(!bWriteStat) {           
if(GetLastError()==ERROR_IO_PENDING)         
{                  
WaitForSingleObject(m_osWrite.hEvent,1000);
}      
}
}
void CRS485CommDlg::OnReceive()
{       
// TODO: Add your control notification handler code here    
OVERLAPPED m_osRead;  
memset(&m_osRead,0,sizeof(OVERLAPPED));    
m_osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);   
COMSTAT ComStat;      
DWORD dwErrorFlags;              
char str[100]; memset(str,''\0'',100);       
DWORD dwBytesRead=100;//
d的字节数     
BOOL bReadStat;    
ClearCommError(hCom,&dwErrorFlags,&ComStat); 
dwBytesRead=min(dwBytesRead, (DWORD)ComStat.cbInQue);   
bReadStat=ReadFile(hCom,str,        dwBytesRead,&dwBytesRead,&m_osRead); 
if(!bReadStat) {           
if(GetLastError()==ERROR_IO_PENDING)     //GetLastError()
函数q回ERROR_IO_PENDING,表明串口正在q行L?span>       
{                  
WaitForSingleObject(m_osRead.hEvent,2000);                //
使用WaitForSingleObject函数{待Q直到读操作完成或g时已辑ֈ2U钟               //当串口读操作q行完毕后,m_osRead?span>hEvent事g会变为有信号            
}      
}      
PurgeComm(hCom, PURGE_TXABORT|      PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR);  
m_disp=str;   
UpdateData(FALSE);
}

打开ClassWizard,为静态文本框IDC_DISPdCStringcd变量m_dispQ同时添?span>WM_CLOSE的相应函敎ͼ

void CRS485CommDlg::OnClose() {    
// TODO: Add your message handler code here and/or call default   
CloseHandle(hCom);       //
E序退出时关闭串口  
CDialog::OnClose();
}

 



Xiao.Zhu 2007-06-16 23:25 发表评论
]]>
Windows Sockets 2.0:使用完成端口高性能Q可扩展性Winsock服务E序 (?http://www.shnenglu.com/zzh/archive/2007/05/18/24318.htmlXiao.ZhuXiao.ZhuFri, 18 May 2007 01:52:00 GMThttp://www.shnenglu.com/zzh/archive/2007/05/18/24318.htmlhttp://www.shnenglu.com/zzh/comments/24318.htmlhttp://www.shnenglu.com/zzh/archive/2007/05/18/24318.html#Feedback0http://www.shnenglu.com/zzh/comments/commentRss/24318.htmlhttp://www.shnenglu.com/zzh/services/trackbacks/24318.html阅读全文

Xiao.Zhu 2007-05-18 09:52 发表评论
]]>
þþƷAVþþ| Ʒþþþþ| ޾ƷĻþò| þùƷһ| þۺ| þþþ޾Ʒ˵| þþþþþŮú| ޹Ʒþþþ| ŷ˾þۺ| ƷƵþþ| 㽶þҹɫƷ2020| þ97Ʒþþþþþò| ޹պŷۺϾþ| ŮдþӰԺ| ޹ŷۺϾþ| þó18վ| Aݺݾþɫ| 18ƾþþAAAƬ| Ʒ޾þþþþ| ƷɫۺϾþ| þֻоƷҳ| þ뾫Ʒһ| ѹۿþþƵ| 99þ99þþƷѿ| ͵ٸþþþþþþ| þþþavӰ | ޹˾þþƷӰ | 99Ʒ˾þþô߽| þþƷav鶹ɫ | Ʒþþþþþþ| ٸۺϾþĻ| Ůͬþ| þ޾ƷƷ| þۺϾþ| ɫۺϾþþþĻ | þþƷAvӰƬ| ƯޱгĻþ| ޹СƵƷþþ | 69Ʒþþþվ | һþ㽶| ޹˾Ʒ91þþ|