vs 运行库知识点

一、问题

  • 我们编译c/c++的执行程序(exe),如果运行库没有选择静态编译(mt),那么可能在别的电脑上就无法运行(因为没有对应的运行库)
  • 程序选择mt静态遍历,我们链接别的第三库会出现链接报错

上面是我们用c/c++编译经常遇到问题。

二、运行具体文件的作用[vs2019为例子]

  • vcruntime140: c语言运行库,比喻我们用到prinf
  • msvcp140.dll c++ 运行库 ,比喻我们用到的 std::cout
  • 其他补充,以前的版本不一定有,有可能是win10才有

140 代表是运行库的版本号,你用的vs版本不一样,这里数字不一样。

三、解释

1:问题1解决办法

  • exe 运行库选择mt,这个在vs 工程选项 c/c++ 运行库进行设置
  • 安装包写脚本自动检测,安装对应的运行库程序【用的不少,但国内版本比较多,不一定能安装成功】
  • 自己直接在exe运行库放 所有运行库,直接从vs 目录找到对应的版本运行库就可以了【这种比较多】

2:问题2解决办法

如果你exe选择静态遍历,第三方库选择md 动态编译,链接时候会发现运行库的代码已经实现,出现冲突,相当于定义出现2次了,导致无法链接。【凭记忆写的,改天自己用一个第三方库验证一下】

第三方库也选择静态编译就不会冲突了,但实际中非常麻烦,这个c/c++ 非常不方便地方,跟现在高级语言比起来麻烦很多,导致新手门槛太高。我自己都不怎么用c/c++写一些工具,用别的高级语言,直接几行命令就可以继承第三方的库直接开始工作,c/c++还需要单独编译,单独编译还不一定能成功。

四、总结

本篇文章只是为了记录一下,因为前段时间正好思考这个问题,到底他们运行库怎么搞的,为什么会编译出错等等。有些问题不思考,不验证还真不知道具体原因,只知道那样子操作就可以,但具体为什么却不知道

大小端理解

一、背景

这块知识点算是比较常见,我们传送网络二进制自定义协议一定会遇到的,我最近看到一个问题,于是我思考为什么会有大小端的问题,他到底是哪一层导致的。基本知识我这里就不说了,自己搜索一下就能知道

二、思考

我们看的书籍或者网络的文章都说大小端跟CPU有关系,但跟高级语言没有关系,我们用c/c++,c#的时候我们赋值给变量根本不关心大小端,比喻 short i = 0x1234 我们根本不用关心他在内存里面是大端还是小端,因为他的值就是0x1234。不用关心他的内存形式。

另外一个知识点,i = a + b; 如果a 和 b 如果在别的线程可能被修改,那么这样行代码是非线程安全的,那为什么会线程不安全, 因为 i = a + b 在汇编层是多行代码,并不是一行代码,所以高级语言的线程安全,不能只看高级语言层线程安全,单行操作代码也可能是非线程安全的。【这里举例不是非常好,如果以后能想到更合适才写吧】

我们知道cpu 没有变量概念,只认识0 和 1 ,高级语言不关心内存的形态,那么可以断定大小端只是汇编层问题,我们知道汇编 命令 + 操作数, 那么32 位中 操作可以word 也可以是dword ,汇编是跟cpu最直接的,所以厂商不一样,操作数在内存形式不一样,所以才出现大小端的问题。所以问题是在汇编层导致。

三、补充

自己google了一下也没有找到任何英文文档来证明我上概念。

win11 关闭蓝屏 重启不蓝屏

一、背景

蓝屏是ndis.sys 导致的蓝屏,基本百度或者google也找不到解决办法,说明自己这个问题很大可能第三方程序导致的问题。

二、排错

  1. 通过日志查看到生成dump文件所在路径。(右键我的电脑,点击管理,找到window的日志)
  2. 用windbg priview 查看dunmp文件(应用商店可以下载)
  3. 看到回调过程
  4. 找到一个Neo_VPN.sys 调用
  5. 通过搜索在C:\Windows\System32\Drivers\Neo_VPN.sys

三、解决方案

  1. 进入win11 安全模式
  2. 删除 C:\Windows\System32\Drivers\Neo_VPN.sys
  3. 重启电脑,正常

四、总结

