全局变量被修改后又还原为初始值的诡异问题

C++语言 码拜 9年前 (2016-04-04) 852次浏览
例如有一个A.cpp和A.h;cpp中顶一个一个全局bool和获取设置的函数
bool g_Flag = false;
bool getbFlag()
{
return g_Flag;
}
void setFlag(bool b)
{
g_Flag = b;
}
A.h中的内容是:
bool getFlag();
void steFlag(bool b);
本人在B.cpp中包含了A.h
然后在某个函数中setFlag(true);,随后本人就调用getFlag()发现是返回true的;可是本人在C.cpp中在调用时发现getFlag()返回了false,可以肯定的是其他再没有修改这个变量的地方,而且C的调用在B的调用之后,这样诡异的问题,可能的原因是什么呢?本人也完全编译了一次程序,折腾大半天就是不行。还有假如本人初始化为true,那么C中调用时就返回true了。
解决方案

5

这个你都不知道,你都是高手了吧!
1)全局变量,应该定义为易变的,防止编译优化错误。
2)读写全局变量,应该同步

5

extern bool g_Flag; 
bool getFlag();
void steFlag(bool b);

5

没有人extern了你的g_Flag之后手动改了?本人是说不调用你的那个setFlag函数

5

去掉编译时的优化选项及定义变量为volatile试试

5

逻辑上看不出什么问题。是不是编译器的问题,换个编译器试试?

5

弱弱的说,全局搜索g_Flag只有三个结果么,本人也没看出啥问题,构造函数里面呢?
全局变量被修改后又还原为初始值的诡异问题

5

而且C的调用在B的调用之后
这是你从代码看的吧。能不能加上打印输出,证明确实是先调了B才调C

5

引用:
Quote: 引用:

这个你都不知道,你都是高手了吧!
1)全局变量,应该定义为易变的,防止编译优化错误。
2)读写全局变量,应该同步

本人不是高手啊。怎么个同步读写法?

加锁,使用信号量…

10

调试,打断点,内存监视都用上,看看顺序对不对,是不是有其它地方修改,是不是有
溢出覆盖等等,真想只有一个

5

会舍变量值改变断点吗?参考下面:

#include <time.h>
#include <stdlib.h>
#include <windows.h>
int main() {
    int a,b[11];//本来是b[10],为判断哪句越界,故意声明为b[11]
    srand((unsigned int)time(NULL));//按两次F11,等黄色右箭头指向本行时,调试、新建断点、新建数据断点,地址:&b[10],字节计数:4,确定。
    while (1) {//按F5,会停在下面某句,此时a的值为10,b[10]已经被修改为对应0..4之一。
        b[(a=rand()%11)]=0;
        Sleep(100);
        b[(a=rand()%11)]=1;
        Sleep(100);
        b[(a=rand()%11)]=2;
        Sleep(100);
        b[(a=rand()%11)]=3;
        Sleep(100);
        b[(a=rand()%11)]=4;
        Sleep(100);
    }
    return 0;
}

10

引用:

例如有一个A.cpp和A.h;cpp中顶一个一个全局bool和获取设置的函数
bool g_Flag = false;
bool getbFlag()
{
return g_Flag;
}
void setFlag(bool b)
{
g_Flag = b;
}
A.h中的内容是:
bool getFlag();
void steFlag(bool b);
本人在B.cpp中包含了A.h
然后在某个函数中setFlag(true);,随后本人就调用getFlag()发现是返回true的;可是本人在C.cpp中在调用时发现getFlag()返回了false,可以肯定的是其他再没有修改这个变量的地方,而且C的调用在B的调用之后,这样诡异的问题,可能的原因是什么呢?本人也完全编译了一次程序,折腾大半天就是不行。还有假如本人初始化为true,那么C中调用时就返回true了。

不用加volatile,要加上extern。
不加extern的话,表面上不同的cpp #include 同一个”A.h”,实际上bool g_Flag并非全局,只是局部于本cpp单元的变量。
不信 不同的cpp可以输出各自的&g_Flag 确认。

5

加个锁全局变量被修改后又还原为初始值的诡异问题

10

引用:
Quote: 引用:

不用加volatile,要加上extern。
不加extern的话,表面上不同的cpp #include 同一个”A.h”,实际上bool g_Flag并非全局,只是局部于本cpp单元的变量。
不信 不同的cpp可以输出各自的&g_Flag 确认。

