对程序的可移植性而言,各种Lunix(各类unix和linux)系统之间的差异不大,这里主要考虑程序在Lunix与Windows系统之间互相移植的问题,而且限定在C/C++语言范围类,以典型的两种编译环境,即Lunix下的GCC和MS的VC编译工具集为例来阐述问题。
一个编译单元,即这里所指的一个C/C++源程序文件,它所编译出来的二进制目标文件通常被用来构建一个动态共享库,或者被连接进一个可执行程序文件。这两种不同的用途使得我们在编译时需要多考虑一些因素(在Win环境下尤其如此)。具体有:
(1) C/C++头文件在被用于构建动态共享库的源程序文件使用时,以及在被使用动态共享库的其他源程序文件使用时,表现出不同的行为和作用。
(2) 全局对象(变量,函数,类的实例)的声明和定义,以及初始化。
(3) Lunix下的(动态)共享库中所有的全局对象是外部可见的(一般的连接方法下);Windows下的动态(共享)库中的全局对象的外部可见性可由程序员指定。(个人愚见,win下的思路是好的,但是实现的方法不好,包括使用__declspec...,鉴于C语言中的全局对象在申明时默认为extern类型的,可以考虑使用这个保留字来指定全局对象的外部可见性和指明某(些)全局变量来自动态共享库。)
代码是最好的注释,举例如下:
1. 文件f.h
#ifndef F_H
#define F_H
#ifdef F_FULL_LINKED
# ifdef F_BEING_COMPILED
# define F_LINK_SPEC
# define F_DECL_SPEC
# else
# define F_LINK_SPEC
# define F_DECL_SPEC extern
# endif
#else /* To be dynamic linked */
# ifdef F_BEING_COMPILED
# ifdef WIN32
# define F_LINK_SPEC __declspec(dllexport)
# define F_DECL_SPEC
# else
# define F_LINK_SPEC
# define F_DECL_SPEC
# endif
# else
# ifdef WIN32
# define F_LINK_SPEC __declspec(dllimport)
# define F_DECL_SPEC
# else
# define F_LINK_SPEC
# define F_DECL_SPEC extern
# endif
# endif
#endif
# define F_LINK_DECL F_LINK_SPEC F_DECL_SPEC
#ifdef F_GLOBAL_INIT
F_LINK_SPEC int g_f = 1;
F_LINK_SPEC int gfA[2] = {1, 2};
#else
F_LINK_SPEC extern int g_f;
F_LINK_SPEC extern int gfA[2];
#endif
F_LINK_DECL int f(int a);
#endif /* F_H */
2. 文件f.c
#include
#define F_BEING_COMPILED
#define F_GLOBAL_INIT
#include "f.h"
int f(int a)
{
return a * (gfA[0] + gfA[1]);
}
3. 文件t.c
#include
#include "f.h"
int main()
{
gfA[0]++, gfA[1]++;
printf("g_f = %d, f(1) returns %d\n", g_f, f(1));
return 0;
}
说明:有些预处理器不支持类如 #define F_LINK_DECL F_LINK_SPEC F_DECL_SPEC 的预定义方法。
在Linux下,使用命令行 gcc -E f.c > f.i; gcc -E t.c > t.i 生成的预处理代码如下:
预处理后的文件f.i:
......
# 2 "f.c" 2
# 1 "f.h" 1
# 35 "f.h"
int g_f = 1;
int gfA[2] = {1, 2};
int f(int a);
# 7 "f.c" 2
int f(int a)
{
return a * (gfA[0] + gfA[1]);
}
预处理后的文件t.i:
......
# 2 "t.c" 2
# 1 "f.h" 1
# 38 "f.h"
extern int g_f;
extern int gfA[2];
extern int f(int a);
# 4 "t.c" 2
int main()
{
gfA[0]++, gfA[1]++;
printf("g_f = %d, f(1) returns %d\n", g_f, f(1));
return 0;
}
然后,可以通过命令行 gcc -c -fPIC -o f.o f.c; gcc -c -fPIC -o t.o t.c和命令行 gcc -shared -o libf.so f.o; gcc -o t -L. -lf t.o 生成共享(动态)库和可执行程序。
在Windows的VC6.o编译环境下,使用命令行 cl /E /DWIN32 f.c > f.i 和 cl /E /DWIN32 t.c > t.i 生成的预处理代码如下:
预处理后的文件f.i:
......
__declspec(dllexport) int g_f = 1;
__declspec(dllexport) int gfA[2] = {1, 2};
#line 41 "f.h"
__declspec(dllexport) int f(int a);
#line 45 "f.h"
#line 7 "f.c"
int f(int a)
{
return a * (gfA[0] + gfA[1]);
}
预处理后的文件t.i:
......
__declspec(dllimport) extern int g_f;
__declspec(dllimport) extern int gfA[2];
#line 41 "f.h"
__declspec(dllimport) int f(int a);
#line 45 "f.h"
#line 4 "t.c"
int main()
{
gfA[0]++, gfA[1]++;
printf("g_f = %d, f(1) returns %d\n", g_f, f(1));
return 0;
}
然后,可以通过命令行 cl /c /DWIN32 /Fof.obj f.c 和 cl /c /DWIN32 /Fot.obj t.c 以及命令行 link /DLL /IMPLIB:libf.lib /OUT:libf.dll f.obj 和 cl /Fet.exe t.obj /link /LIBPATH:.\ libf.lib(或 cl -o t.exe t.obj /link /LIBPATH:.\ libf.lib) 生成(共享)动态库和可执行程序。
若不生成(共享)动态库而直接构建可执行程序,则可以在Windows的VC6.o编译环境下通过命令行 cl /DWIN32 /DF_FULL_LINKED /Fetest.exe f.c t.c (或 cl -o test.exe /DWIN32 /DF_FULL_LINKED f.c t.c)。源程序经过预处理后的内容如下(使用命令行 cl /E /DWIN32 /DF_FULL_LINKED f.c > f.i 和 cl /E /DWIN32 /DF_FULL_LINKED t.c > t.i):
文件f.i的内容:
......
int g_f = 1;
int gfA[2] = {1, 2};
#line 41 "f.h"
int f(int a);
#line 45 "f.h"
#line 7 "f.c"
int f(int a)
{
return a * (gfA[0] + gfA[1]);
}
文件t.i的内容:
......
extern int g_f;
extern int gfA[2];
#line 41 "f.h"
extern int f(int a);
#line 45 "f.h"
#line 4 "t.c"
int main()
{
gfA[0]++, gfA[1]++;
printf("g_f = %d, f(1) returns %d\n", g_f, f(1));
return 0;
}
在Lunix环境下,使用命令行 gcc -E -DF_FULL_LINKED f.c > f.i; gcc -E -DF_FULL_LINKED t.c > t.i 可得到类似的预处理后的结果。
采用命令行的编译方法,可以写出一份跨平台的Makefile文件。
以上的这些方法,可以用于开发跨平台的软件项目。
阅读(833) | 评论(0) | 转发(0) |