每个蓝屏不一定相同,但基本按照上面流程一般能找到由于那个驱动导致蓝屏,删除对应的蓝屏基本就能解决。我自己这个驱动是很多年前安装一个客户端,但卸载了,驱动没有卸载,导致升级win11造成不兼容

java 压缩的二进制数据传递给node js处理问题

一, 背景

自己压缩android 截图数据压缩然后网络传递给我node程序,然后发现node 无法解压。

二,排错过程

打印java 压缩的二进制数据数组,打印node收到 buffer,发送java数组函数负数,而node 得到没有负数

三,思考

看到这个2边数据不一样,我就想起来是什么导致。java byte 是 -128~127,node byte是 0~255. 这样子导致数据对不上,我要说一点二进制数据是一样的。这个只是node 读同样的二进制,解析数据0~255范围[byte],java虚拟机解析~128-127。这个就跟我们传送数据,会出现大端和小端问题一样,必须保证2边一致。。。。

四,解决

我直接在NODE 处理每个字节 var c = buffer[i] & 0xff 即可

lua 源码简单分析

一,背景

这类技术自己没有研究过,只是用过lua,当时只是感觉跟JS一样,没有特别的感觉,他的代码非常少,我下载1.0的代码。

二,概括

通过lex与yacc 进行词法和语法分析,语法分析加了自己的c语言的逻辑代码。

三,分析

lua_dofile 加载指定的lua文件。

int lua_parse (void)
{
 Byte *initcode = maincode;
 err = 0;
 if (yyparse () || (err==1)) return 1;
 *maincode++ = HALT;
 //PrintCode();
 if (lua_execute (initcode)) return 1;
 maincode = initcode;
 return 0;
}

yyparse 进行词法分析与语法分析【这个是编译通用工具,不过我没有使用过,我只是网上查资料了解到】