是给函数加还是变量加,本人都加了也没有作用,包括直接使用变量,不调用get函数一样是false

A.h增加一个函数 bool* getFlagAddr()
或调试getFlag断点看g_Flag的内存地址。

5

Interlock系列函数 可以对32Bits数据同步
不然就加锁
Windows 临界区,互斥量,都可以。

2

明显是定义了两个变量

5

引用:
Quote: 引用:
Quote: 引用:
Quote: 引用:

不用加volatile,要加上extern。
不加extern的话,表面上不同的cpp #include 同一个”A.h”,实际上bool g_Flag并非全局,只是局部于本cpp单元的变量。
不信 不同的cpp可以输出各自的&g_Flag 确认。

是给函数加还是变量加,本人都加了也没有作用,包括直接使用变量,不调用get函数一样是false

A.h增加一个函数 bool* getFlagAddr()
或调试getFlag断点看g_Flag的内存地址。

调试时通过查看这个变量的地址发现,B中通过setFlag改变的好像不是那个全局的bool,C后来调用的才是那个全局bool,总之就是两个地址,但同一个变量为什么会有两个地址?

本人感觉 并没有全局的bool,都是局部的bool。你可以增加一个D单元来验证一下。

5

定义在头文件中了吧,写cpp里。

5

重建全部

将你的全局变量放在一个明确的自定义命名空间里,且引用:

Quote: 引用:
Quote: 引用:

加static有用么?

看题主已经实现了set和get接口,那么何不用static限制你的全局变量的作用文件,只在本文件生效,这样也不用怕别人外部extern后改掉它

不存在在其他地方被改变的可能,原因是这个被引用:

Quote: 引用:

加static有用么?

显然没用,本人要在不同的文件中用这个变量的

引用:

贴几个单元的代码吧

代码没什么好贴得,就是主贴中说的那些东西了,现在问题就是不同的文件里这个变量的地址不同,也就是本人修改的时候(B文件)的地址和后来C中调用时的地址不相同,也就是修改的不是一个东西,但是本人确信这个变量就只有这一个,不存在什么局部有定义等等的说法

你不是有接口么,都用函数接口访问啊

2

引用:
Quote: 引用:
Quote: 引用:
Quote: 引用:

加static有用么?

显然没用,本人要在不同的文件中用这个变量的

引用:

贴几个单元的代码吧

代码没什么好贴得,就是主贴中说的那些东西了,现在问题就是不同的文件里这个变量的地址不同,也就是本人修改的时候(B文件)的地址和后来C中调用时的地址不相同,也就是修改的不是一个东西,但是本人确信这个变量就只有这一个,不存在什么局部有定义等等的说法

你不是有接口么,都用函数接口访问啊

就是用函数访问这个变量的。现在的问题是调试时这个变量的地址有多个,在B文件中修改的是地址为X的,在C文件里访问到的是地址为Y的

你试过本人46L说的方法吗?出现一个变量多个地址,明显就是在多个编译单元中出现了同名的变量啊,这一般不就是木有加extern造成的嘛。假如源文件既有c,又有cpp,估计还要加extern “C”

3

引用:

刚才调试,遇到其他空指针抛异常程序中途退出了,release运行没有这种异常,本人点击“忽略”也不行,本人想先不管这个先把这个全局变量问题搞明白了,不知道怎么忽略这个问题?

也许应该先把空指针问题解决了,这个诡异的问题就消失了。这个诡异的问题也许就是空指针问题导致的。

2

先搞清楚真正的全局变量再说啊!真正的全局怎么可能没有extern呢?

2

打数据断点看哪儿修改了

5

那就是模块的问题了,你的cpp是不是也被几个模块包含了.
那两个函数 应该导出的.

2

确定不是多线程情况下的缓存?

10

引用:
Quote: 引用:

那就是模块的问题了,你的cpp是不是也被几个模块包含了.
那两个函数 应该导出的.

昨晚突然意识到这个B.cpp是在一个dll中的源文件,而A.cpp是主程序和动态库都用的一个源文件,这样的话就形成了两种地址,一种是动态库中该变量的地址,一种是主程序中该变量的地址,在B中修改的应该是动态库中的内存空间的地址,而C中调用的是主程序中的,所以有两种地址,这个是本人的猜测,估计就是这一个原因了,那本人动态库里怎么样访问到主程序那个变量呢?假如按照现在的extern的话也是两份变量,怎么样真正共用一个变量呢?

