相同的 C++ 代码,不同的 link 选项会导致不同的行为?
Same C++ code, different link option leads to different behavior?
我有 2 个 cpp 文件,每个文件都声明了 1 个 class+1 个函数+1 个静态对象:
$ cat mya.cpp
#include<stdio.h>
struct A{
A(){printf("%s\n",__FUNCTION__);}
};
void fa(){printf("%s\n",__FUNCTION__);}
static A s_obj;
$ cat myb.cpp
#include<stdio.h>
struct B{
B(){printf("%s\n",__FUNCTION__);}
};
void fb(){printf("%s\n",__FUNCTION__);}
static B s_obj;
然后主函数调用 "fb",但不调用 "fa"。
$ cat nm.cpp
void fb();
int main()
{
fb();
return 0;
}
我尝试以不同的方式编译和link这些文件:
g++ -c mya.cpp -fPIC
g++ -c myb.cpp -fPIC
ar -rvs libmya.a mya.o
ar -rvs libmyb.a myb.o
ar -rvs libmystatic.a mya.o myb.o
g++ --shared -o libmyshare.so mya.o myb.o
g++ --shared -o libadyn.so mya.o
g++ --shared -o libbdyn.so myb.o
g++ nm.cpp -o use1StaticLib -lmystatic -L.
g++ nm.cpp -o use2StaticLib -lmyb -lmya -L.
g++ nm.cpp -o use1DynamicLib -lmyshare -L.
g++ nm.cpp -o use2DynamicLib -ladyn -lbdyn -L.
g++ nm.cpp -o useDirect mya.cpp myb.cpp
然后我发现 5 个可执行文件有不同的行为:
$ ./useDirect
A
B
fb
$ ./use1DynamicLib
A
B
fb
$ ./use2DynamicLib
B
fb
$ ./use1StaticLib
A
B
fb
$ ./use2StaticLib
B
fb
相同的代码,不同的行为,如何才能不混淆?
我好像找到了一些线索,只要把mya.cpp和myb.cpp打包成不同的.a/.so文件,那么"A s_obj"就不会构造了。为什么? A 的构造函数有副作用,我没有指定任何 -O 优化。
如果是因为"A s_obj"是一个没有被使用的对象,所以没有linked,那么,"B s_obj"也没有被main函数使用,为什么总是构造呢?
需要听听各位专家的讲解!
我觉得这里有2个效果需要区分
首先,当您调用外部库时,动态链接器是惰性的,只会加载实际调用的库。但它会加载整个库。使用 use2StaticLib
,您只使用 lib b,因此它只会加载这个。 use2DynamicLib
也是如此
二、写这个:
g++ --shared -o libmyshare.so mya.o myb.o
等同于使用
g++ --shared -o libmyshare.so myfile.o
其中 myfile.cpp 是 mya.cpp
和 myb.cpp
的串联 您只是将两个目标文件复制到一个更大的文件中 libmyshare.so
所以这就是为什么当您调用一个myb.cpp
文件的功能,应用程序加载整个 libmyshare.so
库。然后在这种情况下调用 A 和 B 的构造函数来初始化静态对象。这不会改变 libmyshare.so
的编译是静态的还是动态的。
在任何这些场景中,libA 和 libB 的代码都包含在您的应用程序代码中(即使是静态编译时),并且所有库都通过动态链接调用。
希望对您有所帮助!
我有 2 个 cpp 文件,每个文件都声明了 1 个 class+1 个函数+1 个静态对象:
$ cat mya.cpp
#include<stdio.h>
struct A{
A(){printf("%s\n",__FUNCTION__);}
};
void fa(){printf("%s\n",__FUNCTION__);}
static A s_obj;
$ cat myb.cpp
#include<stdio.h>
struct B{
B(){printf("%s\n",__FUNCTION__);}
};
void fb(){printf("%s\n",__FUNCTION__);}
static B s_obj;
然后主函数调用 "fb",但不调用 "fa"。
$ cat nm.cpp
void fb();
int main()
{
fb();
return 0;
}
我尝试以不同的方式编译和link这些文件:
g++ -c mya.cpp -fPIC
g++ -c myb.cpp -fPIC
ar -rvs libmya.a mya.o
ar -rvs libmyb.a myb.o
ar -rvs libmystatic.a mya.o myb.o
g++ --shared -o libmyshare.so mya.o myb.o
g++ --shared -o libadyn.so mya.o
g++ --shared -o libbdyn.so myb.o
g++ nm.cpp -o use1StaticLib -lmystatic -L.
g++ nm.cpp -o use2StaticLib -lmyb -lmya -L.
g++ nm.cpp -o use1DynamicLib -lmyshare -L.
g++ nm.cpp -o use2DynamicLib -ladyn -lbdyn -L.
g++ nm.cpp -o useDirect mya.cpp myb.cpp
然后我发现 5 个可执行文件有不同的行为:
$ ./useDirect
A
B
fb
$ ./use1DynamicLib
A
B
fb
$ ./use2DynamicLib
B
fb
$ ./use1StaticLib
A
B
fb
$ ./use2StaticLib
B
fb
相同的代码,不同的行为,如何才能不混淆?
我好像找到了一些线索,只要把mya.cpp和myb.cpp打包成不同的.a/.so文件,那么"A s_obj"就不会构造了。为什么? A 的构造函数有副作用,我没有指定任何 -O 优化。
如果是因为"A s_obj"是一个没有被使用的对象,所以没有linked,那么,"B s_obj"也没有被main函数使用,为什么总是构造呢?
需要听听各位专家的讲解!
我觉得这里有2个效果需要区分
首先,当您调用外部库时,动态链接器是惰性的,只会加载实际调用的库。但它会加载整个库。使用 use2StaticLib
,您只使用 lib b,因此它只会加载这个。 use2DynamicLib
二、写这个:
g++ --shared -o libmyshare.so mya.o myb.o
等同于使用
g++ --shared -o libmyshare.so myfile.o
其中 myfile.cpp 是 mya.cpp
和 myb.cpp
的串联 您只是将两个目标文件复制到一个更大的文件中 libmyshare.so
所以这就是为什么当您调用一个myb.cpp
文件的功能,应用程序加载整个 libmyshare.so
库。然后在这种情况下调用 A 和 B 的构造函数来初始化静态对象。这不会改变 libmyshare.so
的编译是静态的还是动态的。
在任何这些场景中,libA 和 libB 的代码都包含在您的应用程序代码中(即使是静态编译时),并且所有库都通过动态链接调用。
希望对您有所帮助!