int lua_execute (Byte *pc)
{
 while (1)
 {
  switch ((OpCode)*pc++)
  {
   case NOP: break;
   
   case PUSHNIL: tag(top++) = T_NIL; break;
   
   case PUSH0: tag(top) = T_NUMBER; nvalue(top++) = 0; break;
   case PUSH1: tag(top) = T_NUMBER; nvalue(top++) = 1; break;
   case PUSH2: tag(top) = T_NUMBER; nvalue(top++) = 2; break;

   case PUSHBYTE: tag(top) = T_NUMBER; nvalue(top++) = *pc++; break;
   
   case PUSHWORD: 
    tag(top) = T_NUMBER; nvalue(top++) = *((Word *)(pc)); pc += sizeof(Word);
   break;
...............

while 取指令,然后执行对应行为,变量处理通过top 堆栈进行操作。

四,样例

local a = 10 * 6;
print(a)
PUSHBYTE   10
PUSHBYTE   6
MULTOP
PUSHGLOBAL   4 //获取print对象,这个存在全局table 函数数组里面
PUSHMARK
PUSHGLOBAL   33 //a 这个变量
CALLFUNC
ADJUST   0

这个生成对应的字节码,然后调用lua_execute 执行,你可以下断点自己测试。

五,vscode 编译调试[windows]

因为我觉得直接用vs 编译调试感觉太重了,于是采用vscode来编译。

编程语言虚拟机基本原理学习

一,背景

最近突然想研究语言虚拟机到底怎么运行的,于是我网上找了一些资料,稍微记录一下,让自己有一个映像,并不是为了自己写虚拟机。

二,原理[加载程序]

while(true){

取指令

解析指令

执行指令

}

这里也是本文的核心内容, 实际上虚拟机很简单, 遵循这样的模式:

  • 读取: 从文件读取内容,解析生成指定集合等等
  • 解码: 解析指定指令。【一条条执行,然后读取指令 push eax】
  • 执行: 执行解码后的指令 【当读取这个指定,执行 压入一个eax 的值到栈顶】

三,参考资料

用 Lua 实现一个微型虚拟机-基本篇 – 自由布鲁斯 – 博客园 (cnblogs.com)

Felix Angell’s personal website

/**
	This is almost identical to the articles
	VM
**/

#include <stdio.h>
#include <stdbool.h>

bool running = true;
int ip = 0;
int sp = -1;

int stack[256];

typedef enum {
   PSH,
   ADD,
   POP,
   HLT
} InstructionSet;

const int program[] = {
    PSH, 5,
    PSH, 6,
    ADD,
    POP,
    HLT
};

int fetch() {
    return program[ip];
}

void eval(int instr) {
    switch (instr) {
        case HLT: {
            running = false;
            printf("done\n");
            break;
        }
        case PSH: {
    	    sp++;
	        stack[sp] = program[++ip];
	        break;
        }
        case POP: {
	        int val_popped = stack[sp--];
	        printf("popped %d\n", val_popped);
	        break;
	    }
	    case ADD: {
	        // first we pop the stack and store it as a
	        int a = stack[sp--];
	    
	        // then we pop the top of the stack and store it as b
	        int b = stack[sp--];

	        // we then add the result and push it to the stack
	        int result = b + a;
	        sp++; // increment stack pointer **before**
	        stack[sp] = result; // set the value to the top of the stack

	        // all done!
	        break;
	    }
    }
}

int main() {
    while (running) {
        eval(fetch());
        ip++; // increment the ip every iteration
    }
}

这是网上文章写的超级简单的虚拟机程序,基本解释了

其他

突然我想起来,我以前写过android 自动化脚本,自己用json定义行为,里面定义很多行文的关键词,然后不断读取这些行为,然后执行对应的行文。这里json其实相当于我们编译器生成的二进制文件。一般编译器生成的他们执行的文件,为什么用二进制表示,只是为了省占用空间。对于我们自己研究,用字符串表示最简单,最好用json或者xml,因为自带描述属性。

这个之前进入设置自己写自动化脚本,内部实现用java驱动,然后用json来描述,这样子可以做做到,动态下发脚本行文,只要底层实现所有指令就可以了,这么说来,我以前也写过商用的虚拟机了。。。。。

上面文章貌似都没有说解析过程,如果还想仔细了解,可以看<自己动手实现lua:虚拟机、编译器>

wpf dispacher 实现原理分析

一,作用

提供线程队列服务,通过windows message 这个基本原理逻辑代码。

二,代码

异步调用

dispath 代码 :https://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,9e84b372d672e449,references

dispacher 把我们行为封装成为DispatcherOperation,然后投递队列,同时投递事件给绑定的线程对应的 message only 窗口,窗口收到消息就运行我的行为。

写c++开发时候,为了拥有这样功能,我们都要自己写窗口,封装异步行为,到了WPF 这个已经是自带功能。

private void InvokeAsyncImpl(DispatcherOperation operation, CancellationToken cancellationToken)
        {
            DispatcherHooks hooks = null;
            bool succeeded = false;
 
            // Could be a non-dispatcher thread, lock to read
            lock(_instanceLock)
            {
                if (!cancellationToken.IsCancellationRequested &&
                    !_hasShutdownFinished &&
                    !Environment.HasShutdownStarted)
                {
                    // Add the operation to the work queue
                    //当前行为添加到队列里面去
                    operation._item = _queue.Enqueue(operation.Priority, operation);
 
                    // Make sure we will wake up to process this operation.
                    //投递消息给创建window,这样子处理消息
                    succeeded = RequestProcessing();
 
                    if (succeeded)
                    {
                        // Grab the hooks to use inside the lock; but we will
                        // call them below, outside of the lock.
                        hooks = _hooks;
                    }
                    else
                    {
                        // Dequeue the item since we failed to request
                        // processing for it.  Note we will mark it aborted
                        // below.
                        _queue.RemoveItem(operation._item);
                    }
                }
            }
 
            if (succeeded == true)
            {
                // We have enqueued the operation.  Register a callback
                // with the cancellation token to abort the operation
                // when cancellation is requested.
                if(cancellationToken.CanBeCanceled)
                {
                    CancellationTokenRegistration cancellationRegistration = cancellationToken.Register(s => ((DispatcherOperation)s).Abort(), operation);
 
                    // Revoke the cancellation when the operation is done.
                    operation.Aborted += (s,e) => cancellationRegistration.Dispose();
                    operation.Completed += (s,e) => cancellationRegistration.Dispose();
                }
 
                if(hooks != null)
                {
                    hooks.RaiseOperationPosted(this, operation);
                }
 
                if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info))
                {
                    EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextPost, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, operation.Priority, operation.Name, operation.Id);
                }
            }
            else
            {
                // We failed to enqueue the operation, and the caller that
                // created the operation does not expose it before we return,
                // so it is safe to modify the operation outside of the lock.
                // Just mark the operation as aborted, which we can safely
                // return to the user.
                operation._status = DispatcherOperationStatus.Aborted;
                operation._taskSource.SetCanceled();
            }
        }

