C运行时库函数
C运行时库函数是指C语言本身支持的一些基本函数,通常是汇编直接实现的。
API函数
API函数是操作系统为方便用户设计应用程序而提供的实现特定功能的函数,API函数也是C语言的函数实现的。
区别
他们之间区别是:API函数是针对操作系统的,C语言运行时函数则是针对C语言本身的。
·1、运行时库就是 C run-time library,是C而非C++语言世界的概念。
取这个名字就是因为你的C程序运行时需要这些库中的函数。
·2、C语言是所谓的“小内核”语言,就其语言本身来说很小(不多的关键字,程序流程控制,数据类型等);
所以,C语言内核开发出来之后,Dennis Ritchie和Brian Kernighan就用C本身重写了90%以上的UNIX系统
函数,并且把其中最常用的部分独立出来,形成头文件和对应的LIBRARY,C run-time Library就是这样
形成的。
·3、随后,随着C语言的流行,各个C编译器的生产商/个体/团体都遵循老的传统,在不同平台上都有相对应
的Standard Library,但大部分实现都是与各个平台有关的。由于各个C编译器对C的支持和理解有很多
分歧和微妙的差别,所以就有了ANSI C;ANSI C(主观意图上)详细的规定了C语言各个要素的具体含义
和编译器实现要求,引进了新的函数声明方式,同时订立了Standard Library的标准形式。所以C运行时
库由编译器生产商提供。至于由其他厂商/个人/团体提供的头文件和库函数,应当称为第三方C运行库
(Third party C runtime libraries)。
·4、C run-time library里面含有初始化代码,还有错误处理代码(例如divide by zero处理)。你写的程序
可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()
就不会被调用,exit()也不能被响应。因为 C run-time Library 包含了C程序运行的最基本和最常用的
函数。
·5、到了C++世界里,有另外一个概念:Standard C ++ Library,它包括了上面所说的C run-time Library
和STL。包含C run-time Library的原因很明显,C++是C的超集,没有理由再重新来一个C++ run-time
Library。VC针对C++加入的Standard C ++ Library主要包括:LIBCP.LIB、LIBCPMT.LIB和MSVCPRT.LIB。
·6、Windows环境下,VC提供的 C run-time Library又分为动态运行时库和静态运行时库。
动态运行时库
动态运行时库主要包括:
·DLL库文件:msvcrt.dll(或 MSVCRTD.DLL for debug build)
·对应的Import Library文件:MSVCRT.LIB(或 MSVCRTD.LIB for debug build)
静态运行时库
静态运行时库(release版)对应的主要文件包括:
·LIBC.LIB(Single thread static library, retail version)
·LIBCMT.LIB(Multithread static library, retail version)
msvcrt.dll提供几千个C函数,即使是像printf这么低级的函数都在msvcrt.dll里。其实你的程序运行时,很大一部分时间是在这些运行库里运行。在你的程序(release版)被编译时,VC会根据你的编译选项(单线程、多线程或DLL)自动将相应的运行时库文件(libc.lib、libcmt.lib或Import Library msvcrt.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++,并在重装在时选中安装运行库源代码选项。
下面看一个未正确使用C运行时库的控制台程序:
#include
#include
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运行库和C标准库的关系
C标准库,顾名思义既然是标准,就是由标准组织制定的。是由“美国国家标准协会(American National Standards Institute,ANSI)”为了规范C语言库而制定的标准。在最初,各个大学各个公司使用的C语言库都不尽相同,造成相互移植非常困难,在这个背景下,制定了这个标准。
C运行库,是和平台相关的,即和操作系统相关的。它由不同操作系统不同开发平台提供不同的C运行库。但是C运行库的部分实现是基于C标准库的,即C运行库是各个操作系统各个开发工具根据自身平台开发的库,某种程度上,可以说C运行库是C标准库的一个扩展库,只是加了很多C标准库所没有的与平台相关的或者不相关的库接口函数。举例子如:c标准库的strcpy函数负责字符串的拷贝,但是由于缺少对目地字符串缓冲区大小的控制,极有可能导致缓冲区溢出(大量的缓冲区溢出攻击都是由于这种漏洞而产生的);相反,Windows提供了能够实现同样功能的安全的字符串拷贝函数,减少了缓冲区攻击的可能,strcpy_s。这些函数是以c运行库的方式提供的,当然,不同的操作系统,c运行时库可能不同,但是对c标准库的支持是完全一致的,也就是说,在不同的操作系统上,使用同一个c标准库的函数必然产生一致的结果。
C标准库中提供的有:
l 标准输入输出(stdio.h)。
l 文件操作(stdio.h)。
l 字符操作(ctype.h)。
l 字符串操作(string.h)。
l 数学函数(math.h)。
l 资源管理(stdlib.h)。
l 格式转换(stdlib.h)。
l 时间/日期(time.h)。
l 断言(assert.h)。
l 各种类型上的常数(limits.h & float.h)。
你写的程序可以没有math库,程序照样运行,只是不能处理复杂的数学运算,不过如果没有了C run-time库,main()就不会被调用,exit()也不能被响应。因为C run-time library包含了C程序运行的最基本和最常用的函数。
如下是C运行库与C标准库的关系:
一个C运行库大致包含了如下功能:
l 启动与退出:包括入口函数及入口函数所依赖的其他函数等。
l 标准函数:由C语言标准规定的C语言标准库所拥有的函数实现。(C标准库)
l I/O:I/O功能的封装和实现,参见上一节中I/O初始化部分。
l 堆:堆的封装和实现,参见上一节中堆初始化部分。
l 语言实现:语言中一些特殊功能的实现。
l 调试:实现调试功能的代码。
操作系统API和C运行库CRT,C标准库之间区别
首先,C语言要早于Windows出现,而且C语言实际标准制定的开始时间也要早于Windows(API概念出现的)系统的开发时间。所以Windows系统在开发的时候是完全可以使用C语言的。目前最多的说法是用C和汇编实现的。那么只要用C,就可能用C标准库。
我们假设两种情况,一是Windows API的实现包含部分C标准库函数的功能实现,这就决定了这部分操作系统API的实现是由调用标准库实现的,那么在发布时需要加入所用到的c标准库DLL一同发布。
二是微软的内核(包括API)开发是使用着一个和平台严格相关的C语言的静态的链接库,这样不必提供Dll也能开发和发行。而且必然的这个C库是在汇编的基础上实现的,也就是说这个库里面的C函数都是(至少有很大比例)披着C语法的汇编代码。
要你是微软,你选择哪个呢?也许是两者兼而有之,也许是后者。
一般情况下,我们说C运行库暗含的意思是哪种平台哪个开发平台的C运行库,
CRT的实现是基于Windows API的,而WindowsAPI的开发也是基于C语言的,但不是或者不一定基于CRT(或者C标准库)的。
再深一步,虽然CRT是基于操作系统 API实现的,但并不代表所有的CRT封装了操作系统 API,如一些用户的权限控制,操作系统线程创建等都不属于C运行库,于是对于这些操作我们就不得不直接调用操作系统API或者其他库。
总结一下,C标准库就是任何平台都可以使用的基本C语言库。而CRT除了将C标准库加入所属范围外,还扩展了与平台相关的接口库,这些接口实现根据不同平台调用不同平台的操作系统API。
如下图所示,采用C标准库编写的程序可以应用到windows平台,也可以应用到linux平台;而用CRT另外与平台相关的库函数编写的应用程序不能跨平台运行。
而不同平台的操作系统API实现,是用C标准库呢,还是汇编呢,这个可有,可没有。毕竟那么多windows API只要发现一个调用C标准库的了,就有了。概念理解了即可,至于微软实现的时候基于何种考虑不使用C标准库,或者使用C标准库都有自己的考虑。那就是操作系统内部的研究范围了,等我知道了之后再确定这点。哈哈。。。。。。
windows下的c语言和linux 下的c语言以及C标准库和系统API
1.引出我们的问题?
标准c库都是一样的!大家想必都在windows下做过文件编程,在linux下也是一样的函数名,参数都一样。当时就有了疑问,因为我们非常清楚
其本质是不可能一样的,源于这是俩个操作系统啊!但由于c标准库的封装就实现了接口一样。但是如果往深入,我们就非常清楚了在这层封装上还有一层封装其实就是操作系统对文件操作的封装,也就是文件IO。而我们c语言的封装叫标准IO,他们有什么区别那?显然标准iO是基于文件io的,标准IO调用了文件IO,也优化了文件IO,例如引入缓存机制。
2.上面我说的就是下面的几个点
但如果涉及到系统调用那就不同了!
不同的系统提供的系统调用是不同的,有的调用可能相同(接口),但是内部实现不同!
如果没有涉及系统独有的库函数或者编程方法(即只用符合ANSI C标准的代码和方法)就没有区别 (标准库,标准IO)
如果涉及到系统编程,二者有所区别,主要是库函数有所不同,而且文件系统不同、对某些函数的执行方式不同。(文件IO,操作系统API)
3.具体的一些区别
\n 为ASCII的0x0a 换行
\r 为ASCII的0x0d 回车
在windows 系统中,当你输入回车时会自动变成\r\n
在linux下的回车键只代表\n
而在windows下的回车键表示\r\n
\n为进入下一行,\r为打印头回到行首上
linux/unix下只用\n,它就表示回车+换行
而windows下,\r只回车不换行的,\n是换行,但在有些编辑中,单独的\n是不会换行的(如notepad)
一般在程序中,写\n就可以了,它在linux或windows中都能实现回车+换行的功能(只是在文本文件中,linux只会有0x0a,windows会自动换为0x0d 0x0a)
下面举个例子:
#include int main() { char a[10]="abc\r"; printf(a); return 0; }
程序运行时没有任何输出。
原因就是 \r 回车 表示打印头回到该行起始位置,从而覆盖了abc,所以控制台上就没有任何输出啦!
函数库的区别
linux下的C函数库和windows下的函数库系统调用的机制不一样
Glibc包含了主要的C库。这个库提供了基本例程,用于分配内存、搜索目录、打开关闭文件、读写文件、字串处理、模式匹配、数学计算等等。所说的机制不一样不单是指中断号的问题,中断号也是通过input参数和output把函数地址和输出地址定位在寄存器的,那些函数在windows和linux下的实现应该是不一样的,就拿文件系统来说,ext3和fat32的怎么可能一样.还有mm内存管理,都是不一样的.中断还是属于硬件层的,X86上的应该都差不多,但操作系统层的实现就大不相同了.
4.其他区别:
1。系统平台不一样
底层开发就涉及到了系统内核的问题,对于linux来说,你可以知道它里面是什么结构,而windows。。。
2。编译器环境不一样
linux采用gcc编译器,gdb调试工具,和多种可视化的编辑器如 emacs,kedit等等,也有文本的vi/vim,GDB的功能是非常强大的,个人认为较win下的好
尽管win下地mingw,devcpp集成了gcc,但是搞的总让人不爽~,gcc对标准的支持是相当的好
3。针对人群不一样
win下主要还是面向商业化的开发,而绝大多数的编程爱好者则喜欢属于自己开阔自由的系统下编程,不愿意禁锢在windows下(MS)的包围中
4。发展方向不一样。
OpenSource的思想已经在linux这片净土开花,参见GPL....。找资料方便,源代码公开,可以体验开发的乐趣
win下,ms逐步把一批开发人员束缚在它自己的系统里面,开发环境越来越傻瓜,这能不能叫人性化呢?搞到最后。。。可能越走越远~
5.版权问题
win下的好多好多东西都涉及到版权问题,linux的free软件虽然是开放软件,不过好多好多都是免费用于商业化的。。。当然有的需要开发源代码,好多也不需要~
C++的区别也差不多
从程序完成的功能来看,函数库提供的函数通常是不需要操作系统的服务,函数是在用户空间内执行的,除非函数涉及到I/O操作等,一般是不会切到核心态的。系统调用是要求操作系统为用户提供进程,提供某种服务,通常是涉及系统的硬件资源和一些敏感的软件资源等。
函数库的函数,尤其与输入输出相关的函数,大多必须通过linux的系统调用来完成。因此我们可以将函数库的函数当成应用程序设计人员与系统调用程序之间的一个中间层,通过这个中间层,我们可以用一致的接口来安全的调用系统调用。这样程序员可以只要写一次代码就能够在不同版本的linux系统间使用积压种具体实现完全不同的系统调用。至于如何实现对不同的系统调用的兼容性问题,那是函数库开发者所关心的问题。
从程序执行效率来看,系统调用的执行效率大多要比函数高,尤其是处理输入输出的函数。当处理的数据量比较小时,函数库的函数执行效率可能比较好,因为函数库的作法是将要处理的数据先存入缓冲区内,等到缓冲区装满了,再将数据一次写入或者读出。这种方式处理小量数据时效率比较高,但是在进行系统调用时,因为用户进程从用户模式进入系统核心模式,中间涉及了许多额外的任务的切换工作,这些操作称为上下文切换,此类的额外工作会影响系统的执行效率。但是当要处理的数据量比较大时,例如当输入输出的数据量超过文件系统定义的尽寸时,利用系统调用可获得较高的效率。
从程序的可移植性的角度来看,相对于系统调用,C语言的标准备函数库(ANSI C) 具备较高的可移植性,在不同的系统环境下,只要做很少的修改,通常情况是不需要修改的。
Linux C中库函数和系统调用的区别
库函数是高层的,完全运行在用户空间,为程序员提供调用真正的在幕后完成实际事务的系统调用的更方便的接口。系统调用在内核态运行并且由内核自己提供。标准C库函数printf()可以被看做是一个通用的输出语句,但它实际做的是将数据转化为符合格式的字符串并且调用系统调用 write()输出这些字符串。
是否想看一看printf()究竟使用了哪些系统调用? 这很容易,编译下面的代码。
复制代码 代码如下:
#include
int main(void)
{ printf("hello"); return 0; }
使用命令gcc -Wall -o hello hello.c编译。用命令 strace hello 跟踪该可执行文件。是否很惊讶? 每一行都和一个系统调用相对应。 strace是一个非常有用的程序,它可以告诉你程序使用了哪些系统调用和这些系统调用的参数,返回值。 这是一个极有价值的查看程序在干什么的工具。在输出的末尾,你应该看到这样类似的一行 write(1, "hello", 5hello)。这就是我们要找的。藏在面具printf() 的真实面目。既然绝大多数人使用库函数来对文件I/O进行操作(像 fopen, fputs, fclose)。 你可以查看man说明的第二部分使用命令man 2 write 。man说明的第二部分专门介绍系统调用(像kill()和read())。 man说明的第三部分则专门介绍你可能更熟悉的库函数(像cosh()和random())。
你甚至可以编写代码去覆盖系统调用,正如我们不久要做的。骇客常这样做来为系统安装后门或木马。 但你可以用它来完成一些更有益的事,像让内核在每次某人删除文件时输出 “ Tee hee, that tickles!” 的信息。