A.cpp同时存在于主程序和动态库?那相当于有两个你定义的全局变量了,所以会有两个地址存在,读写都是不同的,

2

动态库包含进去,也是在主程序的进程空间里面的的一部分,不是独立的空间

5

引用:
Quote: 引用:
Quote: 引用:
Quote: 引用:

那就是模块的问题了,你的cpp是不是也被几个模块包含了.
那两个函数 应该导出的.

昨晚突然意识到这个B.cpp是在一个dll中的源文件,而A.cpp是主程序和动态库都用的一个源文件,这样的话就形成了两种地址,一种是动态库中该变量的地址,一种是主程序中该变量的地址,在B中修改的应该是动态库中的内存空间的地址,而C中调用的是主程序中的,所以有两种地址,这个是本人的猜测,估计就是这一个原因了,那本人动态库里怎么样访问到主程序那个变量呢?假如按照现在的extern的话也是两份变量,怎么样真正共用一个变量呢?

A.cpp同时存在于主程序和动态库?那相当于有两个你定义的全局变量了,所以会有两个地址存在,读写都是不同的,

确实是这个原因,本人现在通过动态库中的接口的返回值来判断能否在dll中修改了这个bool,也就是说dll中改不改这个已经无所谓了,改了也只是dll内部的那个,本人主程序只要后来知道dll中改没改就可以了。假如本人现在想在动态库中修改到主程序的那一份,怎么样做比较方便呢?或说主程序中的怎么样真正和dll共享一份呢?

搞清楚了原因就行了;这样可将全局变量声明到A.h中,用到这个全局全变量的文件不包含A.h,只加外部声明:extern bool b_Flag;这样就行了,在其他编译单元中该读就读,该写就写;直接用这个变量就行了;

2

个人比较认可46楼的代码。
另外说dll与exe程序处于不同地址空间的,纯属本人臆想。

5

建议把g_flag在dll中定义为进程间数据共享,然后export function提供给exe使用。
#pragma data_seg (“shareddata”)
bool g_flag = true;//共享数据
#pragma data_seg()
__declspec(dllexport) void set_flag(bool flag)
{
g_flag = flag;
}
__declspec(dllexport) bool get_flag()
{
return g_flag;
}

15

也可以把exe中的g_flag的地址(指针)传到dll中,dll保存一份地址(指针),由于备份地址可能被其他的exe修改,所以这个dll只能被这一个exe使用了,不能再被其他exe映射了。与使用dll的初衷冲突,不建议。

10

引用:
Quote: 引用:

建议把g_flag在dll中定义为进程间数据共享,然后export function提供给exe使用。
#pragma data_seg (“shareddata”)
bool g_flag = true;//共享数据
#pragma data_seg()
__declspec(dllexport) void set_flag(bool flag)
{
g_flag = flag;
}
__declspec(dllexport) bool get_flag()
{
return g_flag;
}

假如本人主程序中也要用这个全局变量呢?只有从dll中的接口来取或改?除了这样还有没有更加简便的实现共享数据的方法?

这也是一种方法,下面说说本人的方法:
1、建一个dll工程,仅包含两个文件:dll.h和dll.cpp
如下结构:
dll.h:

#pragma once
extern "C"
{
#ifdef DLL_EXPORTS
	__declspec(dllexport) extern bool g_Flag;//只导出声明,导出定义
#else
	__declspec(dllimport) extern bool g_Flag;
#endif
	__declspec(dllexport) bool getFlag();
	__declspec(dllexport) void setFlag(bool flag);
}

dll.cpp

#include "stdafx.h"
#include "dll.h"
#include <iostream>
using namespace std;
extern "C"
{
	__declspec(dllexport) bool g_Flag=true;//真正的定义在这里
	__declspec(dllexport) bool getFlag()
	{
		cout<<"dll,"<<hex<<&g_Flag<<"="<<dec<<g_Flag<<endl;//输出值的同时输出变量地址
		return g_Flag;
	}
	__declspec(dllexport) void setFlag(bool flag)
	{
		g_Flag=flag;
	}
};