创建dispacher

  1. 添加全局list dispacher【全局管理】
  2. 创建MessageOnlyHwndWrappe 窗口,这个是核心的功能。
  3. 添加 窗口消息处理函数 WndProcHook
     [SecurityCritical, SecurityTreatAsSafe]
        private Dispatcher()
        {
            _queue = new PriorityQueue<DispatcherOperation>();
 
            _tlsDispatcher = this; // use TLS for ownership only
            _dispatcherThread = Thread.CurrentThread;
 
            // Add ourselves to the map of dispatchers to threads.
            lock(_globalLock)
            {
                //全局list 增加 当前的list
                _dispatchers.Add(new WeakReference(this));
            }
 
            _unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
            _exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);
 
            _defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);
 
            // Create the message-only window we use to receive messages
            // that tell us to process the queue.
            MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
            _window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
 
            _hook = new HwndWrapperHook(WndProcHook);
            _window.Value.AddHook(_hook);
 
            // DDVSO:447590
            // Verify that the accessibility switches are set prior to any major UI code running.
            AccessibilitySwitches.VerifySwitches(this);
        }

WndProcHook

处理窗口消息,用来处理 我们投递过来的异步操作

       private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            WindowMessage message = (WindowMessage)msg;
            if(_disableProcessingCount > 0)
            {
                throw new InvalidOperationException(SR.Get(SRID.DispatcherProcessingDisabledButStillPumping));
            }
 
            if(message == WindowMessage.WM_DESTROY)
            {
                if(!_hasShutdownStarted && !_hasShutdownFinished) // Dispatcher thread - no lock needed for read
                {
                    // Aack!  We are being torn down rudely!  Try to
                    // shut the dispatcher down as nicely as we can.
                    ShutdownImpl();
                }
            }
            else if(message == _msgProcessQueue)
            {
                //如果消息,异步投递消息
                //这个就是我们用dispacher beginInvoke的
                // 内部调用 RequestProcessing(); 投递回调消息过来
                ProcessQueue();
            }
            else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_BACKGROUND)
            {
                // This timer is just used to process background operations.
                // Stop the timer so that it doesn't fire again.
                SafeNativeMethods.KillTimer(new HandleRef(this, hwnd), TIMERID_BACKGROUND);
 
                ProcessQueue();
            }
            else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_TIMERS)
            {
                // We want 1-shot only timers.  So stop the timer
                // that just fired.
                KillWin32Timer();
 
                PromoteTimers(Environment.TickCount);
            }
 
            // We are about to return to the OS.  If there is nothing left
            // to do in the queue, then we will effectively go to sleep.
            // This is the condition that means Idle.
            DispatcherHooks hooks = null;
            bool idle = false;
 
            lock(_instanceLock)
            {
                idle = (_postedProcessingType < PROCESS_BACKGROUND);
                if (idle)
                {
                    hooks = _hooks;
                }
            }
 
            if (idle)
            {
                if(hooks != null)
                {
                    hooks.RaiseDispatcherInactive(this);
                }
 
                ComponentDispatcher.RaiseIdle();
            }
 
            return IntPtr.Zero ;
        }

获取当前dispatcher

  1. 通过FromThread 函数获取 当前线程的dispacher
  2. 如果当前线程不存在dispacher,那么直接创建。
       public static Dispatcher CurrentDispatcher
        {
            get
            {
                // Find the dispatcher for this thread.
                Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);;
 
                // Auto-create the dispatcher if there is no dispatcher for
                // this thread (if we are allowed to).
                if(currentDispatcher == null)
                {
                    currentDispatcher = new Dispatcher();
                }
 
                return currentDispatcher;
            }
        }

我们可以new Thread ,然后执行获当前dispacher。

