事情是这样的,前段时间来了个新项目,大家都太忙了,没人积极搞啊。于是,领导启用了他前不久招的“得意门生”,把这活儿交给他了。
这项目其实也没很多活要干,最主要的是要从一个C++项目代码移植到平台的基础工程里,当然这个基础工程是C语言的。
这小伙,一顿操作猛如虎,风风火火搞了几周,基本功能终于跑起来了!
领导心想:哎呀果然没看错人。接着没几天领导就向上面领导吹捧一番,说这项目怎样怎样难,这谁谁又怎么怎么排除万难的,最终搞出来了。反正,领导开心,小伙的绩效也差不了哪去了!
直到最近,出了一个bug,离奇古怪,某功能在你意想不到的情况下出错。领导又派出了他的“得意门生”,噼里啪啦的,搞了一个多星期了,啥头绪也没有……
这问题可被甲方发现了的,而且临近交付样品了,这把领导急的不要不要的。
领导憋出了个大招,“三个臭皮匠顶个诸葛亮”,发动整个小组找bug……
尼玛,我也被牵连了,翻了半天也没头绪,这代码一坨坨的具体是啥含义,我也懒得去研究。
干脆直接编译,准备仿真看看的,一段编译警告有点陌生,又特别碍眼,类似长这样的:
[elxr] (warning #222) The program contains no reference to _ctors.
The following C++ dynamic initialization routines will probably not get called:
___sti___12_cpp_main_cpp_mm from cpp_main.o
main() (from c_main.o) was probably not compiled as C++.
呵呵,这只是个warning,估计那小伙也不care!
我第一次见这种warning,很好奇,也还不清楚是否跟这个问题有关,不过我预感八九不离十了。
怎么排查或者确认这个warning就是因为项目的那个问题呢?
1. 直接仿真,查看汇编,这个___sti___12_cpp_main_cpp_mm是什么玩意
这个其实不好看懂,但能肯定的是,在仿真时,它没被调用!
也不好找到直接的答案,但能知道这个_ctor是C++的构造函数,另外还有个叫_dtor。
综合上面的信息看,好像是在说C++的构造函数没有调用,这个构造函数又跟___sti___12_cpp_main_cpp_mm有关。
其实也不用那么麻烦,现在什么年代了,有AI啊,直接问
虽然没有找到具体的问题点,但是可以大概找到,这个warning说,这个代码里面有C++的构造函数没调用,也提到这个C++动态初始化函数___sti___12_cpp_main_cpp_mm没调用。
也许你会去看看那个main函数,好像也没什么特别错误啊!
但是呢,不妨将想象力放开点,是不是说main函数要调用这个___sti___12_cpp_main_cpp_mm呢?
等等,你是说main函数要调用一个C++的动态初始化函数?构造函数来的?
___sti___12_cpp_main_cpp_mm这种函数也不像是直接在main函数调用的啊!
啊!不对,这个是C语言文件里的main函数,握草,应该是C艹函数!
是不是将main函数里的xxx.c改成xxx.cpp就好了呢?
直接试了下,还真是,C你main的,居然跟C艹不一样!
我压制住我心中的激动,让领导和那帮家伙再愁一会,我还没搞清楚C main和C++ main的区别,不然被领导追问,我说不出来来龙去脉,岂不是说我乱猜的,瞎猫碰到死耗子,没啥技术含量!哼!
仿真C++ main的汇编可以看到__main是库里面的函数,其会调用这个___sti___12_cpp_main_cpp_mm函数。
我还是选择等等。因为,这样无法说明怎么直接导致了项目的那个问题啊!并且,有些项目C和C++混合的工程,用C语言main函数也没问题啊,而且也没提示那个warning!
如果领导问我,我也说不出为什么,他还是会说我猜的!
为了保证,我的回答万不一失,我选择低调继续研究下。
再回头看看AI回答的那段话,它提到了构造函数和静态对象,难道是静态对象的初始化赋值问题?
于是,我尝试了好多次不同的场景,终于模拟出了这种情况。(我把完整测试源码附在文末)
typedef struct
{
unsigned int id;
unsigned char len;
} Msg;
class Test {
public:
Test() { }
int n;
static int st_n;
static Msg st_m;
};
Msg mm={0xaa,0x55};
Msg Test::st_m = mm;
int Test::st_n = 0x123;
其实,这个___sti___12_cpp_main_cpp_mm函数里面就是静态变量的初始化!
这里这个-0x142FF94值,即0xFEBD006C,实际是对应MAP文件中的.bss段中的_st_m__4Test变量。
.data febd0000+000008 _mm
.text 00000b9e+000002 _multiBreak
.text 00000b9a+000000 _multiCall
.text 00001b2c+00004a _open
.text 000018b6+000084 _raise
.text 00001b06+000026 _read
.text 00001856+000060 _signal
.bss febd006c+000008 _st_m__4Test
.data febd0008+000004 _st_n__4Test
那么,这个_st_m__4Test变量是什么呢?就是上面代码Test类的静态成员st_m。此时很容易就能理解到,这个___sti___12_cpp_main_cpp_mm函数就是执行了Msg Test::st_m = mm;的赋值。汇编中的-0x1430000就是对应MAP中.data段的_mm变量,就是Msg mm={0xaa,0x55};这个变量。通过仿真就可以证实这些内容。
所以,C++的main跟C的main不一样,C++的main更有内涵,其中还藏着一个__main,且其里面还执行了静态成员的初始化。
-
-
C++的main,内敛且有内涵,还悄悄地帮你做静态成员初始化。
于是,我打算好好科普C和C++的main main的时候,领导却说,那谁谁已经再同步C++项目的启动部分文件就OK了,而且已经检讨移植项目文件不彻底问题了,问题已经解决了。于是,让我去忙别的去了。
extern int test_func(void);
#ifndef CPP_MAIN
int main(void)
{
test_func();
return 0;
}
#endif
typedef struct
{
unsigned int id;
unsigned char len;
} Msg;
class Test {
public:
Test() { }
int n;
static int st_n;
static Msg st_m;
};
Msg mm={0xaa,0x55};
Msg Test::st_m = mm;
int Test::st_n = 0x123;
Test gt;
extern "C" {
int test_func(void)
{
Test t;
int x;
t.n = 12;
if(t.st_n == 0x123)
{
x = 100;
}
if(t.st_m.id == 0xaa)
{
x = 200;
}
if(gt.st_m.id == 0xaa)
{
x = 2200;
}
return x;
}
}
#ifdef CPP_MAIN
int main()
{
test_func();
return 0;
}
#endif
我知道你看完本文就退出的了,就算收藏也是吃灰的,不如你点个转发、点赞和在看再走,我是会很感激你的哦!