C运行时库(C Run-Time Libraries /MD/ML/MT)

本文详细介绍了C运行时库的概念及其在Visual C++中的实现方式,涵盖了单线程与多线程的不同版本,并探讨了它们如何影响程序的链接与运行。

    运行时库是程序在运行时所需要的库文件,通常运行时库是以LIB或DLL形式提供的。C运行时库诞生于20世纪70年代,当时的程序世界还很单纯,应用程序都是单线程的,多任务或多线程机制在此时还属于新观念。所以这个时期的C运行时库都是单线程的。

      随着操作系统多线程技术的发展,最初的C运行时库无法满足程序的需求,出现了严重的问题。C运行时库使用了多个全局变量(例如errno)和静态变量,这可能在多线程程序中引起冲突。假设两个线程都同时设置errno,其结果是后设置的errno会将先前的覆盖,用户得不到正确的错误信息。

  因此,Visual C++提供了两种版本的C运行时库。一个版本供单线程应用程序调用,另一个版本供多线程应用程序调用。多线程运行时库与单线程运行时库有两个重大差别:

  (1)类似errno的全局变量,每个线程单独设置一个;

  这样从每个线程中可以获取正确的错误信息。

  (2)多线程库中的数据结构以同步机制加以保护。

  这样可以避免访问时候的冲突。

  Visual C++提供的多线程运行时库又分为静态链接库和动态链接库两类,而每一类运行时库又可再分为debug版和release版,因此Visual C++共提供了6个运行时库。如下表:

C运行时库库文件
Single thread(static link) libc.lib
Debug single thread(static link) libcd.lib
MultiThread(static link) libcmt.lib
Debug multiThread(static link)libcmtd.lib
MultiThread(dynamic link)msvcrt.lib
Debug multiThread(dynamic link)msvcrtd.lib

  2.C运行时库的作用

  C运行时库除了给我们提供必要的库函数调用(如memcpy、printf、malloc等)之外,它提供的另一个最重要的功能是为应用程序添加启动函数

  C运行时库启动函数的主要功能为进行程序的初始化,对全局变量进行赋初值,加载用户程序的入口函数

  不采用宽字符集的控制台程序的入口点为mainCRTStartup(void)。下面我们以该函数为例来分析运行时库究竟为我们添加了怎样的入口程序。这个函数在crt0.c中被定义,下列的代码经过了笔者的整理和简化:

void mainCRTStartup(void)
{
 int mainret;
 /*获得WIN32完整的版本信息*/
 _osver = GetVersion();
 _winminor = (_osver >> 8) & 0x00FF ;
 _winmajor = _osver & 0x00FF ;
 _winver = (_winmajor << 8) + _winminor;
 _osver = (_osver >> 16) & 0x00FFFF ;

 _ioinit(); /* initialize lowio */

 /* 获得命令行信息 */
 _acmdln = (char *) GetCommandLineA();

 /* 获得环境信息 */
 _aenvptr = (char *) __crtGetEnvironmentStringsA();

 _setargv(); /* 设置命令行参数 */
 _setenvp(); /* 设置环境参数 */

 _cinit(); /* C数据初始化:全局变量初始化,就在这里!*/

 __initenv = _environ;
 mainret = main( __argc, __argv, _environ ); /*调用main函数*/

 exit( mainret );
}

  从以上代码可知,运行库在调用用户程序的main或WinMain函数之前,进行了一些初始化工作。初始化完成后,接着才调用了我们编写的main或WinMain函数。只有这样,我们的C语言运行时库和应用程序才能正常地工作起来。

  除了crt0.c外,C运行时库中还包含wcrt0.c、 wincrt0.c、wwincrt0.c三个文件用来提供初始化函数。wcrt0.c是crt0.c的宽字符集版,wincrt0.c中包含windows应用程序的入口函数,而wwincrt0.c则是wincrt0.c的宽字符集版。

  Visual C++的运行时库源代码缺省情况下不被安装。如果您想查看其源代码,则需要重装Visual C++,并在重装在时选中安装运行库源代码选项。

3.各种C运行时库的区别

  (1)静态链接的单线程库

  静态链接的单线程库只能用于单线程的应用程序,C运行时库的目标代码最终被编译在应用程序的二进制文件中。通过/ML编译选项可以设置Visual C++使用静态链接的单线程库。

  (2)静态链接的多线程库

  静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过/MD编译选项可以设置Visual C++使用静态链接的单线程库。

  (3)动态链接的运行时库

  动态链接的运行时库将所有的C库函数保存在一个单独的动态链接库MSVCRTxx.DLL中,MSVCRTxx.DLL处理了多线程问题。使用/ML编译选项可以设置Visual C++使用动态链接的运行时库。

  /MDd、 /MLd 或 /MTd 选项使用 Debug runtime library(调试版本的运行时刻函数库),与/MD、 /ML 或 /MT分别对应。Debug版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,加强了对错误的检测,因此在运行性能方面比不上Release版本。

  下面看一个未正确使用C运行时库的控制台程序:

