My C++ Blog

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  14 随笔 :: 0 文章 :: 0 评论 :: 0 Trackbacks

#

假设你需要指定范围内的随机数,传统的方法是使用ANSI C的函数random(),然后格式化结果以便结果是落在指定的范围内。但是,使用这个方法至少有两个缺点。
    首先,做格式化时,结果常常是扭曲的,所以得不到正确的随机数(如某些数的出现频率要高于其它数)
    其次,random()只支持整型数;不能用它来产生随机字符,浮点数,字符串或数据库中的记录。
    对于以上的两个问题,C++中提供了更好的解决方法,那就是random_shuffle()算法。不要着急,下面我就会告诉你如何用这种算法来产生不同类型的随机数。

    产生指定范围内的随机元素集的最佳方法是创建一个顺序序列(也就是向量或者内置数组),在这个顺序序列中含有指定范围的所有值。例如,如何你需要产生100个0-99之间的数,那么就创建一个向量并用100个按升序排列的数填充向量: #include <vector>
using std::vector;
int main()
{
  vector<int> vi;
  for (int i = 0; i < 10; i++)
    vi.push_back(i);
/*现在向量包含了 100 个 0-99 之间的整数并且按升序排列*/
}
    填充完向量之后,用random_shuffle()算法打乱元素排列顺序。random_shuffle()定义在标准的头文件<algorithm.h>中。因为
所有的STL算法都是在名字空间std::中声明的,所以你要注意正确地声明数据类型。random_shuffle()有两个参数,第一个参数是指向序列首元素的迭代器,第二个参数则指向序列最后一个元素的下一个位置。下列代码段用random_shuffle()算法打乱了先前填充到向量中的元素:
include <algorithm>
using std::random_shuffle;

random_shuffle(vi.begin(), vi.end()); /* 打乱元素 */

    如果你想检查被打乱的元素,可以用如下方法看一下他们被打乱后存储的次序: for (int i = 0; i < 100; i++)
  cout<<vi[i]; /* 显示被打乱顺序的元素 */
    random_shuffle()是个完全通用的算法-适用于内建的数据类型和用户自定义类型。下面的例子创建了一个有7个字符串对象的向量,它包含一周的天数并使用random_shuffle()打乱他们的排列顺序: #include <string>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
  vector<string> vs;
  vs.push_back(string ("Sunday"));
  vs.push_back (string ("Monday"));
  ...
  vs.push_back (string ("Saturday"));
  random_shuffle(vs.begin(), vs.end()); /* 打乱顺序 */
  for (int i = 0; i << 7; i++)
    cout<<vs[i]; /* 显示打乱顺序后的元素 */
}
如何使用random_shuffle()处理内置数组

    在使用容器代替内置数组时,你不要有什么负担。所有STL算法不仅适用于容器,也适用于序列。因此,你也能将random_shuffle()算法应用于内置数组。只是要注意random_shuffle()的第二个参数要指向数组上界的下一个元素位置: char carr[4] = {'a', 'b', 'c', 'd'};
/*carr+4 指向数组上界的下一个元素位置*/
random_shuffle(carr, carr+4);   
for (int i = 0; i < 4; i++)
  cout<<carr[i]; /* 显示被打乱顺序的元素 */
posted @ 2008-04-30 17:27 IP 阅读(265) | 评论 (0)编辑 收藏

