[?]|登录|免费注册®
高级...

浏览选项

  • 手机网站排名

[+](无标题)

?博客园_首页代码改变世界uuid:69e407a7-5842-4ad5-ad75-280ba85ce3b7;id=47932014-09-01T13:31:42Zfeed.cnblogs.comhttp://www.cnblogs.com/tanlon/p/3950090.html[原]如何在Android用FFmpeg解码图像 - 雪夜&流星前一篇[原]如何用Android NDK编译FFmpeg我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库。这篇文章我们就可以使用Android下的JNI来调用FFMpeg进行解码了。一、编译出来可以使用的动态库,我们会看到如下输出则表示link完成了: CC lib...2014-09-01T13:29:00Z2014-09-01T13:29:00Z雪夜&amp;流星http://www.cnblogs.com/tanlon/<p>前一篇<a href="http://www.cnblogs.com/tanlon/p/3938282.html#3017641" target="_blank">[原]如何用Android NDK编译FFmpeg</a>&nbsp;我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库。这篇文章我们就可以使用Android下的JNI来调用FFMpeg进行解码了。</p><p><strong style="line-height: 1.5;">一、编译出来可以使用的动态库,我们会看到如下输出则表示link完成了:</strong></p><p>  </p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">CC libavcodec/<span style="color: #000000;">log2_tab.o<br/>CC libavutil</span>/<span style="color: #000000;">log2_tab.o<br/>CC libswresample</span>/<span style="color: #000000;">log2_tab.o<br/>AR libavcodec</span>/<span style="color: #000000;">libavcodec.a<br/>LD libavutil</span>/libavutil.so.<span style="color: #800080;">52</span><span style="color: #000000;"><br/>AR libavutil</span>/<span style="color: #000000;">libavutil.a<br/>AR libswresample</span>/<span style="color: #000000;">libswresample.a<br/>LD libavcodec</span>/libavcodec.so.<span style="color: #800080;">55</span><span style="color: #000000;"><br/>LD libswresample</span>/libswresample.so.<span style="color: #800080;">0</span><span style="color: #000000;"><br/>LD libswscale</span>/libswscale.so.<span style="color: #800080;">2</span><span style="color: #000000;"><br/>LD libavformat</span>/libavformat.so.<span style="color: #800080;">55</span><span style="color: #000000;"><br/>INSTALL libavformat</span>/<span style="color: #000000;">libavformat.a<br/>INSTALL libavformat</span>/<span style="color: #000000;">libavformat.so<br/>STRIP install</span>-libavformat-<span style="color: #000000;">shared<br/>INSTALL libavcodec</span>/<span style="color: #000000;">libavcodec.a<br/>INSTALL libavcodec</span>/<span style="color: #000000;">libavcodec.so<br/>STRIP install</span>-libavcodec-<span style="color: #000000;">shared<br/>INSTALL libswresample</span>/<span style="color: #000000;">libswresample.a<br/>INSTALL libswresample</span>/<span style="color: #000000;">libswresample.so<br/>STRIP install</span>-libswresample-<span style="color: #000000;">shared<br/>INSTALL libswscale</span>/<span style="color: #000000;">libswscale.a<br/>INSTALL libswscale</span>/<span style="color: #000000;">libswscale.so<br/>STRIP install</span>-libswscale-<span style="color: #000000;">shared<br/>INSTALL libavutil</span>/<span style="color: #000000;">libavutil.a<br/>INSTALL libavutil</span>/<span style="color: #000000;">libavutil.so<br/>STRIP install</span>-libavutil-<span style="color: #000000;">shared<br/>INSTALL libavformat</span>/<span style="color: #000000;">avformat.h<br/>INSTALL libavformat</span>/<span style="color: #000000;">avio.h<br/>INSTALL libavformat</span>/<span style="color: #000000;">version.h<br/>INSTALL libavformat</span>/<span style="color: #000000;">libavformat.pc<br/>INSTALL libavcodec</span>/<span style="color: #000000;">avcodec.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">avfft.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">dxva2.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">old_codec_ids.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">vaapi.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">vda.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">vdpau.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">version.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">xvmc.h<br/>INSTALL libavcodec</span>/<span style="color: #000000;">libavcodec.pc<br/>INSTALL libswresample</span>/<span style="color: #000000;">swresample.h<br/>INSTALL libswresample</span>/<span style="color: #000000;">version.h<br/>INSTALL libswresample</span>/<span style="color: #000000;">libswresample.pc<br/>INSTALL libswscale</span>/<span style="color: #000000;">swscale.h<br/>INSTALL libswscale</span>/<span style="color: #000000;">version.h<br/>INSTALL libswscale</span>/<span style="color: #000000;">libswscale.pc<br/>INSTALL libavutil</span>/<span style="color: #000000;">adler32.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">aes.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">attributes.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">audio_fifo.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">audioconvert.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">avassert.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">avstring.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">avutil.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">base64.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">blowfish.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">bprint.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">bswap.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">buffer.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">channel_layout.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">common.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">cpu.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">crc.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">error.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">eval.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">fifo.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">file.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">frame.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">hmac.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">imgutils.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">intfloat.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">intfloat_readwrite.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">intreadwrite.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">lfg.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">log.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">mathematics.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">md5.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">mem.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">murmur3.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">dict.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">old_pix_fmts.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">opt.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">parseutils.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">pixdesc.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">pixfmt.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">random_seed.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">rational.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">ripemd.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">samplefmt.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">sha.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">sha512.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">time.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">timecode.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">timestamp.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">version.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">xtea.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">lzo.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">avconfig.h<br/>INSTALL libavutil</span>/<span style="color: #000000;">libavutil.pc<br/><br/> link ffmpeg.</span></div><p><strong>二、新建一个Android工程,在工程目录下新建一个jni文件夹,在文件夹下新建一个ffmpeg文件夹,用来放ffmpeg相关的头文件。在ffmpeg文件夹下新建Android.mk文件用来预先加载ffmpeg动态库。Android.mk文件内容如下:</strong></p><p><strong>&nbsp;</strong></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">LOCAL_PATH := $(call my-<span style="color: #0000ff;">dir</span><span style="color: #000000;">)<br/><br/>include $(CLEAR_VARS)<br/>LOCAL_MODULE :</span>=<span style="color: #000000;"> ffmpeg<br/>LOCAL_SRC_FILES :</span>= /path/to/build/output/<span style="color: #000000;">libffmpeg.so<br/>include $(PREBUILT_SHARED_LIBRARY)</span></div><p><strong>三、在jni下新建Android.mk文件和Application.mk两个文件用来指定编译的顺序和编译的平台以及对应的cpu指令集。</strong></p><p>&nbsp;</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #000000;">Application.mk<br/><br/>APP_ABI :</span>=<span style="color: #000000;"> armeabi<br/>APP_PLATFORM :</span>= android-<span style="color: #800080;">9</span></div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">Android.mk<br /><br />include $(call all-subdir-makefiles)</div><p><strong>四、编写JNI文件,用来绑定java文件与.c文件的交互,文件内容如下:</strong></p><p></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">/*</span><span style="color: #008000;"><br/> * ffmpeg_jni.c<br/> *<br/> * Created on: Sep 1, 2014<br/> * Author: clarck<br/> </span><span style="color: #008000;">*/</span><span style="color: #000000;"><br/>#include </span>&lt;stdlib.h&gt;<span style="color: #000000;"><br/>#include </span>&lt;<span style="color: #0000ff;">string</span>.h&gt;<span style="color: #000000;"><br/>#include </span>&lt;stdio.h&gt;<span style="color: #000000;"><br/>#include </span>&lt;jni.h&gt;<span style="color: #000000;"><br/><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../include/ffmpeg_logger.h</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../include/ffmpeg.h</span><span style="color: #800000;">"</span><br/><br/><span style="color: #008000;">//</span><span style="color: #008000;"> 指定要注册的类,对应完整的java类名</span><br/><span style="color: #0000ff;">#define</span> JNIREG_CLASS "com/clarck/android/ffmpeg/MainActivity"<span style="color: #000000;"><br/><br/>JNIEXPORT </span><span style="color: #0000ff;">void</span> JNICALL native_setDataSource(JNIEnv *<span style="color: #000000;">env, jclass classzz, jstring path) {<br/> </span><span style="color: #0000ff;">char</span> *filepath =<span style="color: #000000;"> ffmpeg_jstringTostr(env, path);<br/> ffmpeg_setDataSource(filepath);<br/>}<br/><br/></span><span style="color: #008000;">//</span><span style="color: #008000;">Java和JNI函数的绑定</span><br/><span style="color: #0000ff;">static</span> JNINativeMethod method_table[] =<span style="color: #000000;"> {<br/> { </span><span style="color: #800000;">"</span><span style="color: #800000;">setDataSource</span><span style="color: #800000;">"</span>, <span style="color: #800000;">"</span><span style="color: #800000;">(Ljava/lang/String;)V</span><span style="color: #800000;">"</span><span style="color: #000000;">, native_setDataSource }<br/>};<br/><br/></span><span style="color: #008000;">//</span><span style="color: #008000;">注冊native方法到java中</span><br/><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span> registerNativeMethods(JNIEnv *env, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> *<span style="color: #000000;">className,<br/> JNINativeMethod </span>*gMethods, <span style="color: #0000ff;">int</span><span style="color: #000000;"> numMethods) {<br/> jclass clazz;<br/> clazz </span>= (*env)-&gt;<span style="color: #000000;">FindClass(env, className);<br/> </span><span style="color: #0000ff;">if</span> (clazz ==<span style="color: #000000;"> NULL) {<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> JNI_FALSE;<br/> }<br/><br/> </span><span style="color: #0000ff;">if</span> ((*env)-&gt;RegisterNatives(env, clazz, gMethods, numMethods) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> JNI_FALSE;<br/> }<br/><br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> JNI_TRUE;<br/>}<br/><br/></span><span style="color: #008000;">//</span><span style="color: #008000;">調用註冊方法</span><br/><span style="color: #0000ff;">int</span> register_ndk_load(JNIEnv *<span style="color: #000000;">env) {<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> registerNativeMethods(env, JNIREG_CLASS, method_table,<br/> (</span><span style="color: #0000ff;">int</span>) (<span style="color: #0000ff;">sizeof</span>(method_table) / <span style="color: #0000ff;">sizeof</span>(method_table[<span style="color: #800080;">0</span><span style="color: #000000;">])));<br/>}<br/><br/>JNIEXPORT jint JNI_OnLoad(JavaVM </span>*vm, <span style="color: #0000ff;">void</span> *<span style="color: #000000;">reserved) {<br/> JNIEnv </span>*env =<span style="color: #000000;"> NULL;<br/> jint result </span>= -<span style="color: #800080;">1</span><span style="color: #000000;">;<br/><br/> </span><span style="color: #0000ff;">if</span> ((*vm)-&gt;GetEnv(vm, (<span style="color: #0000ff;">void</span>**)&amp;env, JNI_VERSION_1_6) !=<span style="color: #000000;"> JNI_OK) {<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> result;<br/> }<br/><br/> register_ndk_load(env);<br/><br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">返回JNI的版本</span><br/> <span style="color: #0000ff;">return</span><span style="color: #000000;"> JNI_VERSION_1_6;<br/>}</span></div><p><strong>五、编写ffmpeg调用函数,内容如下:</strong></p><p></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">/*</span><span style="color: #008000;"><br/> * ffmpeg.c<br/> *<br/> * Created on: Sep 1, 2014<br/> * Author: clarck<br/> </span><span style="color: #008000;">*/</span><span style="color: #000000;"><br/>#include </span>&lt;jni.h&gt;<span style="color: #000000;"><br/>#include </span>&lt;android/native_window_jni.h&gt;<span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../include/ffmpeg.h</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../include/ffmpeg_logger.h</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../ffmpeg/include/libavcodec/avcodec.h</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../ffmpeg/include/libavformat/avformat.h</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../ffmpeg/include/libavutil/pixfmt.h</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/>#include </span><span style="color: #800000;">"</span><span style="color: #800000;">../ffmpeg/include/libswscale/swscale.h</span><span style="color: #800000;">"</span><br/><br/><span style="color: #0000ff;">char</span>* ffmpeg_jstringTostr(JNIEnv*<span style="color: #000000;"> env, jstring jstr) {<br/> </span><span style="color: #0000ff;">char</span>* pStr =<span style="color: #000000;"> NULL;<br/><br/> jclass jstrObj </span>= (*env)-&gt;FindClass(env, <span style="color: #800000;">"</span><span style="color: #800000;">java/lang/String</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> jstring encode </span>= (*env)-&gt;NewStringUTF(env, <span style="color: #800000;">"</span><span style="color: #800000;">utf-8</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> jmethodID methodId </span>= (*env)-&gt;GetMethodID(env, jstrObj, <span style="color: #800000;">"</span><span style="color: #800000;">getBytes</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">(Ljava/lang/String;)[B</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> jbyteArray byteArray </span>= (jbyteArray) (*env)-&gt;<span style="color: #000000;">CallObjectMethod(env, jstr,<br/> methodId, encode);<br/> jsize strLen </span>= (*env)-&gt;<span style="color: #000000;">GetArrayLength(env, byteArray);<br/> jbyte </span>*jBuf = (*env)-&gt;<span style="color: #000000;">GetByteArrayElements(env, byteArray, JNI_FALSE);<br/><br/> </span><span style="color: #0000ff;">if</span> (jBuf &gt; <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> pStr </span>= (<span style="color: #0000ff;">char</span>*) malloc(strLen + <span style="color: #800080;">1</span><span style="color: #000000;">);<br/><br/> </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">pStr) {<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> NULL ;<br/> }<br/><br/> memcpy(pStr, jBuf, strLen);<br/><br/> pStr[strLen] </span>= <span style="color: #800080;">0</span><span style="color: #000000;">;<br/> }<br/><br/> (</span>*env)-&gt;ReleaseByteArrayElements(env, byteArray, jBuf, <span style="color: #800080;">0</span><span style="color: #000000;">);<br/><br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> pStr;<br/>}<br/><br/></span><span style="color: #0000ff;">void</span> ffmpeg_setDataSource(<span style="color: #0000ff;">char</span> *<span style="color: #000000;">file_path) {<br/> LOGI(</span><span style="color: #800000;">"</span><span style="color: #800000;">ffmpeg_setDataSource:%s</span><span style="color: #800000;">"</span><span style="color: #000000;">, file_path);<br/><br/> AVFormatContext </span>*<span style="color: #000000;">pFormatCtx;<br/> AVCodecContext </span>*<span style="color: #000000;">pCodecCtx;<br/> AVCodec </span>*<span style="color: #000000;">pCodec;<br/> AVFrame </span>*pFrame, *<span style="color: #000000;">pFrameYUV;<br/> AVPacket </span>*<span style="color: #000000;">packet;<br/> uint8_t </span>*<span style="color: #000000;">out_buffer;<br/><br/> </span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">struct</span> SwsContext *<span style="color: #000000;">img_convert_ctx;<br/><br/> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> videoStream, i, numBytes;<br/> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> ret, got_picture;<br/><br/> av_register_all();<br/> pFormatCtx </span>=<span style="color: #000000;"> avformat_alloc_context();<br/><br/> </span><span style="color: #0000ff;">if</span> (avformat_open_input(&amp;pFormatCtx, file_path, NULL, NULL) != <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> LOGE(</span><span style="color: #800000;">"</span><span style="color: #800000;">can't open the file. \n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/> }<br/><br/> </span><span style="color: #0000ff;">if</span> (avformat_find_stream_info(pFormatCtx, NULL) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> LOGE(</span><span style="color: #800000;">"</span><span style="color: #800000;">Could't find stream infomation.\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/> }<br/><br/> videoStream </span>= <span style="color: #800080;">1</span><span style="color: #000000;">;<br/> </span><span style="color: #0000ff;">for</span> (i = <span style="color: #800080;">0</span>; i &lt; pFormatCtx-&gt;nb_streams; i++<span style="color: #000000;">) {<br/> </span><span style="color: #0000ff;">if</span> (pFormatCtx-&gt;streams[i]-&gt;codec-&gt;codec_type ==<span style="color: #000000;"> AVMEDIA_TYPE_VIDEO) {<br/> videoStream </span>=<span style="color: #000000;"> i;<br/> }<br/> }<br/><br/> </span><span style="color: #0000ff;">if</span> (videoStream == -<span style="color: #800080;">1</span><span style="color: #000000;">) {<br/> LOGE(</span><span style="color: #800000;">"</span><span style="color: #800000;">Didn't find a video stream.\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/> }<br/><br/> pCodecCtx </span>= pFormatCtx-&gt;streams[videoStream]-&gt;<span style="color: #000000;">codec;<br/> pCodec </span>= avcodec_find_decoder(pCodecCtx-&gt;<span style="color: #000000;">codec_id);<br/><br/> </span><span style="color: #0000ff;">if</span> (pCodec ==<span style="color: #000000;"> NULL) {<br/> LOGE(</span><span style="color: #800000;">"</span><span style="color: #800000;">Codec not found.\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/> }<br/><br/> </span><span style="color: #0000ff;">if</span> (avcodec_open2(pCodecCtx, pCodec, NULL) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> LOGE(</span><span style="color: #800000;">"</span><span style="color: #800000;">Could not open codec.\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/> }<br/><br/> pFrame </span>=<span style="color: #000000;"> av_frame_alloc();<br/> pFrameYUV </span>=<span style="color: #000000;"> av_frame_alloc();<br/><br/> numBytes </span>= avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx-&gt;<span style="color: #000000;">width,<br/> pCodecCtx</span>-&gt;<span style="color: #000000;">height);<br/> out_buffer </span>= (uint8_t *) av_malloc(numBytes * <span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(uint8_t));<br/> avpicture_fill((AVPicture </span>*<span style="color: #000000;">) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,<br/> pCodecCtx</span>-&gt;width, pCodecCtx-&gt;<span style="color: #000000;">height);<br/><br/> </span><span style="color: #0000ff;">int</span> y_size = pCodecCtx-&gt;width * pCodecCtx-&gt;<span style="color: #000000;">height;<br/><br/> packet </span>= (AVPacket *) malloc(<span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(AVPacket));<br/> av_new_packet(packet, y_size);<br/><br/> av_dump_format(pFormatCtx, </span><span style="color: #800080;">0</span>, file_path, <span style="color: #800080;">0</span><span style="color: #000000;">);<br/><br/> </span><span style="color: #0000ff;">while</span> (av_read_frame(pFormatCtx, packet) &gt;= <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> </span><span style="color: #0000ff;">if</span> (packet-&gt;stream_index ==<span style="color: #000000;"> videoStream) {<br/> ret </span>= avcodec_decode_video2(pCodecCtx, pFrame, &amp;<span style="color: #000000;">got_picture,<br/> packet);<br/><br/> LOGI(</span><span style="color: #800000;">"</span><span style="color: #800000;">avcodec_decode_video2 ret:%d</span><span style="color: #800000;">"</span><span style="color: #000000;">, ret);<br/><br/> </span><span style="color: #0000ff;">if</span> (ret &lt; <span style="color: #800080;">0</span><span style="color: #000000;">) {<br/> LOGE(</span><span style="color: #800000;">"</span><span style="color: #800000;">decode error.\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/> }<br/><br/> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (got_picture) {<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">TODO 此处可以将解码出来的图片保存起来。</span><br/><span style="color: #000000;"> }<br/> }<br/> av_free_packet(packet);<br/> }<br/><br/> av_free(out_buffer);<br/> av_free(pFrameYUV);<br/> avcodec_close(pCodecCtx);<br/> avformat_close_input(</span>&amp;<span style="color: #000000;">pFormatCtx);<br/>}</span></div><p><strong>六、编写Android.mk用来编译相关的.c文件,内容如下:</strong></p><p></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">LOCAL_PATH := $(call my-<span style="color: #0000ff;">dir</span><span style="color: #000000;">)<br/><br/>include $(CLEAR_VARS)<br/><br/>FFMPEG_PATH :</span>= ../<span style="color: #000000;">ffmpeg<br/>LOCAL_C_INCLUDES :</span>= $(LOCAL_PATH)/$(FFMPEG_PATH)/<span style="color: #000000;">include<br/><br/>LOCAL_MODULE :</span>=<span style="color: #000000;"> ffmpeg_player<br/>LOCAL_SRC_FILES </span>+=<span style="color: #000000;"> ffmpeg_jni.c <br/>LOCAL_SRC_FILES </span>+=<span style="color: #000000;"> ffmpeg.c<br/><br/>LOCAL_SHARED_LIBRARIES :</span>=<span style="color: #000000;"> ffmpeg<br/>LOCAL_LDLIBS :</span>= -<span style="color: #000000;">llog<br/><br/>include $(BUILD_SHARED_LIBRARY)</span></div><p><strong>七、编写java文件中相关执行调用方法</strong></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">package</span><span style="color: #000000;"> com.clarck.android.ffmpeg;<br/><br/></span><span style="color: #0000ff;">import</span><span style="color: #000000;"> android.app.Activity;<br/></span><span style="color: #0000ff;">import</span><span style="color: #000000;"> android.os.Bundle;<br/><br/></span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> MainActivity <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Activity {<br/><br/> @Override<br/> </span><span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> onCreate(Bundle savedInstanceState) {<br/> </span><span style="color: #0000ff;">super</span><span style="color: #000000;">.onCreate(savedInstanceState);<br/> setContentView(R.layout.activity_main);<br/> <br/> setDataSource(</span>"/sdcard/a.mp4"<span style="color: #000000;">);<br/> }<br/><br/> </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">native</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> setDataSource(String path);<br/> <br/> </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> {<br/> System.loadLibrary(</span>"ffmpeg"<span style="color: #000000;">);<br/> System.loadLibrary(</span>"ffmpeg_player"<span style="color: #000000;">);<br/> }<br/>}</span></div><p><strong>八、执行结果如下图:</strong></p><p><img src="http://images.cnitblog.com/blog/233920/201409/012117432662381.png" alt="" /></p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3950090" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/tanlon/p/3950090.html" target="_blank">[原]如何在Android用FFmpeg解码图像</a>,转载请注明。</p>http://www.cnblogs.com/alljoyn/p/3950083.htmlAlljoyn瘦客户端库介绍(官方文档翻译 下) - alljoyn物联网由于其他事情耽误,这个翻译现在才完成。接上篇——4 瘦客户端核心库架构 由于AllJoyn瘦客户端核心库(AJTCL)必须运行在那些功耗受限、计算能力有限、资源紧缺的设备上,因此它无法像运行在通用型计算机系统上那样使用和AllJoyn标准核心库(AJSCL)一样的架构。 一个AJSL或服务进程的.....2014-09-01T13:27:00Z2014-09-01T13:27:00Zalljoyn物联网http://www.cnblogs.com/alljoyn/<p>由于其他事情耽误,这个翻译现在才完成。接上篇&mdash;&mdash;</p><p><strong><span style="font-size: 18px;">4 瘦客户端核心库架构</span></strong></p><p>  由于AllJoyn瘦客户端核心库(AJTCL)必须运行在那些功耗受限、计算能力有限、资源紧缺的设备上,因此它无法像运行在通用型计算机系统上那样使用和AllJoyn标准核心库(AJSCL)一样的架构。<br />  一个AJSL或服务进程的分层结构如图3所示。《Introduction to the AllJoyn Framework》一文描述了这些层次结构的更详尽细节。需要特别注意的是, 每个Alljoyn客户端或服务器程序都会以这种层次结构来构建AllJoyn应用。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012103038285320.jpg" alt="" width="631" height="389" /></p><p>  每个运行AJSCL的主机上至少都要运行一个路由程序。这个路由程序可以以单独的路由进程形式运行,或者寄生于某个应用程序中运行。AJSCL路由程序的分层结构如图4所示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012106410169901.jpg" alt="" width="739" height="513" /></p><p>  注意到,路由程序可以为路由节点之间路由消息的传递提供额外的支持,并能支持如Wi-Fi Direct的多重网络传输机制。这个功能可以有效地降低计算能力、功耗和内存的开销。<br />  我们很明显无法在嵌入式系统中运行如此数量的程序,所以AJTCL最大程度地缩减了在给定设备上运行所需的代码量。AJTCL只使用最基础的C运行环境,并通过借助其他设备的计算能力实现路由规则。如图5所示,对比AJSCL,AJTCL舍去了大部分AJSL系统开销。AJTL仅仅为总线挂件提供少量必须的API(应用程序接口),并将AllJoyn消息接口直接提供给程序员,而不是提供间接的接口函数。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012106587507116.jpg" alt="" width="553" height="268" /></p><p>  消息层没有提供抽象的传输机制,而是直接使用了用户数据块传输协议(UDP)和TCP协议。分层结构中的端口层非常简单,又几个简单必须的本地系统函数构成。为了是代码体积最小化,其完全以C语言写成。由于使用了这些优化机制,一个AJTCL系统只需25K字节的内存就可运行,而一个绑定了路由功能和C++版本客户端和服务器程序的应用可能需要十倍数额的内存开销,而一个Java语言版本的AllJoyn程序则需要40倍左右的开销。</p><p><strong><span style="font-size: 18px;">5 整合运行</span></strong></p><p>&nbsp;</p><p>  为了使本章的讨论更加具体,在此举了两个分布式系统的例子。第一个例子是一个最小化的AllJoyn系统,由一个运行在智能手机上的AllJoyn应用程序和一个简单的AJTCL设备构成。此例阐述了上文描述的受信路由关系。第二个例子稍微复杂一些,包括一个运行在无线路由器上的路由程序。<br />    注释  通常的情形是由一个运行OpenWRT系统的路由器来运行预装好的AllJoyn路由程序。此路由程序接受那些连接到Wi-Fi网络的瘦客户端库发来的非受信连接请求。<br />  少量AJTCL设备连接到路由器,并在基于AllJoyn的无线传感器网络中扮演传感器节点的角色,而一个通用型计算机则完成数据融合的工作。<br />    注释  在无线传感器网络中,数据融合是将一些不同的节点收集到的结果整合到一起的过程,或者将其结果与其他传感节点获得的结果融合到一起,以便做出决策。</p><p><strong><span style="font-size: 16px;">5.1 一个最小化的瘦客户端系统</span></strong></p><p>&nbsp;</p><p>  一个最小化的使用AJTCL的系统包括一个运行AJSCL的主机和一个瘦客户端设备。AJSCL给将要连接到它的瘦客户端提供AllJoyn路由功能,同时也为使用瘦客户端的应用提供平台。就如之所说,瘦客户端设备通常扮演传感器节点的角色,它向运行在主机上的应用程序发送信息。应用程序以某种方式处理这些信息,并向传感器节点发送一些命令,使其应对当前环境。<br />  考虑一种简单但可能欠考虑的情况,一个壁挂式恒温器控制着一个电炉,并在一个安卓设备上运行着一个控制应用。安卓设备上运行AJSCL,而壁挂式恒温器上运行着AJTCL。该系统可以用图6来表示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012110250786849.jpg" alt="" width="324" height="176" /></p><p>  在本例中,一个需求是壁挂式恒温器,其只能被安卓设备中对应的恒温器控制程序所控制。<br />尽管在本例的需求中说明了恒温器仅可被安卓设备控制,但需求也可以是恒温器连接到某个路由节点,再由该路由节点连接到应用程序。这意味着安卓应用程序应该与AllJoyn路由程序绑定在一起,而这个绑定在一起的路由程序和应用程序应该以一个路由节点的身份提供给瘦客户端使用。这种配置允许在AJTCL和路由节点/应用程序对中建立一种受信关系。<br />  应用程序接着请求与他绑定的路由程序以一个众所周知的命名方式向AJTCL发送一个&ldquo;安静的&rdquo;广播(例如,com.company.BusNode&lt;guid&gt;)。路由程序接着准备响应那些以之前广播的命名方式命名的安静的(单播)的回复。当瘦客户端出现时,它应当在关联的网络前缀(com.company.BusNode)上启用发现过程,如图7所示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012111410166250.jpg" alt="" width="377" height="232" /></p><p>  当路由节点收到一个对其之前&ldquo;安静地&rdquo;广播过的名字的明确的请求时,它将回应一个表明该名字是由此路由节点所广播的消息。接下来AJTCL会尝试连接到这个响应的路由节点。过程如图8所示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012112582031235.jpg" alt="" width="408" height="239" /></p><p>  这样一来,一个逻辑上的AllJoyn总线就已经建立起来了,应用程序和瘦客户端服务通过运行在安卓设备上的路由程序连接起来。以在《Introduction to the AllJoyn Framework》一文中使用过的泡泡图来表示该系统,这种配置看上去就像是AllJoyn路由节点连接了服务器程序和客户端程序,如图9所示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012114056889085.jpg" alt="" width="424" height="127" /></p><p>  此时,AJTCL已经连接到与应用程序绑定在一起的路由程序,但是应用程序和瘦客户端互相都不知道对方的存在。一般在此时,AJTCL便会请求一个众所周知的总线名,并会在AllJoyn的感知下实例化一个服务。如《Introduction to the AllJoyn Framework》一文所描述,瘦客户端会使用瘦客户端核心库的API接口创建一个对话端口并广播一个众所周知的名称。这个名称一般不会和路由节点广播的名称相同;它与瘦客户端和应用间的客户端/服务器的关系有关,而与路由节点&mdash;瘦客户端间的关系无关。运行在安卓设备上的应用程序将会针对这个名称运行发现服务,如图10所示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012116266259594.jpg" alt="" width="391" height="168" /></p><p>  当运行在AJTCL上的服务被运行在安卓设备上的客户端发现时,该客户端会加入此服务创建的对话。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012117368917228.jpg" alt="" width="381" height="130" /></p><p>  从这个角度来说,运行在安卓设备上的应用程序可以访问到AJTCL的服务,而且可以是任何AllJoyn服务。它可能通报服务发送的信号&mdash;&mdash;在此例中,可能是包含当前温度的周期信号。此应用可能构建一个用户界面,允许用户键入期望达到的温度,并将此温度使用如《Introduction to the AllJoyn Framework》一文所描述的AllJoyn远程方法发送给AJTCL。一旦收到一个呼叫方法,运行在AJTCL上的服务程序便会将请求转发到电炉以设置理想温度。<br />在瘦客户端上使用的API和在AJSCL或服务程序上使用的API有很大的不同;尽管在两种情况下,传输协议是完全一样的,但对于其中一方而言,另一方组件的性状是不可见的。从这方面而言,AllJoyn是独一无二的,而之前框图中的各个泡泡,包括瘦客户端,从其目的或行为来看都是没有区别的。</p><p><strong><span style="font-size: 16px;">5.2 一个基于瘦客户端库的无线传感器网络</span></strong><br />  本例描述了一个非常基础的家庭管理系统。该系统的无线接入点是一个运行OpenWRT的路由器,在其上预装了一个允许来自瘦客户端的非受信连接的AllJoyn路由程序。这样AJTCL客户端便可以通过连接到该路由节点接入系统。网络中的瘦客户端设备可以是温度传感器、运动检测传感器、电灯开关、热水器、电炉或空调。<br />  如之前所言,本例中的数据融合功能由一个运行在通用型计算机上的应用程序实现并整合显示。这并不是说在该网络中一定要有一个通用型计算机&mdash;&mdash;数据融合可以以其他方式实现;但是,在本例中的通用型计算机可以帮助我们理解AJSCL和瘦客户端设备是如何互动的。整合显示可以使用壁挂式的显示设备,或者简单地在家中的某个PC上显示。举例而言,该显示程序可以提供不同房间的温控器和温度计的用户接口;或者是虚拟的电灯开关,或运动检测仪。数据融合算法程序将会决定什么时候开灯,如何控制电炉或空调的开关,或者如何最有效地控制热水器的温度。<br />  要考虑的第一个组件是如图12所示的OpenWRT路由器。该路由器管控一个独立的AllJoyn路由域(在图中以黑色粗水平线表示,代表一个AllJoyn分布式软件总线的一个总线段)。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012119431255762.jpg" alt="" width="567" height="254" /></p><p>  在该路由器所在的总线段中有一个AllJoyn服务程序,该程序使用AllJoyn架构提供了一种方式来配置路由器和预装在路由器上的路由程序。此外,图中的一些空槽表示与AJTCL之间的非受信连接。由于这是一个通用AllJoyn路由器,对应的软件总线可以被扩展到其他的总线段以形成如图1所示的分布式总线。<br />  如之前的章节所述,AJTCL设备将会运行发现过程以搜寻他们能连接到的路由节点。尽管在此描述的是一个非受信关系,运行在OpenWRT路由器上的AllJoyn路由程序可以配置成&ldquo;安静地&rdquo;广播一个通用的名称,如org.alljoyn.BusNode,暗示该路由节点是一个AllJoyn分布式总线上的一个节点,并意图接管瘦客户端。<br />  代表传感器节点的AJTL设备通过登录过程接入无线网络。在此过程中,他们以所谓的友好的名称(即有意义的名称)来命名。举例说,一个电灯控制器(开-关-调光控制器)可能被命名为&ldquo;厨房&rdquo;,而另一个可能被命名为&ldquo;卧室&rdquo;。对应的瘦客户端节点开始探索他们连接的路由节点(可能是org.alljoyn.BusNode),并尝试连接。尽管上图中的很多&ldquo;槽&rdquo;假定是非受信的,瘦客户端设备还是可以如图13所示那样加入网络。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012121422505002.jpg" alt="" width="591" height="545" /></p><p>  一旦瘦客户端程序连接到了OpenWRT路由器所在的总线段,它们就会开始广播其对应的服务。如之前所假设的,这是一个家庭控制系统,连接到路由器提供的无线网络。该设备会尝试发现服务,并在系统中寻找瘦客户端库提供的服务,如图14所示。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012123070322668.jpg" alt="" width="620" height="389" /></p><p>一旦家庭控制系统发现了某个瘦客户端广播的服务,它将尝试与该瘦客户端开始对话。其结果是路由器所在的总线段和家庭控制系统融合成一个虚拟分布式总线。</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="http://images.cnitblog.com/blog/603253/201409/012124247972011.jpg" alt="" width="587" height="315" /></p><p><br /> <br /><br />  当这个融合的总线完全形成时,连接到总线的设备就成了一个标准的AllJoyn客户端或服务器。布式设备上的其他部件不会知道这些ALlJoyn瘦客户端传感器/执行器实际上是嵌入式设备,并通过TCP协议连接到AllJoyn路由节点,也不会知道家庭控制程序以Java编写并运行在一个运行安卓系统的通用型计算机上。这些客户端和服务器仅仅只是执行远程呼叫方法和收发信号。<br />  读者现在可以了解运行在数据融合节点上的算法。举例说明。在分布式总线上传输的一个重要的AllJoyn信号可能是与CARBON-MONOXIDE-DETECTED(检测到一氧化碳)对应的某种东西。这个信号将被家庭控制系统(数据融合中心)接收。家庭控制系统收到这个信号以后,可能会发送一个远程方法给某个执行节点,使其TURN-FAN_ON(打开风扇),它也可能发送一个远程方法给另一个执行节点,使其SOUND-ALARM(播放报警音),还可能发送短信给屋主,告诉他家中出现过量的一氧化碳。<br />  更为常见的情形下,家庭控制系统还可能向电炉发送一个远程方法,使其在房间中没人的情况下(通过运动检测装置的检测结果和日程表判断)降低房间温度。房屋控制单元可能向热水器发送一个消息,使其在工作时间和午夜降低水温,而在午夜洗碗器需要工作时向其发送一个呼叫方法使其提高水温,这样一来便可以在电费最低的时候工作。<br />  所有这些家庭控制系统响应的信号和发送的呼叫方法都与信号发送/接受设备的类型完全无关。</p><p><strong><span style="font-size: 18px;">6 总结</span></strong><br />  AllJoyn是一个综合的系统,其设计目的是为了在成分各异的系统上开发分布式应用程序。AJTCL使嵌入式设备可以加入AllJoyn分布式软件总线,并能以忽略细节的方式在系统中存在,而这一点正是开发人员所头疼的。这个成果可以让应用开发者专注于应用程序的内容,而不必考虑太多底层的、嵌入式系统网络方面的事情。<br />  AllJoyn系统可以以一个整体共同工作,而不像点对点网络,不同节点间固有的不匹配会造成很多问题。我们相信,与在其他平台上开发的应用相比,AllJoyn系统可以以更简单的方式构建包含嵌入式设备的更为强大的分布式应用。</p><p><strong><span style="font-size: 18px;">了解更多</span></strong><br />  想要了解更多关于如何在开发中使用AllJoyn的信息,请在AllSeen联盟的网站上浏览并下载相关文档:(www.allseenalliance.org)<br />    介绍型说明书&mdash;&mdash;描述了ALlJoyn技术和相关概念。<br />    开发型说明说&mdash;&mdash;介绍了如何构建环境,并提供了对于特殊问题的解决方案,包括代码片段的注释。<br />    API参考&mdash;&mdash;提供了在所有支持的编程语言中使用AllJoyn源代码编写应用程序的相关细节。<br />    下载&mdash;&mdash;软件开发包(SDK,Software Development Kits),提供了相关资源用于帮助用户编译、修改、测试和执行某个项目。</p><p>&nbsp;</p><p>看完了,大家有何感想呢??</p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3950083" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/alljoyn/p/3950083.html" target="_blank">Alljoyn瘦客户端库介绍(官方文档翻译 下)</a>,转载请注明。</p>http://www.cnblogs.com/csp277/p/3950049.html【原创】总结dSploit二次开发经历——开源代码的阅读和梳理 - 陈少鹏dSploit二次开发,apk替换2014-09-01T13:08:00Z2014-09-01T13:08:00Z陈少鹏http://www.cnblogs.com/csp277/该文只有注册用户登录后才能阅读。<a href='http://www.cnblogs.com/csp277/p/3950049.html' target='_blank'>阅读全文</a>。http://www.cnblogs.com/wangiqngpei557/p/3950048.html.NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务) - 王清培一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之间的互操作需要涉及数据库操作。两个业务逻辑对象彼此之间需要互相调用,如果之间的互相操作是在一个业务事务范围内的,很容易完成,但是如果本次业务逻辑操作涉及到多个业务对象一起协作完成时问题就来了。在以往,我们...2014-09-01T13:07:00Z2014-09-01T13:07:00Z王清培http://www.cnblogs.com/wangiqngpei557/<p><span style="font-size: 14pt;">阅读目录:</span></p><ul><li><span style="text-decoration: underline;"><span style="color: #0080c0; font-size: 14pt;">1.背景介绍 </span></span></li><li><span style="text-decoration: underline;"><span style="color: #0080c0; font-size: 14pt;">2.过程式代码的真正困境 </span></span></li><li><span style="text-decoration: underline;"><span style="color: #0080c0; font-size: 14pt;">3.工作单元模式的简单示例 </span></span></li><li><span style="text-decoration: underline;"><span style="color: #0080c0; font-size: 14pt;">4.总结 </span></span></li></ul><p><strong>1.背景介绍</strong></p><p><span style="font-size: medium;">一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之间的互操作需要涉及数据库操作。两个业务逻辑对象彼此之间需要互相调用,如果之间的互相操作是在一个业务事务范围内的,很容易完成,但是如果本次业务逻辑操作涉及到多个业务对象一起协作完成时问题就来了。</span></p><p><span style="font-size: medium;">在以往,我们使用过程式的代码(事务脚本模式),将所有与本次业务事务范围内相关的所有逻辑都写在一个大的代码中,就算你适当的提取重复代码,效果也不大,因为你永远都摆脱不了夸多个对象互相操作的困境。如何确认你是否在这个困境中,你只要看你的所有事务操作的入口都只有一个业务方法。比如当你添加一个订单的时候,你同时将订单跟随的商品都一起在&ldquo;添加订单&rdquo;的方法中处理的,而不是在另外一个&ldquo;添加订单商品&rdquo;的方法中,这两个方法位于不同的表模块类中。</span></p><p><span style="font-size: medium;">本章将介绍一个模式,此模式专门用来在开发企业应用系统时,协调多个业务对象在一个业务事务范围内,保证一个完整的事务。</span></p><p><strong>2.过程式代码的困境</strong></p><p><span style="font-size: medium;">其实开发应用系统与开发某个框架或者组件之间的最大区别就是需要考虑数据的持久化,而持久化的逻辑也是和业务逻辑息息相关的,某个方法的最后动作就有可能是添加一行数据或者更新一个字段。而非应用系统的代码往往在最后的时候才去统一刷新最终的持久化文件,而且此类程序很少存在事务性数据操作。就算有,使用内存事务处理也是比较简单的,不需要考虑那么多的服务端的事情。</span></p><p><span style="font-size: medium;">我之前也写过很多组件、框架,虽然谈不上什么复杂的东西,但是给我的经验和感悟就是,如何将其细致的设计粒度用在企业应用系统中,如何进行复杂而细致的OO设计开发。其实,如果我们不能够打破过程式代码的格局,那么看再多的OO知识也是心有余而力不足,反而会让你产生很多负面的情绪(因为我有过这个经历)。</span></p><p><span style="font-size: medium;">其实我们还是缺少正确的方法而已,本文中UnitOfWork模式将帮助我们走出过程式的业务逻辑,走向起码的面向对象开发。有了UnitOfWork你可以随意使用Table module 、Activa Record、Domin Driven 模式,而且你可以根据自己的项目需要将其在大的布局上进行SOA划分(CQRS),让各个模式在各自适合的场景中发挥极致。</span></p><p><strong>3.工作单元模式的简单示例</strong></p><p><span style="font-size: medium;">这里我们依然使用简单的订单购物业务作为示例来讲,毕竟大家都懂得这部分的的业务概念。</span><span style="font-size: medium;">本实例业务层使用Active Record模式。</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.Business<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Collections.Generic; <br/></span><span style="color: #008080;"> 4</span> <br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">partial</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Order<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">long</span> OId { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } <br/></span><span style="color: #008080;"> 8</span> <br/><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">public</span> List&lt;OrderProducts&gt; Products { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">11</span> }</div><p><span style="font-size: medium;"><span style="font-size: medium;">Order活动记录对象的字段部分。</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.Business<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">partial</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> Order<br/></span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> CheckOrder()<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 7</span> <span style="color: #008000;">//</span><span style="color: #008000;">执行部分业务验证工作</span><br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.OId &lt;= <span style="color: #800080;">0</span>) <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">; <br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">12</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">13</span> }</div><p><span style="font-size: medium;">Order活动记录对象主体,纯粹为了演示而用,包含了一个简单的判断业务逻辑。</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.Business<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">partial</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> OrderProducts<br/></span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">long</span> OrderId { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } <br/></span><span style="color: #008080;"> 6</span> <br/><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">long</span> PId { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; } <br/></span><span style="color: #008080;"> 8</span> <br/><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span> Price { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">set</span><span style="color: #000000;">; }<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">11</span> }</div><p><span style="font-size: medium;"><span style="font-size: medium;">订单商品部分字段。</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.Business<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">partial</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> OrderProducts<br/></span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> CheckProducts()<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 7</span> <span style="color: #008000;">//</span><span style="color: #008000;">执行部分业务验证工作</span><br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">this</span>.OrderId &lt;= <span style="color: #800080;">0</span>) <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">; <br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">12</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">13</span> }</div><p><span style="font-size: medium;">每一个商品都包含了自己的逻辑验证。</span></p><p><span style="font-size: medium;">我们接着看一下应用层入口方法是如何协调两个活动记录对象之间的业务操作和数据存储的。</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> OrderManager.Business;<br/></span><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> OrderManager.DataSource; <br/></span><span style="color: #008080;"> 5</span> <br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> OrderManagerController : ControllerBase<br/></span><span style="color: #008080;"> 7</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> AddOrder(Order order)<br/></span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">10</span> <span style="color: #0000ff;">using</span> (UnitOfWork unitOfWork = <span style="color: #0000ff;">new</span><span style="color: #000000;"> UnitOfWork())<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">12</span> order.CheckOrder();<span style="color: #008000;">//</span><span style="color: #008000;">执行业务检查 </span><br/><span style="color: #008080;">13</span> <br/><span style="color: #008080;">14</span> order.Products.ForEach(item =&gt;<br/><span style="color: #008080;">15</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">16</span> item.CheckProducts();<span style="color: #008000;">//</span><span style="color: #008000;">执行每个活动记录对象的业务检查,这里也可以使用表模块来处理。</span><br/><span style="color: #008080;">17</span> <span style="color: #000000;"> }); <br/></span><span style="color: #008080;">18</span> <br/><span style="color: #008080;">19</span> OrderGateway orderGateway = <span style="color: #0000ff;">new</span><span style="color: #000000;"> OrderGateway(unitOfWork);<br/></span><span style="color: #008080;">20</span> <span style="color: #0000ff;">var</span> orderDbResult = orderGateway.AddOrder(order);<span style="color: #008000;">//</span><span style="color: #008000;">第一个数据库表操作 </span><br/><span style="color: #008080;">21</span> <br/><span style="color: #008080;">22</span> OrderProductsGateway productGateway = <span style="color: #0000ff;">new</span><span style="color: #000000;"> OrderProductsGateway(unitOfWork);<br/></span><span style="color: #008080;">23</span> <span style="color: #0000ff;">var</span> productDbResult = productGateway.AddOrderProducts(order.Products);<span style="color: #008000;">//</span><span style="color: #008000;">第二个数据库表操作 </span><br/><span style="color: #008080;">24</span> <br/><span style="color: #008080;">25</span> <span style="color: #0000ff;">if</span> (orderDbResult &amp;&amp;<span style="color: #000000;"> productDbResult)<br/></span><span style="color: #008080;">26</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">27</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> (unitOfWork.Commit())<br/></span><span style="color: #008080;">28</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">29</span> <span style="color: #0000ff;">this</span>.SendOrderIntegrationMssage(order);<span style="color: #008000;">//</span><span style="color: #008000;">发送成功集成订单消息 </span><br/><span style="color: #008080;">30</span> <br/><span style="color: #008080;">31</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">32</span> <span style="color: #000000;"> } <br/></span><span style="color: #008080;">33</span> <br/><span style="color: #008080;">34</span> <span style="color: #0000ff;">this</span>.PushOrderProcessQueue(order);<span style="color: #008000;">//</span><span style="color: #008000;">将本次订单发送到处理队列中</span><br/><span style="color: #008080;">35</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">36</span> <span style="color: #000000;"> } <br/></span><span style="color: #008080;">37</span> <br/><span style="color: #008080;">38</span> <span style="color: #0000ff;">this</span>.LogBusinessException(order);<span style="color: #008000;">//</span><span style="color: #008000;">记录一个业务处理异常LOG,以备排查问题。</span><br/><span style="color: #008080;">39</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">40</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">41</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">42</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">43</span> }</div><p><span style="font-size: medium;">为了简单演示示例,我直接使用实例化的方式来构造数据访问对象,实际使用时可以使用IOC工具来动态注入。</span></p><p><span style="font-size: medium;">我们接着看一下数据层代码,数据层我使用表入口模式。</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.DataSource<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">abstract</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> GatewayBase<br/></span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">protected</span> UnitOfWork UnitOfWork { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">set</span><span style="color: #000000;">; } <br/></span><span style="color: #008080;"> 6</span> <br/><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">public</span><span style="color: #000000;"> GatewayBase(UnitOfWork unit)<br/></span><span style="color: #008080;"> 8</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">this</span>.UnitOfWork =<span style="color: #000000;"> unit;<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> } <br/></span><span style="color: #008080;">11</span> <br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> Commit()<br/></span><span style="color: #008080;">13</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">14</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.UnitOfWork.Commit();<br/></span><span style="color: #008080;">15</span> <span style="color: #000000;"> } <br/></span><span style="color: #008080;">16</span> <br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> Rollback()<br/></span><span style="color: #008080;">18</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">19</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.UnitOfWork.Rollback();<br/></span><span style="color: #008080;">20</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">21</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">22</span> }</div><p><span style="font-size: medium;"><span style="font-size: medium;">这是一个表入口基类。</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.DataSource<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> OrderManager.Business; <br/></span><span style="color: #008080;"> 4</span> <br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> OrderGateway : GatewayBase<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">public</span> OrderGateway(UnitOfWork unit) : <span style="color: #0000ff;">base</span><span style="color: #000000;">(unit) { } <br/></span><span style="color: #008080;"> 8</span> <br/><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> AddOrder(Order order)<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">11</span> <span style="color: #008000;">//</span><span style="color: #008000;">这里可以使用你所熟悉的拼接SQL的方式直接操作数据库,而不需要ORM。</span><br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">13</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">14</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">15</span> }</div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.DataSource<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> OrderManager.Business;<br/></span><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> System.Collections.Generic; <br/></span><span style="color: #008080;"> 5</span> <br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> OrderProductsGateway : GatewayBase<br/></span><span style="color: #008080;"> 7</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">public</span> OrderProductsGateway(UnitOfWork unit) : <span style="color: #0000ff;">base</span><span style="color: #000000;">(unit) { } <br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span> AddOrderProducts(List&lt;OrderProducts&gt;<span style="color: #000000;"> products)<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">12</span> <span style="color: #008000;">//</span><span style="color: #008000;">这里可以使用你所熟悉的拼接SQL的方式直接操作数据库,而不需要ORM。</span><br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">14</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">15</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">16</span> }</div><p><span style="font-size: medium;">这是两个表入口对象,其实这部分代码是大家都比较熟悉的,所以我这里省略了,你可以直接拼接SQL语句来插入数据库。</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> OrderManager.DataSource<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">using</span><span style="color: #000000;"> System; <br/></span><span style="color: #008080;"> 4</span> <br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> UnitOfWork : IDisposable<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> Dispose()<br/></span><span style="color: #008080;"> 8</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> NotImplementedException();<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> } <br/></span><span style="color: #008080;">11</span> <br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">bool</span><span style="color: #000000;"> Commit()<br/></span><span style="color: #008080;">13</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">14</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">15</span> <span style="color: #000000;"> } <br/></span><span style="color: #008080;">16</span> <br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> Rollback()<br/></span><span style="color: #008080;">18</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">19</span> <span style="color: #008000;">//<br/></span><span style="color: #008080;">20</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">21</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">22</span> }</div><p>&nbsp;</p><p><span style="font-size: medium;">UnitOfWrok对象其实就是对数据库对象的System.Data.Common.DbConnection对象的封装,这里你可以使用你熟悉的方式来构造这个数据库连接对象和开启事务。</span></p><p><span style="font-size: medium;">其实值得我们去欣赏的是应用控制器中的代码,在这里很协调的处理各个逻辑,最后记录下一些必要的日志和发送一些集成消息。你是不是发现你完全可以不使用DDD也可以处理部分业务系统了。</span></p><p><strong>4.总结</strong></p><p><span style="font-size: medium;">活动记录模式+表入口模式+工作单元模式,其实我觉得可以很好的处理中小型业务逻辑,随着现在SOA化架构,很少再有多大的项目在一个解决方案里面。</span></p><p><span style="font-size: medium;">最后还是那句话,提供一个参考资料,如果有兴趣可以进一步交流具体的设计,由于时间关系文章就到这里了,谢谢大家。</span></p><p>&nbsp;</p><div id="Copyright"><p><span style="font-size: 15px;">作者:<a href="http://www.cnblogs.com/wangiqngpei557/">王清培</a></span></p><p><span style="font-size: 15px;">出处:<a href="http://www.cnblogs.com/wangiqngpei557/" target="_blank">http://www.cnblogs.com/wangiqngpei557/</a></span></p><p><span style="font-size: 15px;">本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面</span></p></div><img src="http://counter.cnblogs.com/blog/rss/3950048" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/wangiqngpei557/p/3950048.html" target="_blank">.NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)</a>,转载请注明。</p>http://www.cnblogs.com/GuoJiaSheng/p/3950001.htmlJava 从单核到多核的多线程(并发) - 郭佳盛哈哈JAVA 并发 java的并行编程比较复杂,我也理解不深。但是最近由于要并行训练分类器,琢磨了一点,有错误请指正。只是大体介绍一下而已。 很多问题我们使用顺序编程便可以解决,但是有些问题如果能够使用多线程并行的执行其中的任务则可以很大程度的提高时间效率,所以多线程还是很有必要的。 我自己...2014-09-01T13:04:00Z2014-09-01T13:04:00Z郭佳盛哈哈http://www.cnblogs.com/GuoJiaSheng/<p style="text-align: center;"><strong>JAVA 并发</strong></p><p style="text-align: left;"><strong>&nbsp; &nbsp; &nbsp; java的并行编程比较复杂,我也理解不深。但是最近由于要并行训练分类器,琢磨了一点,有错误请指正。只是大体介绍一下而已。</strong></p><p style="text-align: left;">&nbsp; &nbsp; &nbsp; 很多问题我们使用顺序编程便可以解决,但是有些问题如果能够使用多线程并行的执行其中的任务则可以很大程度的提高时间效率,所以多线程还是很有必要的。</p><p style="text-align: left;">&nbsp; &nbsp; &nbsp; 我自己总结了JAVA并行的3个发展阶段(菜鸟总结,请体谅)</p><p style="text-align: left;">&nbsp; &nbsp; &nbsp; 第一阶段:Thread ,Runable &nbsp;</p><p style="text-align: left;">&nbsp; &nbsp; &nbsp; 第二阶段:Executor框架</p><p style="text-align: left;">&nbsp; &nbsp; &nbsp; 第三阶段: <span class="tcnt">ForkJoin并行框架 &nbsp;</span></p><p style="text-align: left;"><span class="tcnt">&nbsp; &nbsp; &nbsp; 并发很大一方面是为了提高程序运行速度,如果想要一个程序运行的更快,那么可以将其分为多个片段,然后在每个单独的处理器上运行多个任务。现在是多核时代我们应该掌握,</span></p><p style="text-align: left;"><span class="tcnt"><strong>&nbsp; &nbsp; &nbsp; 但是我们知道并发通常是用在提高单核机器的程序性能上,这个咋一听感觉有点不能理解,学过操作系统的人应该知道,从一个任务切换到另一个任务是会有上下文开销的。</strong>但是因为有了&ldquo;<strong>阻塞</strong>&rdquo;,使得这个问题变得有些不同。</span></p><p style="text-align: left;"><span class="tcnt">&nbsp; &nbsp; &nbsp;&ldquo;阻塞&rdquo;通常指的是一个程序的某个任务由于要执行程序控制之外的事,比如请求I/O资源,由于需要等待请求的I/O资源,所以会导致程序的暂停,就是cpu空闲。我们知道cpu是很宝贵的资源,我们应当充分利用它才对,这时候多线程就出来了,想想啊,当某个线程阻塞导致cpu空闲,这时候操作系统就将cpu分配给其他等待的线程使得cpu无时无刻不在运行。单个进程可以有多个并发执行的任务,我们感觉好像每个任务都有自己的cpu一样,其底层机制就是切分cpu时间,通常来说不需要我们管。</span></p><p style="text-align: left;"><span class="tcnt">&nbsp; &nbsp;<strong>&nbsp; &nbsp;从事实上来看,如果程序没有任何阻塞,那么在单处理器上的并发是没有意义的。</strong></span></p><p style="text-align: left;"><span class="tcnt"><strong>&nbsp; &nbsp;(1)传统的并发编程(Thead、Runable)</strong></span></p><p style="text-align: left;"><span class="tcnt"><strong>&nbsp; &nbsp; </strong>&nbsp; &nbsp; &nbsp; 我们看到最多的就是继承Thread类或者继承Runable接口。这两中方式都可以,如下继承Thread:</span></p><p style="text-align: left;"><span class="tcnt"><strong>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</strong></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> App {<br/><br/> <br/> </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">class</span> demo <span style="color: #0000ff;">extends</span><span style="color: #000000;"> Thread <br/> {<br/> </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> x;<br/> </span><span style="color: #0000ff;">public</span> demo(<span style="color: #0000ff;">int</span><span style="color: #000000;"> x)<br/> {<br/> </span><span style="color: #0000ff;">this</span>.x=<span style="color: #000000;">x;<br/> }<br/> </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {<br/> System.out.print(</span>"线程"+<span style="color: #000000;">x);<br/> } <br/> }</span><br/> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {<br/><br/> demo dem</span>=<span style="color: #0000ff;">new</span> demo(1<span style="color: #000000;">);<br/> dem.start();<br/> <br/> <br/> }<br/><br/>}</span></div><p>&nbsp; &nbsp; &nbsp; 无论是Thread还是Runable其实都只要我们覆盖实现Run方法就好了,但是由于java类只能继承一次而接口可以有无数个所以我们更经常使用Ruanble接口。我们调用新线程都是使用start()方法而不是run()方法。</p><p>&nbsp; &nbsp; &nbsp; start方法的本质:从cpu中申请另一个线程空间来执行run方法,这样是并发线程。(其实它也是会自己调用run里面的方法,但是如果我们直接调用run方法的话,那么就是单线程而已)</p><p>&nbsp; &nbsp; &nbsp;以上两种虽然可以实现基本的并行结构,但是对于复杂的问题就会很麻烦,因此就有了在jdk5里面引入的Excutor执行器。</p><p><strong>(2)Executor框架</strong></p><p>&nbsp; &nbsp; &nbsp; &nbsp; Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。</p><p>&nbsp; &nbsp; &nbsp; &nbsp; Executor用来管理Runable对象的执行。用来创建并管理一组Runable对象的线程,这组线程就叫做线程池(Thread pool)</p><p>&nbsp; &nbsp; &nbsp; &nbsp; 并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,<strong>Executor.execute(Runnalbe)</strong>&nbsp;。Executor在执行时使用内部的线程池完成操作。</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> Thread t1 =new Thread(s1); <br/> Thread t2 =new Thread(s2); <br/> Thread t3 =new Thread(s3); <br/><br/> ExecutorService service = Executors.newCachedThreadPool(); <br/> service.execute(t1); <br/> service.execute(t2); <br/> service.execute(t3); <br/></div><p>   &nbsp; 在Executor里面。我们可以使用<strong>Callable,Future返回结果,</strong>Future&lt;V&gt;代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞。FutureTask&lt;V&gt;实现了Future&lt;V&gt;和Runable&lt;V&gt;。Callable代表一个有返回值得操作。</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> ThreadPoolExecutor myExecutor = <span style="color: #0000ff;">new</span> ThreadPoolExecutor(3, 10, 200<span style="color: #000000;">, TimeUnit.SECONDS,<br/> </span><span style="color: #0000ff;">new</span> LinkedBlockingDeque&lt;Runnable&gt;<span style="color: #000000;">());<br/> List</span>&lt;Future&lt;ArrayList&lt;Integer&gt;&gt;&gt; results = <span style="color: #0000ff;">new</span> ArrayList&lt;Future&lt;ArrayList&lt;Integer&gt;&gt;&gt;<span style="color: #000000;">();<br/> <br/> </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0; i &lt; 3; i++<span style="color: #000000;">) {<br/> <br/> AcoTask task </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> AcoTask(choose);<br/> Future</span>&lt;ArrayList&lt;Integer&gt;&gt; result = <span style="color: #0000ff;">null</span><span style="color: #000000;">;<br/> result </span>=<span style="color: #000000;"> myExecutor.submit(task);<br/> results.add(result);<br/> }<br/> <br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成</span><br/> <span style="color: #0000ff;">for</span> (Future&lt;ArrayList&lt;Integer&gt;&gt;<span style="color: #000000;"> f : results) {<br/> </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {<br/> f.get();<br/> } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception ex) {<br/></span><span style="color: #008000;">//</span><span style="color: #008000;"> ex.printStackTrace();</span><br/> f.cancel(<span style="color: #0000ff;">true</span><span style="color: #000000;">);<br/> }<br/> }</span></div><p>&nbsp; &nbsp;上面并发执行的挺好的,但是有个问题。不同的线程执行有块有慢,有的任务会提早执行完毕,因此为了利用这些提早执行完毕的线程,使用了一种工作窃取(work-stealing)算法。</p><p><strong>&nbsp; (3)&nbsp;<span class="tcnt">ForkJoin并行框架 &nbsp;</span></strong></p><p><span class="tcnt">&nbsp; &nbsp; Fork/Join框架是Java7提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。是不是很像map/reduce。</span></p><p><span class="tcnt">&nbsp; &nbsp;&nbsp;<img src="http://images.cnitblog.com/blog/633472/201409/012049337503037.png" alt="" />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<img src="http://images.cnitblog.com/blog/633472/201409/012050015783502.png" alt="" /></span></p><p style="text-align: left;"><span class="tcnt"><strong>&nbsp; &nbsp;</strong></span></p><p style="text-align: left;"><span class="tcnt">&nbsp; &nbsp; &nbsp; </span></p><p style="text-align: left;"><span class="tcnt">&nbsp; &nbsp; &nbsp;Fork/Join 模式有自己的适用范围。如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。<span lang="EN-US">ForkJoin是将一个问题递归分解成子问题,再将子问题并行运算后合并结果。</span></span></p><p>&nbsp; &nbsp; 让我们通过一个简单的需求来使用下Fork/Join框架,需求是:计算1+2+3+4的结果。</p><p>使用Fork/Join框架首先要考虑到的是如何分割任务,如果我们希望每个子任务最多执行两个数的相加,那么我们设置分割的阈值是2,由于是4个数字相加,所以Fork/Join框架会把这个任务fork成两个子任务,子任务一负责计算1+2,子任务二负责计算3+4,然后再join两个子任务的结果。</p><p>因为是有结果的任务,所以必须继承RecursiveTask。</p><p>&nbsp; &nbsp; 我们只需要关注子任务的划分和中间结果的组合。ForkJoinTask完成子任务的划分,然后将它提交给ForkJoinPool来完成应用。</p><p>&nbsp; &nbsp; 关于Fork/Join的代码查看 : &nbsp; &nbsp;<a href="http://blog.csdn.net/lubeijing2008xu/article/details/18036931">http://blog.csdn.net/lubeijing2008xu/article/details/18036931</a></p><p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a href="http://www.oracle.com/technetwork/cn/articles/java/fork-join-422606-zhs.html">http://www.oracle.com/technetwork/cn/articles/java/fork-join-422606-zhs.html</a></p><p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<a href="http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/">http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/</a></p><p><strong style="font-size: 14px; line-height: 1.5;">&nbsp;&nbsp;</strong></p><p style="text-align: left;"><strong>&nbsp; &nbsp;&nbsp;</strong></p><img src="http://counter.cnblogs.com/blog/rss/3950001" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/GuoJiaSheng/p/3950001.html" target="_blank">Java 从单核到多核的多线程(并发)</a>,转载请注明。</p>http://www.cnblogs.com/aspnetx/p/3949984.html[译]SSAS下玩转PowerShell(三) - 哥本哈士奇(aspnetx)在第一篇中简单介绍了PowerShell,包含基本的一些命令,以及如何打开PowerShell,并且导航到SSAS对象。第二篇中学习了如何使用变量根据当前日期创建SSAS备份,以及如何运行MDX和XMLA脚本。2014-09-01T12:38:00Z2014-09-01T12:38:00Z哥本哈士奇(aspnetx)http://www.cnblogs.com/aspnetx/<p>在<a href="http://www.cnblogs.com/aspnetx/p/3751392.html">第一篇</a>中简单介绍了PowerShell,包含基本的一些命令,以及如何打开PowerShell,并且导航到SSAS对象。<a href="http://www.cnblogs.com/aspnetx/p/3751392.html">第二篇</a>中学习了如何使用变量根据当前日期创建SSAS备份,以及如何运行MDX和XMLA脚本。</p><p>&nbsp;</p><p><strong>原文地址: </strong></p><p><a href="http://www.mssqltips.com/sqlservertip/2980/using-powershell-for-ssas-to-check-service-status-and-database-availability/">http://www.mssqltips.com/sqlservertip/2980/using-powershell-for-ssas-to-check-service-status-and-database-availability/</a></p><p>&nbsp;</p><p>&nbsp;</p><p>在这一篇中,主要介绍以下几个话题:</p><p>- 使用PowerShell命令验证SSAS服务的状态</p><p>- 如果SSAS服务当掉自动发送消息</p><p>- 验证SSAS数据库的硬盘使用情况</p><p>- 每小时运行脚本验证数据库的状态</p><p>&nbsp;</p><p><strong>环境: </strong></p><p><a href="http://msftdbprodsamples.codeplex.com/releases/view/55330">Adventureworks微软示例项目。</a></p><p>SQL Server 2008之后的版本。</p><p>&nbsp;</p><p><strong>开始: </strong></p><p>1. 首先使用PowerShell获取Windows Services状态。</p><p><span style="background-color: #e7e6e6;">get-service </span></p><p>这个命令返回当前的Windows Services以及其状态:</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037384074711.jpg" alt="" /></p><p>2. 此篇中我们关注的是SSAS服务,所以命令将会如下所示:</p><p><span style="background-color: #e7e6e6;">get-service | select status, name | Where-Object {$_.Name -like "*MSSQLServerOLAP*"} </span></p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037401255153.jpg" alt="" /></p><p>这条命令显示名称中包含"MSSQLServerOLAP"的服务名称以及服务状态,可以看到服务当前的状态是开启还是停止。</p><p>3. 接下来将实现如果服务停止,自动发送信息。</p><p><span style="background-color: #e7e6e6;">$servicestatus=get-service | select status,name | Where-Object {$_.Name -like "*MSSQLServerOLAP*"} </span></p><p><span style="background-color: #e7e6e6;">$Message="The SSAS Service is down" </span></p><p><span style="background-color: #e7e6e6;">if($servicestatus.status -eq "Stopped"){ </span></p><p><span style="background-color: #e7e6e6;">echo $Message </span></p><p><span style="background-color: #e7e6e6;"> } </span></p><p>这条命令如果监测到SSAS服务停止,将显示消息"The SSAS Service is down"。</p><p>4. 以下命令显示磁盘的剩余空间。</p><p><span style="background-color: #e7e6e6;">$driveinformation=gwmi win32_volume -Filter 'drivetype = 3' | select driveletter, label, @{LABEL='GBfreespace';EXPRESSION={$_.freespace/1GB} } </span></p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037437503279.jpg" alt="" /></p><p>这条命令以GB为单位显示每个磁盘的剩余空间,当我们需要知道SSAS是否有足够的剩余空间的时候,这条命令很有用。</p><p>5. 为了验证SSAS下Cube的状态可以每五分钟发送一条MDX语句。在此篇我们将创建一条简单的MDX脚本然后以每小时的方式运行以此验证Cube是处于在线状态。</p><p>6. 打开SSMS。</p><p>7. 连接到SSAS服务并且浏览Adentureworks多维数据集。</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037449225821.jpg" alt="" /></p><p>8. 拖拽Internet Gross Profit到透视表区域,然后切换到MDX视图。</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037507666303.jpg" alt="" /></p><p>9. 自动生成的查询如下所示:</p><p><span style="background-color: #e7e6e6;">SELECT NON EMPTY { [Measures].[Internet Gross Profit] } ON COLUMNS FROM [Adventure Works] CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS </span></p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037550635057.jpg" alt="" /></p><p>10. 保存脚本为Adventure.mdx文件。</p><p>11. 接下来使用PowerShell脚本来调用这条查询。如果没有结果或者一条没有处理的错误消息返回,自动返回连接错误的信息。</p><p><span style="background-color: #e7e6e6;">$result=Invoke-ASCmd -Database "Adventureworks" -InputFile:"c:\scripts\Adventure.mdx" </span></p><p><span style="background-color: #e7e6e6;">$Message="Adventureworks had a connection error" </span></p><p><span style="background-color: #e7e6e6;">if ((!$result) -or ($resultado -like "*either does not exist or has not been processed*")){$message} </span></p><p>12. 为了让PowerShell脚本周期的运行,在SSMS下创建一个作业:</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012037572356725.jpg" alt="" /></p><p>13. 给作业取一个名字,然后创建一个新步骤。</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012038008758080.jpg" alt="" /></p><p>14. 指定一个步骤名称,在Type下选择PowerShell然后在命令区粘贴第11步的脚本,然后点击OK。</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012038058135706.jpg" alt="" /></p><p>然后选择Schedules页面,点击New按钮。</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012038116571487.jpg" alt="" /></p><p>输入schedule信息,在这里我们设置为每小时运行来验证数据库的状态。</p><p><img src="http://images.cnitblog.com/blog/8622/201409/012038168444569.jpg" alt="" /></p><p>&nbsp;</p><p>至此,一个每小时验证SSAS数据库状态的作业创建完成。希望大家喜欢这篇。</p><p>&nbsp;</p><p><strong>相关内容: </strong></p><p>PowerShell创建自动化任务是一个不错的工具,更多信息请参考以下链接:</p><p><a href="http://technet.microsoft.com/en-us/library/hh849804.aspx">http://technet.microsoft.com/en-us/library/hh849804.aspx</a></p><p><a href="http://technet.microsoft.com/en-us/library/ee177028.aspx">http://technet.microsoft.com/en-us/library/ee177028.aspx</a></p><p><a href="http://blogs.technet.com/b/flaphead/archive/2006/09/12/455555.aspx">http://blogs.technet.com/b/flaphead/archive/2006/09/12/455555.aspx</a></p><p>在MSSQLTips.com上阅读更多关于PowerShell的内容。</p><p><a href="http://www.mssqltips.com/sql-server-tip-category/81/powershell/"><span style="font-family: Verdana; font-size: 10pt;">http://www.mssqltips.com/sql-server-tip-category/81/powershell/</span></a></p><img src="http://counter.cnblogs.com/blog/rss/3949984" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/aspnetx/p/3949984.html" target="_blank">[译]SSAS下玩转PowerShell(三)</a>,转载请注明。</p>http://www.cnblogs.com/wangfupeng1988/p/3949931.htmljs便签笔记(12)——浏览TOM大叔博客的学习笔记 part2 - 王福朋1. 前言昨天写了《js便签笔记(11)——浏览TOM大叔博客的学习笔记 part1》,简单记录了几个问题。part1的重点还是在于最后那个循环创建函数的问题,也就是多个子函数公用一个闭包数据的问题。如果觉得有兴趣,可以再重新翻出来看看。今天继续把剩下的问题写完。2. 作用域链学js的人,即使初级入...2014-09-01T12:05:00Z2014-09-01T12:05:00Z王福朋http://www.cnblogs.com/wangfupeng1988/<p><strong>1. 前言</strong></p><p>昨天写了《<a id="homepage1_HomePageDays_DaysList_ctl01_DayList_TitleUrl_0" href="http://www.cnblogs.com/wangfupeng1988/p/3948113.html">js便签笔记(11)&mdash;&mdash;浏览TOM大叔博客的学习笔记 part1</a>》,简单记录了几个问题。part1的重点还是在于最后那个循环创建函数的问题,也就是多个子函数公用一个闭包数据的问题。如果觉得有兴趣,可以再重新翻出来看看。</p><p>今天继续把剩下的问题写完。</p><p><strong>2. 作用域链</strong></p><p>学js的人,即使初级入门的也都知道&ldquo;原型链&rdquo;,但是&ldquo;作用域链&rdquo;,可能好多人没有听说过。大部分人都知道或者听说过&ldquo;闭包&rdquo;,但是可能有好多人不知道闭包其实和作用域链有莫大的联系。如果理解闭包不从作用域链开始理解,那么你就只能理解闭包的皮毛。</p><p>我也是从TOM大叔的这些博客中才了解到作用域链的,之前也看过了许多本书籍,都没有很清晰的展开作用域链这个概念。其实作用域链简单说来也好理解,如下代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> <span style="color: #0000ff;">var</span> x = 10<span style="color: #000000;">;<br/> </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> fn() {<br/> </span><span style="color: #0000ff;">var</span> y = 20<span style="color: #000000;">;<br/> </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> () {<br/> </span><span style="color: #0000ff;">var</span> z = 30<span style="color: #000000;"><br/> console.log(x </span>+ y +<span style="color: #000000;"> z);<br/> }<br/> }</span></div><p>上面代码中,如果想要打印 x+y+z 的值,就必须要遍历三个层次的上下文环境或者作用域,这其实和原型链的结构表现形式类似。但要细细将来,连同闭包图文并茂的说明白,需要很多内容。</p><p>此处不再深入进去,以后有机会再另起一篇详细介绍。</p><p><strong>3. 二维链查找&nbsp;</strong></p><p>上文讲到通过作用域练向上查找变量,实际在查找变量的过程中,是使用&ldquo;二维链查找&rdquo;&mdash;&mdash;&ldquo;作用域链&rdquo; + &ldquo;原型链&rdquo;。看如下代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> Object.prototype.x = 10<span style="color: #000000;">;<br/> </span><span style="color: #0000ff;">function</span><span style="color: #000000;"> fn() {<br/> </span><span style="color: #0000ff;">var</span> y = 20<span style="color: #000000;">;<br/> </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> () {<br/> </span><span style="color: #0000ff;">var</span> z = 30<span style="color: #000000;"><br/> console.log(x </span>+ y +<span style="color: #000000;"> z);<br/> }<br/> }</span></div><p>这份代码跟上文中演示作用域链的代码差不多,但是它却通过 Object.prototype.x = 10; 这么一句话,表现出了原型链在其中的作用。<br />因此,在查找变量值时,是同时兼顾原型链和作用域链两个方向的,即&ldquo;二维链查找&rdquo;。&nbsp;</p><p><strong>4. 独立作用域只能通过函数来创建</strong></p><p>这句话的下半句是&mdash;&mdash;不能通过if/for等语句块来创建。后半句大家可能知道,但是它的本质确实前半句&mdash;&mdash;独立作用域只能通过函数来创建(除了独立作用域之外,剩下的就是全局作用域)。既然独立作用域只能通过函数来创建,那么函数中任何地方的自由变量就都是函数层级的,因此,以下代码希望不要再次出现:</p><p><img src="http://images.cnitblog.com/blog/138012/201409/011902066888518.x-png" alt="" /></p><p><strong>5. 隐式全局变量的本质</strong></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">var</span> a = 10<span style="color: #000000;">;<br/>b </span>= 20;</div><p>以上两句代码,看似都是声明两个全局变量,但是按照TOM大叔说的,只有var才能声明一个变量,也就是 var a = 10; 是真正的声明变量。</p><p>而下一句 b = 20,其实是相当于设置window的一个属性值而已。</p><p>因此,第一句的本质是声明一个全局变量;第二句的本质是设置window的一个属性值。</p><p>当然,不推荐用第二句的形式。</p><p><strong>6. 函数声明和函数表达式的不同</strong></p><p>js定义函数的方法有多种,但看看以下这段代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #000000;">fn();<br/></span><span style="color: #0000ff;">var</span> fn = <span style="color: #0000ff;">function</span>() { <span style="color: #008000;">//</span><span style="color: #008000;">函数表达式</span><br/> alert(123); <span style="color: #008000;">//</span><span style="color: #008000;"> 报错</span><br/><span style="color: #000000;">}<br/></span><span style="color: #008000;">//</span><span style="color: #008000;">------</span><br/><span style="color: #000000;">fn();<br/></span><span style="color: #0000ff;">function</span> fn() { <span style="color: #008000;">//</span><span style="color: #008000;">函数声明</span><br/> alert(123); <span style="color: #008000;">//</span><span style="color: #008000;"> 123</span><br/>}</div><p>两种函数定义方式,却得出不一样的结果。</p><p>此处我当时没有详细看,因为这样使用的情况不是很多,所以就没有过深入的细看,只是做了个标记。如果有了解的朋友,不放解释一下。</p><p><strong>7.js使用静态作用域</strong></p><p>在part1中讲过,当一个函数作为参数被传入,后者作为一个值被返回的时候,连同它一块被传递的,是它的作用域。也就是咱们常说的闭包。且看如下代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">var</span> x = 10<span style="color: #000000;">; <br/></span><span style="color: #0000ff;">function</span><span style="color: #000000;"> foo() {<br/> alert(x);<br/>}<br/>(</span><span style="color: #0000ff;">function</span><span style="color: #000000;"> (funarg) {<br/> </span><span style="color: #0000ff;">var</span> x = 20<span style="color: #000000;">;<br/> funarg(); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 10, 不是20</span><br/>})(foo); </div><p>foo是一个函数,把它作为参数传入进另一个函数中执行,连同一起传递的,是foo的作用域。而foo使用的是静态作用域,其中的变量x在传递的时候已经被静态赋值,不会受其他环境下x变量的影响。<br />这个道理也同样适用于函数作为返回值。如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">function</span><span style="color: #000000;"> fn() {<br/> </span><span style="color: #0000ff;">var</span> x = 10<span style="color: #000000;">;<br/> </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> () {<br/> alert(x);<br/> }<br/>}<br/></span><span style="color: #0000ff;">var</span> ret =<span style="color: #000000;"> fn();<br/></span><span style="color: #0000ff;">var</span> x = 20<span style="color: #000000;">;<br/>ret(); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 10,不是20</span></div><p>&nbsp;</p><p><strong>更多内容请关注<a href="http://www.weibo.com/madai01" target="_blank">我的微博</a></strong></p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949931" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/wangfupeng1988/p/3949931.html" target="_blank">js便签笔记(12)——浏览TOM大叔博客的学习笔记 part2</a>,转载请注明。</p>http://www.cnblogs.com/clover-toeic/p/3949896.html嵌入式系统C编程之堆栈回溯 - clover_toeic前言 在嵌入式系统C语言开发调试过程中,常会遇到各类异常情况。一般可按需添加打印信息,以便观察程序执行流或变量值是否异常。然而,打印操作会占用CPU时间,而且代码中添加过多打印信息时会显得很凌乱。此外,即使出错打印已非常详尽,但仍难以完全预防和处理段违例(Segment Violation)等错误....2014-09-01T11:49:00Z2014-09-01T11:49:00Zclover_toeichttp://www.cnblogs.com/clover-toeic/<p>&nbsp;</p><p><strong><span style="font-size: 16px;">前言</span></strong></p><p align="left">&nbsp; &nbsp; &nbsp;在嵌入式系统C语言开发调试过程中,常会遇到各类异常情况。一般可按需添加打印信息,以便观察程序执行流或变量值是否异常。然而,打印操作会占用CPU时间,而且代码中添加过多打印信息时会显得很凌乱。此外,即使出错打印已非常详尽,但仍难以完全预防和处理段违例(Segment Violation)等错误。在没有外部调试器(如gdb server)可用或无法现场调试的情况下,若程序能在突发崩溃时自动输出函数的调用堆栈信息(即堆栈回溯),那么对于排错将会非常有用。</p><p align="left">&nbsp; &nbsp; &nbsp;本文主要介绍嵌入式系统C语言编程中,发生异常时的堆栈回溯方法。文中涉及的代码运行环境如下:</p><p align="center"><img src="http://images.cnitblog.com/blog/569008/201409/011936570161878.jpg" alt="" />&nbsp;</p><p align="left">&nbsp; &nbsp; &nbsp;本文假定读者已具备函数调用栈、信号处理等方面的知识。相关性文章也可参见:</p><p align="left">&nbsp; &nbsp; &nbsp;<a href="http://www.cnblogs.com/clover-toeic/p/3755401.html" target="_blank">《C语言函数调用栈(一)》</a></p><p align="left">&nbsp; &nbsp; &nbsp;<a href="http://www.cnblogs.com/clover-toeic/p/3756668.html" target="_blank">《C语言函数调用栈(二)》</a></p><p align="left">&nbsp; &nbsp; &nbsp;<a href="http://www.cnblogs.com/clover-toeic/p/3757091.html" target="_blank">《C语言函数调用栈(三)》</a></p><p align="left">&nbsp; &nbsp; &nbsp;<a href="http://www.cnblogs.com/clover-toeic/p/3919857.html" target="_blank">《嵌入式系统C编程之错误处理》</a></p><p align="left">&nbsp;</p><p align="left">&nbsp;</p><p><strong><span style="font-size: 16px;">一&nbsp; 原理</span></strong></p><p align="left">&nbsp; &nbsp; &nbsp;通常,在多级函数调用过程中,处理器会将调用函数指令的下一条地址压入堆栈。通过分析当前栈帧,找到上层函数在堆栈中的栈帧地址,再分析上层函数的栈帧,进而找到再上层函数的栈帧地址&hellip;&hellip;如此回溯直至最顶层函数。这就组成一条函数执行的路径轨迹(调用顺序)。</p><p align="left">&nbsp; &nbsp; &nbsp;以Intel x86架构为例,由于帧基指针(BP)所指向的内存中存储上一层函数调用时的BP值,而在每层函数调用中都能通过当前BP值向栈底方向偏移得到返回地址。如此递归,可逐层向上找到最顶层函数。</p><p>&nbsp; &nbsp; &nbsp;在GDB里,使用bt命令可获取函数调用栈。若要通过代码获取当前函数调用栈,可借助glibc库提供的backtrace系列函数。由于不同处理器堆栈布局不同,堆栈回溯由编译器内建函数__buildin_frame_address和__buildin_return_address实现,涉及工具glibc和gcc。若编译器不支持该功能,也可自行实现,其步骤如下(以Intel x86架构为例):</p><p align="left">&nbsp; &nbsp; &nbsp;1) 获得当前函数的BP;</p><p align="left">&nbsp; &nbsp; &nbsp;2) 通过BP偏移获得主调函数的IP(返回地址);</p><p align="left">&nbsp; &nbsp; &nbsp;3) 通过当前BP指向的内容,获得主调函数BP地址;</p><p align="left">&nbsp; &nbsp; &nbsp;4) 循环执行以上步骤直至到达栈底。</p><p align="left">&nbsp; &nbsp; &nbsp;glibc2.1及以上版本提供backtrace等GNU扩展函数以获取当前线程的函数调用堆栈,其原型声明在头文件&lt;execinfo.h&gt;内。</p><table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="background-color: #999999;"><td valign="top" width="619"><p><span style="color: #800080;">int <strong>backtrace</strong>(void **buffer, int size);</span></p></td></tr></tbody></table><p align="left">&nbsp; &nbsp; &nbsp;该函数获取当前线程的调用堆栈,并以指针(实为返回地址)列表形式存入参数buffer缓冲区中。参数size指定buffer中可容纳的void*元素数目。该函数返回是实际获取的元素数,且不超过size大小。若返回值小于size,则buffer中保存完整的堆栈信息;若返回值等于size,则堆栈信息可能已被删减(最早的那些栈帧返回地址被丢弃)。</p><table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="background-color: #999999;"><td valign="top" width="619"><p><span style="color: #800080;">char ** <strong>backtrace_symbols</strong>(void *const *buffer, int size);</span></p></td></tr></tbody></table><p align="left">&nbsp; &nbsp; &nbsp;该函数将backtrace函数获取的信息转换为一个字符串数组。参数buffer应指向backtrace函数获取的地址数组,参数size为该数组中的元素个数(backtrace函数返回值)。</p><p align="left">&nbsp; &nbsp; &nbsp;该函数返回一个指向字符串数组的指针,数组元素个数与buffer数组相同(即为size)。每个字符串包含一个对应buffer数组元素的可打印描述信息,若函数名、偏移地址和实际的返回地址(16进制)。</p><p align="left">&nbsp; &nbsp; &nbsp;<span style="color: #800080;">该函数的返回值指向函数内部通过malloc所申请的动态内存,因此调用者必须使用free函数来释放该内存。</span>若不能为字符串申请获取足够的内存,则该函数返回NULL。</p><p align="left">&nbsp; &nbsp; &nbsp;目前,只有在使用ELF二进制格式的程序和库的系统中才能获取函数名和偏移地址。在其他系统中,仅能获取16进制的返回地址。此外,可能需要向链接器传递额外的标志,以支持函数名功能(如在使用GNU ld的系统中,需要传递-rdynamic选项来通知链接器将所有符号添加到动态符号表中)。</p><table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="background-color: #999999;"><td valign="top" width="619"><p><span style="color: #800080;">void <strong>backtrace_symbols_fd</strong>(void *const *buffer, int size, int fd);</span></p></td></tr></tbody></table><p align="left">&nbsp; &nbsp; &nbsp;该函数与backtrace_symbols函数功能相同,但不向调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每条信息字符串对应一行。该函数不会为字符串存储申请动态内存,因此适用于堆内存可能被破坏的情况(此时buffer也应为静态或自动存储空间)。</p><p>&nbsp; &nbsp; &nbsp;举例如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> #include &lt;stdio.h&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;stdlib.h&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;unistd.h&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;execinfo.h&gt;<br/><span style="color: #008080;"> 5</span> <br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> StackTrace(<span style="color: #0000ff;">void</span><span style="color: #000000;">){<br/></span><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">void</span> *pvTraceBuf[<span style="color: #800080;">10</span><span style="color: #000000;">];<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">int</span> dwTraceSize = backtrace(pvTraceBuf, <span style="color: #800080;">10</span><span style="color: #000000;">);<br/></span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> backtrace_symbols_fd(pvTraceBuf, dwTraceSize, STDOUT_FILENO);<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">11</span> <br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">void</span> FuncC(<span style="color: #0000ff;">void</span><span style="color: #000000;">){ StackTrace(); }<br/></span><span style="color: #008080;">13</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> FuncB(<span style="color: #0000ff;">void</span><span style="color: #000000;">){ FuncC(); }<br/></span><span style="color: #008080;">14</span> <span style="color: #0000ff;">void</span> FuncA(<span style="color: #0000ff;">void</span><span style="color: #000000;">){ FuncB(); }<br/></span><span style="color: #008080;">15</span> <span style="color: #0000ff;">int</span> main(<span style="color: #0000ff;">void</span><span style="color: #000000;">){<br/></span><span style="color: #008080;">16</span> <span style="color: #000000;"> FuncA();<br/></span><span style="color: #008080;">17</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">18</span> }</div><p>&nbsp; &nbsp; &nbsp;编译运行结果如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">wangxiaoyuan_@localhost test1</span><span style="color: #800000; font-weight: bold;">]</span><span style="color: #000000;">$ gcc -Wall <span style="color: #800080;">-rdynamic</span> -o StackTrace StackTrace.c<br/></span><span style="color: #008080;">2</span> <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">wangxiaoyuan_@localhost test1</span><span style="color: #800000; font-weight: bold;">]</span><span style="color: #000000;">$ ./StackTrace<br/></span><span style="color: #008080;">3</span> ./StackTrace<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x80485f9</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">4</span> ./StackTrace(<span style="color: #800080;">FuncC</span>+0xb)<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x8048623</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">5</span> ./StackTrace<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x8048630</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">6</span> ./StackTrace(<span style="color: #800080;">FuncA</span>+0xb)<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804863d</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">7</span> ./StackTrace(<span style="color: #800080;">main</span>+0x16)<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x8048655</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">8</span> /lib/libc.so.6(__libc_start_main+0xdc)<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x552e9c</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">9</span> ./StackTrace<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x8048521</span><span style="color: #800000; font-weight: bold;">]</span></div><p>&nbsp; &nbsp; &nbsp;当若干主调函数中的某个以错误的参数调用给定函数时,通过在该函数内检查参数并调用StackTrace()函数,即可方便地定位出错的主调函数。</p><p align="left">&nbsp; &nbsp; &nbsp;使用backtrace系列函数获取堆栈回溯信息时,需要注意以下几点:</p><p align="left">&nbsp; &nbsp; &nbsp;1) 某些编译器优化可能对获取有效的调用堆栈造成干扰。</p><p align="left">&nbsp; &nbsp; &nbsp;忽略帧基指针时(-fomit-frame-pointer),回溯时将无法正确解析堆栈内容。优化级别非0时(如-O2)可能改变函数调用关系;尾调用(Tail-call)优化会替换栈帧内容,这些也会影响回溯结果。</p><p align="left">&nbsp; &nbsp; &nbsp;2) 内联函数和宏定义没有栈帧结构。</p><p align="left">&nbsp; &nbsp; &nbsp;3) 静态函数名无法被内部解析,因其无法被动态链接访问。此时可使用外部工具addr2line解析。</p><p align="left">&nbsp; &nbsp; &nbsp;4) 若内存垃圾导致堆栈自身被破坏,则无法进行回溯。</p><p align="left">&nbsp; &nbsp; &nbsp;若自行实现堆栈回溯功能,可调用dladdr()函数来解析返回地址所对应的文件名和函数名等信息。</p><table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="background-color: #999999;"><td valign="top" width="619"><p><span style="color: #800080;">#include &lt;dlfcn.h&gt;</span></p><p><span style="color: #800080;">int <strong>dladdr</strong>(void *addr, Dl_info *info);</span></p></td></tr></tbody></table><p align="left">&nbsp; &nbsp; &nbsp;该函数出错时(共享库libdl.so目标文件段中不存在该地址)返回0,成功时返回非0值。</p><p align="left">&nbsp; &nbsp; &nbsp;Dl_info结构定义如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> typedef <span style="color: #0000ff;">struct</span><span style="color: #000000;">{<br/></span><span style="color: #008080;">2</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> *dli_fname; <span style="color: #008000;">/*</span><span style="color: #008000;"> Filename of defining object </span><span style="color: #008000;">*/</span><br/><span style="color: #008080;">3</span> <span style="color: #0000ff;">void</span> *dli_fbase; <span style="color: #008000;">/*</span><span style="color: #008000;"> Load address of that object </span><span style="color: #008000;">*/</span><br/><span style="color: #008080;">4</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> *dli_sname; <span style="color: #008000;">/*</span><span style="color: #008000;"> Name of nearest lower symbol </span><span style="color: #008000;">*/</span><br/><span style="color: #008080;">5</span> <span style="color: #0000ff;">void</span> *dli_saddr; <span style="color: #008000;">/*</span><span style="color: #008000;"> Exact value of nearest symbol </span><span style="color: #008000;">*/</span><br/><span style="color: #008080;">6</span> } Dl_info;</div><p>&nbsp; &nbsp; &nbsp;使用dladdr()函数时,需加上-rdynamic编译选项和-ldl链接选项。</p><p align="left">&nbsp; &nbsp; &nbsp;更进一步,可将堆栈回溯置于信号处理程序中。这样,当程序突然崩溃时,当前进程接收到内核发送的信号后,在信号处理程序中自动输出进程的执行信息、当前寄存器内容及函数调用关系等。</p><p align="left">&nbsp; &nbsp; &nbsp;通常使用sigaction()函数检查或修改与指定信号相关联的处理动作(或同时执行这两种操作):</p><table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="background-color: #999999;"><td valign="top" width="619"><p><span style="color: #800080;">#include &lt;signal.h&gt;</span></p><p><span style="color: #800080;">int <strong>sigaction</strong>( int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);</span></p></td></tr></tbody></table><p align="left">&nbsp; &nbsp; &nbsp;该函数成功时返回0,否则返回-1并设置errno值。参数signo为待检测或修改其具体动作的信号编号。若act指针非空,则修改其动作;若oact指针非空,则系统经由oact指针返回该信号的上个动作。sigaction结构的sa_flags字段指定对信号进行处理的各个选项。当设置为SA_SIGINFO标志时,表示信号附带的信息可传递到信号处理函数中。此时,应按下列方式调用信号处理程序:</p><table style="margin-left: auto; margin-right: auto;" border="1" cellspacing="0" cellpadding="0"><tbody><tr style="background-color: #999999;"><td valign="top" width="619"><p><span style="color: #800080;">void <strong>handler</strong>(int signo, siginfo_t *info, void *context);</span></p></td></tr></tbody></table><p align="left">&nbsp; &nbsp; &nbsp;siginfo_t结构包含信号产生原因的有关信息,需针对不同信号选取有意义的属性。其中,si_signo(信号编号)、si_errno(errno值)和si_code(信号产生原因)定义针对所有信号。其余属性只有部分信息对特定信号有用。例如,si_addr指示触发故障的内存地址(尽管该地址可能并不准确),仅对SIGILL、SIGFPE、SIGSEGV和SIGBUS 信号有意义。si_errno字段包含错误编号,对应于引发信号产生的条件,并由实现定义(Linux中通常不使用该属性)。</p><p align="left">&nbsp; &nbsp; &nbsp;信号处理程序的context参数是无类型指针,可被强制转换为ucontext_t结构,用于标识信号产生时的进程上下文(如CPU寄存器)。该结构定义在头文件&lt;ucontext.h&gt;内,且包含mcontext_t类型的uc_mcontext字段(该字段保存特定于机器的寄存器上下文)。</p><p align="left">&nbsp; &nbsp; &nbsp;注意,即使指定信号处理函数,若不设置SA_SIGINFO标志,信号处理函数同样不能得到信号传递过来的附加信息(info和context),在信号处理函数中访问这些信息都将导致段错误。</p><p align="left">&nbsp;</p><p align="left">&nbsp;</p><p><strong><span style="font-size: 16px;">二&nbsp; 实现</span></strong></p><p>&nbsp; &nbsp; &nbsp;本节将实现基于信号处理的用户态进程堆栈回溯功能。该实现假定未忽略帧基指针。</p><p><strong><span style="font-size: 15px;">2.1 数据定义</span></strong></p><p>&nbsp; &nbsp; &nbsp;定义如下宏:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #000000;">#ifndef __i386<br/></span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">#warning</span> "Possibly Non-x86 Platform!"<br/><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;"> 4</span> <br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">#if</span> defined(REG_RIP)<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> REG_IP REG_RIP <span style="color: #008000;">//</span><span style="color: #008000;">指令指针(保存返回地址)</span><br/><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">#define</span> REG_BP REG_RBP <span style="color: #008000;">//</span><span style="color: #008000;">帧基指针</span><br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">#define</span> REG_FMT "%016lx"<br/><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">#elif</span> defined(REG_EIP)<br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">#define</span> REG_IP REG_EIP<br/><span style="color: #008080;">11</span> <span style="color: #0000ff;">#define</span> REG_BP REG_EBP<br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">#define</span> REG_FMT "%08x"<br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">#else</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">#warning</span> "Neither REG_RIP nor REG_EIP is defined!"<br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">#define</span> REG_FMT "%08x" <br/><span style="color: #008080;">16</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">17</span> <br/><span style="color: #008080;">18</span> <span style="color: #0000ff;">#define</span> BTR_FILE_LEN 512 <span style="color: #008000;">//</span><span style="color: #008000;">保存堆栈回溯结果的文件路径最大长度</span><br/><span style="color: #008080;">19</span> #ifndef BTR_FILE <span style="color: #008000;">//</span><span style="color: #008000;">保存堆栈回溯结果的基本文件名</span><br/><span style="color: #008080;">20</span> <span style="color: #0000ff;">#define</span> BTR_FILE "btr" <br/><span style="color: #008080;">21</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">22</span> #ifndef BTR_FILE_PATH <span style="color: #008000;">//</span><span style="color: #008000;">保存堆栈回溯结果的文件路径(默认为当前路径)</span><br/><span style="color: #008080;">23</span> <span style="color: #0000ff;">#define</span> BTR_FILE_PATH "." <span style="color: #008000;">//</span><span style="color: #008000;">"..//var//tmp"</span><br/><span style="color: #008080;">24</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">25</span> <br/><span style="color: #008080;">26</span> #ifndef MAX_BTR_LEVEL <span style="color: #008000;">//</span><span style="color: #008000;">函数回溯的最大层数</span><br/><span style="color: #008080;">27</span> <span style="color: #0000ff;">#define</span> MAX_BTR_LEVEL 20<br/><span style="color: #008080;">28</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">29</span> <br/><span style="color: #008080;">30</span> <span style="color: #008000;">//</span><span style="color: #008000;">用户调用SHOW_STACK宏可触发堆栈回溯</span><br/><span style="color: #008080;">31</span> #ifndef BTR_SIG <span style="color: #008000;">//</span><span style="color: #008000;">触发堆栈回溯的信号</span><br/><span style="color: #008080;">32</span> <span style="color: #0000ff;">#define</span> BTR_SIG SIGUSR1<br/><span style="color: #008080;">33</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">34</span> <span style="color: #0000ff;">#define</span> SHOW_STACK() do{raise(BTR_SIG);}while(0)</div><p>&nbsp; &nbsp; &nbsp;其中,REG_IP、REG_BP分别为x86处理器的指令指针和帧基指针寄存器编号,REG_FMT宏指定寄存器内容的输出格式。BTR_FILE等文件相关的宏指定保存堆栈回溯结果时文件路径和名称。当程序运行于嵌入式单板时,当前路径可能没有写入权限,此时用户可自定义BTR_FILE_PATH宏。</p><p>&nbsp; &nbsp; &nbsp;定义如下全局变量:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> <span style="color: #0000ff;">static</span> FILE *gpStaceFd = NULL; <span style="color: #008000;">//</span><span style="color: #008000;">输出文件描述符(置为stderr时输出到终端,否则将输出存入文件)</span><br/><span style="color: #008080;">2</span> typedef VOID (*<span style="color: #000000;">SignalHandleFunc)(INT32S dwSignal);<br/></span><span style="color: #008080;">3</span> <span style="color: #0000ff;">static</span> SignalHandleFunc gfpCustSigHandler = NULL; <span style="color: #008000;">//</span><span style="color: #008000;">用户自定义的信号处理函数指针</span></div><p><strong><span style="font-size: 15px;">2.2 函数接口</span></strong></p><p>&nbsp; &nbsp; &nbsp;首先定义一组私有函数。这些内部使用的函数已尽可能保证参数安全性,故省去参数校验处理。</p><p>&nbsp; &nbsp; &nbsp;SpecifyStraceOutput()函数指定堆栈回溯结果的输出方式:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*****************************************************************************<br/></span><span style="color: #008080;"> 2</span> <span style="color: #008000;">* 函数名称: SpecifyStraceOutput<br/></span><span style="color: #008080;"> 3</span> <span style="color: #008000;">* 功能说明: 指定回溯结果输出方式<br/></span><span style="color: #008080;"> 4</span> <span style="color: #008000;">*****************************************************************************</span><span style="color: #008000;">*/</span><br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">static</span> FILE *<span style="color: #000000;">SpecifyStraceOutput(VOID)<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;"> 7</span> <span style="color: #000000;">#ifdef __BTR_TO_FILE<br/></span><span style="color: #008080;"> 8</span> <span style="color: #000000;"> time_t tTime;<br/></span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> CHAR szFileName[BTR_FILE_LEN];<br/></span><span style="color: #008080;">10</span> szFileName[<span style="color: #800080;">0</span>] = <span style="color: #800000;">'</span><span style="color: #800000;">\0</span><span style="color: #800000;">'</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">if</span>(time(&amp;tTime) != -<span style="color: #800080;">1</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">12</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">13</span> <span style="color: #0000ff;">struct</span> tm *ptTime = localtime(&amp;<span style="color: #000000;">tTime);<br/></span><span style="color: #008080;">14</span> snprintf(szFileName, <span style="color: #0000ff;">sizeof</span>(szFileName), <span style="color: #800000;">"</span><span style="color: #800000;">%s/[%d]%d%02d%02d_%02d%02d%02d.%s</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/></span><span style="color: #008080;">15</span> BTR_FILE_PATH, getpid(), (ptTime-&gt;tm_year+<span style="color: #800080;">1900</span>), (ptTime-&gt;tm_mon+<span style="color: #800080;">1</span><span style="color: #000000;">),<br/></span><span style="color: #008080;">16</span> ptTime-&gt;tm_mday, ptTime-&gt;tm_hour, ptTime-&gt;tm_min, ptTime-&gt;<span style="color: #000000;">tm_sec, BTR_FILE);<br/></span><span style="color: #008080;">17</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">18</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;">19</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">20</span> snprintf(szFileName, <span style="color: #0000ff;">sizeof</span>(szFileName), <span style="color: #800000;">"</span><span style="color: #800000;">%s/%s</span><span style="color: #800000;">"</span><span style="color: #000000;">, BTR_FILE_PATH, BTR_FILE);<br/></span><span style="color: #008080;">21</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">22</span> <br/><span style="color: #008080;">23</span> FILE *pFile = fopen(szFileName, <span style="color: #800000;">"</span><span style="color: #800000;">w+</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">24</span> <span style="color: #0000ff;">if</span>(NULL ==<span style="color: #000000;"> pFile)<br/></span><span style="color: #008080;">25</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">26</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">Cannot open File '%s'(%s)\n!</span><span style="color: #800000;">"</span><span style="color: #000000;">, szFileName, strerror(errno));<br/></span><span style="color: #008080;">27</span> <span style="color: #0000ff;">return</span> -<span style="color: #800080;">1</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">28</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">29</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> pFile;<br/></span><span style="color: #008080;">30</span> <span style="color: #0000ff;">#else</span><br/><span style="color: #008080;">31</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> stderr;<br/></span><span style="color: #008080;">32</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">33</span> }</div><p>&nbsp; &nbsp; &nbsp;当__BTR_TO_FILE编译选项打开时,堆栈回溯结果输出到指定目录下的文件内。若成功获取当前时间,则该文件名为"[进程号]年月日_时分秒.btr",否则名为"btr"。当__BTR_TO_FILE编译选项关闭时,堆栈回溯结果直接输出到终端设备屏幕上。</p><p>&nbsp; &nbsp; &nbsp;注意,SpecifyStraceOutput()函数返回的文件描述符类型为FILE*。标准流stdin/stdout/stderr均为该类型,用于带缓冲的高级I/O函数(如fread/fwrite/fclose等);而STDIN_FILENO/ STDOUT_FILENO/STDERR_FILENO的类型为 int&nbsp;,用于低级I/O调用(如read/write/close等)。</p><p>&nbsp; &nbsp; &nbsp;信号处理函数SigHandler()依次输出接收到的信号信息、堆栈寄存器内容及堆栈回溯信息:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*****************************************************************************<br/></span><span style="color: #008080;"> 2</span> <span style="color: #008000;">* 函数名称: SigHandler<br/></span><span style="color: #008080;"> 3</span> <span style="color: #008000;">* 功能说明: 信号处理函数<br/></span><span style="color: #008080;"> 4</span> <span style="color: #008000;">* 输入参数: INT32S dwSigNo :信号名<br/></span><span style="color: #008080;"> 5</span> <span style="color: #008000;"> siginfo_t *tSigInfo :信号产生原因等信息<br/></span><span style="color: #008080;"> 6</span> <span style="color: #008000;"> VOID *pvContext :信号传递时的进程上下文<br/></span><span style="color: #008080;"> 7</span> <span style="color: #008000;">* 输出参数: NA<br/></span><span style="color: #008080;"> 8</span> <span style="color: #008000;">* 返 回 值: VOID<br/></span><span style="color: #008080;"> 9</span> <span style="color: #008000;">*****************************************************************************</span><span style="color: #008000;">*/</span><br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">static</span> VOID SigHandler(INT32S dwSigNo, siginfo_t *tSigInfo, VOID *<span style="color: #000000;">pvContext)<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;">12</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\nStart of Stack Trace&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">13</span> <br/><span style="color: #008080;">14</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">Process (%d) receive signal %d\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, getpid(), dwSigNo);<br/></span><span style="color: #008080;">15</span> <br/><span style="color: #008080;">16</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">&lt;Signal Information&gt;:\n</span><span style="color: #800000;">"</span><span style="color: #000000;"> );<br/></span><span style="color: #008080;">17</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\tSigNo: %-2d(%s)\n</span><span style="color: #800000;">"</span>, tSigInfo-&gt;si_signo, OmciStrSigNo(tSigInfo-&gt;<span style="color: #000000;">si_signo));<br/></span><span style="color: #008080;">18</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\tErrNo: %-2d(%s)\n</span><span style="color: #800000;">"</span>, tSigInfo-&gt;si_errno, strerror(tSigInfo-&gt;<span style="color: #000000;">si_errno));<br/></span><span style="color: #008080;">19</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\tSigCode: %-2d\n</span><span style="color: #800000;">"</span>, tSigInfo-&gt;<span style="color: #000000;">si_code);<br/></span><span style="color: #008080;">20</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\tRaised at: %p[Unreliable]\n</span><span style="color: #800000;">"</span>, tSigInfo-&gt;<span style="color: #000000;">si_addr);<br/></span><span style="color: #008080;">21</span> <br/><span style="color: #008080;">22</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">&lt;Register Content&gt;: \n\t</span><span style="color: #800000;">"</span><span style="color: #000000;"> );<br/></span><span style="color: #008080;">23</span> INT32U dwIdx = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">24</span> ucontext_t *ptContext = (ucontext_t*<span style="color: #000000;">)pvContext;<br/></span><span style="color: #008080;">25</span> <span style="color: #0000ff;">for</span>(dwIdx = <span style="color: #800080;">0</span>; dwIdx &lt; NGREG; dwIdx++<span style="color: #000000;">)<br/></span><span style="color: #008080;">26</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">27</span> fprintf(gpStaceFd, REG_FMT<span style="color: #800000;">"</span> <span style="color: #800000;">"</span>, ptContext-&gt;<span style="color: #000000;">uc_mcontext.gregs[dwIdx]);<br/></span><span style="color: #008080;">28</span> <span style="color: #0000ff;">if</span>(<span style="color: #800080;">0</span> == ((dwIdx+<span style="color: #800080;">1</span>)%<span style="color: #800080;">4</span>)) <span style="color: #008000;">//</span><span style="color: #008000;">每行输出4个寄存器值</span><br/><span style="color: #008080;">29</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\n\t</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">30</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">31</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">32</span> <br/><span style="color: #008080;">33</span> <span style="color: #0000ff;">#if</span> defined(REG_RIP) || defined(REG_EIP)<br/><span style="color: #008080;">34</span> dwIdx = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">35</span> VOID *pvIp = (VOID*)ptContext-&gt;<span style="color: #000000;">uc_mcontext.gregs[REG_IP];<br/></span><span style="color: #008080;">36</span> VOID **ppvBp = (VOID**)ptContext-&gt;<span style="color: #000000;">uc_mcontext.gregs[REG_BP];<br/></span><span style="color: #008080;">37</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">&lt;Stack Trace(Customized)&gt;:\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">38</span> <span style="color: #0000ff;">while</span>(ppvBp != &amp;<span style="color: #000000;">pvIp)<br/></span><span style="color: #008080;">39</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">40</span> <span style="color: #000000;"> Dl_info tDlInfo;<br/></span><span style="color: #008080;">41</span> <span style="color: #0000ff;">if</span>(!dladdr(pvIp, &amp;<span style="color: #000000;">tDlInfo))<br/></span><span style="color: #008080;">42</span> <span style="color: #0000ff;">break</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">43</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\t[%2d] (%s) 0x%08x (%s)+0x%02x\n</span><span style="color: #800000;">"</span>, ++<span style="color: #000000;">dwIdx,<br/></span><span style="color: #008080;">44</span> <span style="color: #000000;"> tDlInfo.dli_fname, (INT32U)pvIp,<br/></span><span style="color: #008080;">45</span> (tDlInfo.dli_sname != NULL) ? tDlInfo.dli_sname : <span style="color: #800000;">"</span><span style="color: #800000;">&lt;STATIC&gt;</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/></span><span style="color: #008080;">46</span> (INT32U)(pvIp -<span style="color: #000000;"> tDlInfo.dli_saddr));<br/></span><span style="color: #008080;">47</span> <br/><span style="color: #008080;">48</span> <span style="color: #0000ff;">if</span>(tDlInfo.dli_sname &amp;&amp; !strcmp(tDlInfo.dli_sname, <span style="color: #800000;">"</span><span style="color: #800000;">main</span><span style="color: #800000;">"</span><span style="color: #000000;">))<br/></span><span style="color: #008080;">49</span> <span style="color: #0000ff;">break</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">50</span> pvIp = ppvBp[<span style="color: #800080;">1</span>]; <span style="color: #008000;">//</span><span style="color: #008000;">帧基指针向高地址偏移1个单位(4字节)为返回地址</span><br/><span style="color: #008080;">51</span> ppvBp = (VOID**)(*ppvBp); <span style="color: #008000;">//</span><span style="color: #008000;">帧基指针所指向的空间存放主调函数栈帧的帧基指针</span><br/><span style="color: #008080;">52</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">53</span> <span style="color: #0000ff;">#else</span><br/><span style="color: #008080;">54</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">&lt;Stack Trace(Standard)&gt;:\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">55</span> <br/><span style="color: #008080;">56</span> VOID *<span style="color: #000000;">pvTraceBuf[MAX_BTR_LEVEL];<br/></span><span style="color: #008080;">57</span> INT32U dwTraceSize =<span style="color: #000000;"> backtrace(pvTraceBuf, MAX_BTR_LEVEL);<br/></span><span style="color: #008080;">58</span> CHAR **ppTraceInfos =<span style="color: #000000;"> backtrace_symbols(pvTraceBuf, dwTraceSize);<br/></span><span style="color: #008080;">59</span> <span style="color: #0000ff;">if</span>(!ppTraceInfos || !(*<span style="color: #000000;">ppTraceInfos))<br/></span><span style="color: #008080;">60</span> <span style="color: #000000;"> exit(EXIT_FAILURE);<br/></span><span style="color: #008080;">61</span> <br/><span style="color: #008080;">62</span> <span style="color: #0000ff;">for</span>(dwIdx = <span style="color: #800080;">0</span>; dwIdx &lt; dwTraceSize; dwIdx++<span style="color: #000000;">)<br/></span><span style="color: #008080;">63</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">\t%s\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ppTraceInfos[dwIdx]);<br/></span><span style="color: #008080;">64</span> <br/><span style="color: #008080;">65</span> <span style="color: #000000;"> free(ppTraceInfos);<br/></span><span style="color: #008080;">66</span> <span style="color: #0000ff;">#endif</span><br/><span style="color: #008080;">67</span> <br/><span style="color: #008080;">68</span> fprintf(gpStaceFd, <span style="color: #800000;">"</span><span style="color: #800000;">End of Stack Trace&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;\n\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">69</span> <br/><span style="color: #008080;">70</span> <span style="color: #0000ff;">if</span>(gfpCustSigHandler !=<span style="color: #000000;"> NULL)<br/></span><span style="color: #008080;">71</span> <span style="color: #000000;"> gfpCustSigHandler(dwSigNo);<br/></span><span style="color: #008080;">72</span> <br/><span style="color: #008080;">73</span> <span style="color: #000000;"> exit(EXIT_FAILURE);<br/></span><span style="color: #008080;">74</span> }</div><p>&nbsp; &nbsp; &nbsp;其中,si_signo可由入参dwSigNo代替,si_errno句也可省略。因为输出格式需要使用制表符('\t'),故直接使用backtrace_symbols()函数。若更侧重安全性,则可换用backtrace_symbols_fd()函数。</p><p>&nbsp; &nbsp; &nbsp;若REG_RIP或REG_EIP宏已定义,则根据x86寄存器编号获取主调函数的指令指针IP和帧基指针BP,然后回溯栈帧并自定义输出信息。否则,调用backtrace()相关函数输出回溯信息。</p><p>&nbsp; &nbsp; &nbsp;注意,堆栈回溯时静态函数名不可见,因此自定义输出中将其显示为&lt;STATIC&gt;。此外,直接输出时文件描述符为stderr(不带缓冲),若改为stdout(行缓冲)则"\t%s\n"格式控制将不能正常显示。</p><p>&nbsp; &nbsp; &nbsp;读者也可根据栈帧的布局(入参向低地址偏移依次为返回地址和帧基指针),自行获取IP/BP指针。如:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> VOID **<span style="color: #000000;">GetEbp(INT32U dwDummy)<br/></span><span style="color: #008080;">2</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;">3</span> VOID **ebp = (VOID **)&amp;dwDummy - <span style="color: #800080;">2</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">4</span> <span style="color: #0000ff;">return</span> (*<span style="color: #000000;">ebp);<br/></span><span style="color: #008080;">5</span> }</div><p>&nbsp; &nbsp; &nbsp;则SigHandler()函数中对ppvBp的赋值可改为:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> VOID **ppvBp = getEBP(dwIdx); <span style="color: #008000;">//</span><span style="color: #008000;">或</span><br/><span style="color: #008080;">2</span> VOID **ppvBp = (VOID **)&amp;dwSigNo - <span style="color: #800080;">2</span>;</div><p>&nbsp; &nbsp; &nbsp;注意,此时获得的寄存器指针指向SigHandler()函数栈帧,while循环内应先执行pvIp = ppvBp[1]再解析地址。</p><p>&nbsp; &nbsp; &nbsp;OmciStrSigNo()函数基于NameParser来解析信号名:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">#define</span> NAME_MAP_ENTRY(name) {name, #name}<br/><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">static</span> T_NAME_PARSER gSigNameMap[] =<span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 3</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGHUP),<br/></span><span style="color: #008080;"> 4</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGINT),<br/></span><span style="color: #008080;"> 5</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGQUIT),<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGILL),<br/></span><span style="color: #008080;"> 7</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGTRAP),<br/></span><span style="color: #008080;"> 8</span> NAME_MAP_ENTRY(SIGABRT), <span style="color: #008000;">//</span><span style="color: #008000;">SIGABRT(ANSI) = SIGIOT(4.2 BSD)</span><br/><span style="color: #008080;"> 9</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGBUS),<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGFPE),<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGKILL),<br/></span><span style="color: #008080;">12</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGUSR1),<br/></span><span style="color: #008080;">13</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGSEGV),<br/></span><span style="color: #008080;">14</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGUSR2),<br/></span><span style="color: #008080;">15</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGPIPE),<br/></span><span style="color: #008080;">16</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGALRM),<br/></span><span style="color: #008080;">17</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGTERM),<br/></span><span style="color: #008080;">18</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGSTKFLT),<br/></span><span style="color: #008080;">19</span> NAME_MAP_ENTRY(SIGCHLD), <span style="color: #008000;">//</span><span style="color: #008000;">SIGCHLD(POSIX) = SIGCLD(System V)</span><br/><span style="color: #008080;">20</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGCONT),<br/></span><span style="color: #008080;">21</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGSTOP),<br/></span><span style="color: #008080;">22</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGTSTP),<br/></span><span style="color: #008080;">23</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGTTIN),<br/></span><span style="color: #008080;">24</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGTTOU),<br/></span><span style="color: #008080;">25</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGURG),<br/></span><span style="color: #008080;">26</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGXCPU),<br/></span><span style="color: #008080;">27</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGXFSZ),<br/></span><span style="color: #008080;">28</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGVTALRM),<br/></span><span style="color: #008080;">29</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGPROF),<br/></span><span style="color: #008080;">30</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGWINCH),<br/></span><span style="color: #008080;">31</span> NAME_MAP_ENTRY(SIGIO), <span style="color: #008000;">//</span><span style="color: #008000;">SIGIO(4.2 BSD) = SIGPOLL(System V)</span><br/><span style="color: #008080;">32</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGPWR),<br/></span><span style="color: #008080;">33</span> <span style="color: #000000;"> NAME_MAP_ENTRY(SIGSYS)<br/></span><span style="color: #008080;">34</span> <span style="color: #000000;">};<br/></span><span style="color: #008080;">35</span> <span style="color: #008000;">//</span><span style="color: #008000;">信号值字符串化</span><br/><span style="color: #008080;">36</span> CHAR *<span style="color: #000000;">OmciStrSigNo(INT32S dwSigNo)<br/></span><span style="color: #008080;">37</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;">38</span> <span style="color: #0000ff;">return</span> NameParser(gSigNameMap, ARRAY_SIZE(gSigNameMap), dwSigNo, <span style="color: #800000;">"</span><span style="color: #800000;">UnkownSigNo</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">39</span> }</div><p>&nbsp; &nbsp; &nbsp;NameParser()函数实现参见<a href="http://www.cnblogs.com/clover-toeic/p/3730362.html" target="_blank">《C语言表驱动法编程实践》</a>一文,读者也可自行实现解析函数。</p><p>&nbsp; &nbsp; &nbsp;InstallFaultTrap()为程序异常时安装的信号捕获函数:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*****************************************************************************<br/></span><span style="color: #008080;"> 2</span> <span style="color: #008000;">* 函数名称: InstallFaultTrap<br/></span><span style="color: #008080;"> 3</span> <span style="color: #008000;">* 功能说明: 安装出错时的信号捕获函数<br/></span><span style="color: #008080;"> 4</span> <span style="color: #008000;">* 输入参数: SignalHandleFunc fpCustSigHandler :用户自定义的信号处理函数<br/></span><span style="color: #008080;"> 5</span> <span style="color: #008000;">* 输出参数: NA<br/></span><span style="color: #008080;"> 6</span> <span style="color: #008000;">* 返 回 值: INT32S<br/></span><span style="color: #008080;"> 7</span> <span style="color: #008000;">*****************************************************************************</span><span style="color: #008000;">*/</span><br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> INT32S InstallFaultTrap(SignalHandleFunc fpCustSigHandler)<br/></span><span style="color: #008080;"> 9</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;">10</span> gfpCustSigHandler =<span style="color: #000000;"> fpCustSigHandler;<br/></span><span style="color: #008080;">11</span> <br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">struct</span><span style="color: #000000;"> sigaction tSigAction;<br/></span><span style="color: #008080;">13</span> memset(&amp;tSigAction, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(tSigAction));<br/></span><span style="color: #008080;">14</span> tSigAction.sa_sigaction =<span style="color: #000000;"> SigHandler;<br/></span><span style="color: #008080;">15</span> sigemptyset( &amp;<span style="color: #000000;">tSigAction.sa_mask );<br/></span><span style="color: #008080;">16</span> tSigAction.sa_flags =<span style="color: #000000;"> SA_SIGINFO;<br/></span><span style="color: #008080;">17</span> <br/><span style="color: #008080;">18</span> <span style="color: #008000;">//</span><span style="color: #008000;">检查可能导致进程终止的信号</span><br/><span style="color: #008080;">19</span> INT32S dwRet = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">20</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGSEGV, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">21</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGSEGV(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">22</span> <br/><span style="color: #008080;">23</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGQUIT, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">24</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGQUIT(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">25</span> <br/><span style="color: #008080;">26</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGILL, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">27</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGILL(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">28</span> <br/><span style="color: #008080;">29</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGTRAP, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">30</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGTRAP(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">31</span> <br/><span style="color: #008080;">32</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGABRT, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">33</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGABRT(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">34</span> <br/><span style="color: #008080;">35</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGFPE, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">36</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGFPE(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">37</span> <br/><span style="color: #008080;">38</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGBUS, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">39</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGBUS(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">40</span> <br/><span style="color: #008080;">41</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGXFSZ, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">42</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGXFSZ(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">43</span> <br/><span style="color: #008080;">44</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGXCPU, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">45</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGXCPU(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">46</span> <br/><span style="color: #008080;">47</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(SIGSYS, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">48</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for SIGSYS(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME, errno, strerror(errno));<br/></span><span style="color: #008080;">49</span> <br/><span style="color: #008080;">50</span> <span style="color: #0000ff;">if</span>((dwRet |= sigaction(BTR_SIG, &amp;tSigAction, NULL)) &lt; <span style="color: #800080;">0</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">51</span> fprintf(stderr, <span style="color: #800000;">"</span><span style="color: #800000;">[%s]Sigaction failed for %s(%d, %s)!\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, FUNC_NAME,<br/></span><span style="color: #008080;">52</span> <span style="color: #000000;"> OmciStrSigNo(BTR_SIG), errno, strerror(errno));<br/></span><span style="color: #008080;">53</span> <br/><span style="color: #008080;">54</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> dwRet;<br/></span><span style="color: #008080;">55</span> }</div><p>&nbsp; &nbsp; &nbsp;用户可通过fpCustSigHandler回调函数额外地输出特定的自定义信息。</p><p>&nbsp; &nbsp; &nbsp;通常,并不期望用户显式地初始化堆栈回溯功能。因此,提供__BTR_AUTO_INIT编译选项以支持自动初始化(AutoInitBacktrace):</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*****************************************************************************<br/></span><span style="color: #008080;"> 2</span> <span style="color: #008000;">* 函数名称: AutoInitBacktrace<br/></span><span style="color: #008080;"> 3</span> <span style="color: #008000;">* 功能说明: 自动初始化堆栈回溯功能<br/></span><span style="color: #008080;"> 4</span> <span style="color: #008000;">* 输入参数: VOID<br/></span><span style="color: #008080;"> 5</span> <span style="color: #008000;">* 输出参数: NA<br/></span><span style="color: #008080;"> 6</span> <span style="color: #008000;">* 返 回 值: INT32S<br/></span><span style="color: #008080;"> 7</span> <span style="color: #008000;">* 注意事项: 该函数在main()函数之前执行,无需用户显式调用<br/></span><span style="color: #008080;"> 8</span> <span style="color: #008000;">*****************************************************************************</span><span style="color: #008000;">*/</span><br/><span style="color: #008080;"> 9</span> <span style="color: #000000;">#ifdef __BTR_AUTO_INIT<br/></span><span style="color: #008080;">10</span> <span style="color: #0000ff;">static</span><span style="color: #000000;"> VOID __attribute((constructor)) AutoInitBacktrace(VOID)<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;">12</span> gpStaceFd =<span style="color: #000000;"> SpecifyStraceOutput();<br/></span><span style="color: #008080;">13</span> <span style="color: #000000;"> InstallFaultTrap(NULL);<br/></span><span style="color: #008080;">14</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">15</span> <span style="color: #0000ff;">#endif</span></div><p>&nbsp; &nbsp; &nbsp;其中,声明为gcc(constructor)属性的函数将在main()函数之前被执行,而声明为gcc(destructor)属性的函数则在_after_ main()退出时执行。</p><p>&nbsp; &nbsp; &nbsp;若用户想额外输出自定义信息,则需要显式调用MannInitBacktrace()函数进行手工初始化。该函数调用时可指定fpCustSigHandler回调函数:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #008000;">/*</span><span style="color: #008000;">*****************************************************************************<br/></span><span style="color: #008080;"> 2</span> <span style="color: #008000;">* 函数名称: MannInitBacktrace<br/></span><span style="color: #008080;"> 3</span> <span style="color: #008000;">* 功能说明: 手工初始化堆栈回溯功能<br/></span><span style="color: #008080;"> 4</span> <span style="color: #008000;">* 输入参数: SignalHandleFunc fpCustSigHandler :用户自定义的信号处理函数<br/></span><span style="color: #008080;"> 5</span> <span style="color: #008000;">* 输出参数: NA<br/></span><span style="color: #008080;"> 6</span> <span style="color: #008000;">* 返 回 值: VOID<br/></span><span style="color: #008080;"> 7</span> <span style="color: #008000;">* 注意事项: fpCustSigHandler符合signal()函数原型,用户可借此额外地输出<br/></span><span style="color: #008080;"> 8</span> <span style="color: #008000;"> 特定的自定义信息<br/></span><span style="color: #008080;"> 9</span> <span style="color: #008000;">*****************************************************************************</span><span style="color: #008000;">*/</span><br/><span style="color: #008080;">10</span> <span style="color: #000000;">VOID MannInitBacktrace(SignalHandleFunc fpCustSigHandler)<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;">{<br/></span><span style="color: #008080;">12</span> gpStaceFd =<span style="color: #000000;"> SpecifyStraceOutput();<br/></span><span style="color: #008080;">13</span> <span style="color: #000000;"> InstallFaultTrap(fpCustSigHandler);<br/></span><span style="color: #008080;">14</span> }</div><p>&nbsp;</p><p>&nbsp;</p><p><strong><span style="font-size: 16px;">三&nbsp; 测试</span></strong></p><p>&nbsp; &nbsp; &nbsp;本节将对上文实现的用户态进程堆栈回溯功能进行测试。测试函数如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #000000;">VOID Func1(VOID){<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;"> SHOW_STACK();<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/></span><span style="color: #008080;"> 4</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;"> 5</span> <span style="color: #000000;">VOID Func2(VOID){<br/></span><span style="color: #008080;"> 6</span> <span style="color: #000000;"> Func1();<br/></span><span style="color: #008080;"> 7</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%s\n</span><span style="color: #800000;">"</span>, <span style="color: #800080;">0x123</span><span style="color: #000000;">);<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/></span><span style="color: #008080;"> 9</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;">VOID BtrTest(VOID){<br/></span><span style="color: #008080;">11</span> <span style="color: #000000;"> Func2();<br/></span><span style="color: #008080;">12</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span>, <span style="color: #800080;">5</span>/<span style="color: #800080;">0</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">13</span> <span style="color: #0000ff;">return</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">14</span> }</div><p>&nbsp; &nbsp; &nbsp;指定的编译选项为:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> CFLAGS += -D__BTR_AUTO_INIT -rdynamic &ndash;ldl <span style="color: #008000;">#</span><span style="color: #008000;">-D__BTR_TO_FILE</span><br/><span style="color: #008080;">2</span> CFLAGS += -DMAX_BTR_LEVEL=5<br/><span style="color: #008080;">3</span> CFLAGS += <span style="color: #800080;">-fno-omit-frame-pointer</span></div><p>&nbsp; &nbsp; &nbsp;执行结果如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #000000;">Start of Stack Trace&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<br/></span><span style="color: #008080;"> 2</span> Process (18390) receive signal 10<br/><span style="color: #008080;"> 3</span> <span style="color: #000000;">&lt;Signal Information&gt;:<br/></span><span style="color: #008080;"> 4</span> SigNo: 10<span style="color: #000000;">(<span style="color: #800080;">SIGUSR1</span>)<br/></span><span style="color: #008080;"> 5</span> ErrNo: 0<span style="color: #000000;"> (Success)<br/></span><span style="color: #008080;"> 6</span> SigCode: -6<br/><span style="color: #008080;"> 7</span> Raised at: 0x47d6<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">Unreliable</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;"> 8</span> <span style="color: #000000;">&lt;Register Content&gt;: <br/></span><span style="color: #008080;"> 9</span> 00000033 00000000<span style="color: #000000;"> 0000007b 0000007b <br/></span><span style="color: #008080;">10</span> <span style="color: #000000;"> 006c8ff4 00535ca0 bfb62228 bfb6221c <br/></span><span style="color: #008080;">11</span> 000047d6 0000000a 000047d6 00000000 <br/><span style="color: #008080;">12</span> 00000000 00000000 00480402 00000073 <br/><span style="color: #008080;">13</span> 00000202<span style="color: #000000;"> bfb6221c 0000007b <br/></span><span style="color: #008080;">14</span> <span style="color: #000000;">&lt;Stack Trace(Standard)&gt;:<br/></span><span style="color: #008080;">15</span> ./OmciExec <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804a770</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">16</span> <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x480440</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">17</span> ./OmciExec(<span style="color: #800080;">Func1</span>+0x12) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804ad4e</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">18</span> ./OmciExec(<span style="color: #800080;">Func2</span>+0xb) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804ad5b</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">19</span> ./OmciExec(<span style="color: #800080;">BtrTest</span>+0xb) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804ad7c</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">20</span> ./OmciExec(<span style="color: #800080;">main</span>+0x16) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804eec0</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">21</span> /lib/libc.so.6(__libc_start_main+0xdc) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x552e9c</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">22</span> ./OmciExec <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x8049f31</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">23</span> End of Stack Trace&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</div><p>&nbsp; &nbsp; &nbsp;若注释掉Func1()函数中的SHOW_STACK()语句,则执行结果如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #000000;">Start of Stack Trace&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;<br/></span><span style="color: #008080;"> 2</span> Process (18429) receive signal 11<br/><span style="color: #008080;"> 3</span> <span style="color: #000000;">&lt;Signal Information&gt;:<br/></span><span style="color: #008080;"> 4</span> SigNo: 11<span style="color: #000000;">(<span style="color: #800080;">SIGSEGV</span>)<br/></span><span style="color: #008080;"> 5</span> ErrNo: 0<span style="color: #000000;"> (Success)<br/></span><span style="color: #008080;"> 6</span> SigCode: 1 <br/><span style="color: #008080;"> 7</span> Raised at: 0x123<span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">Unreliable</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;"> 8</span> <span style="color: #000000;">&lt;Register Content&gt;: <br/></span><span style="color: #008080;"> 9</span> 00000033 00000000<span style="color: #000000;"> 0000007b 0000007b <br/></span><span style="color: #008080;">10</span> 00000123<span style="color: #000000;"> bf9a5114 bf9a50ec bf9a4acc <br/></span><span style="color: #008080;">11</span> 0067eff4 00579999 00000003 00000123 <br/><span style="color: #008080;">12</span> 0000000e 00000004 005ad1ab 00000073 <br/><span style="color: #008080;">13</span> 00010206<span style="color: #000000;"> bf9a4acc 0000007b <br/></span><span style="color: #008080;">14</span> <span style="color: #000000;">&lt;Stack Trace(Standard)&gt;:<br/></span><span style="color: #008080;">15</span> ./OmciExec <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804a740</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">16</span> <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0xedc440</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">17</span> /lib/libc.so.6(<span style="color: #800080;">_IO_printf</span>+0x33) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x582e83</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">18</span> ./OmciExec(<span style="color: #800080;">Func2</span>+0x1f) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804ad30</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">19</span> ./OmciExec(<span style="color: #800080;">BtrTest</span>+0xb) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804ad3d</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">20</span> ./OmciExec(<span style="color: #800080;">main</span>+0x16) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x804ee80</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">21</span> /lib/libc.so.6(__libc_start_main+0xdc) <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x552e9c</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">22</span> ./OmciExec <span style="color: #800000; font-weight: bold;">[</span><span style="color: #800000;">0x8049f01</span><span style="color: #800000; font-weight: bold;">]</span><br/><span style="color: #008080;">23</span> End of Stack Trace&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;</div><p>&nbsp;</p><p>&nbsp;</p><p><strong><span style="font-size: 16px;">四&nbsp; 参考</span></strong></p><p>backtrace系列函数用法参考<a href="http://www.kernel.org/doc/man-pages/online/pages/man3/backtrace.3.html">http://www.kernel.org/doc/man-pages/online/pages/man3/backtrace.3.html</a></p><p align="left">sigaction函数用法参考http://man7.org/linux/man-pages/man2/sigaction.2.html</p><p align="left">&nbsp;</p><p align="left">&nbsp;</p><p align="left">&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949896" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/clover-toeic/p/3949896.html" target="_blank">嵌入式系统C编程之堆栈回溯</a>,转载请注明。</p>http://www.cnblogs.com/HopeGi/p/3949878.htmlModel元数据提供机制小结 - 猴健居士在最开始先我得说说我看这部分的情况,最开始被各种ModelMetadata和各种ModelMetadataProvider给搞晕了,就几页书花了我好大的精力去看,直到后来看了一幅类图,细细看各个类之间的关系,重新阅读这部分的内容,我才算有所了解,有所收获,这个估计是以后看书的方法,看代码的方法,.....2014-09-01T11:31:00Z2014-09-01T11:31:00Z猴健居士http://www.cnblogs.com/HopeGi/<p><strong>  </strong>在最开始先我得说说我看这部分的情况,最开始被各种ModelMetadata和各种ModelMetadataProvider给搞晕了,就几页书花了我好大的精力去看,直到后来看了一幅类图,细细看各个类之间的关系,重新阅读这部分的内容,我才算有所了解,有所收获,这个估计是以后看书的方法,看代码的方法,先了解结构,才不会被庞大的类库所混乱。那么我也首先把类图列出来,这幅图依据我个人喜好位置上作了调整,与书上的不同。</p><p><img src="http://images.cnitblog.com/blog/441298/201409/011922497355792.png" alt="" /></p><p>&nbsp;</p><p>整张图左边是ModelMetadata部分的,右边才是元数据的提供者Provider,虽然在章节前面介绍了很多元数据的属性,但是MVC里面用到的元数据并非是ModelMetadata,而是类的子类,那再下面将逐个类作介绍,介绍也是分两部分,一部分是左边Model元数据部分,另一部是右边的元数据Model提供者。</p><h2 style="background: #6BADF6; color: #ffffff; padding: 5px;">Model元数据部分</strong></p><ul><li><span style="color: #6badf6;"><strong>DataAnnotationsModelMetadata</strong></span>:由于MVC采用了基于注解特性声明式定义,所以定义了这个类。在这个类的构造函数中使用到DisplayColumnAttribute,主要跟Display有关,使用了这个Attrubute的类,其Label的值是该类对应属性的值,如书上的Address的显示以Address的DisplayText属性的值。</li><li><span style="color: #6badf6;"><strong>CachedModelMetadata&lt;TPrototypeCache&gt;</strong></span>:使用原型模式构建,或由子类CachedDataAnnotationsModelMetadata的Provider来提供,用于计算所有Model元数据的属性,它的示例在书上也有提示。</li><li><strong><span style="color: #6badf6;">CachedDataAnnotationsModelMetadata</span></strong>:MVC中默认的元数据类型,继承CachedModelMetadata&lt;CachedDataAnnotationsMetadataAttribute&gt;,用于缓存Model元数据信息类型,该Attribute包含所有Model元数据注解特性。</li></ul><h2 style="background: #6BADF6; color: #ffffff; padding: 5px;">Model元数据提供者部分</strong></p><ul><li><strong><span style="color: #6badf6;">ModelMetadataProvider</span></strong>:所有Provider的抽象基类,定义了若干元数据的获得元数据的Get方法。</li><li><strong><span style="color: #6badf6;">AssociatedMetadataProvider</span></strong>:重写ModelMetadataProvider基类的三个方法,实际内部均调用这个类声明的抽象方法CreateMetaData,这个类供两种Provider继承,其一是DataAnnotationsModelMetadataProvider,其二是CachedAssociatedMetadataProvider&lt;TModelMetadata&gt;。</li><li><strong><span style="color: #6badf6;">DataAnnotationsModelMetadataProvider</span></strong>:继承了AssociatedMetadataProvider,定义了这个类的构造函数,所以这是个可以实际使用的Provder,它重写了ociatedMetadataProvider中定义的CreateMetaData抽象方法。达到了提供对应的元数据目的。</li><li><strong><span style="color: #6badf6;">CachedAssociatedMetadataProvider&lt;TModelMetadata&gt;</span></strong>:继承了AssociatedMetadataProvider抽象类,TModelMetadata是集成了ModelMetadata,同样重写父类的CreateMetadata,返回对应Model元数据对象的抽象方法。其中有缓存功能,有缓存则用单例模式生成Model元数据,无缓存的则重新生成一个。</li><li><strong><span style="color: #6badf6;">CachedDataAnnotationsModelMetadataProvider</span></strong>:继承了CachedAssociatedMetadataProvider&lt;TModelMetadata&gt;,实现了上面所有抽象方法。</li><li><strong><span style="color: #6badf6;">ModelMetadataProviders</span></strong>:通过Current获取当前使用的Provider,是ModelMetadataProvider类型,默认是CachedDataAnnotationsModelMetadataProvider类型。</li></ul><p>  书看到这一章节才知道框架里使用的默认Model元数据类型并不是基类ModelMetadata,而是它的子类,但是在基类中定义了很多Model元数据定制相关的属性,这我回想其在MVC模式中控制器控制的只是模型,整个模式中并没有提及专门存储数据用的实体类型,所有数据和业务逻辑处理都归结在Model中去,Model就囊括了属于这个模型的属性(Field或者Prototype)和行为(Method)。使用Model元数据这几个类的结构形式正好解决了这个问题,数据部分就定义为基类,行为部分就放在子类中。减少了数据和行为放在一起的混乱感,同时在数据引用方便又比较方便。</p><img src="http://counter.cnblogs.com/blog/rss/3949878" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/HopeGi/p/3949878.html" target="_blank">Model元数据提供机制小结</a>,转载请注明。</p>http://www.cnblogs.com/nagios/p/3949798.htmlforman安装和配置流程 - 西城小磊目录(一)Foreman介绍 1. foreman概述 2.foreman架构(二)安装配置过程 1. 安装环境 2. 安装包收集 3. 准备工作 4. 安装foreman和puppet 5. 修改配置文件 参考链接(一)Foreman介绍1....2014-09-01T10:34:00Z2014-09-01T10:34:00Z西城小磊http://www.cnblogs.com/nagios/<div class="wiki-content"><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><strong><span style="color: #000000;">目录<br/></span></strong><span style="color: #000000;">(一)<a href="#foreman1">Foreman介绍</a><br /> 1. <a href="#foreman1.1">foreman概述</a><br /> 2.&nbsp;<a href="#foreman1.2">foreman架构</a><br /></span>(二)<a href="#foreman2">安装配置过程</a><br /> 1. <a href="#foreman2.1">安装环境</a><br /> 2. <a href="#foreman2.2">安装包收集</a><br /> 3. <a href="#foreman2.3">准备工作</a><br /> 4. <a href="#foreman2.4">安装foreman和puppet</a><br /> 5. <a href="#foreman2.5">修改配置文件</a><br /> <a href="#foreman3">参考链接</a></div><p>&nbsp;</p><p id="forman安装和配置流程-Forman安装和配置流程"><span id="foreman1" style="font-size: 16px;"><strong>(一)Foreman介绍</strong></span></p><p><span id="foreman1.1" style="font-size: 14px;"><strong>1.&nbsp;</strong><strong>foreman</strong><strong>概述</strong></span></p><p>&nbsp;&nbsp;&nbsp; Foreman是一个集成的数据中心生命周期管理工具,提供了服务开通,配置管理以及报告功能,和Puppet Dahboard一样,Foreman也是一个Ruby on Rails程序,通过它可以很直观的查看puppet所有客户端的同步状态与facter参数。Foreman和 Dashboard不同的地方是在于,Foreman更多的关注服务开通和管理数据中心的能力,例如和引导工具,PXE启动服务器(集成了kickstart),DHCP服务器及服务器开通工具进行集成。</p><p><span id="foreman1.2" style="font-size: 14px;"><strong>2.&nbsp;</strong><strong>foreman</strong><strong>架构</strong></span></p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/foreman_architecture.png?version=1&amp;modificationDate=1407908042312&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/foreman_architecture.png?version=1&amp;modificationDate=1407908042312&amp;api=v2" /></p><p id="forman安装和配置流程-安装配置过程"><span id="foreman2" style="font-size: 16px;"><strong>(二)安装配置过程</strong></span></p><p id="foreman2.1"><strong>1.&nbsp;</strong><strong>安装环境:</strong>CentOS6.5-x86_64, basic-server</p><p>安装更新:yum &ndash;y update</p><p>master: foreman.test.com&nbsp;&nbsp; &nbsp;172.19.146.27</p><p>agent: &nbsp; node1.test.com &nbsp; &nbsp; &nbsp; &nbsp; 172.19.146.28</p><p id="foreman2.2"><strong>2.&nbsp;</strong><strong>安装包收集:</strong></p><p>httpd-2.2.15-30</p><p>puppet-server-3.6.2-1</p><p>puppet-3.6.2-1</p><p>facter-2.1.0-1</p><p>foreman-1.5.2-1</p><p>foreman-proxy-1.5.2-1</p><p>foreman-sqlite-1.5.2-1</p><p>用到的源:</p><p>puppet:&nbsp;<a class="external-link" href="https://yum.puppetlabs.com/el/6/products/x86_64/puppetlabs-release-6-7.noarch.rpm" rel="nofollow">https://yum.puppetlabs.com/el/6/products/x86_64/puppetlabs-release-6-7.noarch.rpm</a></p><p>foreman:&nbsp;<a class="external-link" href="http://yum.theforeman.org/releases/1.5/el6/x86_64/foreman-1.5.2-1.el6.noarch.rpm" rel="nofollow">http://yum.theforeman.org/releases/1.5/el6/x86_64/foreman-1.5.2-1.el6.noarch.rpm</a></p><p>epel:&nbsp;<a class="external-link" href="http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm" rel="nofollow">http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm</a></p><p id="foreman2.3"><strong>3.&nbsp;</strong><strong>准备工作</strong></p><p>a. 关闭SElinux和防火墙(或者开放8140端口);</p><p>b. 设置hosts文件(master和agent端);</p><p>vim /etc/hosts</p><p>foreman.test.com&nbsp;&nbsp; 172.19.146.27</p><p>node1.test.com&nbsp;&nbsp;&nbsp;&nbsp; 172.19.146.28</p><p>c. 把需要安装的包及依赖包下载下来建立本地repo源: update+httpd+foreman+puppet+facter</p><p>d. 安装httpd并启动,设置开机自启;</p><p>yum install http</p><p>service httpd restart</p><p>chkconfig httpd on</p><p id="foreman2.4"><strong>4.&nbsp;</strong><strong>安装foreman和puppet</strong></p><p>a. yum &ndash;y install puppet puppet-server facter</p><p>b. yum &ndash;y install foreman foreman-proxy foreman-sqlite</p><p>注:这里只安装了基本的foreman-proxy代理模块和foreman-sqlite数据库模块;</p><p id="foreman2.5"><strong>5.&nbsp;</strong><strong>修改配置文件</strong></p><p>a. puppet配置: /etc/puppet/puppet.conf</p><p>agent端<br />[agent] 段添加</p><p>server = foreman.test.com</p><p>certname = node1.test.com</p><p>master端 :</p><p>service puppetmaster start</p><p>chkconfig puppetmaster on</p><p>agnet端申请认证: puppet agent --test</p><p>master端签名: puppet cert --sign node1.test.com</p><p>master 端查看认证情况:puppet cert &ndash;list --all</p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/cert.png?version=1&amp;modificationDate=1407908189812&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/cert.png?version=1&amp;modificationDate=1407908189812&amp;api=v2" /></p><p>注:如果master和agent时间不同步,申请认证会发生错误。</p><p>b. foreman-proxy配置:/etc/foreman-proxy/settings.yml</p><p>:puppetca: true</p><p>:puppet: true</p><p>:puppet_conf: /etc/puppet/puppet.conf</p><p>注:暂时不配置tftp,dns,dhcp等;</p><p>c. 配置 foreman的Smart Proxies</p><p>(1) 启动foreman和foreman-proxy:</p><p>service foreman start</p><p>service foreman-proxy start</p><p>(2) 浏览器:&nbsp;<a class="external-link" href="http://foreman.test.com:3000/" rel="nofollow">http://foreman.test.com:3000</a>&nbsp;默认用户名和密码:admin changeme;</p><p>(3) 打开 Infrastructure --&gt; Smart Proxies</p><p><strong>New Smart Proxy</strong>&nbsp;:添加新的智能代理;</p><p>name: test</p><p>url:&nbsp;<a class="external-link" href="http://foreman.test.com:8443/" rel="nofollow">http://foreman.test.com:8443</a></p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/q.png?version=2&amp;modificationDate=1407908042328&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/q.png?version=2&amp;modificationDate=1407908042328&amp;api=v2" /></p><p>注: 如果修改了/etc/foreman-proxy/settings.yml, 重启foreman-proxy服务之后需要在foreman控制面板的Smart-Proxy中<strong>Refresh feasures</strong>;</p><p>c. 数据库设置</p><p>(1) 这里使用sqlite数据库,如果使用mysql或其他数据库,需要修改/etc/foreman/database.yaml文件</p><p>(2) 数据库初始化</p><p>forman-rake&nbsp;<a class="external-link" href="http://dbmigrate/" rel="nofollow">db:migrate</a></p><p>foreman-rake&nbsp;<a class="external-link" href="http://dbseed/" rel="nofollow">db:seed</a></p><p>d. 从导入puppet数据</p><p>(1) 生成fact文件</p><p>master端和agent运行: puppet agent &ndash;test,会把facter信息发送至master端;</p><p>查看:ll /var/lib/puppet/yaml/facts</p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/w.png?version=1&amp;modificationDate=1407908042343&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/w.png?version=1&amp;modificationDate=1407908042343&amp;api=v2" /></p><p>(2) 从puppet导入facter数据给foreman</p><p>旧版本的foreman使用命令:</p><p>foreman-rake&nbsp;<a class="external-link" href="http://puppetimporthosts_and_facts/" rel="nofollow">puppet:import:hosts_and_facts</a>&nbsp;dir=/var/lib/puppet/yaml/facts/ RAILS_ENV=production</p><p>导入,但是1.5.2中不会显示导入facter信息,也不报错!</p><p>新版本:通过ENC(External Nodes Interface)导入[&ndash;&gt; f.&nbsp;&nbsp;通过ENC导入facter数据给foreman]</p><p>e. Puppet 报告</p><p>(1) master端和agent端: puppet.conf &nbsp;[agent] 段添加: report = true;</p><p>(2) puppet默认报告类型为store,内置了如下几个报告处理器:store, log, tagmail, rrdgraph, http,</p><p>(3) Forman自定义了foreman报告处理器,需要手动下载并添加</p><p>下载链接:</p><p><a class="external-link" href="https://raw.github.com/theforeman/puppet-foreman/2.1-stable/templates/foreman-report_v2.rb.erb" rel="nofollow">https://raw.github.com/theforeman/puppet-foreman/2.1-stable/templates/foreman-report_v2.rb.erb</a></p><p>将foreman-report_v2.rb.erb重命名为foreman.rb移动到master端的/usr/lib/ruby/site_ruby/1.8/puppet/reports/下;</p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/wq.png?version=1&amp;modificationDate=1407908042359&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/wq.png?version=1&amp;modificationDate=1407908042359&amp;api=v2" /></p><p><strong>修改foreman.rd</strong><strong>文件:</strong></p><p><strong>$foreman_url='<a class="external-link" href="http://foreman.test.com:3000/" rel="nofollow">http://foreman.test.com:3000</a>'</strong></p><p>(4) 添加foreman报告类型</p><p>master端的 [master]段添:reports = foreman, log</p><p>f. 通过ENC导入facter数据给foreman</p><p>(1) 下载文件</p><p><a class="external-link" href="https://raw.github.com/theforeman/puppet-foreman/2.1-stable/templates/external_node_v2.rb.erb" rel="nofollow">https://raw.github.com/theforeman/puppet-foreman/2.1-stable/templates/external_node_v2.rb.erb</a></p><p>到: /etc/puppet/下,重命名为node.rb;</p><p>(2) 添加可执行权限: chomod +x /etc/puppet/node.rd</p><p>(3) 编辑node.rd (暂时不设置ssl)</p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/qqq.png?version=1&amp;modificationDate=1407908042328&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/qqq.png?version=1&amp;modificationDate=1407908042328&amp;api=v2" /></p><p>(4) 在master端的puppet.conf的[master]段添加:</p><p>external_nodes = /etc/puppet/node.rb</p><p>node_terminus = exec</p><p>(5) 重启: puppetmaster, foreman, foreman-proxy服务;</p><p>在puppet agent端运行:puppet agent --test就可以在foreman的web界面(<a class="external-link" href="http://172.19.146.27:3000/" rel="nofollow">http://172.19.146.27:3000</a>)看到各个节点的facter信息了。</p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/1.png?version=1&amp;modificationDate=1407908042281&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/1.png?version=1&amp;modificationDate=1407908042281&amp;api=v2" /></p><p><img class="confluence-embedded-image" src="http://wiki.cnsuning.com/download/attachments/17925786/2.png?version=1&amp;modificationDate=1407908042296&amp;api=v2" alt="" data-image-src="/download/attachments/17925786/2.png?version=1&amp;modificationDate=1407908042296&amp;api=v2" /></p><p id="foreman3"><strong>参考链接:</strong></p><p>[1]&nbsp;<a class="external-link" href="http://www.jsxubar.info/centos-6-install-puppet-foreman.html" rel="nofollow">http://www.jsxubar.info/centos-6-install-puppet-foreman.html</a></p><p>[2]&nbsp;<a class="external-link" href="http://itnihao.blog.51cto.com/1741976/1143208" rel="nofollow">http://itnihao.blog.51cto.com/1741976/1143208</a></p><p>[3]&nbsp;<a class="external-link" href="http://os.51cto.com/art/201101/244173.htm" rel="nofollow">http://os.51cto.com/art/201101/244173.htm</a></p><p>[4]&nbsp;<a class="external-link" href="http://longgeek.com/2012/10/23/puppet-console-foreman/" rel="nofollow">http://longgeek.com/2012/10/23/puppet-console-foreman/</a></p><p>[5]&nbsp;<a class="external-link" href="http://dywer.blog.51cto.com/678776/415839" rel="nofollow">http://dywer.blog.51cto.com/678776/415839</a></p><p>[6]&nbsp;<a class="external-link" href="http://www.thefarsideoffailure.com/blog/foreman_on_centos" rel="nofollow">http://www.thefarsideoffailure.com/blog/foreman_on_centos</a></p><p>[7]&nbsp;<a class="external-link" href="http://theforeman.org/manuals/1.5/index.html#Releasenotesfor1.5.2" rel="nofollow">http://theforeman.org/manuals/1.5/index.html#Releasenotesfor1.5.2</a></p></div><div id="likes-and-labels-container">&nbsp;</div><img src="http://counter.cnblogs.com/blog/rss/3949798" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/nagios/p/3949798.html" target="_blank">forman安装和配置流程</a>,转载请注明。</p>http://www.cnblogs.com/Yan-C/p/3943940.html拓扑排序 详解 + 并查集 详解 + 最小生成树(MST)详解 【普利姆算法 + 优先队列优化 & 克鲁斯卡尔算法】 - blueppo本人QQ :2319411771 邮箱 : cyb19950118@163.com 若您发现本文有什么错误,请联系我,我会及时改正的,谢谢您的合作! 本文为原创文章,转载请注明出处 本文链接 :http://www.cnblogs.com/Yan-C/p/3943940...2014-09-01T10:18:00Z2014-09-01T10:18:00Zblueppohttp://www.cnblogs.com/Yan-C/<p><span style="color: #ff0000;"><span style="color: #ff0000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本人QQ :2319411771 &nbsp; 邮箱 : cyb19950118@163.com</span></span></p><p><span style="color: #ff0000;">     若您发现本文有什么错误,请联系我,我会及时改正的,谢谢您的合作!</span></p><p><span style="color: #ff0000;">     本文为原创文章,转载请注明出处&nbsp;</span></p><p><span style="color: #ff0000;">     本文链接 &nbsp; :<a id="Editor_Edit_hlEntryLink" title="view: 拓扑排序 详解 + 并查集 详解 + 最小生成树(MST)详解 【普利姆算法 + 优先队列优化 &amp;amp; 克鲁斯卡尔算法】" href="http://www.cnblogs.com/Yan-C/p/3943940.html" target="_blank">http://www.cnblogs.com/Yan-C/p/3943940.html</a> 。</span></p><p>&nbsp;</p><p>    哎呀,好久了啊,想写这篇博文好久了,但是因为懒的原因 一直迟迟没动手啊。</p><p>    今天,终于在长久的懒惰下,突然来了那么一点热度。把这篇博文写一下。</p><p><span style="color: #ff0000; font-size: 18px;">本文分为以下几个部分 :</span></p><p><span style="color: #ff0000; font-size: 18px;">1、&nbsp; 拓扑排序</span></p><p><span style="color: #ff0000; font-size: 18px;">2、&nbsp; 并查集</span></p><p><span style="color: #ff0000; font-size: 18px;">3、&nbsp; 普利姆算法&nbsp; &amp;&nbsp; 优先队列优化</span></p><p><span style="color: #ff0000; font-size: 18px;">4、&nbsp; 克鲁斯卡尔算法</span></p><p>&nbsp;<span style="color: #ff0000; font-size: 14pt;">前情提要 :&nbsp; 本文的存图方式 只有两种 :&nbsp; 邻接矩阵 or&nbsp; 前向星。</span></p><p><span style="font-size: 16px; color: #00ffff;">1、 拓扑排序</span></p><p><span style="font-size: 16px; color: #00ffff;">  <span style="color: #000000;">我们起床穿裤子和鞋子时,相信大部分人的顺序是这样的,先穿上内裤,然后再穿上裤子,再穿上袜子,然后才是鞋子。&nbsp; <span style="color: #ff0000;">那么<span style="color: #000000;">,我们把这些步骤分解:</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(1)穿内裤</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"> (2)穿裤子</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(3)穿袜子</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(4)穿鞋子</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">我们把这四个步骤,按照上述的顺序 给排一下,<span style="color: #ff0000;">这就是所谓的拓扑排序</span>。</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">当然这个排序的顺序是<span style="color: #ff0000;">唯一</span>的,如果你先进行(2)然后(1)(3)(4),哦,不,你不是超人,请不要这样做, 又假如你按照(1)(2)(4)(3), 那显然也是不行的。</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">拓扑排序 也可以描述一个</span>暑假写作业</span><span style="color: #ff0000;"><span style="color: #000000;">的过程 : 语文作业,数学作业,英语作业,生物作业,化学作业,物理作业。</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(1)&nbsp; 语文<br /></span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(2)&nbsp; 数学<br /></span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(3)&nbsp; 英语<br /></span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(4)&nbsp; 生物<br /></span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(5)&nbsp; 化学</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">(6)&nbsp; 物理</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">你可以是(1)(2)(3)(4)(5)(6),也可以是(6)(5)(4)(3)(2)(1),再者英语老师比较凶,那么可以是(3)(1)(2)(4)(5)(6)。等等其他的排序方式。</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">那么这个排序又是<span style="color: #ff0000;">不唯一</span>的。</span></span></span></span></p><p><span style="font-size: 16px; color: #00ffff;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">因此&nbsp; <span style="color: #ff0000;">拓扑排序可能是唯一的又有可能是不唯一的。</span></span></span></span></span></p><p><span style="font-size: 16px; color: #000000;">就像 3个篮球队进行比赛。&nbsp; 编号分别为 1&nbsp; , 2 , 3。</span></p><p><span style="font-size: 16px; color: #000000;">1打赢了2</span></p><p><span style="font-size: 16px; color: #000000;">2打赢了3</span></p><p><span style="font-size: 16px; color: #000000;">3打赢了1。 问谁是最后的冠军。 各一胜一负你问我谁是冠军 ,这不是扯蛋嘛。 So,这是不能判断谁是冠军的,&nbsp; 因为这个事件存在一个 环,互相牵制,进行排序是不行产生结果的。</span></p><p><span style="font-size: 16px; color: #000000;">如果这样&nbsp; :</span></p><p><span style="font-size: 16px; color: #000000;">1打赢了2</span></p><p><span style="font-size: 16px; color: #000000;">3打赢了2</span></p><p><span style="font-size: 16px; color: #000000;">那么最后的冠军可能是不确定的,因为你不知道1和3 谁强。 所以只能是 1,3并列了,你如果喜欢大数在前 那就是3 1 2,反之,就是1 3 2了。</span></p><p><span style="font-size: 16px; color: #ff0000;">拓扑排序其实就是这个样子。</span></p><p><span style="font-size: 16px; color: #000000;">前面大篇幅的扯犊子,主要是介绍什么是拓扑排序。 那么我们要讨论一下,怎么样进行拓扑排序呢?&nbsp; 哎,这个问题好!</span></p><p><span style="font-size: 14pt; color: #ff0000;">插播 :</span></p><p><span style="font-size: 16px; color: #000000;">我们再次的从 1&nbsp; 2&nbsp; 3&nbsp; 这三支队伍的冠军争夺赛说起。</span></p><p><span style="font-size: 16px; color: #000000;">1打赢了2&nbsp; 因为2输了一场比赛,所以要给2做一标记。因此2号的菊花上就出现了一杆长枪。 <span style="color: #ff0000;">我们称这个标记为</span> <span style="color: #ff0000;">入度 <span style="color: #000000;">那么2的入度就是 1了。</span></span><br /></span></p><p><span style="font-size: 16px; color: #000000;"><span style="font-size: 16px; color: #000000;">3打赢了2&nbsp;&nbsp; 因为2又输了一场比赛,又是一杆长枪啊。为什么受伤的总是2。&nbsp; </span></span><span style="color: #ff0000;"><span style="font-size: 16px;"><span style="font-size: 16px;">那么2的入度 就++了&nbsp; 变成</span></span><span style="font-size: 16px;"><span style="font-size: 16px;">了2。<br /></span></span></span></p><p><span style="color: #000000;"><span style="font-size: 16px;"><span style="font-size: 16px;">好了&nbsp; 这就是 什么是&nbsp; 入度&nbsp; 了。&nbsp; 如果你还不是很懂<span style="color: #ff0000;">入度是什么</span>。那我告诉你,<span style="color: #ff0000;">入度 在这里就是2号被打败了几次</span>。</span></span></span></p><p><span style="color: #000000;"><span style="font-size: 16px;"><span style="font-size: 16px;">那我们 就要 进入正题了。</span></span></span></p><p><span style="color: #000000;"><span style="font-size: 16px;"><span style="font-size: 16px;">拓扑排序 :</span></span></span></p><p><span style="color: #000000;"><span style="font-size: 16px;"><span style="font-size: 16px;"> </span></span></span><span style="font-size: 16px;"> 由AOV网构造拓扑序列的拓扑排序算法主要是循环执行以下两步,直到不存在入度为0的顶点为止。</span></p><div><span style="font-size: 16px;">  (1) 选择一个入度为0的顶点并输出之;</span></div><div><span style="font-size: 16px;">  (2) 从网中删除此顶点及所有出边。</span></div><p><span style="color: #000000;"><span style="font-size: 16px;"><span style="font-size: 16px;">  循环结束后,若输出的顶点数小于网中的顶点数,则输出&ldquo;有回路&rdquo;信息,否则输出的顶点序列就是一种拓扑序列。 (摘自 : 百度百科)<br /></span></span></span></p><p>&nbsp;我们继续 以题来进行讲解和理解的加深。</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('0f0d46c6-c50c-4add-b881-09bf1a8bacae')"><div id="cnblogs_code_open_0f0d46c6-c50c-4add-b881-09bf1a8bacae" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> <span style="color: #000000;">Description<br/></span><span style="color: #008080;"> 2</span> 有N个比赛队(<span style="color: #800080;">1</span>&lt;=N&lt;=<span style="color: #800080;">500</span>),编号依次为1,<span style="color: #800080;">2</span>,<span style="color: #800080;">3</span><span style="color: #000000;">,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。<br/></span><span style="color: #008080;"> 3</span> <br/><span style="color: #008080;"> 4</span> <br/><span style="color: #008080;"> 5</span> <span style="color: #000000;">Input<br/></span><span style="color: #008080;"> 6</span> 输入有若干组,每组中的第一行为二个数N(<span style="color: #800080;">1</span>&lt;=N&lt;=<span style="color: #800080;">500</span><span style="color: #000000;">),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。<br/></span><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <br/><span style="color: #008080;"> 9</span> <span style="color: #000000;">Output<br/></span><span style="color: #008080;">10</span> <span style="color: #000000;">给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。<br/></span><span style="color: #008080;">11</span> <br/><span style="color: #008080;">12</span> <span style="color: #000000;">其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。<br/></span><span style="color: #008080;">13</span> <br/><span style="color: #008080;">14</span> <br/><span style="color: #008080;">15</span> <span style="color: #000000;">Sample Input<br/></span><span style="color: #008080;">16</span> <br/><span style="color: #008080;">17</span> <span style="color: #800080;">4</span> <span style="color: #800080;">3</span> <br/><span style="color: #008080;">18</span> <span style="color: #800080;">1</span> <span style="color: #800080;">2</span> <br/><span style="color: #008080;">19</span> <span style="color: #800080;">2</span> <span style="color: #800080;">3</span> <br/><span style="color: #008080;">20</span> <span style="color: #800080;">4</span> <span style="color: #800080;">3</span><br/><span style="color: #008080;">21</span> <br/><span style="color: #008080;">22</span> <br/><span style="color: #008080;">23</span> <br/><span style="color: #008080;">24</span> <span style="color: #000000;">Sample Output<br/></span><span style="color: #008080;">25</span> <br/><span style="color: #008080;">26</span> <span style="color: #800080;">1</span> <span style="color: #800080;">2</span> <span style="color: #800080;">4</span> <span style="color: #800080;">3</span></div></div><p>题目链接:<a href="http://acm.hdu.edu.cn/showproblem.php?pid=1285" target="_blank"><span style="color: #ff0000;">在这</span></a></p><p><span style="color: #000000;">因为数据较小,我们可以使用邻接矩阵进行存储。&nbsp; 这是第一种方法。</span></p><p><span style="color: #000000;">题解在这 :</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('671928d1-5341-49ca-b0ba-6fb60a1f41db')"><div id="cnblogs_code_open_671928d1-5341-49ca-b0ba-6fb60a1f41db" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">503</span>;<span style="color: #008000;">//</span><span style="color: #008000;"> 点的个数</span><br/><span style="color: #008080;">10</span> <br/><span style="color: #008080;">11</span> <span style="color: #0000ff;">bool</span> G[VM][VM];<span style="color: #008000;">//</span><span style="color: #008000;">图</span><br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">int</span> deg[VM];<span style="color: #008000;">//</span><span style="color: #008000;">各个顶点的入度 计数</span><br/><span style="color: #008080;">13</span> <br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">void</span> toposort(<span style="color: #0000ff;">int</span> n) {<span style="color: #008000;">//</span><span style="color: #008000;">拓扑排序</span><br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">int</span> k = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">16</span> <br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">共进行|G.V|次操作</span><br/><span style="color: #008080;">18</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有的顶点 找入度为0的</span><br/><span style="color: #008080;">19</span> <span style="color: #0000ff;">if</span> (deg[j] == <span style="color: #800080;">0</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">找到</span><br/><span style="color: #008080;">20</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d%c</span><span style="color: #800000;">"</span>, j, i == n ? <span style="color: #800000;">'</span><span style="color: #800000;">\n</span><span style="color: #800000;">'</span> : <span style="color: #800000;">'</span> <span style="color: #800000;">'</span>);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">21</span> deg[j]--;<span style="color: #008000;">//</span><span style="color: #008000;">去掉这个点 让deg[j] = -1;</span><br/><span style="color: #008080;">22</span> k = j;<span style="color: #008000;">//</span><span style="color: #008000;">记录这个点</span><br/><span style="color: #008080;">23</span> <span style="color: #0000ff;">break</span>;<span style="color: #008000;">//</span><span style="color: #008000;">跳出循环</span><br/><span style="color: #008080;">24</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">25</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">26</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++)<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有的点</span><br/><span style="color: #008080;">27</span> <span style="color: #0000ff;">if</span> (G[k][j] == <span style="color: #0000ff;">true</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">找被此点打败过的点</span><br/><span style="color: #008080;">28</span> G[k][j] = <span style="color: #0000ff;">false</span>;<span style="color: #008000;">//</span><span style="color: #008000;">标记为找到过</span><br/><span style="color: #008080;">29</span> deg[j]--;<span style="color: #008000;">//</span><span style="color: #008000;">让这个点的入度-1</span><br/><span style="color: #008080;">30</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">31</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">32</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">33</span> <br/><span style="color: #008080;">34</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">35</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">36</span> <br/><span style="color: #008080;">37</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m) == <span style="color: #800080;">2</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">多组输入, 获取n, m</span><br/><span style="color: #008080;">38</span> memset(G, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(G));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">39</span> memset(deg, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(deg));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">40</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">41</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;">42</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v);<span style="color: #008000;">//</span><span style="color: #008000;">获取 u,v u打败过v</span><br/><span style="color: #008080;">43</span> <span style="color: #0000ff;">if</span> (G[u][v] == <span style="color: #0000ff;">false</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">防止重边 如果被同一个对手打败多次,也太伤v的心了</span><br/><span style="color: #008080;">44</span> G[u][v] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;">标记为真</span><br/><span style="color: #008080;">45</span> deg[v]++;<span style="color: #008000;">//</span><span style="color: #008000;">v的入度++ 一杆长枪入洞了。</span><br/><span style="color: #008080;">46</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">47</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">48</span> toposort(n);<span style="color: #008000;">//</span><span style="color: #008000;">调用函数</span><br/><span style="color: #008080;">49</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">50</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">51</span> }</div></div><p>&nbsp; 主函数 对数据的获取 和存图。</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;"> 3</span> <br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m) == <span style="color: #800080;">2</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">多组输入, 获取n, m</span><br/><span style="color: #008080;"> 5</span> memset(G, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(G));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 6</span> memset(deg, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(deg));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;"> 9</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v);<span style="color: #008000;">//</span><span style="color: #008000;">获取 u,v u打败过v</span><br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">if</span> (G[u][v] == <span style="color: #0000ff;">false</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">防止重边 如果被同一个对手打败多次,也太伤v的心了</span><br/><span style="color: #008080;">11</span> G[u][v] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;">标记为真</span><br/><span style="color: #008080;">12</span> deg[v]++;<span style="color: #008000;">//</span><span style="color: #008000;">v的入度++ 一杆长枪入洞了。</span><br/><span style="color: #008080;">13</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">14</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">15</span> toposort(n);<span style="color: #008000;">//</span><span style="color: #008000;">调用函数</span><br/><span style="color: #008080;">16</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">17</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">18</span> }</div><p>&nbsp;拓扑排序的函数 :</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">void</span> toposort(<span style="color: #0000ff;">int</span> n) {<span style="color: #008000;">//</span><span style="color: #008000;">拓扑排序</span><br/><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">int</span> k = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;"> 3</span> <br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">共进行|G.V|次操作</span><br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有的顶点 找入度为0的</span><br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">if</span> (deg[j] == <span style="color: #800080;">0</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">找到</span><br/><span style="color: #008080;"> 7</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d%c</span><span style="color: #800000;">"</span>, j, i == n ? <span style="color: #800000;">'</span><span style="color: #800000;">\n</span><span style="color: #800000;">'</span> : <span style="color: #800000;">'</span> <span style="color: #800000;">'</span>);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;"> 8</span> deg[j]--;<span style="color: #008000;">//</span><span style="color: #008000;">去掉这个点 让deg[j] = -1;</span><br/><span style="color: #008080;"> 9</span> k = j;<span style="color: #008000;">//</span><span style="color: #008000;">记录这个点</span><br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">break</span>;<span style="color: #008000;">//</span><span style="color: #008000;">跳出循环</span><br/><span style="color: #008080;">11</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">12</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">13</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++)<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有的点</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">if</span> (G[k][j] == <span style="color: #0000ff;">true</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">找被此点打败过的点</span><br/><span style="color: #008080;">15</span> G[k][j] = <span style="color: #0000ff;">false</span>;<span style="color: #008000;">//</span><span style="color: #008000;">标记为找到过</span><br/><span style="color: #008080;">16</span> deg[j]--;<span style="color: #008000;">//</span><span style="color: #008000;">让这个点的入度-1</span><br/><span style="color: #008080;">17</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">18</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">19</span> }</div><p><br />此算法的时间复杂度为 O(n * n)&nbsp; 复杂度挺高的呢。</p><p>那我们要想办法优化啊。</p><p>来了 , 第二种&nbsp; 时间复杂度为 O(V + E)&nbsp; 在这个算法中 我们用到了 前向星&nbsp; 和&nbsp; 优先队列。</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('40551d2e-60c9-4226-8020-bdee75739355')"><div id="cnblogs_code_open_40551d2e-60c9-4226-8020-bdee75739355" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">503</span>;<span style="color: #008000;">//</span><span style="color: #008000;"> 点的个数</span><br/><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">struct</span> node {<span style="color: #008000;">//</span><span style="color: #008000;">前向星的结构体</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">int</span> v;<span style="color: #008000;">//</span><span style="color: #008000;">输队编号</span><br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> next;<br/></span><span style="color: #008080;">16</span> <span style="color: #000000;">};<br/></span><span style="color: #008080;">17</span> node edge[VM * <span style="color: #800080;">4</span>];<span style="color: #008000;">//</span><span style="color: #008000;">结构体数组</span><br/><span style="color: #008080;">18</span> <span style="color: #0000ff;">int</span> head[VM];<span style="color: #008000;">//</span><span style="color: #008000;">头指针数组</span><br/><span style="color: #008080;">19</span> <span style="color: #0000ff;">int</span> cnt;<span style="color: #008000;">//</span><span style="color: #008000;">下标</span><br/><span style="color: #008080;">20</span> <span style="color: #0000ff;">int</span> deg[VM];<span style="color: #008000;">//</span><span style="color: #008000;">入度数组</span><br/><span style="color: #008080;">21</span> <br/><span style="color: #008080;">22</span> <span style="color: #0000ff;">void</span> toposort(<span style="color: #0000ff;">int</span><span style="color: #000000;"> n) {<br/></span><span style="color: #008080;">23</span> priority_queue&lt;<span style="color: #0000ff;">int</span>, vector&lt;<span style="color: #0000ff;">int</span>&gt;, greater&lt;<span style="color: #0000ff;">int</span>&gt; &gt; que;<span style="color: #008000;">//</span><span style="color: #008000;">优先队列</span><br/><span style="color: #008080;">24</span> <br/><span style="color: #008080;">25</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++)<span style="color: #008000;">//</span><span style="color: #008000;">找所有点</span><br/><span style="color: #008080;">26</span> <span style="color: #0000ff;">if</span> (deg[i] == <span style="color: #800080;">0</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">入度为 0</span><br/><span style="color: #008080;">27</span> que.push(i);<span style="color: #008000;">//</span><span style="color: #008000;">加入队列</span><br/><span style="color: #008080;">28</span> deg[i]--;<span style="color: #008000;">//</span><span style="color: #008000;">入度 变为 -1</span><br/><span style="color: #008080;">29</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">30</span> <span style="color: #0000ff;">int</span> k = <span style="color: #800080;">1</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">31</span> <span style="color: #0000ff;">while</span> (que.empty() == <span style="color: #0000ff;">false</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">队列不为空</span><br/><span style="color: #008080;">32</span> <span style="color: #0000ff;">int</span> u = que.top();<span style="color: #008000;">//</span><span style="color: #008000;">取出队首的数</span><br/><span style="color: #008080;">33</span> que.pop();<span style="color: #008000;">//</span><span style="color: #008000;">删除</span><br/><span style="color: #008080;">34</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d%c</span><span style="color: #800000;">"</span>, u, k++ == n ? <span style="color: #800000;">'</span><span style="color: #800000;">\n</span><span style="color: #800000;">'</span> : <span style="color: #800000;">'</span> <span style="color: #800000;">'</span>);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">35</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = head[u]; i != -<span style="color: #800080;">1</span>; i = edge[i].next) {<span style="color: #008000;">//</span><span style="color: #008000;">与该点相连的</span><br/><span style="color: #008080;">36</span> node e = edge[i];<span style="color: #008000;">//</span><span style="color: #008000;">便于书写</span><br/><span style="color: #008080;">37</span> deg[e.v]--;<span style="color: #008000;">//</span><span style="color: #008000;">点的入度 -1 </span><br/><span style="color: #008080;">38</span> <span style="color: #0000ff;">if</span> (deg[e.v] == <span style="color: #800080;">0</span>)<span style="color: #008000;">//</span><span style="color: #008000;">若此点的 入度为 0</span><br/><span style="color: #008080;">39</span> que.push(e.v);<span style="color: #008000;">//</span><span style="color: #008000;">放入队列</span><br/><span style="color: #008080;">40</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">41</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">42</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">43</span> <br/><span style="color: #008080;">44</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">45</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">46</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> i;<br/></span><span style="color: #008080;">47</span> <br/><span style="color: #008080;">48</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m) == <span style="color: #800080;">2</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">多组输入 ,获取n,m</span><br/><span style="color: #008080;">49</span> memset(head, -<span style="color: #800080;">1</span>, <span style="color: #0000ff;">sizeof</span>(head));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">50</span> memset(deg, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(deg));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">51</span> cnt = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">52</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">53</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;">54</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v);<span style="color: #008000;">//</span><span style="color: #008000;">获取u,v</span><br/><span style="color: #008080;">55</span> <span style="color: #0000ff;">for</span> (i = head[u]; i != -<span style="color: #800080;">1</span>; i = edge[i].next)<span style="color: #008000;">//</span><span style="color: #008000;">查找重边</span><br/><span style="color: #008080;">56</span> <span style="color: #0000ff;">if</span> (edge[i].v == v)<span style="color: #008000;">//</span><span style="color: #008000;">输入重复数据</span><br/><span style="color: #008080;">57</span> <span style="color: #0000ff;">break</span>;<span style="color: #008000;">//</span><span style="color: #008000;">不再储存</span><br/><span style="color: #008080;">58</span> <span style="color: #0000ff;">if</span> (i == -<span style="color: #800080;">1</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">若不是重复数据</span><br/><span style="color: #008080;">59</span> deg[v]++;<span style="color: #008000;">//</span><span style="color: #008000;">加边</span><br/><span style="color: #008080;">60</span> edge[cnt].v =<span style="color: #000000;"> v;<br/></span><span style="color: #008080;">61</span> edge[cnt].next =<span style="color: #000000;"> head[u];<br/></span><span style="color: #008080;">62</span> head[u] = cnt++<span style="color: #000000;">;<br/></span><span style="color: #008080;">63</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">64</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">65</span> toposort(n);<span style="color: #008000;">//</span><span style="color: #008000;">调用函数</span><br/><span style="color: #008080;">66</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">67</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">68</span> }</div></div><p><br />所用到的数据结构 :</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> priority_queue&lt;<span style="color: #0000ff;">int</span>, vector&lt;<span style="color: #0000ff;">int</span>&gt;, greater&lt;<span style="color: #0000ff;">int</span>&gt; &gt; que;<span style="color: #008000;">//</span><span style="color: #008000;">优先队列</span><br/><span style="color: #008080;">2</span> <span style="color: #0000ff;">struct</span> node {<span style="color: #008000;">//</span><span style="color: #008000;">前向星的结构体</span><br/><span style="color: #008080;">3</span> <span style="color: #0000ff;">int</span> v;<span style="color: #008000;">//</span><span style="color: #008000;">输队编号</span><br/><span style="color: #008080;">4</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> next;<br/></span><span style="color: #008080;">5</span> <span style="color: #000000;">};<br/></span><span style="color: #008080;">6</span> node edge[VM * <span style="color: #800080;">4</span>];<span style="color: #008000;">//</span><span style="color: #008000;">结构体数组</span><br/><span style="color: #008080;">7</span> <span style="color: #0000ff;">int</span> head[VM];<span style="color: #008000;">//</span><span style="color: #008000;">头指针数组</span><br/><span style="color: #008080;">8</span> <span style="color: #0000ff;">int</span> cnt;<span style="color: #008000;">//</span><span style="color: #008000;">下标</span></div><p>主函数对数据的获取和 图的存储</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> i;<br/></span><span style="color: #008080;"> 4</span> <br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m) == <span style="color: #800080;">2</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">多组输入 ,获取n,m</span><br/><span style="color: #008080;"> 6</span> memset(head, -<span style="color: #800080;">1</span>, <span style="color: #0000ff;">sizeof</span>(head));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 7</span> memset(deg, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(deg));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 8</span> cnt = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">10</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;">11</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v);<span style="color: #008000;">//</span><span style="color: #008000;">获取u,v</span><br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">for</span> (i = head[u]; i != -<span style="color: #800080;">1</span>; i = edge[i].next)<span style="color: #008000;">//</span><span style="color: #008000;">查找重边</span><br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">if</span> (edge[i].v == v)<span style="color: #008000;">//</span><span style="color: #008000;">输入重复数据</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">break</span>;<span style="color: #008000;">//</span><span style="color: #008000;">不再储存</span><br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">if</span> (i == -<span style="color: #800080;">1</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">若不是重复数据</span><br/><span style="color: #008080;">16</span> deg[v]++;<span style="color: #008000;">//</span><span style="color: #008000;">加边</span><br/><span style="color: #008080;">17</span> edge[cnt].v =<span style="color: #000000;"> v;<br/></span><span style="color: #008080;">18</span> edge[cnt].next =<span style="color: #000000;"> head[u];<br/></span><span style="color: #008080;">19</span> head[u] = cnt++<span style="color: #000000;">;<br/></span><span style="color: #008080;">20</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">21</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">22</span> toposort(n);<span style="color: #008000;">//</span><span style="color: #008000;">调用函数</span><br/><span style="color: #008080;">23</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">24</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">25</span> }</div><p><br />拓扑排序函数 </p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">void</span> toposort(<span style="color: #0000ff;">int</span><span style="color: #000000;"> n) {<br/></span><span style="color: #008080;"> 2</span> priority_queue&lt;<span style="color: #0000ff;">int</span>, vector&lt;<span style="color: #0000ff;">int</span>&gt;, greater&lt;<span style="color: #0000ff;">int</span>&gt; &gt; que;<span style="color: #008000;">//</span><span style="color: #008000;">优先队列</span><br/><span style="color: #008080;"> 3</span> <br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++)<span style="color: #008000;">//</span><span style="color: #008000;">找所有点</span><br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">if</span> (deg[i] == <span style="color: #800080;">0</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">入度为 0</span><br/><span style="color: #008080;"> 6</span> que.push(i);<span style="color: #008000;">//</span><span style="color: #008000;">加入队列</span><br/><span style="color: #008080;"> 7</span> deg[i]--;<span style="color: #008000;">//</span><span style="color: #008000;">入度 变为 -1</span><br/><span style="color: #008080;"> 8</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">int</span> k = <span style="color: #800080;">1</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">10</span> <span style="color: #0000ff;">while</span> (que.empty() == <span style="color: #0000ff;">false</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">队列不为空</span><br/><span style="color: #008080;">11</span> <span style="color: #0000ff;">int</span> u = que.top();<span style="color: #008000;">//</span><span style="color: #008000;">取出队首的数</span><br/><span style="color: #008080;">12</span> que.pop();<span style="color: #008000;">//</span><span style="color: #008000;">删除</span><br/><span style="color: #008080;">13</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d%c</span><span style="color: #800000;">"</span>, u, k++ == n ? <span style="color: #800000;">'</span><span style="color: #800000;">\n</span><span style="color: #800000;">'</span> : <span style="color: #800000;">'</span> <span style="color: #800000;">'</span>);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = head[u]; i != -<span style="color: #800080;">1</span>; i = edge[i].next) {<span style="color: #008000;">//</span><span style="color: #008000;">与该点相连的</span><br/><span style="color: #008080;">15</span> node e = edge[i];<span style="color: #008000;">//</span><span style="color: #008000;">便于书写</span><br/><span style="color: #008080;">16</span> deg[e.v]--;<span style="color: #008000;">//</span><span style="color: #008000;">点的入度 -1 </span><br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">if</span> (deg[e.v] == <span style="color: #800080;">0</span>)<span style="color: #008000;">//</span><span style="color: #008000;">若此点的 入度为 0</span><br/><span style="color: #008080;">18</span> que.push(e.v);<span style="color: #008000;">//</span><span style="color: #008000;">放入队列</span><br/><span style="color: #008080;">19</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">20</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">21</span> }</div><p>&nbsp;</p><p><span style="color: #ff0000; font-size: 18px;">拓扑排序 讲解 完毕。</span></p><p>&nbsp;</p><p><span style="font-size: 18px; color: #ff0000;">2、并查集</span></p><p><span style="font-size: 15px; color: #000000;">  &nbsp; 并查集从字面上最起码可以看出是一个集合,而且是能并(合并吗?) 能查的集合。集合也就是分组,一组一组的数据,这一组就是一个集合嘛。</span></p><p><span style="font-size: 15px; color: #000000;">   并查集是一种用来管理元素分组情况的数据结构。&nbsp; 并查集,并查集,那么他的功能肯定就是&nbsp; 并&nbsp; 和 查。</span></p><p><span style="font-size: 15px; color: #000000;">   他可以高效的进行 :</span></p><p><span style="font-size: 15px; color: #000000;"> <span style="color: #ff0000;">并 &nbsp;<span style="color: #000000;">合并元素a和元素b所在的组。</span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"> <span style="color: #ff0000;">查&nbsp;&nbsp; <span style="color: #000000;">查询元素a和元素b是否属于同一组。</span><br /></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">   <span style="color: #ff0000;">并查集可以进行合并 但是却不能进行分割。</span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;">   <span style="color: #000000;">并查集的结构 是 <span style="color: #ff0000;">树形结构,但是他却不是二叉树,因为是树,所以必定有根节点,根节点就是这个集合,这个分组中最大的统领着</span>。</span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">   对于并查集呢,主要是有</span></span></span></span></span>两部分函数</span><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">构成, 一个是<span style="color: #ff0000;">union()函数</span> 也就是我们所说的并(合并),另一个是<span style="color: #ff0000;">find()函数</span>&nbsp; 也就是所说的查函数。</span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">   对于并查集不会画图真的是好纠结。</span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">   对于并查集,大家看这个大牛的博客的讲解吧,&nbsp; 如果大家不想看的话,可以直接看下面的代码讲解,注释还是很清晰的。</span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"> <a href="http://www.cnblogs.com/cyjb/p/UnionFindSets.html" target="_blank"> http://www.cnblogs.com/cyjb/p/UnionFindSets.html</a></span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">   看完讲解 大家可以看一下这些题目加深一下。(脑子有点乱乱的,原谅我的&ldquo;乱来&rdquo;)。</span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">我们对并查集的初始化</span></span></span></span></span></span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">2</span> par[i] = i;<span style="color: #008000;">//</span><span style="color: #008000;">这是初始化</span></div><p>这是find() 函数&nbsp;</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> <span style="color: #0000ff;">int</span> find(<span style="color: #0000ff;">int</span> x) {<span style="color: #008000;">//</span><span style="color: #008000;">查找函数</span><br/><span style="color: #008080;">2</span> <span style="color: #0000ff;">if</span> (par[x] == x)<span style="color: #008000;">//</span><span style="color: #008000;">若本身就是根节点 ,那么return</span><br/><span style="color: #008080;">3</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> x;<br/></span><span style="color: #008080;">4</span> <span style="color: #0000ff;">return</span> find(par[x]);<span style="color: #008000;">//</span><span style="color: #008000;">不是的话,继续查找</span><br/><span style="color: #008080;">5</span> }</div><p>这是合并函数</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> <span style="color: #0000ff;">void</span> unite(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y) {<span style="color: #008000;">//</span><span style="color: #008000;">合并函数</span><br/><span style="color: #008080;">2</span> x = find(x);<span style="color: #008000;">//</span><span style="color: #008000;">查找x的根节点</span><br/><span style="color: #008080;">3</span> y = find(y);<span style="color: #008000;">//</span><span style="color: #008000;">查找y的根节点</span><br/><span style="color: #008080;">4</span> <span style="color: #0000ff;">if</span> (x == y)<span style="color: #008000;">//</span><span style="color: #008000;">若这两个数的结点是一样的,那么他们本来就是一个分组里的了,所以没有操作</span><br/><span style="color: #008080;">5</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ;<br/></span><span style="color: #008080;">6</span> par[x] = y;<span style="color: #008000;">//</span><span style="color: #008000;">不然的话,就让其中的一个点成为另一个点的根节点。</span><br/><span style="color: #008080;">7</span> }</div><p>还有一个判断的 same函数</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;">1</span> <span style="color: #0000ff;">bool</span> same(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span><span style="color: #000000;"> y) {<br/></span><span style="color: #008080;">2</span> <span style="color: #0000ff;">return</span> find(x) ==<span style="color: #000000;"> find(y);<br/></span><span style="color: #008080;">3</span> }</div><p>same函数 下面的题解中都是 直接判断的,所以就把same这个函数直接放在了里面,就这样 same 函数 被我隐藏了。</p><p>这上面的&nbsp; 查找函数和合并函数 都是未经优化的,是比较原始的,下面我们用它做一道题。</p><p>题目链接在这&nbsp;&nbsp; <a href="http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&amp;problemid=2428" target="_blank">我就是题目链接</a></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">100003</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> par[VM];<br/></span><span style="color: #008080;">14</span> <br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">int</span> find(<span style="color: #0000ff;">int</span> x) {<span style="color: #008000;">//</span><span style="color: #008000;">查找函数</span><br/><span style="color: #008080;">16</span> <span style="color: #0000ff;">if</span> (par[x] == x)<span style="color: #008000;">//</span><span style="color: #008000;">若本身就是根节点 ,那么return</span><br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> x;<br/></span><span style="color: #008080;">18</span> <span style="color: #0000ff;">return</span> find(par[x]);<span style="color: #008000;">//</span><span style="color: #008000;">不是的话,继续查找</span><br/><span style="color: #008080;">19</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">20</span> <br/><span style="color: #008080;">21</span> <span style="color: #0000ff;">void</span> unite(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y) {<span style="color: #008000;">//</span><span style="color: #008000;">合并函数</span><br/><span style="color: #008080;">22</span> x = find(x);<span style="color: #008000;">//</span><span style="color: #008000;">查找x的根节点</span><br/><span style="color: #008080;">23</span> y = find(y);<span style="color: #008000;">//</span><span style="color: #008000;">查找y的根节点</span><br/><span style="color: #008080;">24</span> <span style="color: #0000ff;">if</span> (x == y)<span style="color: #008000;">//</span><span style="color: #008000;">若这两个数的结点是一样的,那么他们本来就是一个分组里的了,所以没有操作</span><br/><span style="color: #008080;">25</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ;<br/></span><span style="color: #008080;">26</span> par[x] = y;<span style="color: #008000;">//</span><span style="color: #008000;">不然的话,就让其中的一个点成为另一个点的根节点。</span><br/><span style="color: #008080;">27</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">28</span> <br/><span style="color: #008080;">29</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">30</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">31</span> <span style="color: #0000ff;">int</span> t = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">32</span> <br/><span style="color: #008080;">33</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n +<span style="color: #000000;"> m) {<br/></span><span style="color: #008080;">34</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">35</span> par[i] = i;<span style="color: #008000;">//</span><span style="color: #008000;">这是初始化</span><br/><span style="color: #008080;">36</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">37</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;">38</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;<span style="color: #000000;">v);<br/></span><span style="color: #008080;">39</span> <span style="color: #000000;"> unite(u, v);<br/></span><span style="color: #008080;">40</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">41</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">42</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">43</span> <span style="color: #0000ff;">if</span> (i ==<span style="color: #000000;"> par[i])<br/></span><span style="color: #008080;">44</span> ans++;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">45</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">Case %d: %d\n</span><span style="color: #800000;">"</span>, ++t, ans);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">46</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">47</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">48</span> }</div><p>既然说了上面是未优化的,那这儿就要说一下优化的喽。</p><p>  我们的代码需要用到路径压缩 和 这课树(也就是分组)的高度。</p><p>若不知道这两个东东的 话&nbsp; ,还是这位大牛的 <span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><a href="http://www.cnblogs.com/cyjb/p/UnionFindSets.html" target="_blank"> http://www.cnblogs.com/cyjb/p/UnionFindSets.html</a></span></span></span></span></span></span></span></p><p><span style="font-size: 15px; color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="color: #000000;">find函数的优化</span></span></span></span></span></span></span></p><p>&nbsp;</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">int</span> find(<span style="color: #0000ff;">int</span> x) {<span style="color: #008000;">//</span><span style="color: #008000;">查找函数</span><br/> <span style="color: #0000ff;">if</span> (par[x] == x)<span style="color: #008000;">//</span><span style="color: #008000;">若本身就是根节点 ,那么return</span><br/> <span style="color: #0000ff;">return</span><span style="color: #000000;"> x;<br/> </span><span style="color: #0000ff;">return</span> par[x] = find(par[x]);<span style="color: #008000;">//</span><span style="color: #008000;">不是的话,继续查找,并且进行路径压缩。<br/> <br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">上面为递归版本</span><br/> <span style="color: #008000;">/*</span><span style="color: #008000;"><br/> int a = x;<br/> while (a != par[a])//一直找到a的 根节点<br/> a = par[a];<br/> while (x != par[x]) {//路径压缩<br/> int t = par[x];<br/> par[x] = a;<br/> x = t;<br/> }<br/> return a;<br/> </span><span style="color: #008000;">*/</span><br/> <span style="color: #008000;">//</span><span style="color: #008000;">上面为非递归版本</span><br/>}</div><p>&nbsp;</p><p>合并函数的优化</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">void</span> unite(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y) {<span style="color: #008000;">//</span><span style="color: #008000;">合并函数</span><br/><span style="color: #008080;"> 2</span> x = find(x);<span style="color: #008000;">//</span><span style="color: #008000;">查找x的根节点</span><br/><span style="color: #008080;"> 3</span> y = find(y);<span style="color: #008000;">//</span><span style="color: #008000;">查找y的根节点</span><br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">if</span> (x == y)<span style="color: #008000;">//</span><span style="color: #008000;">若这两个数的结点是一样的,那么他们本来就是一个分组里的了,所以没有操作</span><br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ;<br/></span><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">if</span> (rank[x] &lt; rank[y]) <span style="color: #008000;">//</span><span style="color: #008000;">不然的话, 如果x这个分组的高度小于y分组的高度</span><br/><span style="color: #008080;"> 7</span> par[x] = y;<span style="color: #008000;">//</span><span style="color: #008000;">将x并到 y这个分组中,并且是x的父节点是y</span><br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">else</span><span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 9</span> par[y] = x;<span style="color: #008000;">//</span><span style="color: #008000;">不然就是y的父节点为x</span><br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">if</span> (rank[x] == rank[y])<span style="color: #008000;">//</span><span style="color: #008000;">若两个分组的高度相同</span><br/><span style="color: #008080;">11</span> rank[x]++;<span style="color: #008000;">//</span><span style="color: #008000;">x 的分组高度++</span><br/><span style="color: #008080;">12</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">13</span> }</div><p>&nbsp;</p><p>&nbsp;</p><p>  在这给出 这个题目的 优化的代码。</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('b161ee94-6a01-4488-9725-7d6d728274a5')"><div id="cnblogs_code_open_b161ee94-6a01-4488-9725-7d6d728274a5" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">100003</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> par[VM];<br/></span><span style="color: #008080;">14</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> rank[VM];<br/></span><span style="color: #008080;">15</span> <br/><span style="color: #008080;">16</span> <span style="color: #0000ff;">int</span> find(<span style="color: #0000ff;">int</span> x) {<span style="color: #008000;">//</span><span style="color: #008000;">查找函数</span><br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">if</span> (par[x] == x)<span style="color: #008000;">//</span><span style="color: #008000;">若本身就是根节点 ,那么return</span><br/><span style="color: #008080;">18</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> x;<br/></span><span style="color: #008080;">19</span> <span style="color: #0000ff;">return</span> par[x] = find(par[x]);<span style="color: #008000;">//</span><span style="color: #008000;">不是的话,继续查找,并且进行路径压缩。<br/></span><span style="color: #008080;">20</span> <br/><span style="color: #008080;">21</span> <span style="color: #008000;">//</span><span style="color: #008000;">上面为递归版本</span><br/><span style="color: #008080;">22</span> <span style="color: #008000;">/*</span><br/><span style="color: #008080;">23</span> <span style="color: #008000;"> int a = x;<br/></span><span style="color: #008080;">24</span> <span style="color: #008000;"> while (a != par[a])//一直找到a的 根节点<br/></span><span style="color: #008080;">25</span> <span style="color: #008000;"> a = par[a];<br/></span><span style="color: #008080;">26</span> <span style="color: #008000;"> while (x != par[x]) {//路径压缩<br/></span><span style="color: #008080;">27</span> <span style="color: #008000;"> int t = par[x];<br/></span><span style="color: #008080;">28</span> <span style="color: #008000;"> par[x] = a;<br/></span><span style="color: #008080;">29</span> <span style="color: #008000;"> x = t;<br/></span><span style="color: #008080;">30</span> <span style="color: #008000;"> }<br/></span><span style="color: #008080;">31</span> <span style="color: #008000;"> return a;<br/></span><span style="color: #008080;">32</span> <span style="color: #008000;">*/</span><br/><span style="color: #008080;">33</span> <span style="color: #008000;">//</span><span style="color: #008000;">上面为非递归版本</span><br/><span style="color: #008080;">34</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">35</span> <br/><span style="color: #008080;">36</span> <span style="color: #0000ff;">void</span> unite(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y) {<span style="color: #008000;">//</span><span style="color: #008000;">合并函数</span><br/><span style="color: #008080;">37</span> x = find(x);<span style="color: #008000;">//</span><span style="color: #008000;">查找x的根节点</span><br/><span style="color: #008080;">38</span> y = find(y);<span style="color: #008000;">//</span><span style="color: #008000;">查找y的根节点</span><br/><span style="color: #008080;">39</span> <span style="color: #0000ff;">if</span> (x == y)<span style="color: #008000;">//</span><span style="color: #008000;">若这两个数的结点是一样的,那么他们本来就是一个分组里的了,所以没有操作</span><br/><span style="color: #008080;">40</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ;<br/></span><span style="color: #008080;">41</span> <span style="color: #0000ff;">if</span> (rank[x] &lt; rank[y]) <span style="color: #008000;">//</span><span style="color: #008000;">不然的话, 如果x这个分组的高度小于y分组的高度</span><br/><span style="color: #008080;">42</span> par[x] = y;<span style="color: #008000;">//</span><span style="color: #008000;">将x并到 y这个分组中,并且是x的父节点是y</span><br/><span style="color: #008080;">43</span> <span style="color: #0000ff;">else</span><span style="color: #000000;"> {<br/></span><span style="color: #008080;">44</span> par[y] = x;<span style="color: #008000;">//</span><span style="color: #008000;">不然就是y的父节点为x</span><br/><span style="color: #008080;">45</span> <span style="color: #0000ff;">if</span> (rank[x] == rank[y])<span style="color: #008000;">//</span><span style="color: #008000;">若两个分组的高度相同</span><br/><span style="color: #008080;">46</span> rank[x]++;<span style="color: #008000;">//</span><span style="color: #008000;">x 的分组高度++</span><br/><span style="color: #008080;">47</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">48</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">49</span> <br/><span style="color: #008080;">50</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">51</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">52</span> <span style="color: #0000ff;">int</span> t = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">53</span> <br/><span style="color: #008080;">54</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n +<span style="color: #000000;"> m) {<br/></span><span style="color: #008080;">55</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">56</span> par[i] = i;<span style="color: #008000;">//</span><span style="color: #008000;">这是初始化</span><br/><span style="color: #008080;">57</span> memset(rank, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(rank));<span style="color: #008000;">//</span><span style="color: #008000;">树的高度 为 0 初始化</span><br/><span style="color: #008080;">58</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">59</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;">60</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;<span style="color: #000000;">v);<br/></span><span style="color: #008080;">61</span> <span style="color: #000000;"> unite(u, v);<br/></span><span style="color: #008080;">62</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">63</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">64</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">65</span> <span style="color: #0000ff;">if</span> (i ==<span style="color: #000000;"> par[i])<br/></span><span style="color: #008080;">66</span> ans++;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">67</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">Case %d: %d\n</span><span style="color: #800000;">"</span>, ++t, ans);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">68</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">69</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">70</span> }</div></div><p><br />&nbsp;再来一个题&nbsp; : <a href="http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&amp;problemid=2801" target="_blank">题目题目</a></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('511bcb0f-ac2a-4607-8af9-227ae475d6e8')"><div id="cnblogs_code_open_511bcb0f-ac2a-4607-8af9-227ae475d6e8" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">100003</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> par[VM];<br/></span><span style="color: #008080;">14</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> rank[VM];<br/></span><span style="color: #008080;">15</span> <br/><span style="color: #008080;">16</span> <span style="color: #0000ff;">int</span> find(<span style="color: #0000ff;">int</span> x) {<span style="color: #008000;">//</span><span style="color: #008000;">查找函数</span><br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">if</span> (par[x] == x)<span style="color: #008000;">//</span><span style="color: #008000;">若本身就是根节点 ,那么return</span><br/><span style="color: #008080;">18</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> x;<br/></span><span style="color: #008080;">19</span> <span style="color: #0000ff;">return</span> par[x] = find(par[x]);<span style="color: #008000;">//</span><span style="color: #008000;">不是的话,继续查找,并且进行路径压缩。<br/></span><span style="color: #008080;">20</span> <br/><span style="color: #008080;">21</span> <span style="color: #008000;">//</span><span style="color: #008000;">上面为递归版本</span><br/><span style="color: #008080;">22</span> <span style="color: #008000;">/*</span><br/><span style="color: #008080;">23</span> <span style="color: #008000;"> int a = x;<br/></span><span style="color: #008080;">24</span> <span style="color: #008000;"> while (a != par[a])//一直找到a的 根节点<br/></span><span style="color: #008080;">25</span> <span style="color: #008000;"> a = par[a];<br/></span><span style="color: #008080;">26</span> <span style="color: #008000;"> while (x != par[x]) {//路径压缩<br/></span><span style="color: #008080;">27</span> <span style="color: #008000;"> int t = par[x];<br/></span><span style="color: #008080;">28</span> <span style="color: #008000;"> par[x] = a;<br/></span><span style="color: #008080;">29</span> <span style="color: #008000;"> x = t;<br/></span><span style="color: #008080;">30</span> <span style="color: #008000;"> }<br/></span><span style="color: #008080;">31</span> <span style="color: #008000;"> return a;<br/></span><span style="color: #008080;">32</span> <span style="color: #008000;">*/</span><br/><span style="color: #008080;">33</span> <span style="color: #008000;">//</span><span style="color: #008000;">上面为非递归版本</span><br/><span style="color: #008080;">34</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">35</span> <br/><span style="color: #008080;">36</span> <span style="color: #0000ff;">void</span> unite(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y) {<span style="color: #008000;">//</span><span style="color: #008000;">合并函数</span><br/><span style="color: #008080;">37</span> x = find(x);<span style="color: #008000;">//</span><span style="color: #008000;">查找x的根节点</span><br/><span style="color: #008080;">38</span> y = find(y);<span style="color: #008000;">//</span><span style="color: #008000;">查找y的根节点</span><br/><span style="color: #008080;">39</span> <span style="color: #0000ff;">if</span> (x == y)<span style="color: #008000;">//</span><span style="color: #008000;">若这两个数的结点是一样的,那么他们本来就是一个分组里的了,所以没有操作</span><br/><span style="color: #008080;">40</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ;<br/></span><span style="color: #008080;">41</span> <span style="color: #0000ff;">if</span> (rank[x] &lt; rank[y]) <span style="color: #008000;">//</span><span style="color: #008000;">不然的话, 如果x这个分组的高度小于y分组的高度</span><br/><span style="color: #008080;">42</span> par[x] = y;<span style="color: #008000;">//</span><span style="color: #008000;">将x并到 y这个分组中,并且是x的父节点是y</span><br/><span style="color: #008080;">43</span> <span style="color: #0000ff;">else</span><span style="color: #000000;"> {<br/></span><span style="color: #008080;">44</span> par[y] = x;<span style="color: #008000;">//</span><span style="color: #008000;">不然就是y的父节点为x</span><br/><span style="color: #008080;">45</span> <span style="color: #0000ff;">if</span> (rank[x] == rank[y])<span style="color: #008000;">//</span><span style="color: #008000;">若两个分组的高度相同</span><br/><span style="color: #008080;">46</span> rank[x]++;<span style="color: #008000;">//</span><span style="color: #008000;">x 的分组高度++</span><br/><span style="color: #008080;">47</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">48</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">49</span> <br/><span style="color: #008080;">50</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">51</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">52</span> <br/><span style="color: #008080;">53</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m) == <span style="color: #800080;">2</span><span style="color: #000000;">) {<br/></span><span style="color: #008080;">54</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt; n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">55</span> par[i] = i;<span style="color: #008000;">//</span><span style="color: #008000;">这是初始化</span><br/><span style="color: #008080;">56</span> memset(rank, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(rank));<span style="color: #008000;">//</span><span style="color: #008000;">树的高度 为 0 初始化</span><br/><span style="color: #008080;">57</span> <span style="color: #0000ff;">while</span> (m--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">58</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v;<br/></span><span style="color: #008080;">59</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;<span style="color: #000000;">v);<br/></span><span style="color: #008080;">60</span> <span style="color: #000000;"> unite(u, v);<br/></span><span style="color: #008080;">61</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">62</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">63</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt; n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">64</span> <span style="color: #0000ff;">if</span> (find(par[<span style="color: #800080;">0</span>]) ==<span style="color: #000000;"> find(par[i]))<br/></span><span style="color: #008080;">65</span> ans++;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">66</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span>, ans);<span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">67</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">68</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">69</span> }</div></div><p>&nbsp;</p><p>不会画图,就不好讲了。</p><p>并查集 算是马马虎虎的说完了吧。。。。</p><p><span style="color: #ff0000; font-size: 18px;">插播 :</span></p><p><span style="color: #ff0000; font-size: 18px;">  什么是&nbsp; 生成树?什么&nbsp;&nbsp; 又是&nbsp;&nbsp;&nbsp; 最小生成树?<br /></span></p><p><span style="color: #ff0000; font-size: 18px;">  <span style="color: #000000; font-size: 15px;">给定一个无向图,如果它的某个子图中的任意两个顶点都互相联通并且是一棵树,那么这棵树就是&nbsp; <span style="color: #ff0000;">生成树 &nbsp;<span style="color: #000000;">。</span></span></span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;"><span style="color: #ff0000;"><span style="color: #000000;">  <span style="color: #ff0000;">也就是说,在一个图中,有 n 个顶点 ,若有 n - 1 条边,能使得所有的顶点相连 ,就是 生成树了。</span><br /></span></span></span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;">  如果你给这些边&nbsp; 加上权值&nbsp; ,那 权值 总和最小的额生成树&nbsp; 就是最小生成树<br /></span></span></span></span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;"><span style="color: #ff0000;"><span style="color: #000000;"><span style="color: #ff0000;"><span style="font-size: 18px;">再插 :</span><br /></span></span></span></span></span></p><p><span style="font-size: 18px;"><span style="color: #000000; font-size: 15px;"><span style="color: #000000;"><span style="font-size: 18px;">  <span style="font-size: 15px; color: #ff0000;">最小生成树&nbsp; 有两种方法 &nbsp; 一种&nbsp; : 普利姆算法 &nbsp; 另一种 : 克鲁斯卡尔。</span></span></span></span></span></p><p><span style="color: #ff0000; font-size: 18px;">3、&nbsp; 普利姆算法&nbsp; &amp;&nbsp; 优先队列优化</span></p><p><span style="color: #ff0000; font-size: 18px;">  <span style="color: #000000; font-size: 15px;">prim算法和Dijkstra算法十分相似,都是从某个顶点出发,不断加边的算法。</span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">  1. 假设有一棵树只包含一个顶点的v的树T。<br /></span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">  2.贪心的选取T和其他顶点之间相连的最小权值的边,并将它加入T中。</span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">  3.不断重复1,2&nbsp; 知道所有的点相连生成一棵最小生成树。(此算法的正确性,不给予证明)<br /></span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">  下面开始练题。</span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">  题目&nbsp;&nbsp;&nbsp; :&nbsp;&nbsp; <a href="http://acm.hdu.edu.cn/showproblem.php?pid=1863" target="_blank">我是题目 请点击</a>  </span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('4d2849a7-5be6-4885-a5e7-5124203f130f')"><div id="cnblogs_code_open_4d2849a7-5be6-4885-a5e7-5124203f130f" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> <br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">103</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">10</span> <br/><span style="color: #008080;">11</span> <span style="color: #0000ff;">int</span> G[VM][VM];<span style="color: #008000;">//</span><span style="color: #008000;">存图</span><br/><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">void</span> prim(<span style="color: #0000ff;">int</span><span style="color: #000000;"> n) {<br/></span><span style="color: #008080;">14</span> <span style="color: #0000ff;">int</span> dis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">记录 边的权值</span><br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">bool</span> vis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">记录为否访问</span><br/><span style="color: #008080;">16</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span>;<span style="color: #008000;">//<br/></span><span style="color: #008080;">17</span> <br/><span style="color: #008080;">18</span> memset(vis, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(vis));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">19</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;">20</span> dis[i] = G[<span style="color: #800080;">1</span>][i];<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">21</span> dis[<span style="color: #800080;">1</span>] = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span> <br/><span style="color: #008080;">22</span> vis[<span style="color: #800080;">1</span>] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;"> 1 点标记为已访问</span><br/><span style="color: #008080;">23</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> i;<br/></span><span style="color: #008080;">24</span> <span style="color: #0000ff;">for</span> (i = <span style="color: #800080;">2</span>; i &lt;= n; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">进行 n - 1 次操作</span><br/><span style="color: #008080;">25</span> <span style="color: #0000ff;">int</span> u = INF;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">26</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> k;<br/></span><span style="color: #008080;">27</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有顶点</span><br/><span style="color: #008080;">28</span> <span style="color: #0000ff;">if</span> (!vis[j] &amp;&amp; u &gt; dis[j]) {<span style="color: #008000;">//</span><span style="color: #008000;">在所有的未加入的点中 找一个最小的权值</span><br/><span style="color: #008080;">29</span> k = j;<span style="color: #008000;">//</span><span style="color: #008000;">记录下标</span><br/><span style="color: #008080;">30</span> u = dis[j];<span style="color: #008000;">//</span><span style="color: #008000;">更新最小值</span><br/><span style="color: #008080;">31</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">32</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">33</span> <span style="color: #0000ff;">if</span> (u == INF)<span style="color: #008000;">//</span><span style="color: #008000;">若图是不连通的 </span><br/><span style="color: #008080;">34</span> <span style="color: #0000ff;">break</span>;<span style="color: #008000;">//</span><span style="color: #008000;">提前退出</span><br/><span style="color: #008080;">35</span> vis[k] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;">标记为已加入</span><br/><span style="color: #008080;">36</span> ans += u;<span style="color: #008000;">//</span><span style="color: #008000;">加权值</span><br/><span style="color: #008080;">37</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有的点</span><br/><span style="color: #008080;">38</span> <span style="color: #0000ff;">if</span> (!vis[j] &amp;&amp; dis[j] &gt; G[k][j])<span style="color: #008000;">//</span><span style="color: #008000;">对未加入的点&amp;&amp;能找到与此点相连且的权值最小的边 </span><br/><span style="color: #008080;">39</span> dis[j] = G[k][j];<span style="color: #008000;">//</span><span style="color: #008000;">进行更新</span><br/><span style="color: #008080;">40</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">41</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">42</span> <span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">43</span> <span style="color: #0000ff;">if</span> (i - <span style="color: #800080;">1</span> ==<span style="color: #000000;"> n)<br/></span><span style="color: #008080;">44</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ans);<br/></span><span style="color: #008080;">45</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;">46</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">?\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">47</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">48</span> <br/><span style="color: #008080;">49</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">50</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">51</span> <br/><span style="color: #008080;">52</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n) {<span style="color: #008000;">//</span><span style="color: #008000;">对边数 和点数的获取</span><br/><span style="color: #008080;">53</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= m; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">54</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= m; j++<span style="color: #000000;">) {<br/></span><span style="color: #008080;">55</span> G[i][j] = i == j ? <span style="color: #800080;">0</span><span style="color: #000000;"> : INF;<br/></span><span style="color: #008080;">56</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">57</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">58</span> <span style="color: #0000ff;">while</span> (n--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">59</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v, w;<br/></span><span style="color: #008080;">60</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v, &amp;w);<span style="color: #008000;">//</span><span style="color: #008000;">获取 数据</span><br/><span style="color: #008080;">61</span> <span style="color: #0000ff;">if</span> (G[u][v] &gt; w)<span style="color: #008000;">//</span><span style="color: #008000;">防止重边&amp;&amp;存两点之间的最短距离</span><br/><span style="color: #008080;">62</span> G[u][v] = G[v][u] =<span style="color: #000000;"> w;<br/></span><span style="color: #008080;">63</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">64</span> prim(m);<span style="color: #008000;">//</span><span style="color: #008000;">调用函数</span><br/><span style="color: #008080;">65</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">66</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">67</span> }</div></div><p>&nbsp;</p><p>主函数对数据的获取及图的存储</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;"> 3</span> <br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n) {<span style="color: #008000;">//</span><span style="color: #008000;">对边数 和点数的获取</span><br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= m; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= m; j++<span style="color: #000000;">) {<br/></span><span style="color: #008080;"> 7</span> G[i][j] = i == j ? <span style="color: #800080;">0</span><span style="color: #000000;"> : INF;<br/></span><span style="color: #008080;"> 8</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 9</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">10</span> <span style="color: #0000ff;">while</span> (n--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v, w;<br/></span><span style="color: #008080;">12</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v, &amp;w);<span style="color: #008000;">//</span><span style="color: #008000;">获取 数据</span><br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">if</span> (G[u][v] &gt; w)<span style="color: #008000;">//</span><span style="color: #008000;">防止重边&amp;&amp;存两点之间的最短距离</span><br/><span style="color: #008080;">14</span> G[u][v] = G[v][u] =<span style="color: #000000;"> w;<br/></span><span style="color: #008080;">15</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">16</span> prim(m);<span style="color: #008000;">//</span><span style="color: #008000;">调用函数</span><br/><span style="color: #008080;">17</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">18</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">19</span> }</div><p>普利姆函数</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">void</span> prim(<span style="color: #0000ff;">int</span><span style="color: #000000;"> n) {<br/></span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">int</span> dis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">记录 边的权值</span><br/><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">bool</span> vis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">记录为否访问</span><br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span>;<span style="color: #008000;">//<br/></span><span style="color: #008080;"> 5</span> <br/><span style="color: #008080;"> 6</span> memset(vis, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(vis));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= n; i++<span style="color: #000000;">)<br/></span><span style="color: #008080;"> 8</span> dis[i] = G[<span style="color: #800080;">1</span>][i];<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 9</span> dis[<span style="color: #800080;">1</span>] = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span> <br/><span style="color: #008080;">10</span> vis[<span style="color: #800080;">1</span>] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;"> 1 点标记为已访问</span><br/><span style="color: #008080;">11</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> i;<br/></span><span style="color: #008080;">12</span> <span style="color: #0000ff;">for</span> (i = <span style="color: #800080;">2</span>; i &lt;= n; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">进行 n - 1 次操作</span><br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">int</span> u = INF;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> k;<br/></span><span style="color: #008080;">15</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有顶点</span><br/><span style="color: #008080;">16</span> <span style="color: #0000ff;">if</span> (!vis[j] &amp;&amp; u &gt; dis[j]) {<span style="color: #008000;">//</span><span style="color: #008000;">在所有的未加入的点中 找一个最小的权值</span><br/><span style="color: #008080;">17</span> k = j;<span style="color: #008000;">//</span><span style="color: #008000;">记录下标</span><br/><span style="color: #008080;">18</span> u = dis[j];<span style="color: #008000;">//</span><span style="color: #008000;">更新最小值</span><br/><span style="color: #008080;">19</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">20</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">21</span> <span style="color: #0000ff;">if</span> (u == INF)<span style="color: #008000;">//</span><span style="color: #008000;">若图是不连通的 </span><br/><span style="color: #008080;">22</span> <span style="color: #0000ff;">break</span>;<span style="color: #008000;">//</span><span style="color: #008000;">提前退出</span><br/><span style="color: #008080;">23</span> vis[k] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;">标记为已加入</span><br/><span style="color: #008080;">24</span> ans += u;<span style="color: #008000;">//</span><span style="color: #008000;">加权值</span><br/><span style="color: #008080;">25</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> j = <span style="color: #800080;">1</span>; j &lt;= n; j++) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历所有的点</span><br/><span style="color: #008080;">26</span> <span style="color: #0000ff;">if</span> (!vis[j] &amp;&amp; dis[j] &gt; G[k][j])<span style="color: #008000;">//</span><span style="color: #008000;">对未加入的点&amp;&amp;能找到与此点相连且的权值最小的边 </span><br/><span style="color: #008080;">27</span> dis[j] = G[k][j];<span style="color: #008000;">//</span><span style="color: #008000;">进行更新</span><br/><span style="color: #008080;">28</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">29</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">30</span> <span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">31</span> <span style="color: #0000ff;">if</span> (i - <span style="color: #800080;">1</span> ==<span style="color: #000000;"> n)<br/></span><span style="color: #008080;">32</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ans);<br/></span><span style="color: #008080;">33</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;">34</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">?\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">35</span> }</div><p>上面的算法的时间复杂度为O(V * V),是不是和Dijkstra很相似呢?那么可不可用优化Dijkstra算法的方法来优化这个呢? 当然可以</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('40c51c8b-88f7-4eac-bec8-b0877b1e45a7')"><div id="cnblogs_code_open_40c51c8b-88f7-4eac-bec8-b0877b1e45a7" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">103</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> typedef pair&lt;<span style="color: #0000ff;">int</span>, <span style="color: #0000ff;">int</span>&gt;P;<span style="color: #008000;">//</span><span style="color: #008000;">对组</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">struct</span> node {<span style="color: #008000;">//</span><span style="color: #008000;">前向星 结构体</span><br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> v, w;<br/></span><span style="color: #008080;">16</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> next;<br/></span><span style="color: #008080;">17</span> <span style="color: #000000;">};<br/></span><span style="color: #008080;">18</span> node edge[<span style="color: #800080;">4</span> * VM];<span style="color: #008000;">//</span><span style="color: #008000;">前向星数组</span><br/><span style="color: #008080;">19</span> <span style="color: #0000ff;">int</span> head[VM];<span style="color: #008000;">//</span><span style="color: #008000;">头指针数组</span><br/><span style="color: #008080;">20</span> <span style="color: #0000ff;">int</span> cnt;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">21</span> <br/><span style="color: #008080;">22</span> <span style="color: #0000ff;">void</span> add(<span style="color: #0000ff;">int</span> u, <span style="color: #0000ff;">int</span> v, <span style="color: #0000ff;">int</span> w) {<span style="color: #008000;">//</span><span style="color: #008000;">加边函数</span><br/><span style="color: #008080;">23</span> edge[cnt].v = v;<span style="color: #008000;">//</span><span style="color: #008000;">顶点</span><br/><span style="color: #008080;">24</span> edge[cnt].w = w;<span style="color: #008000;">//</span><span style="color: #008000;">权值</span><br/><span style="color: #008080;">25</span> edge[cnt].next = head[u];<span style="color: #008000;">//</span><span style="color: #008000;">下一个</span><br/><span style="color: #008080;">26</span> head[u] = cnt++;<span style="color: #008000;">//</span><span style="color: #008000;">头指针</span><br/><span style="color: #008080;">27</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">28</span> <br/><span style="color: #008080;">29</span> <span style="color: #0000ff;">void</span> prim(<span style="color: #0000ff;">int</span> n) {<span style="color: #008000;">//</span><span style="color: #008000;">普利姆函数</span><br/><span style="color: #008080;">30</span> <span style="color: #0000ff;">bool</span> vis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">标记是否访问过</span><br/><span style="color: #008080;">31</span> <span style="color: #0000ff;">int</span> dis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">记录权值</span><br/><span style="color: #008080;">32</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">最小生成树的总值</span><br/><span style="color: #008080;">33</span> <span style="color: #0000ff;">int</span> count = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">34</span> priority_queue&lt;P, vector&lt;P&gt;, greater&lt;P&gt; &gt;que;<span style="color: #008000;">//</span><span style="color: #008000;">权值从小到大的队列</span><br/><span style="color: #008080;">35</span> <br/><span style="color: #008080;">36</span> fill(dis, dis + VM, INF);<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">37</span> memset(vis, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(vis));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">38</span> dis[<span style="color: #800080;">1</span>] = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">39</span> que.push(P(<span style="color: #800080;">0</span>, <span style="color: #800080;">1</span>));<span style="color: #008000;">//</span><span style="color: #008000;">将 1点 和 dis[1] = 0 放入队列</span><br/><span style="color: #008080;">40</span> <span style="color: #0000ff;">while</span> (que.empty() == <span style="color: #0000ff;">false</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">队列不为空时</span><br/><span style="color: #008080;">41</span> P p = que.top();<span style="color: #008000;">//</span><span style="color: #008000;">取出队首</span><br/><span style="color: #008080;">42</span> que.pop();<span style="color: #008000;">//</span><span style="color: #008000;">删除</span><br/><span style="color: #008080;">43</span> <span style="color: #0000ff;">int</span> u = p.second;<span style="color: #008000;">//<br/></span><span style="color: #008080;">44</span> <span style="color: #0000ff;">if</span> (vis[u] == <span style="color: #0000ff;">true</span>)<span style="color: #008000;">//</span><span style="color: #008000;">若此顶点已经加入生成树</span><br/><span style="color: #008080;">45</span> <span style="color: #0000ff;">continue</span>;<span style="color: #008000;">//<br/></span><span style="color: #008080;">46</span> vis[u] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;">否则,就标记为加入</span><br/><span style="color: #008080;">47</span> ans += dis[u];<span style="color: #008000;">//<br/></span><span style="color: #008080;">48</span> count++;<span style="color: #008000;">//</span><span style="color: #008000;">加入点个数</span><br/><span style="color: #008080;">49</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = head[u]; i != -<span style="color: #800080;">1</span>; i = edge[i].next) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历与该点相邻的点</span><br/><span style="color: #008080;">50</span> node e =<span style="color: #000000;"> edge[i];<br/></span><span style="color: #008080;">51</span> <span style="color: #0000ff;">if</span> (dis[e.v] &gt; e.w) {<span style="color: #008000;">//</span><span style="color: #008000;">更新他们的权值</span><br/><span style="color: #008080;">52</span> dis[e.v] = e.w;<span style="color: #008000;">//<br/></span><span style="color: #008080;">53</span> que.push(P(dis[e.v], e.v));<span style="color: #008000;">//</span><span style="color: #008000;">放入队列</span><br/><span style="color: #008080;">54</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">55</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">56</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">57</span> <span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">58</span> <span style="color: #0000ff;">if</span> (count ==<span style="color: #000000;"> n)<br/></span><span style="color: #008080;">59</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ans);<br/></span><span style="color: #008080;">60</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;">61</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">?\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">62</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">63</span> <br/><span style="color: #008080;">64</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">65</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">66</span> <br/><span style="color: #008080;">67</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n) {<span style="color: #008000;">//</span><span style="color: #008000;">边的个数 顶点个数</span><br/><span style="color: #008080;">68</span> memset(head, -<span style="color: #800080;">1</span>, <span style="color: #0000ff;">sizeof</span>(head));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">69</span> cnt = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">70</span> <span style="color: #0000ff;">while</span> (n--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">71</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v, w;<br/></span><span style="color: #008080;">72</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v, &amp;w);<span style="color: #008000;">//</span><span style="color: #008000;">获取数据</span><br/><span style="color: #008080;">73</span> add(u, v, w);<span style="color: #008000;">//</span><span style="color: #008000;">加边</span><br/><span style="color: #008080;">74</span> add(v, u, w);<span style="color: #008000;">//</span><span style="color: #008000;">无向图</span><br/><span style="color: #008080;">75</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">76</span> prim(m);<span style="color: #008000;">//</span><span style="color: #008000;">普利姆算法</span><br/><span style="color: #008080;">77</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">78</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">79</span> }</div></div><p>主函数对数据的获取 和 图的存储</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;"> 3</span> <br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n) {<span style="color: #008000;">//</span><span style="color: #008000;">边的个数 顶点个数</span><br/><span style="color: #008080;"> 5</span> memset(head, -<span style="color: #800080;">1</span>, <span style="color: #0000ff;">sizeof</span>(head));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 6</span> cnt = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 7</span> <span style="color: #0000ff;">while</span> (n--<span style="color: #000000;">) {<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v, w;<br/></span><span style="color: #008080;"> 9</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d %d</span><span style="color: #800000;">"</span>, &amp;u, &amp;v, &amp;w);<span style="color: #008000;">//</span><span style="color: #008000;">获取数据</span><br/><span style="color: #008080;">10</span> add(u, v, w);<span style="color: #008000;">//</span><span style="color: #008000;">加边</span><br/><span style="color: #008080;">11</span> add(v, u, w);<span style="color: #008000;">//</span><span style="color: #008000;">无向图</span><br/><span style="color: #008080;">12</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">13</span> prim(m);<span style="color: #008000;">//</span><span style="color: #008000;">普利姆算法</span><br/><span style="color: #008080;">14</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">15</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">16</span> }</div><p>prim函数</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">void</span> prim(<span style="color: #0000ff;">int</span> n) {<span style="color: #008000;">//</span><span style="color: #008000;">普利姆函数</span><br/><span style="color: #008080;"> 2</span> <span style="color: #0000ff;">bool</span> vis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">标记是否访问过</span><br/><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">int</span> dis[VM];<span style="color: #008000;">//</span><span style="color: #008000;">记录权值</span><br/><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">最小生成树的总值</span><br/><span style="color: #008080;"> 5</span> <span style="color: #0000ff;">int</span> count = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;"> 6</span> priority_queue&lt;P, vector&lt;P&gt;, greater&lt;P&gt; &gt;que;<span style="color: #008000;">//</span><span style="color: #008000;">权值从小到大的队列</span><br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> fill(dis, dis + VM, INF);<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;"> 9</span> memset(vis, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(vis));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">10</span> dis[<span style="color: #800080;">1</span>] = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">11</span> que.push(P(<span style="color: #800080;">0</span>, <span style="color: #800080;">1</span>));<span style="color: #008000;">//</span><span style="color: #008000;">将 1点 和 dis[1] = 0 放入队列</span><br/><span style="color: #008080;">12</span> <span style="color: #0000ff;">while</span> (que.empty() == <span style="color: #0000ff;">false</span>) {<span style="color: #008000;">//</span><span style="color: #008000;">队列不为空时</span><br/><span style="color: #008080;">13</span> P p = que.top();<span style="color: #008000;">//</span><span style="color: #008000;">取出队首</span><br/><span style="color: #008080;">14</span> que.pop();<span style="color: #008000;">//</span><span style="color: #008000;">删除</span><br/><span style="color: #008080;">15</span> <span style="color: #0000ff;">int</span> u = p.second;<span style="color: #008000;">//<br/></span><span style="color: #008080;">16</span> <span style="color: #0000ff;">if</span> (vis[u] == <span style="color: #0000ff;">true</span>)<span style="color: #008000;">//</span><span style="color: #008000;">若此顶点已经加入生成树</span><br/><span style="color: #008080;">17</span> <span style="color: #0000ff;">continue</span>;<span style="color: #008000;">//<br/></span><span style="color: #008080;">18</span> vis[u] = <span style="color: #0000ff;">true</span>;<span style="color: #008000;">//</span><span style="color: #008000;">否则,就标记为加入</span><br/><span style="color: #008080;">19</span> ans += dis[u];<span style="color: #008000;">//<br/></span><span style="color: #008080;">20</span> count++;<span style="color: #008000;">//</span><span style="color: #008000;">加入点个数</span><br/><span style="color: #008080;">21</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = head[u]; i != -<span style="color: #800080;">1</span>; i = edge[i].next) {<span style="color: #008000;">//</span><span style="color: #008000;">遍历与该点相邻的点</span><br/><span style="color: #008080;">22</span> node e =<span style="color: #000000;"> edge[i];<br/></span><span style="color: #008080;">23</span> <span style="color: #0000ff;">if</span> (dis[e.v] &gt; e.w) {<span style="color: #008000;">//</span><span style="color: #008000;">更新他们的权值</span><br/><span style="color: #008080;">24</span> dis[e.v] = e.w;<span style="color: #008000;">//<br/></span><span style="color: #008080;">25</span> que.push(P(dis[e.v], e.v));<span style="color: #008000;">//</span><span style="color: #008000;">放入队列</span><br/><span style="color: #008080;">26</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">27</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">28</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">29</span> <span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">30</span> <span style="color: #0000ff;">if</span> (count ==<span style="color: #000000;"> n)<br/></span><span style="color: #008080;">31</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ans);<br/></span><span style="color: #008080;">32</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;">33</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">?\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">34</span> }</div><p>此算法的时间复杂度为O(E*log(V));&nbsp;&nbsp; 是不是很棒!</p><p>次算法结束。</p><p><span style="color: #ff0000; font-size: 18px;">4、&nbsp; 克鲁斯卡尔算法</span></p><p><span style="color: #ff0000; font-size: 18px;">  <span style="color: #000000; font-size: 15px;">克鲁斯卡尔算法就是利用了并查集这一方法,通过对所有边从小到大排序后,判断这两个顶点是否在一个分组中,若在一个分组中,说明这两个点已经加入到生成树之中,若不在一个分组中</span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">就可以直接加上这个边了。</span></span></p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">&nbsp;  还是以上一题为例</span></span></p><p>&nbsp;</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008080;"> 1</span> #include &lt;cstdio&gt;<br/><span style="color: #008080;"> 2</span> #include &lt;cstring&gt;<br/><span style="color: #008080;"> 3</span> #include &lt;iostream&gt;<br/><span style="color: #008080;"> 4</span> #include &lt;algorithm&gt;<br/><span style="color: #008080;"> 5</span> #include &lt;queue&gt;<br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">#define</span> LL long long<br/><span style="color: #008080;"> 7</span> <br/><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span><span style="color: #000000;"> std;<br/></span><span style="color: #008080;"> 9</span> <br/><span style="color: #008080;">10</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> INF = 1e9+<span style="color: #800080;">7</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">11</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> VM = <span style="color: #800080;">103</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">12</span> <br/><span style="color: #008080;">13</span> <span style="color: #0000ff;">struct</span> node {<span style="color: #008000;">//</span><span style="color: #008000;">边的结构体</span><br/><span style="color: #008080;">14</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> u, v, w;<br/></span><span style="color: #008080;">15</span> <span style="color: #000000;">};<br/></span><span style="color: #008080;">16</span> node edge[VM * <span style="color: #800080;">2</span><span style="color: #000000;">];<br/></span><span style="color: #008080;">17</span> <span style="color: #0000ff;">int</span> rank[VM];<span style="color: #008000;">//</span><span style="color: #008000;">分组的高度</span><br/><span style="color: #008080;">18</span> <span style="color: #0000ff;">int</span> par[VM];<span style="color: #008000;">//</span><span style="color: #008000;">父节点</span><br/><span style="color: #008080;">19</span> <br/><span style="color: #008080;">20</span> <span style="color: #0000ff;">bool</span> cmp(<span style="color: #0000ff;">const</span> node &amp;a, <span style="color: #0000ff;">const</span> node &amp;<span style="color: #000000;">b) {<br/></span><span style="color: #008080;">21</span> <span style="color: #0000ff;">return</span> a.w &lt; b.w;<span style="color: #008000;">//</span><span style="color: #008000;">按w从小到大排序</span><br/><span style="color: #008080;">22</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">23</span> <br/><span style="color: #008080;">24</span> <span style="color: #0000ff;">int</span> find(<span style="color: #0000ff;">int</span><span style="color: #000000;"> x) {<br/></span><span style="color: #008080;">25</span> <span style="color: #0000ff;">if</span> (par[x] == x)<span style="color: #008000;">//</span><span style="color: #008000;">若根节点为本身</span><br/><span style="color: #008080;">26</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> x;<br/></span><span style="color: #008080;">27</span> <span style="color: #0000ff;">return</span> par[x] = find(par[x]);<span style="color: #008000;">//</span><span style="color: #008000;">路径压缩</span><br/><span style="color: #008080;">28</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">29</span> <br/><span style="color: #008080;">30</span> <span style="color: #0000ff;">bool</span> same(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span> y) {<span style="color: #008000;">//</span><span style="color: #008000;">判断为否在同一分组中 </span><br/><span style="color: #008080;">31</span> <span style="color: #0000ff;">return</span> find(x) ==<span style="color: #000000;"> find(y);<br/></span><span style="color: #008080;">32</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">33</span> <br/><span style="color: #008080;">34</span> <span style="color: #0000ff;">void</span> unite(<span style="color: #0000ff;">int</span> x, <span style="color: #0000ff;">int</span><span style="color: #000000;"> y) {<br/></span><span style="color: #008080;">35</span> x = find(x);<span style="color: #008000;">//</span><span style="color: #008000;">查找根节点</span><br/><span style="color: #008080;">36</span> y = find(y);<span style="color: #008000;">//</span><span style="color: #008000;">查找根节点</span><br/><span style="color: #008080;">37</span> <span style="color: #0000ff;">if</span> (x == y)<span style="color: #008000;">//</span><span style="color: #008000;">若以在同一分组</span><br/><span style="color: #008080;">38</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ;<br/></span><span style="color: #008080;">39</span> <span style="color: #0000ff;">if</span> (rank[x] &lt; rank[y])<span style="color: #008000;">//</span><span style="color: #008000;">y所在分组的高度 大于x的</span><br/><span style="color: #008080;">40</span> par[x] = y;<span style="color: #008000;">//</span><span style="color: #008000;">将y作x的父节点。</span><br/><span style="color: #008080;">41</span> <span style="color: #0000ff;">else</span><span style="color: #000000;"> {<br/></span><span style="color: #008080;">42</span> par[y] = x;<span style="color: #008000;">//</span><span style="color: #008000;">将x作为y的父节点</span><br/><span style="color: #008080;">43</span> <span style="color: #0000ff;">if</span> (rank[x] == rank[y])<span style="color: #008000;">//</span><span style="color: #008000;">若两个分组高度相同</span><br/><span style="color: #008080;">44</span> rank[x]++;<span style="color: #008000;">//</span><span style="color: #008000;">x分组所在高度++</span><br/><span style="color: #008080;">45</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">46</span> <span style="color: #000000;">}<br/></span><span style="color: #008080;">47</span> <br/><span style="color: #008080;">48</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> main() {<br/></span><span style="color: #008080;">49</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> n, m;<br/></span><span style="color: #008080;">50</span> <br/><span style="color: #008080;">51</span> <span style="color: #0000ff;">while</span> (scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d</span><span style="color: #800000;">"</span>, &amp;n, &amp;m), n) {<span style="color: #008000;">//</span><span style="color: #008000;">获取边的个数 和顶点个数</span><br/><span style="color: #008080;">52</span> <span style="color: #0000ff;">int</span> cnt = <span style="color: #800080;">0</span>;<span style="color: #008000;">//<br/></span><span style="color: #008080;">53</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">1</span>; i &lt;= m; i++)<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">54</span> par[i] =<span style="color: #000000;"> i;<br/></span><span style="color: #008080;">55</span> memset(rank, <span style="color: #800080;">0</span>, <span style="color: #0000ff;">sizeof</span>(rank));<span style="color: #008000;">//</span><span style="color: #008000;">初始化</span><br/><span style="color: #008080;">56</span> <span style="color: #0000ff;">while</span> (n--<span style="color: #000000;">) {<br/></span><span style="color: #008080;">57</span> scanf(<span style="color: #800000;">"</span><span style="color: #800000;">%d %d %d</span><span style="color: #800000;">"</span>, &amp;edge[cnt].u, &amp;edge[cnt].v, &amp;edge[cnt].w);<span style="color: #008000;">//</span><span style="color: #008000;">获取数据</span><br/><span style="color: #008080;">58</span> cnt++<span style="color: #000000;">;<br/></span><span style="color: #008080;">59</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">60</span> sort(edge, edge + cnt, cmp);<span style="color: #008000;">//</span><span style="color: #008000;">按权值从小到大排序</span><br/><span style="color: #008080;">61</span> <span style="color: #0000ff;">int</span> ans = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">最小生成树 权值</span><br/><span style="color: #008080;">62</span> <span style="color: #0000ff;">int</span> count = <span style="color: #800080;">0</span>;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">63</span> <span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = <span style="color: #800080;">0</span>; i &lt; cnt; i++) {<span style="color: #008000;">//</span><span style="color: #008000;">对所有的边</span><br/><span style="color: #008080;">64</span> node e =<span style="color: #000000;"> edge[i];<br/></span><span style="color: #008080;">65</span> <span style="color: #0000ff;">if</span> (!same(e.u, e.v)) {<span style="color: #008000;">//</span><span style="color: #008000;">若两点不属于一个分组</span><br/><span style="color: #008080;">66</span> ans += e.w;<span style="color: #008000;">//</span><span style="color: #008000;">权值总和</span><br/><span style="color: #008080;">67</span> unite(e.u, e.v);<span style="color: #008000;">//</span><span style="color: #008000;">合并两点</span><br/><span style="color: #008080;">68</span> count++;<span style="color: #008000;">//</span><span style="color: #008000;">计数</span><br/><span style="color: #008080;">69</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">70</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">71</span> <span style="color: #008000;">//</span><span style="color: #008000;">输出</span><br/><span style="color: #008080;">72</span> <span style="color: #0000ff;">if</span> (count == m - <span style="color: #800080;">1</span><span style="color: #000000;">)<br/></span><span style="color: #008080;">73</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">%d\n</span><span style="color: #800000;">"</span><span style="color: #000000;">, ans);<br/></span><span style="color: #008080;">74</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;">75</span> printf(<span style="color: #800000;">"</span><span style="color: #800000;">?\n</span><span style="color: #800000;">"</span><span style="color: #000000;">);<br/></span><span style="color: #008080;">76</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">77</span> <span style="color: #0000ff;">return</span> <span style="color: #800080;">0</span><span style="color: #000000;">;<br/></span><span style="color: #008080;">78</span> }</div><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p><span style="font-size: 18px; color: #ff0000;">自己感觉这个专题写的好糟糕啊,哎,深夜了,睡觉吧。明天又是新的一天啊。因为明天,不,今天8点就要开始新学期第一堂课了啊。</span></p><p>&nbsp;</p><p>&nbsp;</p><p><span style="color: #ff0000; font-size: 18px;"><span style="color: #000000; font-size: 15px;">  </span></span></p><img src="http://counter.cnblogs.com/blog/rss/3943940" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/Yan-C/p/3943940.html" target="_blank">拓扑排序 详解 + 并查集 详解 + 最小生成树(MST)详解 【普利姆算法 + 优先队列优化 &amp; 克鲁斯卡尔算法】</a>,转载请注明。</p>http://www.cnblogs.com/yetsen/p/3949727.html弱引用和弱事件 - yetsen默認對象實例化後得到的都是強引用,不過有時候對於一些複雜的對象,出於性能考慮,并不希望進行頻繁的初始化,此時弱引用就可以派上用場。 用法:先用WeakReference包裝複雜對象,到需要該複雜對象的時候,檢查一下弱引用的IsAlive屬性,如果true,就可以通過Target直接得到複雜對象,省去...2014-09-01T09:58:00Z2014-09-01T09:58:00Zyetsenhttp://www.cnblogs.com/yetsen/<p><span style="font-size: 12pt;"><span style="font-family: 宋体;">默認對象實例化後得到的都是強引用,不過有時候對於一些複雜的對象,出於性能考慮,并不希望進行頻繁的初始化,此時弱引用就可以派上用場。 </span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">用法:先用WeakReference包裝複雜對象,到需要該複雜對象的時候,檢查一下弱引用的IsAlive屬性,如果true,就可以通過Target直接得到複雜對象,省去了實例化的過程。 </span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">簡單的例子:</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> <span style="color: #0000ff;">static <span style="color: #0000ff;">void Main(<span style="color: #0000ff;">string<span style="color: #000000;">[] args)<br/> {<br/> <span style="color: #0000ff;">var weakRef =<span style="color: #000000;"> GetWeakRef();<br/><br/> GC.Collect();<br/><br/> <span style="color: #0000ff;">if<span style="color: #000000;"> (weakRef.IsAlive)<br/> {<br/> <span style="color: #0000ff;">var obj = weakRef.Target <span style="color: #0000ff;">as<span style="color: #000000;"> ComplexObject;<br/> Console.WriteLine(obj);<br/> }<br/> <span style="color: #0000ff;">else<span style="color: #000000;"><br/> {<br/> Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">Reference is not available.<span style="color: #800000;">"<span style="color: #000000;">);<br/> }<br/><br/> Console.Read();<br/> }<br/><br/> <span style="color: #0000ff;">private <span style="color: #0000ff;">static<span style="color: #000000;"> WeakReference GetWeakRef()<br/> {<br/> <span style="color: #0000ff;">return <span style="color: #0000ff;">new WeakReference(<span style="color: #0000ff;">new<span style="color: #000000;"> ComplexObject());<br/> }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></div><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">本例中如果調用了GC回收,輸出爲 </span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">Reference is not available. </span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">如果不調用GC回收,輸出爲 </span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">ConsoleApplication1.ComplexObject </span></span></p><p>&nbsp; &nbsp; &nbsp; &nbsp;</p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">雖然都是弱,但弱事件的情形卻不太一樣,它的目的在於避免内存泄漏。</span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">普通事件建立了一個發佈者到偵聽者之間的強引用,衹要發佈者存在,偵聽者就無法被回收,即使偵聽者已沒有存在的必要。</span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">内存泄漏的一個簡單例子:</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('31c422ed-c1db-4506-9415-2ea989566a49')"><div id="cnblogs_code_open_31c422ed-c1db-4506-9415-2ea989566a49" class="cnblogs_code_hide"> <span style="color: #0000ff;">class<span style="color: #000000;"> Publisher<br/> {<br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">event<span style="color: #000000;"> EventHandler SomeEvent;<br/><br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">void<span style="color: #000000;"> RaiseEvent()<br/> {<br/> <span style="color: #0000ff;">if (SomeEvent != <span style="color: #0000ff;">null<span style="color: #000000;">)<br/> {<br/> SomeEvent(<span style="color: #0000ff;">this<span style="color: #000000;">, EventArgs.Empty);<br/> }<br/> }<br/> }<br/><br/> <span style="color: #0000ff;">class<span style="color: #000000;"> Listener<br/> {<br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">void Listener_SomeEvent(<span style="color: #0000ff;">object<span style="color: #000000;"> sender, EventArgs e)<br/> {<br/> Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">SomeEvent happens<span style="color: #800000;">"<span style="color: #000000;">);<br/> }<br/> }<br/><br/> <span style="color: #0000ff;">class<span style="color: #000000;"> Program<br/> {<br/> <span style="color: #0000ff;">static <span style="color: #0000ff;">void Main(<span style="color: #0000ff;">string<span style="color: #000000;">[] args)<br/> {<br/> <span style="color: #0000ff;">var pub = <span style="color: #0000ff;">new<span style="color: #000000;"> Publisher();<br/> <span style="color: #0000ff;">var lsn = <span style="color: #0000ff;">new<span style="color: #000000;"> Listener();<br/><br/> pub.SomeEvent +=<span style="color: #000000;"> lsn.Listener_SomeEvent;<br/> pub.RaiseEvent();<br/><br/> lsn = <span style="color: #0000ff;">null<span style="color: #000000;">;<br/> GC.Collect();<br/><br/> pub.RaiseEvent();<br/><br/> Console.Read();<br/> }<br/> } </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></div></div><p><span style="font-family: 宋体; font-size: 12pt;">程序輸出了兩次SomeEvent happens,説明lsn對象仍然保留在内存中,即使已經將其置空。</span></p><p>&nbsp;</p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">直接的解決辦法就是:偵聽者主動解除事件訂閲,沒了強引用,自然就可以回收了。 不過有時候難以確定解除事件的時機,此時弱事件就有用場了,它的訣竅在於引入第三方的管理器來間接關聯發佈者和偵聽者,這樣就不會在它們之間建立強引用,從而方便各自的回收。</span></span></p><p><span style="font-size: 12pt;"><span style="font-family: 宋体;">不過相對於普通的事件偵聽者,弱事件下的偵聽者必須實現<span style="color: #000000;">IWeakEventListener<span style="font-size: 12pt;"><span style="font-family: 宋体;">接口,如下:</span></span></span></span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> <span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> Publisher<br/> {<br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">event<span style="color: #000000;"> EventHandler SomeEvent;<br/><br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">void<span style="color: #000000;"> RaiseEvent()<br/> {<br/> <span style="color: #0000ff;">if (SomeEvent != <span style="color: #0000ff;">null<span style="color: #000000;">)<br/> {<br/> SomeEvent(<span style="color: #0000ff;">this<span style="color: #000000;">, EventArgs.Empty);<br/> }<br/> }<br/> }<br/><br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> Listener : IWeakEventListener<br/> {<br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">void Listener_SomeEvent(<span style="color: #0000ff;">object<span style="color: #000000;"> sender, EventArgs e)<br/> {<br/> Console.WriteLine(<span style="color: #800000;">"<span style="color: #800000;">SomeEvent happens<span style="color: #800000;">"<span style="color: #000000;">);<br/> }<br/><br/> <span style="color: #0000ff;">public <span style="color: #0000ff;">bool ReceiveWeakEvent(Type managerType, <span style="color: #0000ff;">object<span style="color: #000000;"> sender, EventArgs e)<br/> {<br/> Listener_SomeEvent(sender, e);<br/> <span style="color: #0000ff;">return <span style="color: #0000ff;">true<span style="color: #000000;">;<br/> }<br/> }<br/><br/> <span style="color: #0000ff;">class<span style="color: #000000;"> Program<br/> {<br/> <span style="color: #0000ff;">static <span style="color: #0000ff;">void Main(<span style="color: #0000ff;">string<span style="color: #000000;">[] args)<br/> {<br/> <span style="color: #0000ff;">var pub = <span style="color: #0000ff;">new<span style="color: #000000;"> Publisher();<br/> <span style="color: #0000ff;">var lsn = <span style="color: #0000ff;">new<span style="color: #000000;"> Listener();<br/> WeakEventManager&lt;Publisher, EventArgs&gt;.AddHandler(pub, <span style="color: #800000;">"<span style="color: #800000;">SomeEvent<span style="color: #800000;">"<span style="color: #000000;">, lsn.Listener_SomeEvent);<br/><br/> pub.RaiseEvent();<br/><br/> lsn = <span style="color: #0000ff;">null<span style="color: #000000;">;<br/> GC.Collect();<br/><br/> pub.RaiseEvent();<br/><br/> Console.Read();<br/> }<br/> }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></div><p><span style="font-size: 12pt;"><span style="font-family: 宋体;"><span style="font-family: 宋体; font-size: 12pt;">程序衹會輸出了一次SomeEvent happens,説明lsn確實被回收了。</span></span></span></p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949727" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/yetsen/p/3949727.html" target="_blank">弱引用和弱事件</a>,转载请注明。</p>http://www.cnblogs.com/HouZhiHouJueBlogs/p/3949632.htmlLinux有趣的历史概览 - 灬后知后觉1965年,Bell、MIT和GE公司发起Multics计划,目标是实现一个操作系统可以让大型主机实现连接三百个终端的目标。(那个时候的分时操作系统可不像现在。。。) 1969年,Multics计划滞后,资金紧缺,Bell实验室退出该计划,但原本参与Multics计划的人员,却从中得到了一些启...2014-09-01T09:38:00Z2014-09-01T09:38:00Z灬后知后觉http://www.cnblogs.com/HouZhiHouJueBlogs/<p>  1965年,Bell、MIT和GE公司发起Multics计划,目标是实现一个操作系统可以让大型主机实现连接三百个终端的目标。(那个时候的分时操作系统可不像现在。。。)</p><p>  1969年,Multics计划滞后,资金紧缺,Bell实验室退出该计划,但原本参与Multics计划的人员,却从中得到了一些启发。&nbsp;Ken Thompson就是其中一位。</p><p>  据说&nbsp;Ken Thompson为了移植一套"<span style="color: #ff0000;">太空旅游</span>"的游戏,希望研发一套操作系统以满足自己的需求。经过四个星期的奋斗(<span style="color: #ff0000;">这厮简直不是人。。</span>),他终于以汇编语言(Assembler)写出了一组核心程序,同时包括一些核心工具程序, 以及一个小小的文件系统。那个系统就是 Unix 的原型! 当时 Thompson 将 Multics 庞大而复杂系统简化了不少,于是同实验室的朋友都戏称这个系统为:Unics。  </p><p>  由于Thompson 写的那个操作系统实在太好用了,所以在贝尔实验室内部广为流传,并且数度经过改版。后来 Thompson 与 Ritchie 合作想将 Unics 改以高阶程序语言来撰写。当时现成的高级程序语言有B语言<span style="line-height: 1.5;">。</span> 但是由 B 语言所编译出来的核心功能效能不太好。后来 Dennis Ritchie 将 B 语言改写为C语言(<span style="color: #ff0000;">换句话说C语言就是这个时候被开发完成的</span>),接着再以C语言重新改写和编译Unics的核心,最后发行出Unix的正式版本。<span style="color: #ff0000;">(这群高级黑客。。。)</span></p><p><span style="color: #ff0000;">  <span style="color: #000000;">由于</span></span>贝尔实验室是隶属二美国电信大厂 AT&amp;T 公司的, 只是 AT&amp;T 当时忙于其他商业活劢,对于Unix并不支持也不排斥<span style="line-height: 1.5;">。此外,Unix在这个时期的发展着都是贝尔实验室的工程师,这群工程师对程序相当有研究</span><span style="line-height: 1.5;">,但是,Unix在当时可不是一般人可以接受的。不过对于学术界的学者来说,Unix真是学者们进行研究的福音,因为程序代码可以改写并且作为学术研究之用啊。</span></p><p><span style="line-height: 1.5;">  1977年,Unix的重要版本,BSD诞生。</span></p><p><span style="line-height: 1.5;">  1979年,</span><span style="line-height: 1.5;">AT&amp;T 由由于在商业上的考虑,以及在当时现实环境下的思考,于是想将 Unix 的版权收回去。因</span>此, AT&amp;T 在 1979 年发行的第七版 Unix 中,特别提到了不可对学生提供源码的严格限制。同时,也造成 Unix 业界之间的紧张气氛,并且也引爆了很多的商业纠纷~</p><p>  这是最不爽的是谁呢?<span style="color: #ff0000;">教授。</span><span style="line-height: 1.5;">想一</span>想,如果没有核心原始码,那举如何教导学生认识 Unix 呢?这问题对于 Andrew Tanenbaum(谭宁邦)教授来说,实在是很伤脑经!不过,学校的课程还得继续,那怎么办?谭宁邦教授于是乎自己动手写了 Minix 这个 Unix Like 的核心程序! 在撰写的过程中,为了避免版权纠纷,谭宁邦完全不看 Unix 核心原始码!(<span style="color: #ff0000;">不愧是教授。。。</span><span style="color: #ff0000;">)</span>&nbsp;并且强调他的inix 必须能够与 Unix 兼容才行!谭宁邦在 1984 年开始撰写核心程序, 到了 1986 年终于完成。称之为Minix(意思为:mini unix)</p><p>  Minux 操作系统的开发者仅有谭宁邦教授,因为学者很忙啊!加上谭宁邦始终认为 Minix 主要用在教育用途上面, 所以对于 Minix 是点到为止!没错,Minix 是很受欢迎,不过,使用者的要求/需求的声音可能就比较没有办法上升到比较高的地方了!</p><p>  这个时候是不是Linux就出现了呢,还没有,介绍另一条故事主线。</p><p>  在谭宁邦教授撰写minix时,同时进行的还有另外一件事。那就是GNU计划。GNU计划是由Richard Mathew Stallman(史托曼)发起,这个计划对于现今的自由软件风潮,具有不可磨灭的作用。</p><p>  Richard Mathew Stallman(生于 1953 年)从小就很聪明。他在1971 年的时候,进入黑客圈中相当出名的人工智能实验室(AI Lab.),当时的黑客圈对于软件的着眼点几乎都是在『分享』,所以并没有专利方面的困扰, 这个特色对于史托曼影响很大,不过,后来由于管理阶层的问题,导致实验室里优秀黑客离开该实验室, 并进入其他商业公司继续发展优秀的软件。但史托曼不服输,仍然持续在原来的实验室开发新的程序和软件。 后来,他发现到,自己一个人并无法完成所有的工作,于是想要成立一个开放的团队来继续努力。</p><p>  1983 年以后,因为实验室硬件的更换,使得史托曼无法继续以原有的硬件和操作系统继续自由程序的撰写~ 而且他进一步发现到,过去他所使用的Lisp 操作系统,是麻省理工学院的专利软件, 是无法共享的,这对于想要成立一个开放团体的史托曼是个阻碍。二是他便放弃了 Lisp 这个系统。 后来,他接触到 Unix 这个系统,并且发现,Unix 在理论上与实际上,都可以在不同的机器间进行移植。虽然 Unix 依旧是专利软件, 但至少 Unix 架构上还是比较开放的!于是他开始转而使用 Unix 系统。</p><p>  1984 年,史托曼开始 GNU 计划, 这个计划的目的是:建立一个自由、开放的 Unix 操作系统(Free Unix)。 但是建立一个操作系统谈何容易啊!而且在当时的 GNU 是仅有自己一个人单打独斗的史托曼~ 这实在太麻烦,但又不想放弃这个计划,那可怎么办啊?</p><p>  聪明的史托曼干脆反其道而行~『既然操作系统太复杂,我就先写可以在 Unix 上面运行的小程序,这总可以了吧?』在这个想法上, 史托曼开始参考 Unix 上面现有的软件,幵依据这些软件的作用开发出功能相同的软件,在开发期间史托曼绝不看其他软件的原始码, 以避克吃上官司。后来一堆人知道免费的 GNU 软件,开始实际使用后发现与原有的专利软件相比差不了太多,于是便转而使用 GNU 软件, GNU 计划逐渐打开知名度。</p><p>  虽然 GNU 计划渐渐打开知名度,但是能见度还是不够。这时史托曼又想:不论是什么软件, 都得要事先编译成为事二进制文件(binary program)后才能够执行,如果能够写出一个不错的编译程序,那不就是大家都需要的软件了吗? 因此他便开始撰写 C 程序的编译程序,那就是现在相当有名的 GNU C Compiler(<strong><span style="color: #ff0000;">gcc</span></strong>)!(<span style="color: #ff0000;">linux上著名的编译器浮出水面咯~!</span>),在撰写过程中,他成立<span style="color: #ff0000;">自由软件基金会</span>(FSF, Free Software Foundation)。此外,他还撰写了更多可以被呼叫的 C 函式库(GNU C library),以及可以被使用来操作操作系统的基本接口 BASH shell!</p><p>这些都在 1990 年左右完成了!(<span style="color: #ff0000;">linux上著名的bash shell又浮出水面了咯~!</span>)</p><p>  到了 1985 年,为了避克 GNU 所开发的自由软件被其他人所利用而成为专利软件, 所以他与律师草拟了有名的通用公共许可证(General Public License, GPL), 并且称呼他为 copyleft(相对与利软件的 copyright!)(<span style="color: #ff0000;">玩git都知道GPL吧,但是不知道有多少人知道这一段历史~</span>)</p><p>  由于有 GNU 所开发的几个重要软件(比如GCC,Glibc,bash shell)造成后来很多的软件开发者可以藉由这些基础的工具来进行程序开发! 进一步壮大了自由软件团体,这是很重要的。不过,对于 GNU的最初构想 『建立一个自由的 Unix 操作系统』来说,这些优秀的程序是仍无法满足, 因为,当下并没有『自由的 Unix 核心』存在...所以这些软件仍只能在那些专利的 Unix 平台上工作~~一直到 Linux 的出现...</p><p>  &nbsp;1991 年,芬兰的赫尔辛基大学的的Linus Torvalds 在 BBS 上面贴了一则消息, 宣称他以 bash,&nbsp;gcc 等工具写了一个小小的核心程序,这个核心程序可以在 Intel 的 386 机器上面运作, 让很多人很感兴趣!从此开始了 Linux 不平凡的路程!</p><p>  Linus Torvalds(托瓦兹, 1969 年出生)的外祖父是赫尔辛基大学的统计学家, 他的外祖父为了让自己的小孙子能够学点东西,所以很小就将托瓦兹带到身边来管理一些微计算机。 在这个时期,托瓦兹接觉了汇编语言(Assembly Language),那是一种直接和芯片对谈的程序语言,也就是所谓的低级语言。必须要很了解硬件的架构,否则很难以汇编语言编写程序的。</p><p>  在 1988 年间,托瓦兹顺利的进入了赫尔辛基大学,幵选读了计算机科学系。在就学期间,因为学业的需要和自己的兴趣, 托瓦兹接触到了 Unix 这个操作系统。当时整个赫尔辛基叧有一部最新的 Unix 系统,同时仅提供 16 个终端机(terminal)。 早期的计算机仅有主机具有运算功能,terminal 仅负责提供Input/Output 而已。在这种情冴下, 实在很难满满足托瓦兹的需求,因为.....光是等待使用 Unix 的时间,就很耗时~为此,他不禁想到: 『我何不自己搞一部 Unix 来玩?』不过,就如同 Stallman 当年的 GNU 计划一样,要写核心程序,谈何容易~不过,幸运之神并未背离托瓦兹,因为不久之后,他就知道有一个类似 Unix 的系统, 并且和 Unix 完全兼容,还可以在 Intel 386 (<span style="color: #ff0000;">就是教科书上的I386咯</span>) 机器上面跑的操作系统, 那就是我们上面提过的,谭宁邦教授为了教育需要而撰写的 Minix 系统!<span style="color: #ff0000;">(真的不要小看教授们。。。)</span> 他在购买了最新的 Intel 386 的个人计算机后,就立即安装了 Minix 这个操作系统。 另外,Minix 这个操作系统是有附上原始码的, 所以托瓦兹也经由这个原始码学习到了很多的核心程序的设计概念。</p><p>  托瓦兹跟在研究Minix的过程中,发现 Minix 虽然真的很棒,但是谭宁邦教授就是不愿意进行功能的加强,导致一堆工程师在操作系统功能上面的欲求不满! 这个时候年轻的托瓦兹就想:『既然如此,那我何不自己来改写一个我想要的操作系统?』 二是他就开始了核心程序的撰写了。</p><p>  撰写程序需要什么呢?首先需要的是能够进行工作的环境,再来则是可以将原始码编译成为可执行文件的编译秳序。 好在有 GNU 计划提供的 bash 工作环境软件以及 gcc 编译程序等自由软件, 让托瓦兹得以顺利的撰写核心秳序。他参考 Minix 的设计理念和书上的程序代码,然后仔绅研究出 386 个人计算机的性能优化, 然后使用 GNU 的自由软件将核心程序代码不 386 紧紧的结合在一起,最终写出他所需要的核心程序。 而这个小玩意竟然真的可以在 386 上面顺利的跑起来~还可以读取 Minix 的文件系统。 真是太好了!不过还不够,他希望这个程序可以获得大家的一些修改建议, 二是他便将这个核心放置在网绚上提供大家下载,同时在 BBS 上面贴了一则消息:(<span style="color: #ff0000;">这就是Linux出现时的宣言吧</span>)</p><p> <span style="color: #ff6600;"> Hello everybody out there using minixI'm doing a (free) operation system (just a hobby,</span><span style="color: #ff6600;">won't be big and professional like gnu) for 386(486) AT clones.</span><span style="color: #ff6600;">I've currently ported bash (1.08) and gcc (1.40),&nbsp;</span><span style="color: #ff6600;">and things seem to work. This implies that i'll get&nbsp;</span><span style="color: #ff6600;">something practical within a few </span><span style="color: #ff6600;">months, and I'd like to know&nbsp;</span><span style="color: #ff6600;">what features most people want. A</span><span style="color: #ff6600;">ny suggestions are welcome,&nbsp;</span><span style="color: #ff6600;">but I won't promise I'll implement them :-)</span></p><p>&nbsp;  他说,他完成了一个小小的操作系统,这个核心是用在 386 机器上的, 同时,他真的仅是好玩,并不是想要做一个跟 GNU 一样大的计划! 另外,他希望能够得到更多人的建讧与回馈来发展这个操作系统!这个概念跟 Minix 刚好背道而驰。 这则新闻引起很多人的注意,他们也去托瓦兹提供的网站上下载了这个核心来安装。 有趣的是,因为托瓦兹放置核心的那个 FTP 网站的目录为:Linux, 因此,大家便称这个核心为 Linux 了。(此时的 Linux只是现在linux的 kernel)</p><p>  后续的故事我就不讲了,Linux发展到现在,已经尽人皆知了,另外关于linux的吉祥物,也有个有趣的故事:托瓦兹是因为小时候去动物园被企鹅咬了一口念念不忘, 而正式的linux 2.0推出时,大家要他想一个吉祥物。他在想也想不到什么动物的情况下, 就将这个念念不忘的企鹅当成Linux的吉祥物了。。。</p><p>  呵呵~大家同乐~</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>  </p><p>&nbsp;</p><p>  </p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949632" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/HouZhiHouJueBlogs/p/3949632.html" target="_blank">Linux有趣的历史概览</a>,转载请注明。</p>http://www.cnblogs.com/zhouxin/p/3949659.htmlAxiom3D:Ogre地形代码解析 - 天天不在大致流程.这里简单介绍下,Axiom中采用的Ogre的地形组件的一些概念与如何生成地形. 先说下大致流程,然后大家再往下看.(只说如何生成地形与LOD,除高度纹理图外别的纹理暂时不管.) 1.生成TerrainGroup,增加Request与Response处理,设置大小,高度图. 比较...2014-09-01T09:31:00Z2014-09-01T09:31:00Z天天不在http://www.cnblogs.com/zhouxin/<p><strong>大致流程.</strong></p><p> 这里简单介绍下,Axiom中采用的Ogre的地形组件的一些概念与如何生成地形.&nbsp;</p><p>  先说下大致流程,然后大家再往下看.(只说如何生成地形与LOD,除高度纹理图外别的纹理暂时不管.)&nbsp;&nbsp;</p><p>  1.生成TerrainGroup,增加Request与Response处理,设置大小,高度图.</p><p>&nbsp; &nbsp; &nbsp; &nbsp; 比较重要的属性是DefaultImportSettings(ImportData),包含地形的大小,分块最大与最小值,&nbsp;</p><p>  2.TerrainGroup生成与设置各地形Terrain块的大小,高度图,ImportData.&nbsp;</p><p>  3.TerrainGroup调用LoadAllTerrains,各地形放入Request队列.&nbsp;</p><p>  4.TerrainGroup的Request事件调用Terrain的Prepare.&nbsp;</p><p>  5.Terrain的Prepare获取高度纹理里的数据,生成TerrainQuadTreeNode类型的QuadTree,在生成时,会根据ImportData里的数据来生成子节点(TerrainQuadTreeNode)与对应子节点的LodLevels,Movable,Rend.(比较重要,下面详细说)&nbsp;</p><p>  6.Terrain的Prepare调用CalculateHeightDeltas生成QuadTree与子节点里LodLevel的高度差.&nbsp;</p><p>  7.Terrain的Prepare调用FinalizeHeightDeltas生成QuadTree与子节点里AABB包围盒子.&nbsp;</p><p>  8.Terrain的Prepare调用DistributeVertexData,这个方法主要生成子节点的数据顶点信息mVertexDataRecord,只包含位置,不包含索引.注意mVertexDataRecord会由多个树层次共用(比较重要,下面详细说).在这里Prepare调用就完成.&nbsp;</p><p>  9.返回到TerrainGroup的Response事件发生,主要调用对应的Terrain的Load方法.&nbsp;</p><p>  10.Terrain里的Load主要是对QuadTree.Load调用.&nbsp;</p><p>  11.QuadTree的Load调用,主要是针对子节点的TerrainQuadTreeNode的LodLevel生成顶点索引,对应Terrain-&gt;Prepare-&gt;DistributeVertexData生成的mVertexDataRecord生成的顶点.(比较重要,下面详细说)&nbsp;</p><p>  12.渲染时LOD动态计算,Camera调用RenderScene,引发对应Terrain里的CalculateCurrentLod计算QuadTree以及对应各子节点的Lodlevel.对应第11步里的QuadTree的Load生成的各LodLevel.&nbsp;</p><p><strong>TerrainQuadTreeNode生成子块.</strong></p><p>  在第五步中,Terrain有三个属性(TerrainGroup里的DefaultImportSettings设置)来决定如何分区分块,分别Size,MaxBatchSize,MinBatchSize.意思分别是这个Terrain的大小,Terrain里的单个块里的最大值与最小值,注意这三个值是同一量程,并且他们的长度都满足&nbsp;2^n+1,我们都可以看做是地形的虚拟单位,他的实际大小是worldSize,比如worldSize是10240,而Size是257,那么Size,MaxBatchSize,MinBatchSize里的一个单位1可以看做是实际长度40.而MaxBatchSize与Size影响Terrain的如何分块,而MaxBatchSize与MinBatchSize影响Terrain精度最高的块的再细分.综合MinBatchSize与size就是地形的LOD层数.如下公式</p><p><span style="color: #010101; font-family: Tahoma;">&nbsp; &nbsp; &nbsp;  LODlevels = log2(size - 1) - log2(minBatch - 1) + 1</span></p><p><span style="color: #010101; font-family: Tahoma;">&nbsp; &nbsp; &nbsp;  TreeDepth = log2(size - 1) - log2(maxBatch - 1) + 1</span>&nbsp;</p><p>  Terrain在创建时,会生成一个TerrainQuadTreeNode节点,TerrainQuadTreeNode根据地形的Size,MaxBatchSize,MinBatchSize生成四叉树,这里我们先假设Size等于257,MaxBatchSize等33,而MinBatchSize等于17,根据公式LODlevels=5,TreeDepth=4这个TerrainQuadTreeNode的Size就是Terrain的Size,然后生成四个子节点,想象一下把一个正方形中间横竖各一条线分隔成四个正方形.TerrainQuadTreeNode生成的子节点也是这个过程,那么子节点的边长为父节点边长的1/2,也就是(257-1)/2+1=129.这个过程会递归下去,一直到MaxBatchSize的长度,也就是257,129,65,33一共是四层(这四层的顶点密度都是MinBatchSize),也就是上面TreeDepth的结果,而到33后,TerrainQuadTreeNode不会生成子节点了,而是生成log2(maxBatch - 1)-log2(maxBatch - 1)=1个LodLevel.请看下图.(节点生成的顺序是LOD4-LOD0,不要搞反了.)</p><p><img src="http://images.cnitblog.com/blog/81011/201409/011727406881047.png" alt="" /></p><p>  其中Lod越高,Terr depth越低,地度的精度越低.我们可以看到深度对应着地形的分块大小,代码同TerrainQuadTreeNode生成子节点的过程,一共四层.而在LOD1后,没有生成子节点,而是再生成一个LodLevel,其中BatchSize为33(对应面积每边取点),之前每个TerrainQuadTreeNode对应的BatchSize都为17.而MaxBatchSize与MinBatchSize是针对图上的每1/256的Terrain的细分.TreeDepth加上最后细分的LodLevel就等于LODlevels.总结如下:根据Size与MaxBatchSize得到最小块 (256/32)^2&nbsp;=8*8,8对应四层,分别是8*8,4*4,2*2,1*1.而4*4,2*2,1*1这三块的精度都为MinBatchSize就17,而8*8精度从MaxBatchSize到MinBatchSize.&nbsp;</p><p><strong>地形顶点计算.</strong></p><p>  那么我们如何根据上面的LOD来给出对应的顶点与索引数据.这个在Terrain的方法DistributeVertexData里的注释详细讲解了如何根据Terrain的Size,MaxBatchSize,MinBatchSize来创建顶点元素,在这里设Size为2049,MaxBatchSize为65,MinBatchSize为33.&nbsp;</p><p>  因为要支持16位的索引,故最大为2^8*2^8也就是256*256的值,考虑这个值太大,TERRAIN_MAX_BATCH_SIZE为128+1.就是说支持最大的正方形每边的顶点为129个,总索引为129*129.根据前面一节我们来分成如下段:</p><p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LODlevels = log2(2049 - 1) - log2(33 - 1) + 1 = 11 - 5 + 1 = 7<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TreeDepth = log2((2049 - 1) / (65 - 1)) + 1 = 6<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Number of vertex data splits at most detailed level:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (size - 1) / (TERRAIN_MAX_BATCH_SIZE - 1) = 2048 / 128 = 16<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 0: 2049 vertices, 32 x 65(总1024块) vertex tiles (tree depth 5) vdata 0-15&nbsp; [129x16](总256块)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 1: 1025 vertices, 32 x 33(总1024块)&nbsp;vertex tiles (tree depth 5) vdata 0-15&nbsp; [129x16](总256块)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 2: 513&nbsp; vertices, 16 x 33(总256块)&nbsp;vertex tiles (tree depth 4) vdata 0-15&nbsp; [129x16](总256块)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 3: 257&nbsp; vertices, 8&nbsp; x 33(总64块)&nbsp;vertex tiles (tree depth 3) vdata 16-17 [129x2](总4块)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 4: 129&nbsp; vertices, 4&nbsp; x 33(总16块)&nbsp;vertex tiles (tree depth 2) vdata 16-17 [129x2](总4块)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 5: 65&nbsp;&nbsp; vertices, 2&nbsp; x 33(总4块)&nbsp;vertex tiles (tree depth 1) vdata 16-17 [129x2](总4块)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LOD 6: 33&nbsp;&nbsp; vertices, 1&nbsp; x 33(总1块)&nbsp;vertex tiles (tree depth 0) vdata 18&nbsp;&nbsp;&nbsp; [33](总一块)&nbsp;</p><p>  这个和前面差不多,唯一就是最后多出来的129*16,129*2,33这三段,前面的LODLevels可以说是数据索引分区,而这129*16,129*2,33是数据分区.直接来看,32*65,32*33,16*33如何分进129*16这个块,而为什么8*33以及如下又不能分进这块.我们看到的32*65,12*33,129*16都是正方形一边的点数,而129是每小块最大点数(TERRAIN_MAX_BATCH_SIZE),那么边为2049点的正方形在每块最大占129点的情况下,可以分为16*16个129点的小正方形块.在LOD 0 32*(65点)的情况下,就是一个16*16*(129点)的块分成四个32*(65点)的块.而LOD1和LOD0一样也是一个16*16*129的块分成了四个,但是每块的点数只有33点,就是四个32*(33点)的块.而LOD2的一块也是16*16*(33点),也就是说,LOD2的一块大小和16*16*(129点)大小一样,只是原始的每块每边有129个基点,现在LOD2的每块只有33个顶点,但是他们的大小是一样.那么在LOD3开始,他是分成8*8*(33点),这一块有16*16*(129点)四个大,这样LOD3里的索引匹配不到16*16*(129点)里的数据,所以重新开始分割,然后LOD3,4,5和前面的分块一样,都能匹配到2*2*(129)点块上.最后一块是直接分成33*33,这里对应注释告诉我们有二个选择,为了减少渲染次数而分成33*33.而不是分成四块17*17.&nbsp;</p><p>  大家可能要说,为什么不为每LOD直接生成对应每层数据.这样完全没必要,因为就和上面分析一样,LOD0,1,2完全可以共用顶点,只需要选择好相应索引就能正确的渲染,而每层生成顶点数据,直接造成内存紧张.这样分三层的第一层是2049*2049=4198401,第二层是513*513=263169,第三层是33*33=1089.第二层和第三层只占第一层的3%左右,在合适的情况下,可以只要第二层的数据,大大缩减内存使用.&nbsp;</p><p><strong>地形顶点索引计算.</strong></p><p>  如上数据,在Terrain的方法DistributeVertexData里,分别是LOD2,LOD5,LOD6.在这三层分别会进入地形的根TerrainQuadTreeNode里的方法AssignVertexData.在LOD2时,根节点从tree dapth 0找到tree dapth 4,可以找到256个TerrainQuadTreeNode,在这每个TerrainQuadTreeNode调用CreateCPUVertexData首先生成数据顶点属性所需的空间.然后调用UpdateVertexBuffer生成最终的顶点数据(里面有代码用skiter skill来填LOD边的点.),LOD0,LOD1会经UseAncsetorVertexData得到父节点的VertexDataRecord,就是LOD2的一块对应LOD1的四块,LOD0比较特殊,按前面所说,和LOD1是共用对应的TerrainQuadTreeNode的.同样,会在LOD5再次进入AssignVertexData,给对应4块地图分配VertexDataRecord,而LOD4,LOD3会被分到LOD5的VertexDataRecord,LOD5的一块对应LOD4的四块,LOD3的16块.最后LOD6再分一次,就一块33*33. &nbsp; &nbsp;&nbsp;</p><p>  TerrainQuadTreeNode的Load调用PopulateIndexData来生成对应LOD的顶点索引,这个过程主要交给对应Terrain里的GpuBufferAllocator,通过调用GetSharedIndexBuffer来生成IndexBuffer空间,而IndexBuffer里的数据又会回到Terrain里的PopulateIndexBuffer生成.在这里对PopulateIndexBuffer方法需要的一些参数说明下,我们以LOD5来说明,batchSize是33,指的是LOD5每块每边分到的顶点是33.vdatasize是指LOD5对应每边分给VertexDataRecord的顶点,去掉LOD6,余下的LOD应该全是129.在各LOD索引顶点生成后,然后就是对Layel层的处理,这个先不细说.这样整个Load过程就相当于完成了.&nbsp;</p><p>  最后第十二步渲染时,如何根据摄像机的位置选择正确的LOD渲染,大致由Camera调用RenderScene,到Terrain里的CalculateCurrentLod,这里面会对上面的各个TerrainQuadTreeNode里的LodLevel计算正确的值,不过Axiom里这个方法BUG有二处,大家有兴趣可以先不看我下面的修改,自己来动手修改下. &nbsp; &nbsp;&nbsp;&nbsp;</p><p><strong>修改的源码部分.</strong></p><p>  为了运行这个例子,源代码几乎不有运行,我想可能是Axiom这个项目关注的人不多,并且这只是一个组件功能,不影响主要的功能有关.我修改的地方总结一下有:&nbsp;</p><p>  1.Axiom\Core\DisposableObject.cs 255行屏掉,因为再把对应高度图片里的数据转换成对应的高度值时,在Terrain类里Prepare里调用PixelConverter.BulkPixelConversion时花费大量时间。&nbsp;</p><p>  2.Axiom\Media\DDSCodec.cs796-799,检查DDSHeader的数据时,因为DDSHeader不能用GCHandle.Alloc来生成空间,因为包含非基元成员。&nbsp;</p><p>  3.Terrain里的DistributeVertexData并没有正确按注释所分,需要调整currentresolution = (ushort)(((currentresolution - 1) &gt;&gt; 1) + 1);这句在if(splits == targetsplits)之前才会如注释所说生成结果.改不改不影响正常渲染,只会影响地形的数据层级.(Ogre也是这样,所以此处只是提出来,应该不用修改.)&nbsp;</p><p>  4.GpuBufferAllocator里的HashIndexBuffer,这句会造成相当多的重复值,完全不能用.地图小点还好,如设129,可以看到有一些块是正常的,有些就黑块不断闪烁,如设大些如2049,就会发现有一些块没显示出来(Ogre是用的boost::hash_combine,C#里替代方法很多,下面是修改的源码.)如下简单的一种,用Tuple替换,记注Tuple是值类型.</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;" onclick="cnblogs_code_show('8843da59-116a-43cc-bf80-590a4bf79c16')"><div id="cnblogs_code_open_8843da59-116a-43cc-bf80-590a4bf79c16" class="cnblogs_code_hide"><span style="color: #008080;"> 1</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> DefaultGpuBufferAllocator : GpuBufferAllocator<br/></span><span style="color: #008080;"> 2</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 3</span> <span style="color: #0000ff;">protected</span> List&lt;HardwareVertexBuffer&gt; FreePosBufList = <span style="color: #0000ff;">new</span> List&lt;HardwareVertexBuffer&gt;<span style="color: #000000;">();<br/></span><span style="color: #008080;"> 4</span> <span style="color: #0000ff;">protected</span> List&lt;HardwareVertexBuffer&gt; FreeDeltaBufList = <span style="color: #0000ff;">new</span> List&lt;HardwareVertexBuffer&gt;<span style="color: #000000;">();<br/></span><span style="color: #008080;"> 5</span> <span style="color: #008000;">//</span><span style="color: #008000;">protected Dictionary&lt;int, HardwareIndexBuffer&gt; SharedIBufMap = new Dictionary&lt;int, HardwareIndexBuffer&gt;();</span><br/><span style="color: #008080;"> 6</span> <span style="color: #0000ff;">protected</span> Dictionary&lt;Tuple&lt;<span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">int</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>&gt;, HardwareIndexBuffer&gt; SharedIBufMap = <span style="color: #0000ff;">new</span> Dictionary&lt;Tuple&lt;<span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">int</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>, <span style="color: #0000ff;">ushort</span>&gt;, HardwareIndexBuffer&gt;<span style="color: #000000;">();<br/></span><span style="color: #008080;"> 7</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 8</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> AllocateVertexBuffers(Terrain forTerrain, <span style="color: #0000ff;">int</span> numVertices, <span style="color: #0000ff;">out</span><span style="color: #000000;"> HardwareVertexBuffer destPos,<br/></span><span style="color: #008080;"> 9</span> <span style="color: #0000ff;">out</span><span style="color: #000000;"> HardwareVertexBuffer destDelta)<br/></span><span style="color: #008080;"> 10</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 11</span> <span style="color: #008000;">//</span><span style="color: #008000;">destPos = this.GetVertexBuffer( ref FreePosBufList, forTerrain.PositionBufVertexSize, numVertices );<br/></span><span style="color: #008080;"> 12</span> <span style="color: #008000;">//</span><span style="color: #008000;">destDelta = this.GetVertexBuffer( ref FreeDeltaBufList, forTerrain.DeltaBufVertexSize, numVertices );</span><br/><span style="color: #008080;"> 13</span> <br/><span style="color: #008080;"> 14</span> destPos = GetVertexBuffer(<span style="color: #0000ff;">ref</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.FreePosBufList, forTerrain.PositionVertexDecl, numVertices);<br/></span><span style="color: #008080;"> 15</span> destDelta = GetVertexBuffer(<span style="color: #0000ff;">ref</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.FreeDeltaBufList, forTerrain.DeltaVertexDecl, numVertices);<br/></span><span style="color: #008080;"> 16</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 17</span> <br/><span style="color: #008080;"> 18</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 19</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> FreeVertexBuffers(HardwareVertexBuffer posbuf, HardwareVertexBuffer deltabuf)<br/></span><span style="color: #008080;"> 20</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 21</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.FreePosBufList.Add(posbuf);<br/></span><span style="color: #008080;"> 22</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.FreeDeltaBufList.Add(deltabuf);<br/></span><span style="color: #008080;"> 23</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 24</span> <br/><span style="color: #008080;"> 25</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 26</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> HardwareIndexBuffer GetSharedIndexBuffer(<span style="color: #0000ff;">ushort</span> batchSize, <span style="color: #0000ff;">ushort</span> vdatasize, <span style="color: #0000ff;">int</span><span style="color: #000000;"> vertexIncrement,<br/></span><span style="color: #008080;"> 27</span> <span style="color: #0000ff;">ushort</span> xoffset, <span style="color: #0000ff;">ushort</span> yoffset, <span style="color: #0000ff;">ushort</span><span style="color: #000000;"> numSkirtRowsCols,<br/></span><span style="color: #008080;"> 28</span> <span style="color: #0000ff;">ushort</span><span style="color: #000000;"> skirtRowColSkip)<br/></span><span style="color: #008080;"> 29</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 30</span> <span style="color: #008000;">//</span><span style="color: #008000;">int hsh = HashIndexBuffer(batchSize, vdatasize, vertexIncrement, xoffset, yoffset, numSkirtRowsCols, skirtRowColSkip);</span><br/><span style="color: #008080;"> 31</span> <span style="color: #0000ff;">var</span> hsh =<span style="color: #000000;"> Tuple.Create(batchSize, vdatasize, vertexIncrement, xoffset, yoffset, numSkirtRowsCols, skirtRowColSkip);<br/></span><span style="color: #008080;"> 32</span> <span style="color: #0000ff;">if</span> (!<span style="color: #0000ff;">this</span><span style="color: #000000;">.SharedIBufMap.ContainsKey(hsh))<br/></span><span style="color: #008080;"> 33</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 34</span> <span style="color: #008000;">//</span><span style="color: #008000;"> create new</span><br/><span style="color: #008080;"> 35</span> <span style="color: #0000ff;">int</span> indexCount =<span style="color: #000000;"> Terrain.GetNumIndexesForBatchSize(batchSize);<br/></span><span style="color: #008080;"> 36</span> HardwareIndexBuffer ret =<span style="color: #000000;"> HardwareBufferManager.Instance.CreateIndexBuffer(IndexType.Size16, indexCount,<br/></span><span style="color: #008080;"> 37</span> <span style="color: #000000;"> BufferUsage.StaticWriteOnly);<br/></span><span style="color: #008080;"> 38</span> <span style="color: #0000ff;">var</span> pI =<span style="color: #000000;"> ret.Lock(BufferLocking.Discard);<br/></span><span style="color: #008080;"> 39</span> <span style="color: #000000;"> Terrain.PopulateIndexBuffer(pI, batchSize, vdatasize, vertexIncrement, xoffset, yoffset, numSkirtRowsCols,<br/></span><span style="color: #008080;"> 40</span> <span style="color: #000000;"> skirtRowColSkip);<br/></span><span style="color: #008080;"> 41</span> <span style="color: #000000;"> ret.Unlock();<br/></span><span style="color: #008080;"> 42</span> <br/><span style="color: #008080;"> 43</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.SharedIBufMap.Add(hsh, ret);<br/></span><span style="color: #008080;"> 44</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ret;<br/></span><span style="color: #008080;"> 45</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 46</span> <span style="color: #0000ff;">else</span><br/><span style="color: #008080;"> 47</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 48</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.SharedIBufMap[hsh];<br/></span><span style="color: #008080;"> 49</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 50</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 51</span> <br/><span style="color: #008080;"> 52</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 53</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> FreeAllBuffers()<br/></span><span style="color: #008080;"> 54</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 55</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.FreePosBufList.Clear();<br/></span><span style="color: #008080;"> 56</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.FreeDeltaBufList.Clear();<br/></span><span style="color: #008080;"> 57</span> <span style="color: #0000ff;">this</span><span style="color: #000000;">.SharedIBufMap.Clear();<br/></span><span style="color: #008080;"> 58</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 59</span> <br/><span style="color: #008080;"> 60</span> <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span><br/><span style="color: #008080;"> 61</span> <span style="color: #808080;">///</span><span style="color: #008000;"> 'Warm start' the allocator based on needing x instances of <br/></span><span style="color: #008080;"> 62</span> <span style="color: #808080;">///</span><span style="color: #008000;"> terrain with the given configuration.<br/></span><span style="color: #008080;"> 63</span> <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span><br/><span style="color: #008080;"> 64</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 65</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> WarmStart(<span style="color: #0000ff;">int</span> numInstances, <span style="color: #0000ff;">ushort</span> terrainSize, <span style="color: #0000ff;">ushort</span> maxBatchSize, <span style="color: #0000ff;">ushort</span><span style="color: #000000;"> minBatchSize)<br/></span><span style="color: #008080;"> 66</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 67</span> <span style="color: #008000;">//</span><span style="color: #008000;"> TODO</span><br/><span style="color: #008080;"> 68</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 69</span> <br/><span style="color: #008080;"> 70</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span>, <span style="color: #800000;">"</span><span style="color: #800000;">~DefaultGpuBufferAllocator</span><span style="color: #800000;">"</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 71</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">override</span> <span style="color: #0000ff;">void</span> dispose(<span style="color: #0000ff;">bool</span><span style="color: #000000;"> disposeManagedResources)<br/></span><span style="color: #008080;"> 72</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 73</span> <span style="color: #0000ff;">if</span> (!<span style="color: #000000;">IsDisposed)<br/></span><span style="color: #008080;"> 74</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 75</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> (disposeManagedResources)<br/></span><span style="color: #008080;"> 76</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 77</span> <span style="color: #000000;"> FreeAllBuffers();<br/></span><span style="color: #008080;"> 78</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 79</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 80</span> <br/><span style="color: #008080;"> 81</span> <span style="color: #0000ff;">base</span><span style="color: #000000;">.dispose(disposeManagedResources);<br/></span><span style="color: #008080;"> 82</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 83</span> <br/><span style="color: #008080;"> 84</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 85</span> <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">int</span> HashIndexBuffer(<span style="color: #0000ff;">ushort</span> batchSize, <span style="color: #0000ff;">ushort</span> vdatasize, <span style="color: #0000ff;">int</span> vertexIncrement, <span style="color: #0000ff;">ushort</span> xoffset, <span style="color: #0000ff;">ushort</span><span style="color: #000000;"> yoffset,<br/></span><span style="color: #008080;"> 86</span> <span style="color: #0000ff;">ushort</span> numSkirtRowsCols, <span style="color: #0000ff;">ushort</span><span style="color: #000000;"> skirtRowColSkip)<br/></span><span style="color: #008080;"> 87</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;"> 88</span> <span style="color: #0000ff;">int</span> ret =<span style="color: #000000;"> batchSize.GetHashCode();<br/></span><span style="color: #008080;"> 89</span> ret ^=<span style="color: #000000;"> vdatasize.GetHashCode();<br/></span><span style="color: #008080;"> 90</span> ret ^=<span style="color: #000000;"> vertexIncrement.GetHashCode();<br/></span><span style="color: #008080;"> 91</span> ret ^=<span style="color: #000000;"> xoffset.GetHashCode();<br/></span><span style="color: #008080;"> 92</span> ret ^=<span style="color: #000000;"> yoffset.GetHashCode();<br/></span><span style="color: #008080;"> 93</span> ret ^=<span style="color: #000000;"> numSkirtRowsCols.GetHashCode();<br/></span><span style="color: #008080;"> 94</span> ret ^=<span style="color: #000000;"> skirtRowColSkip.GetHashCode();<br/></span><span style="color: #008080;"> 95</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ret;<br/></span><span style="color: #008080;"> 96</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;"> 97</span> <br/><span style="color: #008080;"> 98</span> [OgreVersion(<span style="color: #800080;">1</span>, <span style="color: #800080;">7</span>, <span style="color: #800080;">2</span><span style="color: #000000;">)]<br/></span><span style="color: #008080;"> 99</span> <span style="color: #008000;">//</span><span style="color: #008000;">protected HardwareVertexBuffer GetVertexBuffer( ref List&lt;HardwareVertexBuffer&gt; list, int vertexSize, int numVertices )</span><br/><span style="color: #008080;">100</span> <span style="color: #0000ff;">protected</span> HardwareVertexBuffer GetVertexBuffer(<span style="color: #0000ff;">ref</span> List&lt;HardwareVertexBuffer&gt;<span style="color: #000000;"> list, VertexDeclaration decl,<br/></span><span style="color: #008080;">101</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> numVertices)<br/></span><span style="color: #008080;">102</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">103</span> <span style="color: #0000ff;">int</span> sz = decl.GetVertexSize() * numVertices; <span style="color: #008000;">//</span><span style="color: #008000;"> vertexSize* numVertices;</span><br/><span style="color: #008080;">104</span> <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> i <span style="color: #0000ff;">in</span><span style="color: #000000;"> list)<br/></span><span style="color: #008080;">105</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">106</span> <span style="color: #0000ff;">if</span> (i.Size ==<span style="color: #000000;"> sz)<br/></span><span style="color: #008080;">107</span> <span style="color: #000000;"> {<br/></span><span style="color: #008080;">108</span> HardwareVertexBuffer ret =<span style="color: #000000;"> i;<br/></span><span style="color: #008080;">109</span> <span style="color: #000000;"> list.Remove(i);<br/></span><span style="color: #008080;">110</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> ret;<br/></span><span style="color: #008080;">111</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">112</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">113</span> <br/><span style="color: #008080;">114</span> <span style="color: #008000;">//</span><span style="color: #008000;"> Didn't find one?</span><br/><span style="color: #008080;">115</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> HardwareBufferManager.Instance.CreateVertexBuffer(decl, numVertices, BufferUsage.StaticWriteOnly);<br/></span><span style="color: #008080;">116</span> <br/><span style="color: #008080;">117</span> <span style="color: #008000;">//</span><span style="color: #008000;">TODO It should looks like this<br/></span><span style="color: #008080;">118</span> <span style="color: #008000;">//</span><span style="color: #008000;">return HardwareBufferManager.Instance.CreateVertexBuffer( vertexSize, numVertices, BufferUsage.StaticWriteOnly );</span><br/><span style="color: #008080;">119</span> <span style="color: #000000;"> }<br/></span><span style="color: #008080;">120</span> };</div></div><p>  5.地形的LOD没跟摄像机变化,经调试查找主要是Root.cs里的NextFrameNumber,这个值一直为0,导致Terrain里的_preFindVisialeObjects里不会执行CalculateCurrentLod,也就是所有地形的LOD值是你一进去就定好了,后面你移动位置不会改变LOD值,改变方法,对比Ogre,NextFrameNumber应该是CurrentFrameCount,这个值在每桢时会加1,而NextFrameNumber都没被赋值过,故各个LOD一直是初始的值.&nbsp;</p><p>  6.当地形的wordsize与地形图片的长宽不同时,对应图片的缩放方法resize有问题,这个先不管,我把地形的wordsize与图片长宽都调整为2049,然后渲染的窗口是一团乱,因为TerrainQuadTreeNode里的UpdateVertexBuffer里计算高度时,相关高度的偏移没有正常计算,修改如下三处后面加上*sizeof(float).</p><p>&nbsp; &nbsp; &nbsp;pBaseHeight += rowskip * sizeof(float);</p><p>&nbsp; &nbsp; &nbsp;pBaseDelta += rowskip * sizeof(float);</p><p>&nbsp; &nbsp; &nbsp;pBaseHeight += this.mTerrain.Size * skirtSpacing * sizeof(float);</p><p>&nbsp; &nbsp; &nbsp;然后能看到生成的正确高度.此处可参见&nbsp;<a href="http://www.cnblogs.com/zhouxin/p/3627446.html" target="_blank">Axiom3D:Buffer漫谈</a>.&nbsp;</p><p>  7.还是LOD的计算,我们可以发现,改变以上几点后,地形的LOD显示有些问题,比如我脚下的这块LOD有时还比不上前面一块LOD的精度,经过调试查找发现主要是因为LodLevel是Struct类型,故如下:</p><p>&nbsp; &nbsp; &nbsp;LodLevel tmp = this.mLodLevels[0];<br />&nbsp; &nbsp; &nbsp;tmp.CalcMaxHeightDelta = System.Math.Max(tmp.CalcMaxHeightDelta, maxChildDelta * (Real)1.05);<br />&nbsp; &nbsp; &nbsp;this.mChildWithMaxHeightDelta = childWithMaxHeightDelta;<br />  这段代码并不能改变&nbsp;this.mLodLevels[0]里的值(这处代码我查找了下,共有差不多十处左右),有二种方法,一是在如上十处改变对应LodLevel&nbsp;后,调用this.mLodLevels[0] = tmp,注意下CalculateCurrentLod这个里面有段foreach this.mLodLevels,这里的foreach首先要改变成for,在C#中foreach中不能对值类型赋值.二是直接把LodLevel&nbsp;由Struch改为class.&nbsp;</p><p>  8.上面的全部修改后,会发现一排的边有些高度对应不上,就是有突起,我们修改如下代码:</p><p>&nbsp; &nbsp; &nbsp; &nbsp; public Real GetSquaredViewDepth(Camera cam)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.mRend.Technique != null &amp;&amp; this.mRend.Technique.PassCount &gt; 0)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.mRend.Technique.GetPass(0).PolygonMode = cam.PolygonMode;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.mMovable.ParentSceneNode.GetSquaredViewDepth(cam);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p><div>  使地形的PolygonMode和摄像机的一起变,查看Wireframe模式下,发现是裙子节点不同,查看TerrainQuadTreeNode里的UpdateVertexBuffer里的x-y裙子点,这个位置取高度值不同前面y-x下指针移位,是直接定位索引,根进发现this.mTerrain.GetHeightAtPoint(x, y)里如下代码:&nbsp;</div><p>&nbsp; &nbsp; &nbsp;public float GetHeightAtPoint(long x, long y)</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //clamp<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = Utility.Min(x, (long)this.mSize - 1L);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; x = Utility.Max(x, 0L);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = Utility.Min(y, (long)this.mSize - 1L);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; y = Utility.Max(y, 0L);<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return this.mHeightData[y + this.mSize * x];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p><p>  this.mHeightData[y + this.mSize * x]修改成this.mHeightData[x + this.mSize * y].</p><p>  同样的,在Terrain里的GetPointAlign也同样这样修改.&nbsp;</p><p><strong>效果图</strong></p><p>  上面这些修改后,我们就可以看到比较完美的显示效果了.下面是效果图.</p><p><img src="http://images.cnitblog.com/blog/81011/201409/011725377823494.png" alt="" /></p><p><img src="http://images.cnitblog.com/blog/81011/201409/011726164079593.png" alt="" /></p><p><img src="http://images.cnitblog.com/blog/81011/201409/011726376109448.png" alt="" /></p><p><img src="http://images.cnitblog.com/blog/81011/201409/011726543759294.png" alt="" /><span style="line-height: 1.5;">&nbsp; &nbsp; &nbsp;</span></p><p>  PS:非常感谢Axiom里的这些还没修正的BUG,因为这些BUG,我想了更多,得到了更多.后面如果有时间,我会针对地形组件的TerrainMaterialGeneratorA来说明如何生成material及对应的着色器方法,以及在Axiom中如何来使用着色器的一些流程.</p><img src="http://counter.cnblogs.com/blog/rss/3949659" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/zhouxin/p/3949659.html" target="_blank">Axiom3D:Ogre地形代码解析</a>,转载请注明。</p>http://www.cnblogs.com/milkmap/p/3949295.html【高德地图API】汇润做爱地图技术大揭秘 - 酸奶小妹汇润云购利用高德云图制作了一张能存储海量位置数据的,匿名发布做爱地点与详情的地图。虽然这只是一个运营活动,感觉似乎仿佛也抄袭了国外的I just made love,但其庞大的参与量,还是让我觉得可以写一篇相关的技术分析,让更多人可以利用高德云图,制作形式有趣的地图。更重要的是,这是一个很好的获得U...2014-09-01T09:11:00Z2014-09-01T09:11:00Z酸奶小妹http://www.cnblogs.com/milkmap/<p><span style="line-height: 1.5;">  昨日收到了高德地图微信公众号的消息推送,说有【一大波免费情趣用品正在袭来】,点进去看了一眼,说一个电商公司(估计是卖情趣用品的)用高德云图制作了一张可以标记做爱地点与详情的地图。</span>这不就是中国版的<span style="background-color: #00ff00;">I just made love</span>麽?</p><p>  滑到屏幕底下,看了看阅读量,哇塞,居然有<span style="background-color: #ffff00;">4万3</span>!!!说明实在是有很多人关注做爱地图啊。本着研究地图的心情(绝对不是为了什么价值300的智能情趣用品!),我也就点击了【阅读原文】&hellip;&hellip;</p><p>  好吧,为了证明我真的不是为了奖品,我会一边写活动步骤,一边<span style="color: #ffffff; background-color: #ff0000;">揭秘其中的LBS技术</span>。</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011415411411400.png" alt="" width="218" height="387" />&nbsp;<img style="line-height: 1.5;" src="http://images.cnitblog.com/blog/249635/201409/011416271728236.png" alt="" width="220" height="387" /></p><p>&nbsp;</p><p>&nbsp;<span style="line-height: 1.5;">&nbsp;----------------------------------------------------------------------------------------</span></p><p><strong><strong>一、</strong><strong>说明页面</strong></strong></p><p><strong><strong>1、浏览器定位</strong></strong></p><p>进入说明页面,即跳出一个定位允许的弹窗。</p><p>在微信里的webview页面是如何定位的呢,答案肯定是&ldquo;<span style="background-color: #00ffff;">浏览器定位</span>&rdquo;啦。</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011443419538034.png" alt="" /></p><p>&nbsp;</p><p>浏览器定位代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">function</span><span style="color: #000000;"> mapInit () {<br/> mapObj </span>= <span style="color: #0000ff;">new</span> AMap.Map('iCenter'<span style="color: #000000;">);<br/> mapObj.plugin(</span>'AMap.Geolocation', <span style="color: #0000ff;">function</span><span style="color: #000000;"> () {<br/> geolocation </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> AMap.Geolocation({<br/> enableHighAccuracy: </span><span style="color: #0000ff;">true</span>,<span style="color: #008000;">//</span><span style="color: #008000;">是否使用高精度定位,默认:true</span><br/> timeout: 10000, <span style="color: #008000;">//</span><span style="color: #008000;">超过10秒后停止定位,默认:无穷大</span><br/> maximumAge: 0, <span style="color: #008000;">//</span><span style="color: #008000;">定位结果缓存0毫秒,默认:0</span><br/> convert: <span style="color: #0000ff;">true</span>, <span style="color: #008000;">//</span><span style="color: #008000;">自动偏移坐标,偏移后的坐标为高德坐标,默认:true</span><br/> showButton: <span style="color: #0000ff;">true</span>, <span style="color: #008000;">//</span><span style="color: #008000;">显示定位按钮,默认:true</span><br/> buttonPosition: 'LB', <span style="color: #008000;">//</span><span style="color: #008000;">定位按钮停靠位置,默认:'LB',左下角</span><br/> buttonOffset: <span style="color: #0000ff;">new</span> AMap.Pixel(10, 20),<span style="color: #008000;">//</span><span style="color: #008000;">定位按钮与设置的停靠位置的偏移量,默认:Pixel(10, 20)</span><br/> showMarker: <span style="color: #0000ff;">true</span>, <span style="color: #008000;">//</span><span style="color: #008000;">定位成功后在定位到的位置显示点标记,默认:true</span><br/> showCircle: <span style="color: #0000ff;">true</span>, <span style="color: #008000;">//</span><span style="color: #008000;">定位成功后用圆圈表示定位精度范围,默认:true</span><br/> panToLocation: <span style="color: #0000ff;">true</span>, <span style="color: #008000;">//</span><span style="color: #008000;">定位成功后将定位到的位置作为地图中心点,默认:true</span><br/> zoomToAccuracy:<span style="color: #0000ff;">true</span> <span style="color: #008000;">//</span><span style="color: #008000;">定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false</span><br/><span style="color: #000000;"> });<br/> mapObj.addControl(geolocation);<br/> AMap.event.addListener(geolocation, </span>'complete', onComplete);<span style="color: #008000;">//</span><span style="color: #008000;">返回定位信息</span><br/> AMap.event.addListener(geolocation, 'error', onError); <span style="color: #008000;">//</span><span style="color: #008000;">返回定位出错信息</span><br/><span style="color: #000000;"> });<br/>  geolocation.getCurrentPosition(); </span><span style="color: #008000;">//</span><span style="color: #008000;">启动定位</span><br/>};</div><p>&nbsp;</p><p><strong>2、提升用户体验</strong></p><p>因为浏览器定位需要时间,如果进入地图界面后,再使用浏览器定位,会让用户感觉要等待很久。</p><p>不信大家可以看官方的浏览器定位,速度不会太快的:<a href="http://lbs.amap.com/api/javascript-api/example/g/0704-2/" target="_blank">http://lbs.amap.com/api/javascript-api/example/g/0704-2/</a></p><p>&nbsp;</p><p>所以为了提高用户体验,<span style="background-color: #ff99cc;">让用户感觉没有等待时间</span>,这就需要打开页面立刻定位,但又不能显示出地图。</p><p>于是,可以猜到说明页面只是一个覆盖层,是一个&ldquo;<strong>障眼法</strong>&rdquo;。</p><p>当用户点击立刻参与的时候,这个层<span style="color: #000000;">display:none</span>了而已。</p><p>&nbsp;</p><p><strong>3、定位失败策略</strong></p><p>浏览器定位当然不可能100%成功。原因是:</p><p>1、用户不允许网页使用位置</p><p>2、浏览器不支持HTML5中的定位</p><p>3、PC浏览器没有手机浏览器定位成功率高,因为手机例如iPhone上可以获取GPS信息</p><p>&nbsp;</p><p>所以,这个活动在定位失败时,会自动定位到深圳一个点。</p><p>难道这就是活动里介绍的,&ldquo;邂逅泷泽萝拉&rdquo;麽?呵呵,关掉定位就好了啊。</p><p>不过我也百度了一下,泷泽萝拉的确是在这个位置这个时间,给该品牌做了带盐,还穿着夜光衣&hellip;&hellip;</p><p>还在百度上发现了一个秘密,这女孩儿是92年的&hellip;&hellip;混血&hellip;&hellip;女明星&hellip;&hellip;</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011456219382656.png" alt="" /></p><p>&nbsp;</p><p><strong>二、地图页面</strong></p><p><strong>1、添加覆盖物</strong></p><p>&nbsp;当用户允许位置使用,并且定位成功的话,就会自动定位到用户的地点。</p><p>这时会显示周围有哪些marker,这里的marker都是分男女的,是2种不同的marker,通过更改图片url即可实现。</p><p>覆盖物代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">//</span><span style="color: #008000;">实例化点标记</span><br/><span style="color: #0000ff;">function</span><span style="color: #000000;"> addMarker(){<br/> marker </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> AMap.Marker({ <br/> icon:</span>"marker-female.png", <span style="color: #008000;">//</span><span style="color: #008000;">换图片即可实现男女marker喔</span><br/> position:<span style="color: #0000ff;">new</span> AMap.LngLat(110.405467,39.927761<span style="color: #000000;">)<br/> });<br/> marker.setMap(mapObj); </span><span style="color: #008000;">//</span><span style="color: #008000;">在地图上添加点</span><br/>}</div><p>&nbsp;</p><p>覆盖物的添加逻辑,用到的是云图的多边形检索,将多边形设置为当前屏幕可视范围。</p><p><span style="line-height: 1.5;">获取可视区域,用mapObj.getBounds()。</span>然后得到西南角(左下角)和东北角(右上角)。</p><p>用<span style="background-color: #cc99ff;">2点即可构建一个矩形</span>,是不是超级方便!!可视区域云检索代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">//</span><span style="color: #008000;">多边形检索函数 </span><br/><span style="color: #0000ff;">function</span><span style="color: #000000;"> cloudSearch() {<br/> </span><span style="color: #0000ff;">var</span> curView = mapObj.getBounds(); <span style="color: #008000;">//</span><span style="color: #008000;">获取可视区域</span><br/><span style="color: #000000;"> mapObj.clearMap();<br/> </span><span style="color: #0000ff;">var</span> arr = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Array();<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">绘制多边形 </span><br/> arr.push(curView.getSouthWest()); <span style="color: #008000;">//</span><span style="color: #008000;">获取左下角</span><br/> arr.push(curView.getNorthEast()); <span style="color: #008000;">//</span><span style="color: #008000;">获取右上角</span><br/> <span style="color: #0000ff;">var</span><span style="color: #000000;"> search;<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">加载CloudDataSearch服务插件</span><br/> mapObj.plugin(["AMap.CloudDataSearch"], <span style="color: #0000ff;">function</span><span style="color: #000000;">() { <br/> search </span>= <span style="color: #0000ff;">new</span> AMap.CloudDataSearch('【您的云图tableid】'); <span style="color: #008000;">//</span><span style="color: #008000;">构造云数据检索类</span><br/> AMap.event.addListener(search, "complete", cloudSearch_CallBack); <span style="color: #008000;">//</span><span style="color: #008000;">查询成功时的回调函数</span><br/> AMap.event.addListener(search, "error", errorInfo); <span style="color: #008000;">//</span><span style="color: #008000;">查询失败时的回调函数</span><br/> search.searchInPolygon(arr); <span style="color: #008000;">//</span><span style="color: #008000;">多边形检索,自动变成矩形。</span><br/><span style="color: #000000;"> });<br/>}</span></div><p>&nbsp;</p><p><strong>2、覆盖物动画</strong></p><p>点击地图上的男女图标,都会跳动一下。这里用到的是覆盖物动画。代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">function</span><span style="color: #000000;"> cartoon(){<br/> marker.setAnimation(</span>'AMAP_ANIMATION_BOUNCE'); <span style="color: #008000;">//</span><span style="color: #008000;">设置点标记的动画效果,此处为弹跳效果</span><br/>}</div><p>&nbsp;</p><p>延时2秒关闭覆盖物动画,代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">function</span><span style="color: #000000;"> closeCartoon(){<br/> marker.setAnimation(</span>'AMAP_ANIMATION_NONE'); <span style="color: #008000;">//</span><span style="color: #008000;">关闭动画 </span><br/><span style="color: #000000;">} <br/>setTimout(closeCartoon(),</span>2000); <span style="color: #008000;">//</span><span style="color: #008000;">延时2秒关闭动画</span></div><p>&nbsp;</p><p><strong>3、地图事件</strong></p><p>当屏幕扩大,覆盖物会增加;地图经过的地方,marker都会被保留,不会重复渲染。</p><p>这里给地图添加拖拽结束事件,当拖拽结束,就进行当前可视区域的云检索。地图事件代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">//地图移动结束,进行云检索</span><br/>AMap.event.addListener(mapObj,'moveend',<span style="color: #0000ff;">function</span><span style="color: #000000;">(){<br/> </span>cloudSearch();<br /><span>});</span></div><p>&nbsp;</p><p>如图:</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011507034692964.png" alt="" width="237" height="421" />&nbsp;<img src="http://images.cnitblog.com/blog/249635/201409/011507115786086.png" alt="" width="239" height="420" /></p><p>&nbsp;</p><p><strong>4、麻点图</strong></p><p>当地图越缩越小,图标达到一定数量后,就会密密麻麻看不见。</p><p>于是,麻点图派上了用场。</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011553310009790.png" alt="" /></p><p>&nbsp;</p><p>代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">//</span><span style="color: #008000;">加载云图层插件</span><br/><span style="color: #0000ff;">function</span><span style="color: #000000;"> addCloudLayer() { <br/> mapObj.plugin(</span>'AMap.CloudDataLayer', <span style="color: #0000ff;">function</span> () {<br /><span style="color: #0000ff;">    var</span> cloudDataLayer = <span style="color: #0000ff;">new</span> AMap.CloudDataLayer('<span style="background-color: #ffff00;">【您的云图tableid】</span>'); <span style="color: #008000;">//</span><span style="color: #008000;">实例化云图层类</span><br/> cloudDataLayer.setMap(mapObj); <span style="color: #008000;">//</span><span style="color: #008000;">叠加云图层到地图 </span><br/> AMap.event.addListener(cloudDataLayer, 'click', <span style="color: #0000ff;">function</span><span style="color: #000000;"> (result) {<br/> cartoon();</span><span style="color: #008000;">//</span><span style="color: #008000;">marker动画,谈起tip</span><br/><span style="color: #000000;">   });<br/>}</span></div><p>&nbsp;</p><p><strong>5、地址解析</strong></p><p>在地图顶部,有个小小的横条,里面有地图中心点的位置信息。这里就是用的地址解析。代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">var</span> lnglatXY = mapObj.getCenter(); <span style="color: #008000;">//</span><span style="color: #008000;">获取地图中心点</span><br/><span style="color: #0000ff;">function</span><span style="color: #000000;"> geocoder() {<br/> </span><span style="color: #0000ff;">var</span><span style="color: #000000;"> MGeocoder;<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">加载地理编码插件</span><br/> mapObj.plugin(["AMap.Geocoder"], <span style="color: #0000ff;">function</span><span style="color: #000000;">() { <br/> MGeocoder </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> AMap.Geocoder({<br/> radius: </span>1000<span style="color: #000000;">,<br/> extensions: </span>"all"<span style="color: #000000;"><br/> });<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">返回地理编码结果</span><br/> AMap.event.addListener(MGeocoder, "complete"<span style="color: #000000;">, geocoder_CallBack);<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">逆地理编码</span><br/><span style="color: #000000;"> MGeocoder.getAddress(lnglatXY);<br/> });<br/>}<br/></span><span style="color: #008000;">//</span><span style="color: #008000;">回调函数</span><br/><span style="color: #0000ff;">function</span><span style="color: #000000;"> geocoder_CallBack(data) {<br/> </span><span style="color: #008000;">//</span><span style="color: #008000;">返回地址描述</span><br/> <span style="color: #0000ff;">var</span> address =<span style="color: #000000;"> data.regeocode.formattedAddress;<br/> alert(address);</span></div><p>&nbsp;</p><p><strong>&nbsp;三、内容选择页面</strong></p><p><strong>1、云存储</strong></p><p>用户选择完毕详情内容,点提交按钮,就发起请求。</p><p>这里使用云存储接口,官方说明:<a href="http://lbs.amap.com/yuntu/reference/cloudstorage/#yuntureference_creatdata" target="_blank">http://lbs.amap.com/yuntu/reference/cloudstorage/#yuntureference_creatdata</a></p><p><img src="http://images.cnitblog.com/blog/249635/201409/011618032195053.png" alt="" width="225" height="391" />&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>上图中有&ldquo;女生,室外&rdquo;等选项,那么发送请求代码为:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">http:<span style="color: #008000;">//</span><span style="color: #008000;">yuntuapi.amap.com/datamanage/data/create?key=<span style="background-color: #ffff00;">【用户key】</span>&amp;tableid=<span style="background-color: #ffff00;">【云图tableid】</span>&amp;data=["name":"1","location":"116,39","sex":"female","place":"outside","other":"tt|drag","star":"5","healthy":"130"]</span></div><p>&nbsp;</p><p>根据选项设计数据库结构:</p><table style="width: 100%;" border="1" cellspacing="0" cellpadding="0"><tbody><tr><td colspan="2" valign="top" width="34%"><p>名称</p></td><td colspan="2" valign="top" width="50%"><p>说明</p></td><td valign="top" width="15%"><p>是否必填</p></td></tr><tr><td colspan="2" valign="top" width="34%"><p>Key</p></td><td colspan="2" valign="top" width="50%"><p>客户唯一标识</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td colspan="2" valign="top" width="34%"><p>Tableid</p></td><td colspan="2" valign="top" width="50%"><p>数据表唯一标识</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td colspan="2" valign="top" width="34%"><p>data</p></td><td colspan="2" valign="top" width="50%"><p>数据</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_name</p></td><td colspan="2" valign="top" width="50%"><p>数据名称(用id号)</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_location</p></td><td colspan="2" valign="top" width="50%"><p>坐标</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_sex</p></td><td colspan="2" valign="top" width="50%"><p>性别</p><p>男:male</p><p>女:female</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_place</p></td><td colspan="2" valign="top" width="50%"><p>家:home</p><p>酒店:hotel</p><p>室外:outdoor</p><p>车内:car</p><p>船上:boat</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_other</p></td><td colspan="2" valign="top" width="50%"><p>其他准备</p><p>至少选1个</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>&nbsp;</p></td><td valign="top" width="20%"><p>_tt</p></td><td valign="top" width="30%"><p>安全套</p><p>Yes:1</p><p>No:0</p></td><td valign="top" width="15%"><p>&nbsp;</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>&nbsp;</p></td><td valign="top" width="20%"><p>_bath</p></td><td valign="top" width="30%"><p>洗澡</p><p>Yes:1</p><p>No:0</p></td><td valign="top" width="15%"><p>&nbsp;</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>&nbsp;</p></td><td valign="top" width="20%"><p>_hottea</p></td><td valign="top" width="30%"><p>热水</p><p>Yes:1</p><p>No:0</p></td><td valign="top" width="15%"><p>&nbsp;</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>&nbsp;</p></td><td valign="top" width="20%"><p>_drag</p></td><td valign="top" width="30%"><p>药物</p><p>Yes:1</p><p>No:0</p></td><td valign="top" width="15%"><p>&nbsp;</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>&nbsp;</p></td><td valign="top" width="20%"><p>_nothing</p></td><td valign="top" width="30%"><p>什么都没有</p><p>Yes:1</p><p>No:0</p></td><td valign="top" width="15%"><p>&nbsp;</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_stars</p></td><td colspan="2" valign="top" width="50%"><p>用户打分,满意度</p><p>值:5,4,3,2,1</p><p>类型:number</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_healthy</p></td><td colspan="2" valign="top" width="50%"><p>幸福指数</p><p>计算规则详见5、幸福指数计算</p><p>类型:number</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_level</p></td><td colspan="2" valign="top" width="50%"><p>击败百分之多少的人,计算规则详见6、等级计算规则</p><p>类型:number</p></td><td valign="top" width="15%"><p>是</p></td></tr><tr><td valign="top" width="7%"><p>&nbsp;</p></td><td valign="top" width="27%"><p>_duihuanma</p></td><td colspan="2" valign="top" width="50%"><p>兑换码</p><p>类型:string</p></td><td valign="top" width="15%"><p>否</p></td></tr></tbody></table><p>&nbsp;</p><p><strong>2、云检索</strong></p><p>点击一个marker,谈起tip;点击tip则到详情页面。</p><p>这里用的是云检索中的ID检索。</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011641278287736.png" alt="" width="217" height="389" />&nbsp;<img src="http://images.cnitblog.com/blog/249635/201409/011619087971519.png" alt="" width="218" height="390" /></p><p>&nbsp;</p><p>代码:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #008000;">//</span><span style="color: #008000;">根据数据id查询数据详情</span><br/><span style="color: #0000ff;">function</span><span style="color: #000000;"> cloudSearch(){<br/> mapObj.clearMap(); <br/> </span><span style="color: #0000ff;">var</span><span style="color: #000000;"> search;<br/> mapObj.plugin([</span>"AMap.CloudDataSearch"], <span style="color: #0000ff;">function</span><span style="color: #000000;">() { <br/> search </span>= <span style="color: #0000ff;">new</span> AMap.CloudDataSearch('<span style="background-color: #ffff00;">【云图tableid】</span>'); <span style="color: #008000;">//</span><span style="color: #008000;">构造云数据检索类</span><br/> AMap.event.addListener(search, "complete", cloudSearch_CallBack); <span style="color: #008000;">//</span><span style="color: #008000;">查询成功时的回调函数</span><br/> AMap.event.addListener(search, "error", errorInfo); <span style="color: #008000;">//</span><span style="color: #008000;">查询失败时的回调函数</span><br/> search.searchById("1"); <span style="color: #008000;">//</span><span style="color: #008000;">根据id查询</span><br/><span style="color: #000000;"> });<br/>}</span></div><p>&nbsp;</p><p><strong><span style="line-height: 1.5;">四、得分页面</span></strong></p><p><span style="line-height: 1.5;">最后一个得分页面,与LBS没有太多关系。但里面也有几个点可以拿来说一说。</span></p><p><span style="line-height: 1.5;">一是分数计算,在内容选择页面发起请求时,根据每个选项的得分不同,就计算好分数,然后云存储时,直接把分数发送出去。</span></p><p><span style="line-height: 1.5;">二是中奖规则,中奖规则应该由后台给出,这样可以防止作弊。</span></p><p><span style="line-height: 1.5;">三是微博话题,看了看这个活动只有客观选择题,没有主观题,可能是为了规避敏感词的法律风险。所以,把讨论都放在了微博话题里,并设置了微博抽奖。</span></p><p><span style="line-height: 1.5;">四是微信分享,微信中的分享必须调起native组件,所以做了一个界面提示用户去点击即可。</span></p><p><span style="line-height: 1.5;"><img src="http://images.cnitblog.com/blog/249635/201409/011654348285237.png" alt="" width="232" height="416" />&nbsp;<img src="http://images.cnitblog.com/blog/249635/201409/011657196256357.png" alt="" width="232" height="416" /></span></p><p>&nbsp;</p><p><strong><span style="line-height: 1.5;">五、高分秘籍!!</span></strong></p><p><span style="line-height: 1.5;">技术部分已经结束,这里是我玩游戏的心得,算是攻略吧。</span></p><p><span style="line-height: 1.5;">我是这样玩的,先选最少的选项。多选里面,每次只选1个,这样很容易确定多选中的分数。</span></p><p><span style="line-height: 1.5;">但是药物那个不知道具体分数,好像每次都不太一样。</span></p><table style="width: 100%;" border="1" cellspacing="0" cellpadding="0"><tbody><tr><td colspan="2" valign="top" width="50%"><p>项目</p></td><td valign="top" width="49%"><p>分数</p></td></tr><tr><td valign="top" width="25%"><p>&nbsp;</p></td><td valign="top" width="25%">&nbsp;</td><td valign="top" width="49%">&nbsp;</td></tr><tr><td valign="top" width="25%"><p>其他准备</p></td><td valign="top" width="25%"><p>TT</p></td><td valign="top" width="49%"><p>30</p></td></tr><tr><td valign="top" width="25%"><p>&nbsp;</p></td><td valign="top" width="25%"><p>洗澡</p></td><td valign="top" width="49%"><p>10</p></td></tr><tr><td valign="top" width="25%"><p>&nbsp;</p></td><td valign="top" width="25%"><p>热水</p></td><td valign="top" width="49%"><p>5</p></td></tr><tr><td valign="top" width="25%"><p>&nbsp;</p></td><td valign="top" width="25%"><p>药物</p></td><td valign="top" width="49%"><p>每次都不太一样</p></td></tr><tr><td valign="top" width="25%"><p>&nbsp;</p></td><td valign="top" width="25%"><p>Nothing</p></td><td valign="top" width="49%"><p>-5</p></td></tr><tr><td valign="top" width="25%"><p>&nbsp;</p></td><td valign="top" width="25%">&nbsp;</td><td valign="top" width="49%">&nbsp;</td></tr></tbody></table><p>&nbsp;</p><p>微博上有人晒分数,居然有个玩家得了134!!!好高的分数,我从来没玩出来过&hellip;&hellip;桑心&hellip;&hellip;</p><p><img src="http://images.cnitblog.com/blog/249635/201409/011709130473770.jpg" alt="" width="306" height="544" /></p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949295" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/milkmap/p/3949295.html" target="_blank">【高德地图API】汇润做爱地图技术大揭秘</a>,转载请注明。</p>http://www.cnblogs.com/xiaofeixiang/p/3943890.htmljQuery开发插件的两种方式 - Fly_Elephant最近挺多人写jQuery的,都是关于jQuery扩展方面的,使用方面的讲的比较多,但是关于详细的一个基础的过程讲的比较少一点,做web开发的基本上都会用到jQuery,本人就根据jQuery的使用经验讲讲插件开发。jQuery插件开发两种方式:一种是类扩展的方式开发插件,jQuery添加新的全局函数...2014-09-01T08:46:00Z2014-09-01T08:46:00ZFly_Elephanthttp://www.cnblogs.com/xiaofeixiang/<p><span style="font-family: 'Microsoft YaHei'; font-size: 16px;">最近挺多人写jQuery的,都是关于jQuery扩展方面的,使用方面的讲的比较多,但是关于详细的一个基础的过程讲的比较少一点,做web开发的基本上都会用到jQuery,本人就根据jQuery的使用经验讲讲插件开发。</span><span style="font-family: 'Microsoft YaHei'; font-size: 16px;">jQuery插件开发两种方式:</span><span style="font-family: 'Microsoft YaHei'; font-size: 16px;">一种是类扩展的方式开发插件,jQuery添加新的全局函数(jQuery的全局函数是属于jQuery命名空间的函数),如果将jQuery看成一个类,那么就相当于给jQuery类本身添加方法。第二种是对象扩展的方式开发插件,即jQuery对象添加方法。</span></p><p><strong><span style="font-family: 'Microsoft YaHei';">类扩展的插件</span></strong></p><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;">类扩展的插件开发最直接的理解就是给jQuery类添加类方法,可以理解为添加静态方法。典型的例子就是$.AJAX()这个函数,将函数定义于jQuery的命名空间中。关于类扩展的插件开发可以采用如下几种形式进行扩展:</span></p><p><span style="font-family: 'Microsoft YaHei';"><strong>&nbsp;1.添加全局函数</strong></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $.ltrim = function( str ) {<br/> return str.replace( /^\s+/, "" );<br/> };<br/></div><p> <span style="font-family: 'Microsoft YaHei'; font-size: 16px;"> <span style="font-size: 14px;">调用方式</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> var str=" 去除左空格 ";<br/> console.log("去除前:"+str.length+"去除后:"+$.ltrim(str).length);<br/></div><p><span style="font-size: 14px;"><strong><span style="font-family: 'Microsoft YaHei';"> 2.添加多个全局函数</span></strong></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $.ltrim = function( str ) {<br/> return str.replace( /^\s+/, "" );<br/> };<br/> <br/> $.rtrim = function( str ) {<br/> return str.replace( /\s+$/, "" );<br/> };<br/></div><p><span style="font-family: 'Microsoft YaHei'; font-size: 15px;"><strong> <span style="font-size: 14px;">上面那种如果你写的全局函数比较少的情况下使用挺好,如果多的话建议使用&nbsp;使用$.extend(object)</span></strong></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $.extend({<br/> ltrim:function( str ) {<br/> return str.replace( /^\s+/, "" );<br/> },<br/> rtrim:function( str ) {<br/> return str.replace( /\s+$/, "" );<br/> }<br/> });<br/></div><p><span style="font-size: 14px;"><strong><span style="font-family: 'Microsoft YaHei';"> 3.独立的命名空间</span></strong></span></p><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;">虽然在jQuery命名空间中,我们禁止使用了大量的javaScript函数名和变量名。但是仍然不可避免某些函数或变量名将于其他jQuery插件冲突,因此我们习惯将一些方法封装到另一个自定义的命名空间。</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $.myPlugin={<br/> ltrim:function( str ) {<br/> return str.replace( /^\s+/, "" );<br/> },<br/> rtrim:function( str ) {<br/> return str.replace( /\s+$/, "" );<br/> }<br/> };<br/></div><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;">使用独立的插件名,可以避免命名空间内函数的冲突,调用方式:</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> var str=" 去除左空格 ";<br/> console.log("调用前:"+str.length+"调用后:"+$.myPlugin.ltrim(str).length);</div><p><strong><span style="font-family: 'Microsoft YaHei';"><strong>对象扩展的插件</strong></span></strong></p><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;"><strong>1.添加一个对象扩展方法</strong></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $.fn.changeColor= function() {<br/> this.css( "color", "red" );<br/>};<br/> $.fn.changeFont= function() {<br/> this.css( "font-size", "24px" );<br/>};</div><p>  <span style="font-family: 'Microsoft YaHei'; font-size: 14px;">调用方式:</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $(function () {<br/> $("a").showColor();<br />&nbsp; $("div").changeFont();<br/> }); <br/></div><p><strong><span style="font-family: 'Microsoft YaHei'; font-size: 16px;"> <span style="font-size: 14px;">2.添加多个对象扩展方法</span></span></strong></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> (function($){<br/> $.fn.changeColor= function() {<br/> this.css( "color", "red" );<br/>};<br/> $.fn.changeFont=function() {<br/> this.css( "font-size", "24px" );<br/>};<br/> })(jQuery);<br/></div><p><span style="font-size: 14px;">  <span style="font-family: 'Microsoft YaHei';">兼容写法(防止前面的函数漏写了;):</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">;(function($){<br/> $.fn.changeColor= function() {<br/> this.css( "color", "red" );<br/>};<br/> $.fn.changeFont=function() {<br/> this.css( "font-size", "24px" );<br/>};<br/> })(jQuery);<br/></div><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;"> &nbsp; 上面都定义了一个jQuery函数,形参是$,函数定义完成之后,把jQuery这个实参传递进去.立即调用执行。这样的好处是,我们在写jQuery插件时,也可以使用$这个别名,而不会与prototype引起冲突.</span></p><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;"><strong>3. 使用$.fn.extend(object)</strong></span></p><p><span style="font-family: 'Microsoft YaHei'; font-size: 16px;">题外话,查看jQuery源码(版本1.11.1)可以看到:</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">jQuery.fn = jQuery.prototype = {<br/>// The current version of jQuery being used<br/>jquery: version,<br/>constructor: jQuery,<br/>......................<br/>},</div><p><span style="font-size: 14px;"><span style="font-family: 'Microsoft YaHei';">jQuery是一个封装得非常好的类,比如语句$("a")&nbsp;会生成一个&nbsp;jQuery类的实例。</span><span style="font-family: 'Microsoft YaHei';">jQuery.fn.extend(object)实际上是对jQuery.prototype进得扩展,就是为jQuery类添加&ldquo;成员函数&rdquo;。jQuery类的实例可以使用这个&ldquo;成员函数&rdquo;。</span></span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"> $.fn.extend({<br/> changeColor:function() {<br/> this.css( "color", "red" );<br/>},<br/>changeFont:function() {<br/> this.css( "font-size", "24px" );<br/>}<br/>});<br/></div><p><span style="font-family: 'Microsoft YaHei'; font-size: 14px;"> 介绍了基本是关于对象扩展的基础的用法,下面开发一个简单的类似于代码高亮的功能,如下:</span></p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">(function($) {<br/> $.fn.highlight = function(options) {<br/> //插件参数的可控制性,外界可以修改默认参数<br/> var defaults=$.extend($.fn.highlight.defaults, options );<br/> //遍历函数,然后根据参数改变样式<br/> return this.each(function() {<br/> var elem = $( this ); <br/> var markup = elem.html();<br/> markup = $.fn.highlight.format( markup );<br/> elem.html(markup);<br/> elem.css({<br/> color: defaults.color,<br/> fontSize:defaults.fontSize,<br/> backgroundColor: defaults.backgroundColor<br/> });<br/> });<br/>};<br/>//参数默认值<br/>$.fn.highlight.defaults={<br/> color: "#556b2f",<br/> backgroundColor:"white",<br/> fontSize: "48px"<br/> };<br/>//格式化字体<br/>$.fn.highlight.format = function( txt ) {<br/> return "&lt;strong&gt;" + txt + "&lt;/strong&gt;";<br/>};<br/>})(jQuery);<br/><br/> $(function () {<br/> //调用插件<br/> $("a").highlight({color:"red",fontSize:"24px"});<br/> }); <br/></div><p><strong> 小结</strong></p><p><span style="font-family: 'Microsoft YaHei'; font-size: 16px;">jQuery这两种插件开发的使用,需要根据开发过程中的具体情况而定,第一种类扩展的方法类似于C#添加一个静态方法,第二种对象扩展主要是根据自己的实际业务而确定的,你的网站有些地方常用的功能肯定可以自己写成一个插件,比如说图片的查看,侧边栏的点击,有的时候你同样可以研究网上别人写的插件,也可以学到不少东西.</span></p><p><span style="font-family: 'Microsoft YaHei';"><span style="font-size: 15.555556297302246px; line-height: 26.666667938232422px;">如果你觉得本文还不错,有所收货,给个推荐吧,多谢~</span></span></p><p>  </p><img src="http://counter.cnblogs.com/blog/rss/3943890" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/xiaofeixiang/p/3943890.html" target="_blank">jQuery开发插件的两种方式</a>,转载请注明。</p>http://www.cnblogs.com/nbjk/p/3949494.htmlAndroid Xposed框架中创建模块的指导手册 - 处处动人当然,你可以去学习如何创建一个Xposed模块。所以你可以阅读这篇教程(官方教程)去学习怎样解决这个问题。这不仅仅讲解如何新建模块、如何编写模块,我们要往更深处思考,为什么按照这些步骤,为什么要新建这个类。如果你是“TL博士”那样的人,那么可以直接阅读"MakingtheprojectanXpose...2014-09-01T08:34:00Z2014-09-01T08:34:00Z处处动人http://www.cnblogs.com/nbjk/<p>当然,你可以去学习如何创建一个Xposed模块。所以你可以阅读这篇教程(官方教程)去学习怎样解决这个问题。这不仅仅讲解如何新建模块、如何编写模块,我们要往更深处思考,为什么按照这些步骤,为什么要新建这个类。如果你是&ldquo;TL博士&rdquo;那样的人,那么可以直接阅读"Making&nbsp;the&nbsp;project&nbsp;an&nbsp;Xposed&nbsp;module"&nbsp;这一章节。如果你想看完整个教程那么你需要很好的理解能力。你将会花费时间去阅读这篇文章,因为你不能但靠自己解决任何的问题。</p><p><strong>here)。综观类XposedBridge,你可以看到的main&nbsp;方法。这就是我上面写的,这个类会在进程启动之前被调用。在那时候执行一些初始化和模块的加载(我会在后面讲解模块的加载)。</strong></p><p>&nbsp;</p><p><strong>这里下载XposedBridgeApi-.jar&nbsp;。把它复制到子文件夹名为lib目录下。然后在其上单击右键,选择Build&nbsp;Path&nbsp;=&gt;&nbsp;Add&nbsp;to&nbsp;Build&nbsp;Path&nbsp;。你需要将版本名插入到xposedminversion的声明清单中。<a href="http://www.nibao.net/sitemap.html" target="_blank">转自</a></strong></p><p>&nbsp;</p><p>可以选择库引用的方式。但是确保你的API类被正确地编译到APK文件中&nbsp;,否则你会得到一个IllegalAccessError&nbsp;。通过引用libs&nbsp;文件(有&ldquo;s&rdquo;),通过Eclipse的简单设置可以不用把XposedBridgeApi-.jar&nbsp;包含进去。</p><p><strong>here&nbsp;)并且浏览他。但这官方ROM可能与你的不一样,但在这种情况下,它是一个类似甚至相同的实现。第一,我想看看AOSP,看看是否是一样的。如果我需要更多的细节,看看实际的反编译代码。&nbsp;</strong></p><p>&nbsp;</p><p>你可以寻找与&ldquo;时钟&rdquo;类名称或包含该字符串的类。下一步就是,寻找他所使用的资源和布局。如果您下载了官方AOSP的代码,就可以开始在这里开始寻找:frameworks/base/packages/SystemUI&nbsp;。你会发现不少地方出现&ldquo;时钟&rdquo;。这是正常的,的确会有不同的方式来实现修改。请记住,你仅仅可以hook方法&nbsp;。所以,你必须要找到一个可以在他之前、之后、或全部替换可以插入一些代码的地方。你应该hook&nbsp;住尽可能具体的方法,而不是那些会被调用上千次的方法,去避免性能问题和意想不到的副作用。&nbsp;</p><p>在这种情况下,您可能会发现这个layout布局res/layout/status_bar.xml&nbsp;包含了一个自定义视图类:com.android.systemui.statusbar.policy.Clock。多个想法可能会现在你的头脑中。文字颜色的定义是通过textAppearance属性,所以最简单的方法就是改变它,将会改变外观的定义&nbsp;。然而,这可能有效也可能无效(因为它可能存在于更深的native&nbsp;代码中)。更换布局状态栏将是可能的,但是你们只可以做最小的变化去更改他,相反,看看这个类。有一个叫updateClock方法,它看上去会被每分钟调用去更新时间</p><p>&nbsp;<img src="http://img.blog.csdn.net/20140825205709609?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29tZW9uaGFja2Vy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" /></p><p>看起来完美的修改,因为它是这似乎是唯一设置文本时钟的非常具体的方法。假如我们改变了这个clock的颜色或者字体,那么任何调用这个方法的都会受此影响。就达成我们的需求了,我们立刻行动.</p><p>(单独的文本颜色,这里有一种更好的方式.看到&ldquo;修改布局&rdquo;的例子在&nbsp;"Replacing&nbsp;resources".)</p><p><strong>helper&nbsp;类的一个方法。请注意,它是静态导入的,如果你配置了它描述的链接页面就会自动添加&nbsp;。此方法通过ClassLoader&nbsp;在ClassLoader&nbsp;包中查找Clock类&nbsp;。然后,它会在里面寻找updateClock方法。如果这种方法有任何参数,那你就必须列出这些参数的类型。不同的情况不一样的处理,但我们的方法没有任何参数,可以跳过这个假设。作为最后一个参数,你需要提供XC_MethodHook类的实现。对于较小的改动,就可以使用一个匿名类。如果你有太多的代码,最好创建一个普通的类,只在这里创建实例。随后,helper&nbsp;将尽一切方法hook住以上的函数。&nbsp;</strong></p><p>&nbsp;</p><p>你可以重写XC_MethodHook的两个方法。您可以同时覆盖,甚至不做操作,但后者是完全没有意义的。这两个方法是beforeHookedMethod和afterHookedMethod。这不是太难猜测,这两个方法会在原始的方法的之前和之后执行。您可以使用beforeHookedMethod&nbsp;方法来评价/篡改方法调用的参数(通过param.args)&nbsp;,甚至阻止调用原来的方法(发送自己的结果)。afterHookedMethod&nbsp;方法可以用来做基于原始方法的结果的事情。您还可以用它来操纵结果&nbsp;。当然,你可以添加自己的代码,它将会准确地在原始方法的前或后执行。</p><p>(如果你想完全取代方法,看看子类XC_MethodReplacement相反,你只需要覆盖replaceHookedMethod&nbsp;)</p><p>XposedBridge保留着一个记录了每个已经hook了的函数的注册回调函数&nbsp;的列表。那些具有最高优先级(如hookMethod定义)会首先调用。原始方法始终是优先级最低的。所以,假如你hook了一个函数并注册了回调A(PRIO高点)和B(PRIO默认值),那么每当hook的方法被调用,控制流将是这样的:A.before&nbsp;-&nbsp;&gt;&nbsp;B.before&nbsp;-&nbsp;&gt;原始的方法&nbsp;-&nbsp;&gt;&nbsp;B.after&nbsp;-&nbsp;&gt;&nbsp;A.after。因此,A修改了的参数,B是可以看到的,这样可以在传递给原始方法之前多步地改变它。原方法的结果首先会被B处理,但是这个原始方法最终返回的结果是由A来决定的。</p><p><strong>九、最后一个步骤:执行自己的代码在方法调用之前/之后&nbsp;</strong></p><p>好了,你现在有一个每次updateClock&nbsp;调用时,都会被调用的方法,而且可以精确到原始方法的前后(你已经在SystemUI&nbsp;的进程里面了)。现在,让我们来修改一些东西。&nbsp;</p><p>首先要检查:我们有没有得到具体的时钟对象?是的,我们有,它在param.thisObject参数里。因此,如果该方法被myClock.updateClock()调用,然后param.thisObject将会使myClock这个对象。&nbsp;</p><p>下一步:我们可以做什么用的时钟?这个Clock&nbsp;类是不可以利用的,你可以不转换param.thisObject变成类(甚至不要去尝试)。然而,它继承自TextView的。所以,你可以使用像的setText,gettext和setTextColor的方法,一旦你已经把Clock引用映射成TextView。这些改变应该在原始方法调用后去设置新的时间。由于在方法调用前没有事做,我们就不考虑&nbsp;beforeHookedMethod。调用&nbsp;(empty)&nbsp;"super"&nbsp;方法是没有必要的。所以不要重写这方法。</p><p>这是完整的源代码&nbsp;:</p><p>&nbsp;<img src="http://img.blog.csdn.net/20140825205718532?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY29tZW9uaGFja2Vy/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" /></p><p><strong>十、对结果满意&nbsp;</strong></p><p>现在安装/重新启动您的应用程序。正如你在运行之前已经在XposedInstaller&nbsp;启用了它,你就不需要再来一次了,重新启动就足够了。不过,如果你想使用它停用这个红色时钟的例子。两者都使用缺省的优先级给他们的updateClock处理程序,那么你不知道哪一个会胜出(它实际上取决于处理方法的字符串表示形式,但并不依赖于此)。</p><p><strong>十一、结论</strong></p><p>我知道,这个教程很长。但我希望你现在不仅可以实现一个绿色的时钟,还可以实现和这个完全不同的东西。找到好的方法来hook是一个经验上的问题,所以开始的东西比较容易。尝试刚开始就多使用日志功能去确保被调用的是预期的事件。现在:玩得开心!</p><img src="http://counter.cnblogs.com/blog/rss/3949494" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/nbjk/p/3949494.html" target="_blank">Android Xposed框架中创建模块的指导手册</a>,转载请注明。</p>http://www.cnblogs.com/nyzhai/p/3949484.htmlMongodb集群搭建过程及常见错误 - 翟中龙Replica SetsMongoDB 支持在多个机器中通过异步复制达到故障转移和实现冗余。多机器中同一时刻只 有一台是用于写操作。正是由于这个情况,为 MongoDB 提供了数据一致性的保障。担当Primary 角色的机器能把读操作分发给 slave。Replica Sets的结构非常类似一个集群...2014-09-01T08:30:00Z2014-09-01T08:30:00Z翟中龙http://www.cnblogs.com/nyzhai/<div>Replica Sets</div><div>MongoDB 支持在多个机器中通过异步复制达到故障转移和实现冗余。多机器中同一时刻只 有一台是用于写操作。正是由于这个情况,为 MongoDB 提供了数据一致性的保障。担当<span class="Apple-converted-space">&nbsp;Primary 角色的机器能把读操作分发给 slave。</span></div><div>Replica Sets的结构非常类似一个集群。因 为它确实跟集群实现的作用是一样的, 其中一个节点如果出现故障, 其它节点马上会将业务接过来而无须停机操作。</div><div>下面以本机为例介绍一下集群的部署过程,以及部署过程中常见的注意点及错误</div><div>本例环境是Linux操作系统,mongodb版本:mongodb-linux-x86_64-2.6.1.tgz,Vmwre虚拟机,虚拟机IP:192.168.169.129,集群以本机不同端口模拟三台服务器。</div><div>1.集群主要分为三个节点master主节点,slaver备用节点,arbiter仲裁节点</div><div>建立数据文件夹</div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">mkdir -p /mongodb/data/master<br/>mkdir -p /mongodb/data/slaver<br/>mkdir -p /mongodb/data/arbiter<br/></div><p>&nbsp;</p>ps:三个目录分别对应主,备,仲裁节点</div><div>2.建立配置文件夹</div><div>1)master.conf</div><div>&nbsp; &nbsp; 打开编辑器:</div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">vi /etc/master.conf<br/></div><p>按i 输入下列配置</p></div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">dbpath=/home/mongodb/data/master <br/>logpath=/home/mongodb/log/master.log<br/>logappend=true<br/>replSet=rep1<br/>port=10000<br/>fork=true<br/>journal=true<br/></div><p>完成之后按esc &nbsp;》》 : &nbsp;&gt;&gt;wq&gt;&gt;回车</p></div><div>2)slaver.conf</div><div>编辑器打开和保存按上边的步骤,下边只写详细内容</div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">dbpath=/home/mongodb/data/slaver<br/>logpath=/home/mongodb/log/slaver.log<br/>logappend=true<br/>replSet=rep1<br/>port=10001<br/>fork=true<br/>journal=true<br/></div><p>3)arbiter.conf</p></div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">dbpath=/home/mongodb/data/arbiter<br/>logpath=/home/mongodb/log/arbiter.log<br/>logappend=true<br/>replSet=rep1<br/>port=10002<br/>fork=true<br/>journal=true<br/>smallfiles=true<br/></div><p>参数解释:</p></div><div><div><div><div><div><div><p>dbpath:数据存放目录</p><p>logpath:日志存放路径</p><p>logappend:以追加的方式记录日志</p><p>replSet:replica set的名字</p><p>port:mongodb进程所使用的端口号,默认为27017</p><p>fork:以后台方式运行进程</p><p>journal:写日志</p><p>smallfiles:当提示空间不够时添加此参数</p><p>其他参数</p><p>pidfilepath:进程文件,方便停止mongodb</p><p>directoryperdb:为每一个数据库按照数据库名建立文件夹存放</p><p>bind_ip:mongodb所绑定的ip地址</p><p>oplogSize:mongodb操作日志文件的最大大小。单位为Mb,默认为硬盘剩余空间的5%</p><p>noprealloc:不预先分配存储</p><p>3.启动Mongodb&nbsp;&nbsp;&nbsp;</p><div><div><div><div><div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">cd /home/mongodb/bin<br/></div><p>&nbsp;<img src="http://images.cnitblog.com/blog/467150/201409/011626362033499.x-png" alt="" /></p><p>启动服务</p></div></div></div></div></div></div></div></div></div></div></div></div><div><div><div><div><div><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;">./mongod -f /etc/master.conf<br/><br/>./mongod -f /etc/slaver.conf<br/><br/>./mongod -f /etc/arbiter.conf<br/></div><p>&nbsp;有这样的提示说明启动成功</p><p><img src="http://images.cnitblog.com/blog/467150/201409/011625510323533.x-png" alt="" /></p><p>如果是下列的提示说明启动失败</p><p><img src="http://images.cnitblog.com/blog/467150/201409/011626598752168.x-png" alt="" /></p><p>启动失败的原因有很多,检查完配置文件,如果没有错误,可打开相应的配置文件查看详细的错误信息</p><p>cat /etc/master.conf</p><p>最常见的一个错误就是磁盘空间不足,会提示这样的错误</p><p><img src="http://images.cnitblog.com/blog/467150/201409/011627226575507.x-png" alt="" /></p></div></div></div></div></div><div>因为Mongodb的日志文件是成2g的增长,所以所需空间比较大,这时你可以在配置文件里添加这样的一个配置</div><div>smallfiles=true。</div><div>全部三个服务全部启动成功之后</div><div><div><div><div><div><p>4.配置主(master),备(slaver),仲裁(arbiter)节点</p><div><div><div><div><div><p>可以通过客户端连接mongodb,也可以直接在三个节点中选择一个连接mongodb。</p><p>./mongo 192.168.169.129:10000 &nbsp; #ip和port是某个节点的地址</p><p>&gt;use admin</p></div></div></div></div></div></div></div></div></div></div><div>&gt;cfg={ _id:"rep1", members:[ {_id:0,host:'192.168.169.129:10000',priority:2}, {_id:1,host:'192.168.169.129:10001',priority:1},</div><div>{_id:2,host:'192.168.169.129:10002',arbiterOnly:true}] };</div><div>&gt;rs.initiate(cfg) #使配置生效</div><div><img src="http://images.cnitblog.com/blog/467150/201409/011627528757042.x-png" alt="" /></div><div>{</div><div>&nbsp; &nbsp; &nbsp; &nbsp; "set" : "rep1",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; "date" : ISODate("2014-09-05T02:44:43Z"),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; "myState" : 1,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; "members" : [</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "_id" : 0,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "name" : "192.168.169.129:10000",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "health" : 1,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "state" : 1,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "stateStr" : "PRIMARY",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "uptime" : 200,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "optime" : Timestamp(1357285565000, 1),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "optimeDate" : ISODate("2013-01-04T07:46:05Z"),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "self" : true</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "_id" : 1,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "name" : "192.168.169.129:10001",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "health" : 1,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "state" : 2,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "stateStr" : "SECONDARY",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "uptime" : 200,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "optime" : Timestamp(1357285565000, 1),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "optimeDate" : ISODate("2013-01-04T07:46:05Z"),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "lastHeartbeat" : ISODate("2013-01-05T02:44:42Z"),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "pingMs" : 0</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "_id" : 2,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "name" : "192.168.169.129:10002",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "health" : 1,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "state" : 7,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "stateStr" : "ARBITER",</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "uptime" : 200,</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "lastHeartbeat" : ISODate("2013-01-05T02:44:42Z"),</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "pingMs" : 0</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ],</div><div>&nbsp; &nbsp; &nbsp; &nbsp; "ok" : 1</div><div>}</div><div>&nbsp;</div><div>配置过程中可能还会出现其他的一些错误,不过都可以去查看相应的日志文件,去解决。</div><img src="http://counter.cnblogs.com/blog/rss/3949484" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/nyzhai/p/3949484.html" target="_blank">Mongodb集群搭建过程及常见错误</a>,转载请注明。</p>http://www.cnblogs.com/fengfenggirl/p/zhihu_shenhuifu.html如何找出知乎的所有神回复 - CodeMeals写一个爬虫,找出知乎的神回复2014-09-01T08:27:00Z2014-09-01T08:27:00ZCodeMealshttp://www.cnblogs.com/fengfenggirl/<p>  有时候看到神回复,感觉真是惊叹!先上几个看看</p><blockquote><p><a href="http://www.zhihu.com/question/24826063">你小时候父母为了让你能够努力学习,都用过什么丧尽天良的方法? - 知乎</a><br />给了我这张脸</p></blockquote><blockquote><p><a href="http://www.zhihu.com/question/24924044">求职者在答应企业面试后,没有任何说明而爽约是否妥当? - 知乎</a><br />你们企业也经常说&ldquo;回去等我们的电话吧&rdquo;然后没有任何下文啊。</p></blockquote><blockquote><p><a href="http://www.zhihu.com/question/20193012" target="_blank">什么叫见过大世面 ? -知乎</a></p><p>能享受最好的,能承受最坏的</p></blockquote><p>  神回复总是言简意赅,吐槽精准,回答到位,甚至是让人忍俊不禁,如何找到知乎的所有神回复,有人说找那些投票多并且回答字数少的answer,对于一个计算机的同学,不可能就是这样一条条去翻一吧,让我们写一个爬虫,抓下知乎的问题,每个问题保留投票最高的回答。首先需要一个得到问题列表,这里列表可以在http://www.zhihu.com/log/questions找到,爬问题列表的代码如下:  </p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">def</span> getQuestions(start,offset=<span style="color: #800000;">'</span><span style="color: #800000;">20</span><span style="color: #800000;">'</span><span style="color: #000000;">):<br/> </span><span style="color: #008000;">#</span><span style="color: #008000;">cookies = urllib2.HTTPCookieProcessor()</span><br/> <span style="color: #008000;">#</span><span style="color: #008000;">opener = urllib2.build_opener(cookies)</span><br/> <span style="color: #008000;">#</span><span style="color: #008000;">urllib2.install_opener(opener)</span><br/><span style="color: #000000;"><br/> header </span>= {<span style="color: #800000;">"</span><span style="color: #800000;">Accept</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">*/*</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Accept-Encoding</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">gbk,utf-8,gzip,deflate,sdch</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Accept-Language</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">zh-CN,zh;q=0.8,en;q=0.6</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Connection</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">keep-alive</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Content-Length</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">64</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Content-Type</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">application/x-www-form-urlencoded; charset=utf-8</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">'</span><span style="color: #800000;">Cookie</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">*************</span><span style="color: #800000;">'</span><br/> <span style="color: #800000;">"</span><span style="color: #800000;">Host</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">www.zhihu.com</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Origin</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">http://www.zhihu.com</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">Referer</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">http://www.zhihu.com/log/questions</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">User-Agent</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.137 Safari/537.36</span><span style="color: #800000;">"</span><span style="color: #000000;">,<br/> </span><span style="color: #800000;">"</span><span style="color: #800000;">X-Requested-With</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">XMLHttpRequest</span><span style="color: #800000;">"</span><span style="color: #000000;"><br/> }<br/><br/> parms </span>= {<span style="color: #800000;">'</span><span style="color: #800000;">start</span><span style="color: #800000;">'</span><span style="color: #000000;">:start,<br/> </span><span style="color: #800000;">'</span><span style="color: #800000;">offset</span><span style="color: #800000;">'</span><span style="color: #000000;">:offset,<br/> </span><span style="color: #800000;">'</span><span style="color: #800000;">_xsrf</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">*************</span><span style="color: #800000;">'</span><span style="color: #000000;">}<br/> url </span>= <span style="color: #800000;">'</span><span style="color: #800000;">http://www.zhihu.com/log/questions</span><span style="color: #800000;">'</span><span style="color: #000000;"><br/> req </span>= urllib2.Request(url,headers=header,data=<span style="color: #000000;">urllib.urlencode(parms))<br/> content </span>=<span style="color: #000000;"> urllib2.urlopen( req ).read()<br/> html </span>= gzip.GzipFile(fileobj =<span style="color: #000000;"> cStringIO.StringIO(content)).read()<br/> html </span>= eval(html)[<span style="color: #800000;">'</span><span style="color: #800000;">msg</span><span style="color: #800000;">'</span>][1<span style="color: #000000;">]<br/> pageSoup </span>=<span style="color: #000000;"> BeautifulSoup(html)<br/> questions </span>=<span style="color: #000000;"> []<br/> items </span>= pageSoup.find_all(<span style="color: #800000;">'</span><span style="color: #800000;">div</span><span style="color: #800000;">'</span>,{<span style="color: #800000;">'</span><span style="color: #800000;">class</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">zm-item</span><span style="color: #800000;">'</span><span style="color: #000000;">})<br/> </span><span style="color: #0000ff;">for</span> item <span style="color: #0000ff;">in</span><span style="color: #000000;"> items:<br/> url </span>= item.find_all(<span style="color: #800000;">'</span><span style="color: #800000;">a</span><span style="color: #800000;">'</span>,{<span style="color: #800000;">'</span><span style="color: #800000;">target</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">_blank</span><span style="color: #800000;">'</span>})[0].get(<span style="color: #800000;">'</span><span style="color: #800000;">href</span><span style="color: #800000;">'</span>).rsplit(<span style="color: #800000;">'</span><span style="color: #800000;">/</span><span style="color: #800000;">'</span>,1)[1<span style="color: #000000;">]<br/> questions.append(url)<br/> lastId </span>= items[-1].get(<span style="color: #800000;">'</span><span style="color: #800000;">id</span><span style="color: #800000;">'</span>).split(<span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>)[1<span style="color: #000000;">]<br/> </span><span style="color: #0000ff;">return</span> questions,lastId</div><p>  得到问题列表后再抓取每个问题投票最高的回复,代码如下:</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">def</span><span style="color: #000000;"> getArticle(url):<br/> page </span>=<span style="color: #000000;"> getPage(url)<br/> pageSoup </span>=<span style="color: #000000;"> BeautifulSoup(page)<br/> title </span>= str(pageSoup.title).replace(<span style="color: #800000;">'</span><span style="color: #800000;">&lt;title&gt;</span><span style="color: #800000;">'</span>,<span style="color: #800000;">''</span>).replace(<span style="color: #800000;">'</span><span style="color: #800000;">&lt;/title&gt;</span><span style="color: #800000;">'</span>,<span style="color: #800000;">''</span><span style="color: #000000;">).strip()<br/> item </span>= pageSoup.find_all(<span style="color: #800000;">'</span><span style="color: #800000;">div</span><span style="color: #800000;">'</span>,{<span style="color: #800000;">'</span><span style="color: #800000;">class</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">zm-item-answer</span><span style="color: #800000;">'</span><span style="color: #000000;">})<br/> </span><span style="color: #0000ff;">if</span> item <span style="color: #0000ff;">is</span> None <span style="color: #0000ff;">or</span> len(item) ==<span style="color: #000000;"> 0:<br/> </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None<br/> anwser </span>= item[0].find(<span style="color: #800000;">'</span><span style="color: #800000;">div</span><span style="color: #800000;">'</span>,{<span style="color: #800000;">'</span><span style="color: #800000;">class</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">fixed-summary zm-editable-content clearfix</span><span style="color: #800000;">'</span><span style="color: #000000;">}).get_text().strip()<br/> vote </span>= item[0].find(<span style="color: #800000;">'</span><span style="color: #800000;">div</span><span style="color: #800000;">'</span>,{<span style="color: #800000;">'</span><span style="color: #800000;">class</span><span style="color: #800000;">'</span>:<span style="color: #800000;">'</span><span style="color: #800000;">zm-item-vote-info </span><span style="color: #800000;">'</span>}).get(<span style="color: #800000;">'</span><span style="color: #800000;">data-votecount</span><span style="color: #800000;">'</span><span style="color: #000000;">).strip()<br/> anwser </span>=<span style="color: #000000;"> formatStr(anwser)<br/> ans_len </span>=<span style="color: #000000;"> len(anwser)<br/> </span><span style="color: #0000ff;">if</span> ans_len &gt; 100<span style="color: #000000;">:<br/> anwser </span>= anwser[0:100<span style="color: #000000;">]<br/> title </span>=<span style="color: #000000;"> formatStr(title)<br/> out </span>=<span style="color: #000000;"> [title, anwser, str(ans_len),vote,url]<br/> </span><span style="color: #0000ff;">return</span> out</div><p>  现在我们得到了每个问题的标题、投票最高的回复、问题链接。接下来我们需要把&ldquo;回复短投票高&rdquo;这样的规则进行量化计算,很明显一个回复是神回复的可能性与投票数成正比,与回复文本的长度成反比,但实现上,我们需要注意一些细节,比如有些神配图,一言不语,文本长度为0,所以需要平滑一些,另外回复越段应该越精辟,于是我定义了如下公式:$$Score=\frac{vote}{5+\frac{answer\_len^2}{10}}$$</p><p>  这个公式整体来说,认为是神回复的可能与投票数成正比,与回复的长度的平方成反比,加5是为了平滑哪些神配图。</p><p>  爬了一个晚上,爬取了2万个问题,然后按Score计算,取Score最大的top 1000,<span style="color: #ff0000;"><strong>欣赏神回复</strong></span>,请移步至<a href="https://github.com/BigPeng/zhihu_shenhuifu" target="_blank">github</a>。</p><p>  问题爬得太少,更多的精辟神回复没有找出来。</p><p>转载请注明出处:<a href="http://www.cnblogs.com/fengfenggirl/">http://www.cnblogs.com/fengfenggirl/</a>  &nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949341" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/fengfenggirl/p/zhihu_shenhuifu.html" target="_blank">如何找出知乎的所有神回复</a>,转载请注明。</p>http://www.cnblogs.com/ZXdeveloper/p/3949470.htmlWPF调用Matlab函数方法 - 眾尋有的时候用C#写图像处理方法,比较费事,不如Matlab简单,但是Matlab又做不出WPF那样的好看界面,怎么办呢。今天正好我要实现这个功能,就顺便写个小例子,给需要的人做个借鉴。想要用WPF调用Matlab代码,就用到了Matlab生成.DLL文件的功能。注:我的VS版本是2013,Matlab...2014-09-01T08:26:00Z2014-09-01T08:26:00Z眾尋http://www.cnblogs.com/ZXdeveloper/<p>有的时候用C#写图像处理方法,比较费事,不如Matlab简单,但是Matlab又做不出WPF那样的好看界面,怎么办呢。</p><p>今天正好我要实现这个功能,就顺便写个小例子,给需要的人做个借鉴。</p><p>想要用WPF调用Matlab代码,就用到了Matlab生成.DLL文件的功能。</p><p>注:我的VS版本是2013,Matlab版本是2012a,两个软件不算新也不算老,应该是现在普遍用的版本</p><p>首先,生成Matlab的.DLL文件</p><p>在matlab的左下角选择start-matlab-NE-deploytool,或者直接在command界面输入deploytool。<img src="http://images.cnitblog.com/blog/609679/201409/011600276101580.jpg" alt="" /></p><p>在弹出的对话框里,输入你需要输入的内容,可以默认,也可以手动填入,Type选项一定要选择.NET Assembly选项。</p><p><img src="http://images.cnitblog.com/blog/609679/201409/011602272827719.jpg" alt="" /></p><p>在Matlab窗口右侧会出现一个对话框,点击Add Class增加一个Class,名字自己填写,然后点击Add Files,选择需要的生成DLL的Matlab函数文件。</p><p><img src="http://images.cnitblog.com/blog/609679/201409/011604402977050.jpg" alt="" /><img src="http://images.cnitblog.com/blog/609679/201409/011605215789204.jpg" alt="" /><img src="http://images.cnitblog.com/blog/609679/201409/011605354699294.jpg" alt="" /><img src="http://images.cnitblog.com/blog/609679/201409/011606023287688.jpg" alt="" /></p><p>然后点击Build按钮,等待进度条走完</p><p><img src="http://images.cnitblog.com/blog/609679/201409/011606495006311.jpg" alt="" /></p><p>当进度条走完以后,在刚才的目录下会生成一个以name命名的文件夹,此文件夹下会有两个文件夹,分别是distrib和src。</p><p>在WPF程序下进行引用,浏览,找到MWArray.dll,我的路径是&ldquo;D:\Program Files\MATLAB\R2012a\toolbox\dotnetbuilder\bin\win32\v2.0&rdquo;,这个也就是前面会根据不同人安装的Matlab路径不一样而不一样,后面基本是一样的,同时引用distrib文件夹下的JLFG.dll文件。</p><p><img src="http://images.cnitblog.com/blog/609679/201409/011610333444259.jpg" alt="" /></p><p>在WPF的代码中进行引用</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">using</span><span style="color: #000000;"> MathWorks.MATLAB.NET.Arrays;<br/></span><span style="color: #0000ff;">using</span><span style="color: #000000;"> MathWorks.MATLAB.NET.Utility;<br/></span><span style="color: #0000ff;">using</span> JLFG;</div><p>写实现部分代码</p><div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"><span style="color: #0000ff;">string</span> proPath =<span style="color: #000000;"> selPath();<br/>JLFG.Iorig iorig </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Iorig();<br/></span><span style="color: #000000;">iorig.I_orig(proPath);<br/>ImgPro.Source </span>= BitImg(<span style="color: #800000;">"</span><span style="color: #800000;">C:\\I_orig.jpg</span><span style="color: #800000;">"</span>);</div><p>中间的两行代码为调用实现部分,第一行代码是获取图片的路径,最后一行是展示图像用的。</p><p>因为Matlab输出是直接用Figure输出,因此需要imwrite进行保存,因此才有了最后一句的调用图像路径。</p><p>最终的实现效果</p><p><img src="http://images.cnitblog.com/blog/609679/201409/011620145163941.jpg" alt="" /></p><p><span style="color: #800000;">但是,有一个问题出现了,就是我实现的过程用的是Win7 32位的的系统,没有问题,用Win8.1 64位系统就不好使,虽然更换了MWArray.dll的路径为64位的路径,依然不好使,出现如下错误,有知道如何解决这个问题的大神,请告知方法,在此谢谢了。</span></p><p><img src="http://images.cnitblog.com/blog/609679/201409/011622254076185.png" alt="" /></p><p>&nbsp;</p><img src="http://counter.cnblogs.com/blog/rss/3949470" width="1" height="1" alt=""/><br/><p>本文链接:<a href="http://www.cnblogs.com/ZXdeveloper/p/3949470.html" target="_blank">WPF调用Matlab函数方法</a>,转载请注明。</p>

高级...
微信扫一扫
关注无线梦工厂
返回主页 手机网推广 加入Timewe 关于我们 加入收藏 点击这里给我发消息加关注加微信好友

触屏版,3G,Wap,手机网站,浏览器,pc版,在线,嵌入式,Wap浏览器,电脑上的Wap浏览器 各主流搜索引擎收录Timewe
(R)2009-2019 Timewe 时维网络科技有限公司 (86)0755-36601136
简体/繁体