vc 环境全局变量赋值引发的访问异常和解决过程

昨天写代码时候不注意 在加载时候跳一个如下图的异常:

 用vc调试运行就出如下错误:

经过半天的修改测试总算明白我工程中错误了。

为了说明问题和如何解决我在测试工程写了一个代码如:

#include "stdafx.h"
#include <map>
void initmap();
using namespace std;

class testclass
{
public:
	testclass();
	~testclass();

private:

};

testclass::testclass()
{
	void initmap();
	initmap();
}

testclass::~testclass()
{
}
testclass  g_testobj;
map<int, int>   g_testmap;
void initmap()
{
	g_testmap[1] = 3;
}
int main()
{
	getchar();
    return 0;
}

初看起来感觉代码没有问题,运行就报如第一个图片上的错误:再看下面一个代码:

#include "stdafx.h"
#include <map>
void initmap();
using namespace std;

class testclass
{
public:
	testclass();
	~testclass();

private:

};

testclass::testclass()
{
	void initmap();
	initmap();
}

testclass::~testclass()
{
}
map<int, int>   g_testmap;
testclass  g_testobj;
void initmap()
{
	g_testmap[1] = 3;
}
int main()
{
	getchar();
    return 0;
}

就完全没有问题了。vc范例中很多都是这么干的,直接在全局变量里去初始化而不是在main()函数中实现初始化。变量设置交换一下竟发生错误了。

vc编译好的程序初始化过程会调用__cdecl __scrt_common_main_seh()这个函数

tatic __declspec(noinline) int __cdecl __scrt_common_main_seh() throw()
{
    if (!__scrt_initialize_crt(__scrt_module_type::exe))
        __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);

    bool has_cctor = false;
    __try
    {
        bool const is_nested = __scrt_acquire_startup_lock();

        if (__scrt_current_native_startup_state == __scrt_native_startup_state::initializing)
        {
            __scrt_fastfail(FAST_FAIL_FATAL_APP_EXIT);
        }
        else if (__scrt_current_native_startup_state == __scrt_native_startup_state::uninitialized)
        {
            __scrt_current_native_startup_state = __scrt_native_startup_state::initializing;

            if (_initterm_e(__xi_a, __xi_z) != 0)
                return 255;

            _initterm(__xc_a, __xc_z);//关键在这里会调用全局变量相关初始化

            __scrt_current_native_startup_state = __scrt_native_startup_state::initialized;
        }
        else
        {
            has_cctor = true;
        }

        __scrt_release_startup_lock(is_nested);

        // If this module has any dynamically initialized __declspec(thread)
        // variables, then we invoke their initialization for the primary thread
        // used to start the process:
        _tls_callback_type const* const tls_init_callback = __scrt_get_dyn_tls_init_callback();
        if (*tls_init_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_init_callback))
        {
            (*tls_init_callback)(nullptr, DLL_THREAD_ATTACH, nullptr);
        }

        // If this module has any thread-local destructors, register the
        // callback function with the Unified CRT to run on exit.
        _tls_callback_type const * const tls_dtor_callback = __scrt_get_dyn_tls_dtor_callback();
        if (*tls_dtor_callback != nullptr && __scrt_is_nonwritable_in_current_image(tls_dtor_callback))
        {
            _register_thread_local_exe_atexit_callback(*tls_dtor_callback);
        }

        //
        // Initialization is complete; invoke main...
        //

        int const main_result = invoke_main();

        //
        // main has returned; exit somehow...
        //

        if (!__scrt_is_managed_app())
            exit(main_result);

        if (!has_cctor)
            _cexit();

        // Finally, we terminate the CRT:
        __scrt_uninitialize_crt(true, false);
        return main_result;
    }
    __except (_seh_filter_exe(GetExceptionCode(), GetExceptionInformation()))
    {
        // Note:  We should never reach this except clause.
        int const main_result = GetExceptionCode();

        if (!__scrt_is_managed_app())
            _exit(main_result);

        if (!has_cctor)
            _c_exit();

        return main_result;
    }
}

函数中通过这个_initterm(__xc_a, __xc_z);//关键在这里会调用全局变量相关初始化

去初始化相关全局变量的如下图下的断点:

 首先断点停在第一个全局中

看下反汇编里做什么?

 原来g_testmap初始化都在这里实现了

记录一下两个变量地址吧

 回头看看_initterm函数循环调用有设置的函数数组

extern "C" void __cdecl _initterm(_PVFV* const first, _PVFV* const last)
{
    for (_PVFV* it = first; it != last; ++it)
    {
        if (*it == nullptr)
            continue;

        (**it)();
    }
}

原因在于:windows 程序启动main()之前进行的全局变量初始化这个函数_initterm从头到尾

通过IDA反汇编可以看到看看first和last

 first的地址

last的地址

 在first与last代码中间保存着如下图的地址

 _initterm函数循环后执行自然就先g_testmap的初始化了,而将代码修改后两个全局变量对调位置后可以看到地址相对位置也对调,而在testclass类中我们恰好写了如下代码:

testclass::testclass()
{
    void inidmap();
    inidmap();
}

void inidmap()
{
    g_testmap[1] = 3;
}

g_testmap还没有初始化就使用就会造成我们开头2个图片的错误。至此综于搞清楚问题所在了,全局变量中不要试图使用或初始化别的全局变量,否则会产生不可预测的结果。想要做通过测试可以定义一个指针,自己new一个对象给全局变量再使用也没有问题;

        码字不易,对您有用就随手点个赞!万分感谢!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

界忆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值