第一阶段
此阶段主要是能熟练地使用某种语言。这就相当于练武中的套路和架式这些表面的东西。
第二阶段
此阶段能精通基于某种平台的接口(例如我们现在常用的Win 32的API函数)以及所对应语言的自身的库函数。到达这个阶段后,也就相当于可以进行真实散打对练了,可以真正地在实践中做些应用。
第三阶段
此阶段能深入地了解某个平台系统的底层,已经具有了初级的内功的能力,也就是“手中有剑,心中无剑”。
第四阶段
此阶段能直接在平台上进行比较深层次的开发。基本上,能达到这个层次就可以说是进入了高层次。这时进入了高级内功的修炼。比如能进行VxD或操作系统的内核的修改。
这时已经不再有语言的束缚,语言只是一种工具,即使要用自己不会的语言进行开发,也只是简单地熟悉一下,就手到擒来,完全不像是第一阶段的时候学习语言的那种情况。一般来说,从第三阶段过渡到第四阶段是比较困难的。为什么会难呢?这就是因为很多人的思想变不过来。
第五阶段
此阶段就已经不再局限于简单的技术上的问题了,而是能从全局上把握和设计一个比较大的系统体系结构,从内核到外层界面。可以说是“手中无剑,心中有剑”。到了这个阶段以后,能对市面上的任何软件进行剖析,并能按自己的要求进行设计,就算是MS Word这样的大型软件,只要有充足的时间,也一定会设计出来。
第六阶段
此阶段也是最高的境界,达到“无招胜有招”。这时候,任何问题就纯粹变成了一个思路的问题,不是用什么代码就能表示的。也就是“手中无剑,心中也无剑”。
此时,对于练功的人来说,他已不用再去学什么少林拳,只是在旁看一下少林拳的对战,就能把此拳拿来就用。这就是真正的大师级的人物。这时,Win 32或Linux在你眼里是没有什么差别的。
每一个阶段再向上发展时都要按一定的方法。第一、第二个阶段通过自学就可以完成,只要多用心去研究,耐心地去学习。
要想从第二个阶段过渡到第三个阶段,就要有一个好的学习环境。例如有一个高手带领或公司里有一个好的练手环境。经过二、三年的积累就能达到第三个阶段。但是,有些人到达第三个阶段后,常常就很难有境界上的突破了。他们这时会产生一种观念,认为软件无非如此,认为自己已无所不能。其实,这时如果遇到大的或难些的软件,他们往往还是无从下手。
现在我们国家大部分程序员都是在第二、三级之间。他们大多都是通过自学成才的,不过这样的程序员一般在软件公司也能独当一面,完成一些软件的模块。
但是,也还有一大堆处在第一阶段的程序员,他们一般就能玩玩VB,做程序时,去找一堆控件集成一个软件。

感觉自己处于第二阶段与第三阶段之间,但是想跨入第三阶段感觉真的很难啊。要是能有一个人带带就好了。

posted @ 2008-03-17 01:16 IP 阅读(329) | 评论 (0)编辑 收藏

今天调试程序,发现有内存泄漏但是没有提示具体是哪一行,搞得我很头疼。结果在网上搜索了一些资料,经自己实践后整理如下:

 

    第一种:通过"OutPut窗口"定位引发内存泄漏的代码(下面转,我写的没原文好,也懒得写)。

 

我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏。例如:

class CMyApp : public CWinApp
{
public:
   BOOL InitApplication()
   {
       
int* leak = new int[10];
       
return TRUE;
   }
};

产生的内存泄漏报告大体如下:

Detected memory leaks!
Dumping objects 
->
c:\work\test.cpp(
186) : {52} normal block at 0x003C441040 bytes long.
 Data: 
<                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

这挺好。问题是,如果我们不喜欢MFC,那么难道就没有办法?或者自己做? 

呵呵,这不需要。其实,MFC也没有自己做。内存泄漏检测的工作是VC++的C运行库做的。也就是说,只要你是VC++程序员,都可以很方便地检测内存泄漏。我们还是给个样例:

#include <crtdbg.h>

inline 
void EnableMemLeakCheck()
{
   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) 
| _CRTDBG_LEAK_CHECK_DF);
}

void main()
{
   EnableMemLeakCheck();
   
int* leak = new int[10];
}

 运行(提醒:不要按Ctrl+F5,按F5),你将发现,产生的内存泄漏报告与MFC类似,但有细节不同,如下:

Detected memory leaks!
Dumping objects 
->
{
52} normal block at 0x003C441040 bytes long.
 Data: 
<                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

为什么呢?看下面。

 

定位内存泄漏由于哪一句话引起的

你已经发现程序存在内存泄漏。现在的问题是,我们要找泄漏的根源。

一般我们首先确定内存泄漏是由于哪一句引起。在MFC中,这一点很容易。你双击内存泄漏报告的文字,或者在Debug窗口中按F4,IDE就帮你定位到申请该内存块的地方。对于上例,也就是这一句:

   int* leak = new int[10];

这多多少少对你分析内存泄漏有点帮助。特别地,如果这个new仅对应一条delete(或者你把delete漏写),这将很快可以确认问题的症结。 

我们前面已经看到,不使用MFC的时候,生成的内存泄漏报告与MFC不同,而且你立刻发现按F4不灵。那么难道MFC做了什么手脚? 

其实不是,我们来模拟下MFC做的事情。看下例: 

inline void EnableMemLeakCheck()
{
   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) 
| _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
   EnableMemLeakCheck();
   
int* leak = new int[10];
}

再运行这个样例,你惊喜地发现,现在内存泄漏报告和MFC没有任何分别了。

 

 

    第二种方法:直接定位指定内存块错误的代码行(下面转)。

 