2、新建一个console application Demo;Demo和dll放在同一个sln下面,Demo包含三个文件:A.h、A.cpp、main.cpp
A.h如下:

#include "../dll/dll.h"//包含dll.h中对g_Flag的声明
extern "C"
{
	__declspec(dllexport) bool getAFlag();
	__declspec(dllexport) void setAFlag(bool flag);
};

A.cpp如下:

#include "stdafx.h"
#include "A.h"
#include <iostream>
using namespace std;
extern "C"
{
	__declspec(dllexport) bool getAFlag()
	{
		cout<<"a.cpp,"<<hex<<&g_Flag<<"="<<dec<<g_Flag<<endl;//输出的同时输出地址
		return g_Flag;
	}
	__declspec(dllexport) void setAFlag(bool flag)
	{
		g_Flag=flag;
	}
};

main.cpp如下:

// ConsoleApplication3.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
using namespace std;
#pragma comment(lib,"../Debug/dll.lib")//链接库,包含了调用dll.h是声明的一切符号,如:g_Flag、getFlag(),setFlag()等符号
#include "../dll/dll.h"//导入dll.h可以完全使用g_Flag
#include "A.h"//导入A.h只为测试A.h中声明的两个函数,其实完全可以不使用A.h中的函数;只使用dll.h中的函数来完成显示与设置g_Flag的功能
int _tmain(int argc, _TCHAR* argv[])
{
		cout<<"main,"<<hex<<&g_Flag<<"="<<dec<<g_Flag<<endl;
		getFlag();
		getAFlag();
		g_Flag=false;//通过g_Flag直接修入值
		cout<<"main,"<<hex<<&g_Flag<<"="<<dec<<g_Flag<<endl;
		getFlag();
		getAFlag();
		getchar();
		setAFlag(true);/通过setAFlag()修改g_Flag值

		cout<<"main,"<<hex<<&g_Flag<<"="<<dec<<g_Flag<<endl;
		getFlag();
		getAFlag();
		getchar();
	return 0;
}

运行结果如下:
全局变量被修改后又还原为初始值的诡异问题

5

原因解析:
1、全局变量放在dll中,并在dll的头文件中只声明不定义,但需要根据定义的宏指定是导出还是导入,原因是dll.h需要本人主程序中使用,在主程序中使用时为dllimport导入符号;在dll工程中使用时为导出符号(详见dll.h);为了简单,没有将函数的导出像g_Flag那样一样写,最好是定义导出、导入宏来实现
2、在demo工程中,需要指定dll.lib链接库,其中存放了dll工程导出的符号:g_Flag、setFlag()、getFlag()等,其实就是一个符号表,指示链接程序怎么样找到符号;在使用过程中常常导出函数,变量一样可以导出,原因是都是地址(太浅显的理解了,大神们勿喷,给新手本人一些面子)
3、在demo中包含dll.h对全部导出符号的声明,这样在main.cpp中就能直接使用g_Flag的声明了,原因是demo中不包含对DLL_EXPORTS宏的定义,所以就是导入g_Flag了,其他的符号也是如此
4、在编译时,先编译dll工程,将符号导出;再编译demo工程,这样link.exe才能在dll.lib中找到 需要的符 号,如此,demo和dll中使用的是同一个变量——dll中的变量;
5、假如变量放在demo中,则不能直接达到目的,还是有办法达到目的的,但在这里不讨论
以下理解难免有错误之处,望大神不吝指点!

5

引用:
Quote: 引用:

dll中不要保存状态(static, 全局变量等),原因是dll可以被动态加载/卸载, 导致设计更复杂,更容易出问题.

屎山依然形成,本人已无力回天,只做最少的改动。呵呵

HMODULE h=LoadLibrary(“dll.dll”);//动态加载
if(h!=0)
{
FARPROC a=GetProcAddress(h,”g_Flag”);//找到g_Flag的地址,
if(a!=0)
{
cout<<hex<<a<<“=”<<dec<<*(bool*)(a)<<endl;//输出,或修改
}
}
不过正如大神所讲,dll中少保存状态,除非你确保不会在使用中动态卸载


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明全局变量被修改后又还原为初始值的诡异问题
喜欢 (0)
[1034331897@qq.com]
分享 (0)