消息循环

        //<SecurityNote>
        // Critical - as this calls critical methods (GetMessage, TranslateMessage, DispatchMessage).
        // TreatAsSafe - as the critical method is not leaked out, and not controlled by external inputs.
        //</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe ]
        private void PushFrameImpl(DispatcherFrame frame)
        {
            SynchronizationContext oldSyncContext = null;
            SynchronizationContext newSyncContext = null;
            MSG msg = new MSG();
 
            _frameDepth++;
            try
            {
                // Change the CLR SynchronizationContext to be compatable with our Dispatcher.
                oldSyncContext = SynchronizationContext.Current;
                newSyncContext = new DispatcherSynchronizationContext(this);
                SynchronizationContext.SetSynchronizationContext(newSyncContext);
 
                try
                {
                    while(frame.Continue)
                    {
                        if (!GetMessage(ref msg, IntPtr.Zero, 0, 0))
                            break;
 
                        TranslateAndDispatchMessage(ref msg);
                    }
 
                    // If this was the last frame to exit after a quit, we
                    // can now dispose the dispatcher.
                    if(_frameDepth == 1)
                    {
                        if(_hasShutdownStarted)
                        {
                            ShutdownImpl();
                        }
                    }
                }
                finally
                {
                    // Restore the old SynchronizationContext.
                    SynchronizationContext.SetSynchronizationContext(oldSyncContext);
                }
            }
            finally
            {
                _frameDepth--;
                if(_frameDepth == 0)
                {
                    // We have exited all frames.
                    _exitAllFrames = false;
                }
            }
        }

如果普通的windwos 窗口程序,一般只有一个消息循环,消息循环每个线程必须要有一个。如果你创建windows不在UI线程,那么必须自己循环,不然程序跑不起来。

Appliaction 继承了 DispatcherObject,那么默认创建一个dispacker的对象。

      /// <summary>
        ///     Instantiate this object associated with the current Dispatcher.
        /// </summary>
        protected DispatcherObject()
        {
            _dispatcher = Dispatcher.CurrentDispatcher;
        }

可以退出,如果wpf起码有2个窗口,dispacher创建窗口。

wpf 程序会调用dispacher Run

我通过自己写c++ 工具搜索消息窗口(消息窗口不能被枚举函数枚举,所以你用spy++是无法查找到这个窗口,必须通过下面FindWindowEx函数才可以)

void find_hwnd_message(HWND hw){
	HWND child_hwnd = FindWindowEx(HWND_MESSAGE, hw, NULL, NULL);
	if(child_hwnd == NULL){
		return;
	}

	wchar_t class_name[256] = {0};
	GetClassName(hw, class_name, 255);
	wprintf_s(L"class_name: %s\n", class_name );
	find_hwnd_message(child_hwnd);
}

然后再main函数调用 find_hwnd_message (null); 这样子可以查询所有的消息窗口,你也可以填写class name 和 windows name 做过滤。

三、总结

application 启动时候就默认创建 dispacher,通过Dispacher 获取当前的,当前不存在就默认创建逻辑,然后启动主窗口,然后调用当前dispacher中Run ,用来消息循环,从而整个window 应用程序核心逻辑走完了。dispacher会绑定线程,会创建一个消息窗口。通过原理,我们可以推出来,dispacher是不能在很短时间做大量异步行为(如果消息还消耗时间的话),因为dispacher 消息处理不过来,阻塞界面。

四、资料

微软消息窗口文档介绍

cos 或者OSS 网页 IE8访问404

一、问题

自己用COS 的URL 访问HTML 资源,chrome 和firefox ,edge 和 ie11都能正常,但自己用ie8访问会出现跳转到404。

二、排查过程

自己测试各种浏览器,我发现只有在IE8 出现问题,那么证明肯定是IE8兼容性问题,我突然想起自己以前用NODE http 写静态资源,返回数据没有返回对应的content-type,导致在无法访问问题,那么估计就这么问题。

三、结果方案

我开启cos静态资源网站,它的静态网站资源用的不同的url。ie8 不能根据后缀采用对应的content-type,目前主流的浏览器都支持不带content-type,根据后缀才解析。

jquery 为什么要这么写

jqery 生成对象写法,我们调用jquery $(“div”)然后调用对应的函数或者属性我们就能快乐写代码了,通过他历史版本发现他们思路。

历史版本