单确定了内存泄漏发生在哪一行,有时候并不足够。特别是同一个new对应有多处释放的情形。在实际的工程中,以下两种情况很典型: 

  • 创建对象的地方是一个类工厂(ClassFactory)模式。很多甚至全部类实例由同一个new创建。对于此,定位到了new出对象的所在行基本没有多大帮助。 
     
  • COM对象。我们知道COM对象采用Reference Count维护生命周期。也就是说,对象new的地方只有一个,但是Release的地方很多,你要一个个排除。

那么,有什么好办法,可以迅速定位内存泄漏?

答:有。

在内存泄漏情况复杂的时候,你可以用以下方法定位内存泄漏。这是我个人认为通用的内存泄漏追踪方法中最有效的手段。

我们再回头看看crtdbg生成的内存泄漏报告: 

Detected memory leaks!
Dumping objects 
->
c:\work\test.cpp(
186) : {52} normal block at 0x003C441040 bytes long.
 Data: 
<                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

除了产生该内存泄漏的内存分配语句所在的文件名、行号为,我们注意到有一个比较陌生的信息:{52}。这个整数值代表了什么意思呢?

其实,它代表了第几次内存分配操作。象这个例子,{52}代表了第52次内存分配操作发生了泄漏。你可能要说,我只new过一次,怎么会是第52次?这很容易理解,其他的内存申请操作在C的初始化过程调用的呗。:)

有没有可能,我们让程序运行到第52次内存分配操作的时候,自动停下来,进入调试状态?所幸,crtdbg确实提供了这样的函数:即 long _CrtSetBreakAlloc(long nAllocID)。我们加上它:

inline void EnableMemLeakCheck()
{
   _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) 
| _CRTDBG_LEAK_CHECK_DF);
}

#ifdef _DEBUG
#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void main()
{
   EnableMemLeakCheck();
   _CrtSetBreakAlloc(
52);
   
int* leak = new int[10];
}

你发现,程序运行到 int* leak = new int[10]; 一句时,自动停下来进入调试状态。细细体会一下,你可以发现,这种方式你获得的信息远比在程序退出时获得文件名及行号有价值得多。因为报告泄漏文件名及行号,你获得的只是静态的信息,然而_CrtSetBreakAlloc则是把整个现场恢复,你可以通过对函数调用栈分析(我发现很多人不习惯看函数调用栈,如果你属于这种情况,我强烈推荐你去补上这一课,因为它太重要了)以及其他在线调试技巧,来分析产生内存泄漏的原因。通常情况下,这种分析方法可以在5分钟内找到肇事者。

当然,_CrtSetBreakAlloc要求你的程序执行过程是可还原的(多次执行过程的内存分配顺序不会发生变化)。这个假设在多数情况下成立。不过,在多线程的情况下,这一点有时难以保证。

 

个人心得:我在用这种方法时开始没看懂,后来在MSDN中也找到了这方面相关的信息,后来才会用。我感觉在这方面网上介绍的不够详细,下面我就相对详细地解释一下(为什么用“相对详细”?本人比较懒)。首先说明一下,下面的函数不需要上面所添加的宏定义和"crtdbg.h"头文件,也不需要EnableMemLeakCheck()函数。只需在main函数一开始运行 _CrtSetBreakAlloc(long (4459))函数。其中4459是申请内存的序号(上面有说明),然后F5运行(不需要设断点),然后会出现“Find Source”这个对话框,点击“取消”。然后会出现“User breakpoint called from code at xxxx”的对话框,点击“确定”,会看到一些汇编的代码(不要怕,其实我也看不懂,算然原来学过点汇编),调出堆栈窗口(call stack),在其中的“main() line xxx + xxx bytes”上双击(或它的上一行双击,我的上一行是一个自定义函数,双击后直接定位到我new的地方,定位还是很准的,开始我怀疑,但最后检查果然是这地方没释放)会定位到错误行。

 

第三种:用Ctrl+B来设定,不过现在好像忘了。效果根第二种方法基本一样。

 

有人会问,既然第一种方法定位没问题,为什么还要介绍第二种?其实在实际应用中,某些内存泄漏它没有定位到哪一行的,只有内存块的序号(有可能我用的不太会用),这个时候就需要用第二种方法。


原文章链接:http://kyo2008.bokee.com/viewdiary.14396142.html
posted @ 2008-03-17 01:14 IP 阅读(581) | 评论 (0)编辑 收藏