#include <stdio.h>
#include <afx.h>
int main()
{
 CFile file;
 CString str("I love you");
 TRY
 {
  file.Open("file.dat",CFile::modeWrite | CFile::modeCreate);
 }
 CATCH( CFileException, e )
 {
  #ifdef _DEBUG
  afxDump << "File could not be opened " << e->m_cause << "/n";
  #endif
 }
 END_CATCH

 file.Write(str,str.GetLength());
 file.Close();
}

  我们在"rebuild all"的时候发生了link错误:

nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
main.exe : fatal error LNK1120: 2 unresolved externals
Error executing cl.exe.

  发生错误的原因在于Visual C++对控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程。我们只需要在Visual C++6.0中依次点选Project->Settings->C/C++菜单和选项,在Project Options里修改编译选项即可。
 


***********************************************
C运行时库在VC下是指实现了标准C接口的函数库
有静态库,也有动态库
包括三个文件LIBC.LIB,LIBCMT.LIB,MSVCRT.LIB
具体包括了使用了哪个库要看你在编译时候的选项
具体见下表:
C Run-Time Library 特点 编译选项 宏定义
(without iostream)
LIBC.LIB Single threaded, static link /ML
LIBCMT.LIB Multithreaded, static link /MT _MT
MSVCRT.LIB Multithreaded, dynamic link /MD _MT,_DLL
(import library for MSVCRT.DLL)

 
 
 