var jQuery = function(a,c) {
	// If the context is global, return a new object
	if ( window == this )
		return new jQuery(a,c);

	// Make sure that a selection was provided
	a = a || document;
	
	// HANDLE: $(function)
	// Shortcut for document ready
	if ( jQuery.isFunction(a) )
		return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
	
	// Handle HTML strings
	if ( typeof a  == "string" ) {
		// HANDLE: $(html) -> $(array)
		var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a);
		if ( m )
			a = jQuery.clean( [ m[1] ] );
		
		// HANDLE: $(expr)
		else
			return new jQuery( c ).find( a );
	}
	
	return this.setArray(
		// HANDLE: $(array)
		a.constructor == Array && a ||

		// HANDLE: $(arraylike)
		// Watch for when an array-like object is passed as the selector
		(a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||

		// HANDLE: $(*)
		[ a ] );
};

1.1.3以下版本(包括),没有采用闭包,但还是用的匿名函数异常内部实现,这个里面直接写选择逻辑,这种是我们常见写法,这个时候他们还没有单独把选择器代码用init的函数单独封装起来。这里含有new的和选择代码,不同代码逻辑放在一个函数里面,这样子就比较混乱,还是后面代码清晰很多。

1.1.4版本

var jQuery = function(a,c) {
	// If the context is global, return a new object
	if ( window == this || !this.init )
		return new jQuery(a,c);
	
	return this.init(a,c);
};

jquery 为了用户 无论new不new都是返回jquery对象,后面高版本全部都直接new,这样子代码简答一点,同时进行代码隔离,Jquery 函数对象 与 new Jquery对象分开, 因为它的调用

new JQuery.fn.init();

new其实 init 对象,与jquery 没有关系, 为了使用jquery 原始链的方法,又把init 原始链替换成Jquery的原型链

开始init 只是作为入口函数而已,代码清晰好多。

return new jQuery( c ).find( a );

老代码查找用重新new 而已,后面用原型链感觉只是代码更加清晰,别的书籍说了为了隔离,我感觉不是很靠谱,基本上都是为了返回jquery对象而已。

jquery 历史老版本地址

Google Code Archive – Long-term storage for Google Code Project Hosting.

这个google地址,所以需要梯子

补充

直接复制自己笔记,后面可能会补充修改

今天用Java写网络代码,好久没有这么写代码,一气呵成写逻辑代码,感觉就没有停下来过。。写着突然明白以前不是很懂的东西,现在看来真简单。

堆 栈 虚拟内存 物理内存的关系

一,背景

自己发现做开发这么年,竟然不是很清楚,然后查了一下资料,根据自己理解总结了一下

二,概述

windows上面物理内存通过页管理【方便物理内存管理,后面堆也是内存管理的算法,只是他依赖虚拟内存】,每一页大小是4K【跟系统有关】

栈是连续内存块,每个线程都一个固定大小栈【1M】,所以我们在函数里面不要数组设置很多,不然就会栈溢出了。c/c++ 可以在vs里面进行设置,其他应该也可以。地址连续为提高内存的访问速度,他的内存管理也非常简单,我们OD或者其他调试工具的时候就会看这个东西,这个无法程序直接体现,除非你自己写汇编语言。栈溢出常常用来远程溢出,以前的strcpy等等函数都没有检测原始数据大小,按照0为结束符号,导致覆盖原来调用压入的运行地址,从而跳转指定的恶意代码,现在基本都换高级语言,很少应用有这个bug。

堆对应程序员语言的new或者malloc(C语言),windows它本质调用HeapAlloc,delete调用FreeHeap,你进入汇编就能看的到。堆内存我认为只是虚拟内存管理小内存的一种算法,他内部分配内存应该也只是虚拟内存(网上是说这么说的),我自己测试时候你分配3000字节的内存,他分配的实际的内存是4K,走的还是虚拟内存分页管理逻辑,从我们开发角度来说,代码基本都是分层,上一层依赖下一层,如果直接操作物理内存,那么怎么标记这块内存已经使用了。

虚拟内存是页管理中对象,我们访问虚拟地址,从而系统要我们访问实际物理内存。我们无法实际操作物理内存,我们操作别的进程内存,可以通过虚拟内存函数进行操作。

下面是window任务管理对应的关系,我们查内存泄漏最好看提交大小,而不是专用内存,因为专有内存可能放的磁盘上面去,那么实际用的物理偏小,但你的程序new的对象确实比物理多很多,程序内存泄漏应该看虚拟内存,而不是实际内存。