在浏览我的blog的,除了专业的程序员,也有部分是在校大学生,还有两位同学通过QQ问我怎么
学习编程技术,我为这些同学的好学而感动;又因为自己曾经做过两年计算机大专老师,可能天生
喜欢教育人,兴趣一来就开始写这篇blog,其实自己认识也不够。
        现在的大学生面临的环境,用英国著名的小说家狄更斯的名言来形容:
       “这是最好的时代,这是最坏的时代。”
        好时代在于,现在大学里资讯特别发达,电脑、网络、书籍,他们很容易得到,现在网络上的
教程、blog、技术新闻等等多得是,各种开发工具、开发平台都可以免费获得,这不是最好的时代吗?!
想想我们九十年代读书时,操作系统是win95/win98,学习的语言就是Fortron(都不知道怎么拼写了)
和c语言,反正我从来没有上机实习,去机房就是玩电脑版的超级玛丽,四年下来什么都不会。
        坏时代在于,东西太多了,都不知道学什么,很多快餐等着他们,很容易就去学习那种21天能精通的
东西。大学老师要么厉害的自己去开公司赚外快,要么根本不懂软件开发在那里误人子弟。而现在
的网络游戏却一款款引诱着他们,把身体都搞跨了。所以他们现在比我们那时更迷惘。
        那么在校大学生到底怎么学习才好?其实没有标准答案,我只能自己从我现在的观点来谈我的看法。
        总的看法还是要学好基础课程,虽然是老生常谈,但真正理解的人不多。
        具体到课程,数据结构和算法,操作系统,汇编语言,计算机组成原理,编译原理,数据库系统原理。
这些基础知识,我现在还时不时地学习,就是因为当年没有学,或者没有学习好。这些基础的东西学得好
不好,就意味着一个普通技术人员在技术上能走多远,当然有开创性的天才另当别论。
        很多人问到我应该学c还是c++, 还是java, 或者是c#等等,这个问题我用去年底美国一位教授的
文章里的观点回答,他说现在美国大学基本上全是开java课,而把c语言放弃了,这样是害得学生学不到
真正的编程技术,将降低美国在基础研究上的能力。很多人不以为然,可能说他是老糊涂了,其实我认为
这是老教授的高明啊,美国是操作系统的发源地,是各种技术标准的制造者,是各种平台的制造者,它在
计算机领域能领导全球,就是在基础研究啊。
        数学这个老学科,依然是那么重要,还是拿例子来说明吧。北京时间3月12日消息,据国外媒体报道,
微软主席比尔·盖茨(Bill Gates)将怀揣多年来的梦想重返美国国会——为高熟练的技术工人签发更多签证;
在学校中开展更多的数学、科学和工程教育;以及加大科技投资等。注意到没有,比尔·盖茨把数学单独
提出来,数学有多重要就不用说了。其实学数学最终目的就是锻炼自己的抽象思维能力,很多非数学专业
出生的人,也没有专门学数学,但他的设计和开发非常好,你可以发现他的抽象思维能力天生就很强,碰
到一些数学估算,时间/空间复杂度的分析,他很快就估算出来了。
       有同学问到学什么开发工具或平台好,工具和平台不是很重要。学c/c++的话,你在vc上或者gcc上都
可以。学java的话,你用esclipse或jbulider,或者其它开发工具,反正稍微流行一个就可以编译你的代码
了。数据库也一样,反正是学习好sql语句,因为你们不可能现在就学习到oracle优化这么高级的东西。
       注重学习基础知识,养成看书的好习惯。在现在的部门里,很多年轻人都是不看书的,满足于项目
开发得来的那点有限的知识,有了点钱就知道搞股票,最后也没见他赚钱,一两年下来,技术还是没有长足
的进展,做管理就那么一个位子,还远远轮不到他们呢。
        还有,很多大学生可能希望有项目经验,这是没必要的。大学四年,本来时间相对就不长,这些理论的
东西都没有时间学好,你还要求有一个很好的项目经验,而把最重要的东西放到那里没有接触,这不是得了
芝麻丢了西瓜吗。
       一些大四的学生,可能出去面试过,碰到一些小公司,或者是做外包的公司,特别是对日本外包的,
这些公司就想要你一下子会他们所用的开发工具,至于你的基础扎实不扎实,他根本不希望,因为他们就是
准备把你搞成白痴,能看着别人设计好的文档和例子,把代码写出来就是了,这个就是高中毕业生就能做的,你读大学四年干什么,不如高中毕业就去算了。
       总之,在大学时代,基础、基础、还是基础,走上社会了就在实际开发中用好曾经学习过的东西。

原blog地址:http://www.cppblog.com/cool-liangbing/archive/2008/03/12/44283.html

posted @ 2008-03-15 15:45 IP 阅读(374) | 评论 (0)编辑 收藏

仅列出标题
共2页: 1 2