从字面上看,运行库是程序在运行时所需要的库文件。通常运行库是以DLL形式提供的。Delphi和C++ Builder的运行库为.bpl文件,实际还是一个DLL。运行库中一般包括编程时常用的函数,如字符串操作、文件操作、界面等内容。不同的语言所支持的函数通常是不同的,所以使用的库也是完全不同的,这就是为什么有VB运行库、C运行库、Delphi运行库之分的原因。即使都是C++语言,也可能因为提供的函数不同,而使用不同的库。如VC++使用的运行库和C++ Builder就完全不同。
如果不使用运行库,每个程序中都会包括很多重复的代码,而使用运行库,可以大大缩小编译后的程序的大小。但另一方面,由于使用了运行库,所以在分发程序时就必须带有这些库,比较麻烦。如果在操作系统中找不到相应的运行库程序就无法运行。为了解决这个矛盾,Windows总是会带上它自己开发的软件的最新的运行库。象Windows 2000以后的版本都包括Visual Basic 5.0/6.0的库。Internet Explorer总是带有最新的Visual C++ 6.0的库。Windows XP带有Microsoft .NET 1.0(用于VB.NET和C#)的库。Visual C++、Delphi和C++ Builder允许用户选择所编译得到的程序是否依赖于运行库。而VB、FoxPro、PowerBuilder、LabWindows/CVI和Matlab就不允许用户进行这种选择,必须依赖于运行库。
################################################################################ # Automatically-generated file. Do not edit! ################################################################################ -include ../makefile.init RM := rm -rf # All of the sources participating in the build are defined here -include sources.mk -include user/subdir.mk -include libraries/zf_driver/subdir.mk -include libraries/zf_device/subdir.mk -include libraries/zf_components/subdir.mk -include libraries/zf_common/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/_PinMap/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/_Lib/DataHandling/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/_Impl/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Vadc/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Vadc/Adc/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Stm/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Src/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Scu/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Qspi/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Qspi/SpiMaster/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Port/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Mtu/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Gtm/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Gtm/Atom/Pwm/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Gpt12/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Gpt12/IncrEnc/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Flash/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Dma/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Dma/Dma/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Cpu/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Cpu/Irq/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Cpu/CStart/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Ccu6/Timer/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Ccu6/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Asclin/Std/subdir.mk -include libraries/infineon_libraries/iLLD/TC26B/Tricore/Asclin/Asc/subdir.mk -include libraries/infineon_libraries/Service/CpuGeneric/SysSe/Math/subdir.mk -include libraries/infineon_libraries/Service/CpuGeneric/SysSe/Bsp/subdir.mk -include libraries/infineon_libraries/Service/CpuGeneric/StdIf/subdir.mk -include libraries/infineon_libraries/Service/CpuGeneric/If/subdir.mk -include libraries/infineon_libraries/Infra/Platform/Tricore/Compilers/subdir.mk -include code/subdir.mk ifneq ($(MAKECMDGOALS),clean) ifneq ($(strip $(C++_DEPS)),) -include $(C++_DEPS) endif ifneq ($(strip $(CC_DEPS)),) -include $(CC_DEPS) endif ifneq ($(strip $(CPP_DEPS)),) -include $(CPP_DEPS) endif ifneq ($(strip $(CXX_DEPS)),) -include $(CXX_DEPS) endif ifneq ($(strip $(C_DEPS)),) -include $(C_DEPS) endif ifneq ($(strip $(C_UPPER_DEPS)),) -include $(C_UPPER_DEPS) endif endif -include ../makefile.defs OPTIONAL_TOOL_DEPS := \ $(wildcard ../makefile.defs) \ $(wildcard ../makefile.init) \ $(wildcard ../makefile.targets) \ BUILD_ARTIFACT_NAME := Seekfree_TC264_Opensource_Library BUILD_ARTIFACT_EXTENSION := elf BUILD_ARTIFACT_PREFIX := BUILD_ARTIFACT := $(BUILD_ARTIFACT_PREFIX)$(BUILD_ARTIFACT_NAME)$(if $(BUILD_ARTIFACT_EXTENSION),.$(BUILD_ARTIFACT_EXTENSION),) # Add inputs and outputs from these tool invocations to the build variables SECONDARY_SIZE += \ Seekfree_TC264_Opensource_Library.siz \ # All Target all: main-build # Main-build Target main-build: Seekfree_TC264_Opensource_Library.elf secondary-outputs # Tool invocations Seekfree_TC264_Opensource_Library.elf: $(OBJS) makefile $(OPTIONAL_TOOL_DEPS) @echo 'Building target: $@' @echo 'Invoking: TASKING Linker' cctc --lsl-file="../Lcf_Tasking_Tricore_Tc.lsl" -Wl-Oc -Wl-OL -Wl-Ot -Wl-Ox -Wl-Oy "C:\Users\28972\Desktop\W3FIRST\Seekfree_TC264_Opensource_Library\libraries\zf_device\zf_device_config.a" -Wl--map-file="Seekfree_TC264_Opensource_Library.map" -Wl-mc -Wl-mf -Wl-mi -Wl-mk -Wl-ml -Wl-mm -Wl-md -Wl-mr -Wl-mu --no-warnings= -Wl--error-limit=42 --fp-model=3 -lcs_fpu -lfp_fpu -lrt --lsl-core=vtc --exceptions --strict --anachronisms --force-c++ -Ctc26xb -o"Seekfree_TC264_Opensource_Library.elf" -Wl-o"Seekfree_TC264_Opensource_Library.hex:IHEX" $(OBJS) @echo 'Finished building target: $@' @echo ' ' Seekfree_TC264_Opensource_Library.siz: Seekfree_TC264_Opensource_Library.elf makefile $(OPTIONAL_TOOL_DEPS) @echo 'Invoking: Print Size' elfsize "Seekfree_TC264_Opensource_Library.elf" @echo 'Finished building: $@' @echo ' ' # Other Targets clean: -$(RM) Seekfree_TC264_Opensource_Library.siz Seekfree_TC264_Opensource_Library.elf -@echo ' ' secondary-outputs: $(SECONDARY_SIZE) .PHONY: all clean dependents main-build -include ../makefile.targets 修改上面的makefile文件以解决问题
最新发布
05-23
### 修改 Makefile 文件以解决构建问题 当遇到构建错误时,通常需要检查和调整 Makefile 中的依赖项和工具链配置。以下是针对这些问题的具体方法: #### 1. 检查和修改依赖项 Makefile 的核心在于定义目标文件与其依赖的关系。如果某个目标文件未能正确生成,可能是因为其依赖项未被正确定义或更新。可以通过以下方式进行修复: - **重新评估依赖关系**:确保每个目标文件都列出了所有必要的源文件作为依赖项。例如,如果有多个 `.c` 文件参与编译,则应明确列出这些文件[^2]。 ```makefile target: source1.o source2.o $(CC) $^ -o $@ ``` - **使用自动化依赖生成工具**:许多现代编译器支持自动生成头文件依赖关系的功能(如 GCC 的 `-M`, `-MM`, `-MD` 和 `-MMD` 参数)。利用这一特性可以简化维护工作并减少人为失误的风险[^3]: ```makefile %.o: %.c $(CC) -MMD -MP -c $< -o $@ include $(wildcard *.d) ``` #### 2. 调整工具链配置 构建过程中使用的工具链直接影响到最终产物的质量及兼容性。因此,适当调整 CC、CXX 等变量所指向的具体编译器及其附加选项尤为重要。 - **更改默认编译器**:有时原生平台上的 GNU Compiler Collection (GCC) 不适合特定应用场景,这时可以选择替换为 Clang 或 Intel C++ Compiler 等备选方案。只需简单重设对应宏即可[^3]: ```makefile CC = clang CXX = clang++ LD = ld.gold AR = llvm-ar RANLIB = llvm-ranlib STRIP = llvm-strip ``` - **优化编译参数**:除了指定不同类型的编译器外,还可以通过调节 FLAGS 来满足性能需求或是调试目的。比如开启更高的警告等级 (-Wall), 启用地址 sanitizer(-fsanitize=address),或者关闭不必要的优化措施(-O0)[^4]: ```makefile CPPFLAGS += -I/usr/include/myproject -DDEBUG_MODE CFLAGS += -std=c99 -pedantic-errors -Wextra -g3 -O0 LDFLAGS += -L/lib/customlibs -lcustomlib ``` #### 3. 测试改动后的效果 每次对 Makefile 进行修订之后都应该进行全面测试,以验证新设定是否解决了原有的构建难题同时不会引入新的隐患。推荐采用如下策略来进行回归检验: - 清理旧数据后再尝试全新构建(`make clean; make`)- 利用 verbose 输出查看每一步骤详情(`make V=1`)- 若条件允许的话最好能在多种硬件架构之上重复上述流程从而保障跨平台稳定性。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值