• 全部
...

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

starytx 2014-01-03 11:06:24
比如有一个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了。
...全文
给本帖投票
1940 88 打赏 收藏 转发到动态 举报
写回复
用AI写文章
88 条回复
切换为时间正序
请发表友善的回复…
发表回复
chanchan0010 2016-07-04
  • 打赏
  • 举报
回复
你的C和B属于两个不同的进程吧,进程之间不能简单的公用变量
赵4老师 2014-01-05
  • 打赏
  • 举报
回复
C里对于多个文件共享一个全局变量的语法是在一个.h文件中 public int globalVar1; 其它文件中 extern int globalVar1;
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 84 楼 lianshaohua 的回复:
维护项目太正常了,我刚到公司的时候,还维护过将近20年的一个计费程序: 1、效率那叫一个低,在小型机上并发量20几条每秒, 2、代码那叫一个烂,锁那叫一个多, 不过程序既然卖出去了,就得维护了,于是维护了一两年,也无大事,逐步解决效率问题,找到原因,从20几条提升级300条,过年过节不再有人值守了,以前必须有人值守,不断的重启,呵呵 在去年,实在不想这么痛苦了,终于狠心重新架构、写代码、复用些代码片,目前测试结果为 8000条每秒的并发量,已民经足够企业卖十几年了,如果有一天我们做了并发 8000条每秒的单,我们就发达了(不过目前来看是不可能的事情,所以我推测重构后的计费系统能用十几至二十年的时间) 综合来讲:要么享受,要么反抗。
呵呵,那就享受了,好死不如赖活着。ok,再次感谢大家的回复!
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
引用 78 楼 starytx 的回复:
引用 73 楼 nil_affliction 的回复:
也可以把exe中的g_flag的地址(指针)传到dll中,dll保存一份地址(指针),由于备份地址可能被其他的exe修改,所以这个dll只能被这一个exe使用了,不能再被其他exe映射了。与使用dll的初衷冲突,不建议。
实际上对于我这个项目来说这样也可以,因为这个dll是主程序专用的,而且也之辈加载了一次。我也试了这种方法是可行的。不过我的程序中类似的变量还有不少,我现在是没有功夫去一个一个排查了,只能暂时这样处理这个变量了,搞维护的伤不起,这隐患以前的开发没有发现,现在轮到我悲催了。
维护项目太正常了,我刚到公司的时候,还维护过将近20年的一个计费程序: 1、效率那叫一个低,在小型机上并发量20几条每秒, 2、代码那叫一个烂,锁那叫一个多, 不过程序既然卖出去了,就得维护了,于是维护了一两年,也无大事,逐步解决效率问题,找到原因,从20几条提升级300条,过年过节不再有人值守了,以前必须有人值守,不断的重启,呵呵 在去年,实在不想这么痛苦了,终于狠心重新架构、写代码、复用些代码片,目前测试结果为 8000条每秒的并发量,已民经足够企业卖十几年了,如果有一天我们做了并发 8000条每秒的单,我们就发达了(不过目前来看是不可能的事情,所以我推测重构后的计费系统能用十几至二十年的时间) 综合来讲:要么享受,要么反抗。
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
引用 82 楼 starytx 的回复:
引用 81 楼 mujiok2003 的回复:
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中少保存状态,除非你确保不会在使用中动态卸载
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 81 楼 mujiok2003 的回复:
dll中不要保存状态(static, 全局变量等),因为dll可以被动态加载/卸载, 导致设计更复杂,更容易出问题.
屎山依然形成,我已无力回天,只做最少的改动。呵呵
mujiok2003 2014-01-04
  • 打赏
  • 举报
