尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

VS2010下编译GSL 1.8为x86静态库的完整实践指南

VS2010下编译GSL 1.8为x86静态库的完整实践指南 简介一套面向C/C开发者的GSL 1.8科学计算库预编译资源基于Visual Studio 2010构建仅支持32位x86Windows环境。GSL作为开源数值计算库覆盖线性代数、随机数生成、傅立叶变换、常微分方程求解等常用功能适合需要在老旧32位系统或依赖32位组件的工程中快速引入科学计算能力的开发者。压缩包约4.35MB内部以VS2010编译的静态/动态库文件、GSL头文件、项目配置说明及少量示例代码为核心省去源码编译与依赖配置的繁琐过程。当前已有161人学习/下载资源面向具备基础C项目配置经验的用户可直接将库路径与头文件目录挂接到VS2010工程完成链接后调用GSL函数。与自行编译相比该包还附带配置注意事项能帮助避开32位环境下常见的运行时库不匹配问题提升集成效率。 最近在处理一个老项目时对方要求我在 Visual Studio 2010 环境下把 GNU Scientific LibraryGSL编成 x8632 位的库接进去。GSL 是科学计算里很常用的 C 数学库矩阵运算、特殊函数、随机数、曲线拟合这些都有现成实现但麻烦在于官方发布的 gsl-1.8 源码包是用 autotools 那套组织的Windows 下既没有现成的 .vcprojVS2010 也不会自己生成。网上能找到的教程大半是 Linux 下的命令要么就是 MinGW/msys 的流程真要在 VS2010 里编出 x86 库还是得自己手动搭工程。这篇就把我踩过的坑和完整步骤整理出来给同样被老工具链卡住的朋友做个参考。1. 方案选型为什么用源码编译而不是下载现成库1.1 老工具链下的“版本匹配”问题VS2010 的 C 编译器基本停留在 C89/C90 标准对 C99 的支持非常有限。GSL 2.x 之后的版本大量使用了 C99 的声明方式和特性在 VS2010 下面编译会撞出各种语法错误改源码的成本比想象中高得多。而 gsl-1.8 是 2008 年前后发布的版本源码里 C99 的特性用得很少跟 VS2010 的时代基本对得上。我实际编译下来gsl-1.8 在 VS2010 下只需要做少量适配就能通过这是选这个版本最直接的理由。1.2 为什么放弃 DLL选择静态库GSL 官方在 Linux 下默认生成动态库到了 Windows 想编 DLL 就得处理一堆 __declspec(dllexport/dllimport) 的导出声明但 GSL 源码里并没有为 Windows DLL 准备这些宏硬做会非常痛苦。静态库就没有这个烦恼把所有 .c 文件编成 .obj 再打包成 .lib 即可引用方链接时把 .lib 加进去就行不牵扯运行时 DLL 的分发和部署。缺点就是最终 exe 会变大一些但对老项目来说完全能接受。我个人建议直接走静态库路线省心。1.3 两个库的拆分libgsl 与 libgslcblasGSL 把矩阵运算和 BLAS 接口拆开了官方编译会生成两个库libgsl 和 libgslcblas。gslcblas 是 GSL 自带的 CBLAS 参考实现源码在 cblas 目录下。就算你不关心 BLAS 性能最终链接的时候两个库也都得带上因为 libgsl 内部有很多函数会引用 cblas 的符号。所以我在 VS2010 里建了两个工程分别输出 gsl.lib 和 gslcblas.lib并设置 gsl 工程依赖 gslcblas这样每次重新编译顺序不会乱。2. x86 平台配置的核心细节2.1 Win32 平台就是 x86VS2010 新建工程时默认的解决方案平台叫 Win32这个平台无论在 32 位还是 64 位 Windows 上生成的代码都是 32 位的 x86 指令。这个项目明确要求 x86那只需要保持默认平台即可。有一点要特别提醒如果你的目标程序是 x64那库的位数也得跟着是 64 位绝对不能让 x86 的 .lib 被 x64 的 exe 链接链接器会直接报架构不兼容。实践中最常见的报错是 LNK1112一看到这个错误码先检查是不是位数没对齐。2.2 预处理定义三件套加一个打开工程属性C/C → Preprocessor → Preprocessor Definitions至少要加这几个宏宏作用_USE_MATH_DEFINES让 math.h 暴露 M_PI 等数学常量GSL 源码里大量使用 M_PI_CRT_SECURE_NO_WARNINGS屏蔽 strcpy、sprintf 等函数的 deprecated 警告_CRT_NONSTDC_NO_DEPRECATE屏蔽使用 POSIX 名称时触发的 deprecate 警告inline__inline把 C99 的 inline 关键字映射成 MSVC 认识的 __inline最后一个 inline__inline 很关键。VS2010 的 C 编译器不认 C99 的 inline 关键字而 GSL 的 gsl_math.h 等头文件里用了 static inline 函数不加这个宏编译阶段就会报 C2057 或 C2145 之类看不懂的错误。把它加进去之后大部分语法问题直接消掉。2.3 运行时库选择务必跟调用方保持一致这是新手最容易踩的坑。C/C → Code Generation → Runtime Library 有两个常用选项/MT静态多线程和 /MD动态多线程 DLL。选择原则只有一个GSL 库怎么编调用方 exe 就怎么编两边必须一致。如果库用 /MD 而 exe 用 /MT链接阶段大概率报 LNK2005 重复符号或者 LNK2038 运行时库不匹配程序即使勉强编过去运行阶段也可能因为堆管理器不一致而崩溃。我接手的老项目其他模块统一用的 /MD所以我 Release 配置也选 Multi-threaded DLL (/MD)。另外注意选了 /MD 之后目标机器需要安装 VC 2010 运行库vcredist_x86.exe否则程序启动会提示找不到 MSVCR100.dll这个分发细节别漏了。3. 完整的编译实操过程3.1 第一步解压源码了解目录结构从 GSL 官网下载 gsl-1.8.tar.gzWindows 下用 7-Zip 解压注意解压到纯英文无空格的路径比如 D:\libs\gsl-1.8避免 VS2010 对中文路径和空格路径处理异常。解压后你会发现目录下是一堆模块子目录linalg、matrix、vector、specfunc、fft、integration、histogram、rng、randist、poly、fit 等等公共头文件统一放在 gsl 目录里每个模块目录下是对应的实现 .c 文件以及 test.c 这类测试文件。3.2 第二步创建两个静态库工程打开 VS2010新建一个空解决方案然后添加两个 Win32 Console Application 类型的空项目一个叫 gsl一个叫 gslcblas。创建时务必在向导里勾上 Empty project否则 VS 会生成一个带 main 函数的 .cpp后面还得删。建完之后分别在项目属性 → General → Configuration Type 里改成 Static library (.lib)平台保持 Win32Configuration 选 Release。注意 VS2010 的向导默认会生成 .cpp 扩展名的文件而 GSL 源码全是 .c。VS 是根据扩展名决定用 C 还是 C 编译器编译的.c 按 C 编译.cpp 按 C 编译所以千万别把 GSL 源文件改成 .cppC 编译器对类型转换的检查更严格会多出一堆本可以避免的报错。3.3 第三步把源文件加入工程这一步最费时间但没什么技术含量。把 gsl-1.8 下除 cblas 和 gsl这是头文件目录之外的模块目录里的 .c 文件全部拖进 gsl 工程的 Source Filescblas 目录下的 .c 文件全部拖进 gslcblas 工程。这里有两个要点第一test.c 以及任何带 main 函数的测试文件都不要加否则链接时会报 main 重定义第二建议在工程里按模块建虚拟文件夹做归类几百个文件堆在一起后续想定位某个函数会非常痛苦。我的做法是在工程里建了 linalg、specfunc、rng 等虚拟目录跟源码目录结构一一对应排查问题的时候能顺着目录快速找到对应文件。3.4 第四步设置头文件路径GSL 源码里的包含写法都是 #include gsl/gsl_math.h 这种带 gsl/ 前缀的形式所以 Additional Include Directories 必须指向 gsl-1.8 解压后的根目录也就是包含 gsl 子目录的那个父目录。只把 gsl 子目录本身加进去是编译不过的因为头文件之间互相引用时写的也是 gsl/xxx.h 路径。gslcblas 工程要额外把 cblas 子目录加进包含路径因为 CBLAS 的实现文件引用的是同目录下的 cblas.h。3.5 第五步配置预处理宏并开始编译按 2.2 节的表格把宏配置好然后直接 CtrlShiftB 编译。第一次编译一定会报几个错误最常见的就是 inline 语法和 M_PI 未定义这两个用预处理宏就能解决。剩下的个别文件可能是 C99 风格的变量声明放在了语句后面VS2010 的 C 编译器不认这种写法需要手动把声明挪到函数开头。gsl-1.8 里这类问题不多逐个打开改完即可。两个工程都编译通过后看一下输出目录Release 文件夹下就有 gsl.lib 和 gslcblas.lib。3.6 第六步写最小测试程序验证库编出来不代表能用必须实际链接测试。我新建了一个 Win32 Console Application在 main 里同时调用了特殊函数和矩阵接口#include stdio.h #include gsl/gsl_sf_bessel.h #include gsl/gsl_matrix.h int main(void) { double y gsl_sf_bessel_J0(1.0); printf(J0(1.0) %.18f\n, y); gsl_matrix *m gsl_matrix_alloc(3, 3); gsl_matrix_set_all(m, 1.0); printf(matrix(1,1) %g\n, gsl_matrix_get(m, 1, 1)); gsl_matrix_free(m); return 0; }工程设置里把两个 .lib 的路径和文件名填到链接器的 Additional Dependencies或者我在代码里直接写了 #pragma comment(lib, gsl.lib) 和 #pragma comment(lib, gslcblas.lib)后者改动最小测试阶段很省事。运行后能正确输出 J0(1.0) 的结果说明整条编译链接链路是通的。4. 常见问题与排查技巧实录4.1 编译阶段C2057 / C2065 这类语法错误这两个错误在 VS2010 编译 GSL 时非常典型。C2057 说“常量表达式”大概率是 inline 关键字不被识别导致的加 /D inline__inline 基本能解决。C2065 说“未声明的标识符”如果排除了拼写问题多半是源码里用了 C99 的“声明与语句混排”VS2010 的 C 编译器要求声明必须出现在语句块开头需要手动把变量声明上移。遇到这类错误别慌先看是不是预处理宏能解决的再考虑改源码。4.2 编译阶段C1083 无法打开头文件如果报错说 gsl/gsl_math.h 打不开十有八九是 Additional Include Directories 没指到根目录。VS2010 会把搜索路径列表打印在错误信息里你看一眼列表里有没有你的 gsl-1.8 根目录没有那就是路径配错了。还有一种情况是你在包含目录里填了相对路径但当前工作目录不是你想象的位置建议直接用绝对路径稳。4.3 链接阶段LNK2019 无法解析的外部符号这个错误要分两种情况处理。一种是忘记把某个 .lib 加进链接器输入比如只加了 gsl.lib 而漏了 gslcblas.lib一些不涉及 BLAS 的函数可能能过但一旦 gsl_matrix_* 这类内部调用了 BLAS 实现就会报 LNK2019。另一种是运行时库配置不一致报错信息里能看到函数签名和 __cdecl 修饰这种情况去核对两边的 /MD 和 /MT。我的检查顺序是先看库文件列表是否齐全再看运行时库是否统一最后才怀疑源码问题。4.4 运行阶段0xc000007b 和缺 DLL程序能编译出来但双击运行报 0xc000007b这是典型的 x64/x86 架构不匹配exe 是 32 位却加载了 64 位的 DLL或者反过来。用静态库方案没有 DLL 分发问题但如果有人改成了动态库方式就要特别注意 DLL 的位数必须和 exe 一致并且放在 exe 同目录下。另一个运行期问题是缺 MSVCR100.dll那是因为用了 /MD 但目标机器没装 VC 2010 运行库把这个运行库一并分发出去即可。4.5 问题速查表症状可能原因处理方法C2057 常量表达式错误inline 关键字未识别加 /D inline__inlineC2065 未声明标识符C99 声明与语句混排手动上移声明到块开头C1083 找不到 gsl 头文件包含目录指向错误指向 gsl-1.8 根目录LNK2019 外部符号无法解析漏库或运行时库不一致补齐 lib统一 /MD 或 /MTLNK2005 重复符号运行时库混用清理中间文件后统一设置启动报 0xc000007b架构不匹配统一 exe 与 DLL 的 x86/x64缺少 MSVCR100.dll目标机没有 VC2010 运行库部署 vcredist_x86.exe最后再分享一个实在经验。GSL-1.8 在 VS2010 下编译真正要改的源码其实没几处大部分工作量都在建工程、归类文件、统一配置这些体力活上。我是把包含目录、预处理宏、运行时库设置全部做成了一个属性页PropertySheet后续任何要用 GSL 的工程直接引用这个属性页不用再重复配置。编译好的 gsl.lib 和 gslcblas.lib我习惯跟源码放同一个根目录下的 lib 文件夹里头文件单独整理一份干净版只留 gsl_*.h这样整体拷贝给别人时不会拖着一堆没用的源码和测试文件。整个过程跑下来最深的体会就是老版本库配老编译器只要把环境变量和工程设置对齐一样能稳定跑起来。本文还有配套的精品资源点击获取
返回列表