回复
dll中不要保存状态(static, 全局变量等),因为dll可以被动态加载/卸载, 导致设计更复杂,更容易出问题.
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 79 楼 lianshaohua 的回复:
1、在主程序中不能定义,否则就出现了两个同名的变量 2、读取与修改变量均通过dll提供的导出接口来完成; 3、你完全可以不在主程序中直接使用变量值,而是调用dll导出的getFlag()和setFlag()来完成使用的目的,这样就不需要在dll中导出g_Flag了; 4、使用全局变量有危险的,会导致出错难以查找,尤其是在多线程编程是更是存在此问题,所以如果在多个地方需要即读又写,建议还是给dll导出的getFlag()、setFlag()加入互斥锁,同时不在dll中导出g_Flag,以免有的越过锁直接修改变量;通过调用加锁的getFlag()和setFlag()能保证所有的值都是正确的,而不会出现一处修改,另一处未同步的情况,所以不建议在主程序中直接使用g_Flag变量,不论读与写,都不建议
等于说主程序中不定义了,所有的修改、读取都操作dll中的变量?这样也不是不可以。哎,至于你说的少用全局变量问题,我也是没办法,这个项目已经堆成屎山了,我也懒得做大的改动了,只能是修改越少越好,省的引出问题惹麻烦。
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
引用 77 楼 starytx 的回复:
[quote=引用 76 楼 lianshaohua 的回复:] 原因解析: 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中,则不能直接达到目的,还是有办法达到目的的,但在这里不讨论 以下理解难免有错误之处,望大牛不吝指教!
你的意思是在主程序中没有定义这个全局变量,而只是调用dll中的函数来操作dll中的那个变量?可是我主程序中也有2处要修改这个变量的,当然如果找你照样的话我主程序中也要定义一个,处理的逻辑就是当需要读取这个变量值时就调用dll中的get来获取,当主程序修改了主程序的变量时需要调用dll的set来同时修改掉dll中的变量。其实还是要维护两个变量。同时你这样的调用方法好像是隐式加载dll,我程序里都是显式加载的。[/quote] 1、在主程序中不能定义,否则就出现了两个同名的变量 2、读取与修改变量均通过dll提供的导出接口来完成; 3、你完全可以不在主程序中直接使用变量值,而是调用dll导出的getFlag()和setFlag()来完成使用的目的,这样就不需要在dll中导出g_Flag了; 4、使用全局变量有危险的,会导致出错难以查找,尤其是在多线程编程是更是存在此问题,所以如果在多个地方需要即读又写,建议还是给dll导出的getFlag()、setFlag()加入互斥锁,同时不在dll中导出g_Flag,以免有的越过锁直接修改变量;通过调用加锁的getFlag()和setFlag()能保证所有的值都是正确的,而不会出现一处修改,另一处未同步的情况,所以不建议在主程序中直接使用g_Flag变量,不论读与写,都不建议
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 73 楼 nil_affliction 的回复:
也可以把exe中的g_flag的地址(指针)传到dll中,dll保存一份地址(指针),由于备份地址可能被其他的exe修改,所以这个dll只能被这一个exe使用了,不能再被其他exe映射了。与使用dll的初衷冲突,不建议。
实际上对于我这个项目来说这样也可以,因为这个dll是主程序专用的,而且也之辈加载了一次。我也试了这种方法是可行的。不过我的程序中类似的变量还有不少,我现在是没有功夫去一个一个排查了,只能暂时这样处理这个变量了,搞维护的伤不起,这隐患以前的开发没有发现,现在轮到我悲催了。
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 76 楼 lianshaohua 的回复:
原因解析: 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中,则不能直接达到目的,还是有办法达到目的的,但在这里不讨论 以下理解难免有错误之处,望大牛不吝指教!
你的意思是在主程序中没有定义这个全局变量,而只是调用dll中的函数来操作dll中的那个变量?可是我主程序中也有2处要修改这个变量的,当然如果找你照样的话我主程序中也要定义一个,处理的逻辑就是当需要读取这个变量值时就调用dll中的get来获取,当主程序修改了主程序的变量时需要调用dll的set来同时修改掉dll中的变量。其实还是要维护两个变量。同时你这样的调用方法好像是隐式加载dll,我程序里都是显式加载的。
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
原因解析: 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中,则不能直接达到目的,还是有办法达到目的的,但在这里不讨论 以下理解难免有错误之处,望大牛不吝指教!
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
再按一下任意键的结果:
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
引用 72 楼 starytx 的回复:
[quote=引用 71 楼 nil_affliction 的回复:]
建议把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中的接口来取或者改?除了这样还有没有更加简便的实现共享数据的方法?[/quote]

这也是一种方法,下面说说我的方法:

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;
}





运行结果如下:

nilx 2014-01-04
  • 打赏
  • 举报
回复
也可以把exe中的g_flag的地址(指针)传到dll中,dll保存一份地址(指针),由于备份地址可能被其他的exe修改,所以这个dll只能被这一个exe使用了,不能再被其他exe映射了。与使用dll的初衷冲突,不建议。
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 71 楼 nil_affliction 的回复:
建议把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中的接口来取或者改?除了这样还有没有更加简便的实现共享数据的方法?
nilx 2014-01-04
  • 打赏
  • 举报
回复
建议把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; }
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
这个问题终于解决了,待我慢慢道来
ztenv 版主 2014-01-04
  • 打赏
  • 举报
回复
引用 68 楼 starytx 的回复:
引用 65 楼 lianshaohua 的回复:
在主程序中包含a.cpp,在dll工程中也包含了a.cpp肯定是两份的,因为a.cpp中有b_Flag的声明和定义
我如果把A.cpp从动态库项目中删除的话,会报无法解析的外部符号xxx.也就是dll中用到的几个函数就找不到函数体了,即使我假如那个A.h,并且在它里边extern 那些函数声明也是不行的,也就是缺实现,可我的这个全局变量又定义在了这个A.cpp中,也找不到其他合适的地方放,不知道如何搞了
仅用于同一个工程中,有时间我再看看,不知道有没有办法;
starytx 2014-01-04
  • 打赏
  • 举报
回复
引用 65 楼 lianshaohua 的回复:
在主程序中包含a.cpp,在dll工程中也包含了a.cpp肯定是两份的,因为a.cpp中有b_Flag的声明和定义
我如果把A.cpp从动态库项目中删除的话,会报无法解析的外部符号xxx.也就是dll中用到的几个函数就找不到函数体了,即使我假如那个A.h,并且在它里边extern 那些函数声明也是不行的,也就是缺实现,可我的这个全局变量又定义在了这个A.cpp中,也找不到其他合适的地方放,不知道如何搞了
加载更多回复(68)

65,179

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧

手机看
关注公众号

关注公众号

客服 返回
顶部