﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Archangelsy-文章分类-C&amp;C++</title><link>http://www.blogjava.net/Archangelsy/category/20558.html</link><description>LSY</description><language>zh-cn</language><lastBuildDate>Fri, 21 Sep 2007 05:52:36 GMT</lastBuildDate><pubDate>Fri, 21 Sep 2007 05:52:36 GMT</pubDate><ttl>60</ttl><item><title>C编程手册</title><link>http://www.blogjava.net/Archangelsy/articles/146413.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Wed, 19 Sep 2007 04:25:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/146413.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/146413.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/146413.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/146413.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/146413.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: C														和														C++														编程和学习文档																																																																						 																		1  ...&nbsp;&nbsp;<a href='http://www.blogjava.net/Archangelsy/articles/146413.html'>阅读全文</a><img src ="http://www.blogjava.net/Archangelsy/aggbug/146413.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-09-19 12:25 <a href="http://www.blogjava.net/Archangelsy/articles/146413.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ODBC驱动程序不支持动态记录集</title><link>http://www.blogjava.net/Archangelsy/articles/146183.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Tue, 18 Sep 2007 07:47:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/146183.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/146183.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/146183.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/146183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/146183.html</trackback:ping><description><![CDATA[ODBC驱动程序不支持动态记录集,<br /> CSetSourCertInfo certinfo(g_pDB);<br /> if(!certinfo.Open(CRecordset::dynaset,NULL,CRecordset::none))<br /> {<br />  return;<br /> }<br />Open时会报出系统错误<br />后来发现，把dynaset改成snapshot就可以了，动态不支持动态记录集？！<img src ="http://www.blogjava.net/Archangelsy/aggbug/146183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-09-18 15:47 <a href="http://www.blogjava.net/Archangelsy/articles/146183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LNK2005 原因</title><link>http://www.blogjava.net/Archangelsy/articles/146109.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Tue, 18 Sep 2007 03:47:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/146109.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/146109.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/146109.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/146109.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/146109.html</trackback:ping><description><![CDATA[
		<p>
				<font size="2">
						<font style="BACKGROUND-COLOR: #ffffff">
								<font face="Courier New">许多Visual C++的使用者都碰到过<font size="+0">LNK2005</font>:symbol already defined和LNK1169:one or more multiply defined symbols found这样的链接错误，而且通常是在使用第三方库时遇到的。对于这个问题，有的朋友可能不知其然，而有的朋友可能知其然却不知其所以然，那么本文就试图为大家彻底解开关于它的种种疑惑。</font>
						</font>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    大家都知道，从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码，然后由汇编器(assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中；(2)链接器(linker)将一个个的目标文件(或许还会有若干程序库)链接在一起生成一个完整的可执行文件。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    编译器编译源文件时会把源文件的全局符号(global symbol)分成强(strong)和弱(weak)两类传给汇编器，而随后汇编器则将强弱信息编码并保存在目标文件的符号表中。那么何谓强弱呢？编译器认为函数与初始化了的全局变量都是强符号，而未初始化的全局变量则成了弱符号。比如有这么个源文件:</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" color="#a52a2a" size="2">extern int errorno;<br />int buf[2] = {1,2};<br />int *p;</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" color="#a52a2a" size="2">int main()<br />{<br />   return 0;<br />}</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">其中main、buf是强符号，p是弱符号，而errorno则非强非弱，因为它只是个外部变量的使用声明。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    有了强弱符号的概念，我们就可以看看链接器是如何处理与选择被多次定义过的全局符号:</font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<strong>规则1</strong>: 不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号)；</font>
						</font>
				</font>
		</p>
		<font face="Courier New">
				<p>
						<br />
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<strong>规则2</strong>: 如果一个符号在某个目标文件中是强符号，在其它文件中都是弱符号，那么选择强符号；</font>
						</font>
				</p>
				<p>
						<br />
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<strong>规则3</strong>: 如果一个符号在所有目标文件中都是弱符号，那么选择其中任意一个；</font>
						</font>
				</p>
		</font>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    由上可知多个目标文件不能重复定义同名的函数与初始化了的全局变量，否则必然导致<font size="+0">LNK2005</font>和LNK1169两种链接错误。可是，有的时候我们并没有在自己的程序中发现这样的重定义现象，却也遇到了此种链接错误，这又是何解？嗯，问题稍微有点儿复杂，容我慢慢道来。<br /></font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    众所周知，ANSI C/C++ 定义了相当多的标准函数，而它们又分布在许多不同的目标文件中，如果直接以目标文件的形式提供给程序员使用的话，就需要他们确切地知道哪个函数存在于哪个目标文件中，并且在链接时显式地指定目标文件名才能成功地生成可执行文件，显然这是一个巨大的负担。所以C语言提供了一种将多个目标文件打包成一个文件的机制，这就是静态程序库(static library)。开发者在链接时只需指定程序库的文件名，链接器就会自动到程序库中寻找那些应用程序确实用到的目标模块，并把(且只把)它们从库中拷贝出来参与构建可执行文件。几乎所有的C/C++开发系统都会把标准函数打包成标准库提供给开发者使用(有不这么做的吗？)。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    程序库为开发者带来了方便，但同时也是某些混乱的根源。我们来看看链接器是如何解析(resolve)对程序库的引用的。<br />    <br />    在符号解析(symbol resolution)阶段，链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们，在此期间它要维护若干个集合:(1)集合E是将被合并到一起组成可执行文件的所有目标文件集合；(2)集合U是未解析符号(unresolved symbols，比如已经被引用但是还未被定义的符号)的集合；(3)集合D是所有之前已被加入到E的目标文件定义的符号集合。一开始，E、U、D都是空的。</font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<strong>(1):</strong> 对命令行中的每一个输入文件f，链接器确定它是目标文件还是库文件，如果它是目标文件，就把f加入到E，并把f中未解析的符号和已定义的符号分别加入到U、D集合中，然后处理下一个输入文件。</font>
						</font>
				</font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<strong>(2):</strong> 如果f是一个库文件，链接器会尝试把U中的所有未解析符号与f中各目标模块定义的符号进行匹配。如果某个目标模块m定义了一个U中的未解析符号，那么就把m加入到E中，并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的所有目标模块重复这个过程直至到达一个不动点(fixed point)，此时U和D不再变化。而那些未加入到E中的f里的目标模块就被简单地丢弃，链接器继续处理下一输入文件。</font>
						</font>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">(3): 如果处理过程中往D加入一个已存在的符号，或者当扫描完所有输入文件时U非空，链接器报错并停止动作。否则，它把E中的所有目标文件合并在一起生成可执行文件。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    VC带的编译器名字叫cl.exe，它有这么几个与标准程序库有关的选项: /ML、/MLd、/MT、/MTd、/MD、/MDd。这些选项告诉编译器应用程序想使用什么版本的C标准程序库。/ML(缺省选项)对应单线程静态版的标准程序库(libc.lib)；/MT对应多线程静态版标准库(libcmt.lib)，此时编译器会自动定义_MT宏；/MD对应多线程DLL版(导入库msvcrt.lib，DLL是msvcrt.dll)，编译器自动定义_MT和_DLL两个宏。后面加d的选项都会让编译器自动多定义一个_DEBUG宏，表示要使用对应标准库的调试版，因此/MLd对应调试版单线程静态标准库(libcd.lib)，/MTd对应调试版多线程静态标准库(libcmtd.lib)，/MDd对应调试版多线程DLL标准库(导入库msvcrtd.lib，DLL是msvcrtd.dll)。虽然我们的确在编译时明白无误地告诉了编译器应用程序希望使用什么版本的标准库，可是当编译器干完了活，轮到链接器开工时它又如何得知一个个目标文件到底在思念谁？为了传递相思，我们的编译器就干了点秘密的勾当。在cl编译出的目标文件中会有一个专门的区域(关心这个区域到底在文件中什么地方的朋友可以参考COFF和PE文件格式)存放一些指导链接器如何工作的信息，其中有一种就叫缺省库(default library)，这些信息指定了一个或多个库文件名，告诉链接器在扫描的时候也把它们加入到输入文件列表中(当然顺序位于在命令行中被指定的输入文件之后)。说到这里，我们先来做个小实验。写个顶顶简单的程序，然后保存为main.c :</font>
		</p>
		<p>
				<font face="Courier New" color="#a52a2a">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#006400">/* main.c */<br /></font>int main() { return 0; }</font>
						</font>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">用下面这个命令编译main.c(什么？你从不用命令行来编译程序？这个......) :</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">cl /c main.c</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">/c是告诉cl只编译源文件，不用链接。因为/ML是缺省选项，所以上述命令也相当于: cl /c /ML main.c 。如果没什么问题的话(要出了问题才是活见鬼！当然除非你的环境变量没有设置好，这时你应该去VC的bin目录下找到vcvars32.bat文件然后运行它。)，当前目录下会出现一个main.obj文件，这就是我们可爱的目标文件。随便用一个文本编辑器打开它(是的，文本编辑器，大胆地去做别害怕)，搜索"defaultlib"字符串，通常你就会看到这样的东西: "-defaultlib:LIBC -defaultlib:OLDNAMES"。啊哈，没错，这就<br />是保存在目标文件中的缺省库信息。我们的目标文件显然指定了两个缺省库，一个是单线程静态版标准库libc.lib(这与/ML选项相符)，另外一个是oldnames.lib(它是为了兼容微软以前的C/C++开发系统)。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    VC的链接器是link.exe，因为main.obj保存了缺省库信息，所以可以用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">link main.obj libc.lib</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">或者</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">link main.obj</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">来生成可执行文件main.exe，这两个命令是等价的。但是如果你用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">link main.obj libcd.lib</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">的话，链接器会给出一个警告: "warning LNK4098: defaultlib "LIBC" conflicts with use of other libs; use /NODEFAULTLIB:library"，因为你显式指定的标准库版本与目标文件的缺省值不一致。通常来说，应该保证链接器合并的所有目标文件指定的缺省标准库版本一致，否则编译器一定会给出上面的警告，而<font size="+0">LNK2005</font>和LNK1169链接错误则有时会出现有时不会。那么这个有时到底是什么时候？呵呵，别着急，下面的一切正是为喜欢追根究底的你准备的。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    建一个源文件，就叫mylib.c，内容如下:</font>
		</p>
		<p>
				<font face="Courier New" color="#a52a2a">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#006400">/* mylib.c */</font>
										<br />＃i nclude <stdio.h></stdio.h></font>
						</font>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" color="#a52a2a" size="2">void foo()<br />{<br />   printf("%s","I am from mylib!\n");<br />}</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">cl /c /MLd mylib.c</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">命令编译，注意/MLd选项是指定libcd.lib为默认标准库。lib.exe是VC自带的用于将目标文件打包成程序库的命令，所以我们可以用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">lib /OUT:my.lib mylib.obj</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">将mylib.obj打包成库，输出的库文件名是my.lib。接下来把main.c改成:</font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">
										<font color="#006400">/* main.c */<br /></font>
										<font color="#a52a2a">void foo();</font>
								</font>
						</font>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" color="#a52a2a" size="2">int main()<br />{<br />   foo();<br />   return 0;<br />}</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">cl /c main.c</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">编译，然后用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">link main.obj my.lib</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">进行链接。这个命令能够成功地生成main.exe而不会产生<font size="+0">LNK2005</font>和LNK1169链接错误，你仅仅是得到了一条警告信息:"warning LNK4098: defaultlib "LIBCD" conflicts with use of other libs; use /NODEFAULTLIB:library"。我们根据前文所述的扫描规则来分析一下链接器此时做了些啥。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    一开始E、U、D都是空集，链接器首先扫描到main.obj，把它加入E集合，同时把未解析的foo加入U，把main加入D，而且因为main.obj的默认标准库是libc.lib，所以它被加入到当前输入文件列表的末尾。接着扫描my.lib，因为这是个库，所以会拿当前U中的所有符号(当然现在就一个foo)与my.lib中的所有目标模块(当然也只有一个mylib.obj)依次匹配，看是否有模块定义了U中的符号。结果mylib.obj确实定义了foo，于是它被加入到E，foo从U转移到D，mylib.obj引用的printf加入到U，同样地，mylib.obj指定的默认标准库是libcd.lib，它也被加到当前输入文件列表的末尾(在libc.lib的后面)。不断地在my.lib库的各模块上进行迭代以匹配U中的符号，直到U、D都不再变化。很明显，现在就已经到达了这么一个不动点，所以接着扫描下一个输入文件，就是libc.lib。链接器发现libc.lib里的printf.obj里定义有printf，于是printf从U移到D，而printf.obj被加入到E，它定义的所有符号加入到D，它里头的未解析符号加入到U。链接器还会把每个程序都要用到的一些初始化操作所在的目标模块(比如crt0.obj等)及它们所引用的模块(比如malloc.obj、free.obj等)自动加入到E中，并更新U和D以反应这个变化。事实上，标准库各目标模块里的未解析符号都可以在库内其它模块中找到定义，因此当链接器处理完libc.lib时，U一定是空的。最后处理libcd.lib，因为此时U已经为空，所以链接器会抛弃它里面的所有目标模块从而结束扫描，然后合并E中的目标模块并输出可执行文件。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    上文描述了虽然各目标模块指定了不同版本的缺省标准库但仍然链接成功的例子，接下来你将目睹因为这种不严谨而导致的悲惨失败。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    修改mylib.c成这个样子:</font>
		</p>
		<p>
				<font face="Courier New" color="#a52a2a">
						<font size="2">
								<font style="BACKGROUND-COLOR: #ffffff">＃i nclude <crtdbg.h></crtdbg.h></font>
						</font>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" color="#a52a2a" size="2">void foo()<br />{<br />   <font color="#006400">// just a test , don"t care memory leak</font><br />   _malloc_dbg( 1, _NORMAL_BLOCK, __FILE__, __LINE__ );<br />}</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">其中_malloc_dbg不是ANSI C的标准库函数，它是VC标准库提供的malloc的调试版，与相关函数配套能帮助开发者抓各种内存错误。使用它一定要定义_DEBUG宏，否则预处理器会把它自动转为malloc。继续用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">cl /c /MLd mylib.c<br />lib /OUT:my.lib mylib.obj</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">编译打包。当再次用</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">link main.obj my.lib</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">进行链接时，我们看到了什么？天哪，一堆的<font size="+0">LNK2005</font>加上个贵为"fatal error"的LNK1169垫底，当然还少不了那个LNK4098。链接器是不是疯了？不，你冤枉可怜的链接器了，我拍胸脯保证它可是一直在尽心尽责地照章办事。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    一开始E、U、D为空，链接器扫描main.obj，把它加入E，把foo加入U，把main加入D，把libc.lib加入到当前输入文件列表的末尾。接着扫描my.lib，foo从U转移到D，_malloc_dbg加入到U，libcd.lib加到当前输入文件列表的尾部。然后扫描libc.lib，这时会发现libc.lib里任何一个目标模块都没有定义_malloc_dbg(它只在调试版的标准库中存在)，所以不会有任何一个模块因为_malloc_dbg而加入E，但是每个程序都要用到的初始化模块(如crt0.obj等)及它们所引用的模块(比如malloc.obj、free.obj等)还是会自动加入到E中，同时U和D被更新以反应这个变化。当链接器处理完libc.lib时，U只剩_malloc_dbg这一个符号。最后处理libcd.lib，发现dbgheap.obj定义了_malloc_dbg，于是dbgheap.obj加入到E，它里头的未解析符号加入U，它定义的所有其它符号也加入D，这时灾难便来了。之前malloc等符号已经在D中(随着libc.lib里的malloc.obj加入E而加入的)，而dbgheap.obj又定义了包括malloc在内的许多同名符号，这引发了重定义冲突，链接器只好中断工作并报告错误。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    现在我们该知道，链接器完全没有责任，责任在我们自己的身上。是我们粗心地把缺省标准库版本不一致的目标文件(main.obj)与程序库(my.lib)链接起来，导致了大灾难。解决办法很简单，要么用/MLd选项来重编译main.c；要么用/ML选项重编译mylib.c。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    在上述例子中，我们拥有库my.lib的源代码(mylib.c)，所以可以用不同的选项重新编译这些源代码并再次打包。可如果使用的是第三方的库，它并没有提供源代码，那么我们就只有改变自己程序的编译选项来适应这些库了。但是如何知道库中目标模块指定的默认库呢？其实VC提供的一个小工具便可以完成任务，这就是dumpbin.exe。运行下面这个命令</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">dumpbin /DIRECTIVES my.lib</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">然后在输出中找那些"Linker Directives"引导的信息，你一定会发现每一处这样的信息都会包含若干个类似"-defaultlib:XXXX"这样的字符串，其中XXXX便代表目标模块指定的缺省库名。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">    知道了第三方库指定的默认标准库，再用合适的选项编译我们的应用程序，就可以避免<font size="+0">LNK2005</font>和LNK1169链接错误。喜欢IDE的朋友，你一样可以到 "Project属性" -&gt; "C/C++" -&gt; "代码生成(code generation)" -&gt; "运行时库(run-time library)" 项下设置应用程序的默认标准库版本，这与命令行选项的效果是一样的。</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" color="#ff0000" size="2">
						<strong>终极解决办法：</strong>
				</font>
		</p>
		<p>
				<font style="BACKGROUND-COLOR: #ffffff" face="Courier New" size="2">在 Project/Setting/Link/General中的 Project Options: 加入 /FORCE:MULTIPLE即可。</font>
		</p>
<img src ="http://www.blogjava.net/Archangelsy/aggbug/146109.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-09-18 11:47 <a href="http://www.blogjava.net/Archangelsy/articles/146109.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC++的链接错误LNK2005   </title><link>http://www.blogjava.net/Archangelsy/articles/146105.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Tue, 18 Sep 2007 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/146105.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/146105.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/146105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/146105.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/146105.html</trackback:ping><description><![CDATA[VC++的链接错误LNK2005   <br />    <br />  编程中经常能遇到LNK2005错误——重复定义错误，其实LNK2005错误并不是一个很难解决的错误。弄清楚它形成的原因，就可以轻松解决它了。   <br />    <br />  造成LNK2005错误主要有以下几种情况：   <br />  1．重复定义全局变量。可能存在两种情况：   <br />  A、对于一些初学编程的程序员，有时候会以为需要使用全局变量的地方就可以使用定义申明一下。其实这是错误的，全局变量是针对整个工程的。正确的应该是在 一个CPP文件中定义如下：int   g_Test;那么在使用的CPP文件中就应该使用：extern   int   g_Test即可，如果还是使用int   g_Test，那么就会产生LNK2005错误，一般错误错误信息类似：AAA.obj   error   LNK2005   int   book   c？book@@3HA   already   defined   in   BBB.obj。切记的就是不能给变量赋值否则还是会有LNK2005错误。   <br />                这里需要的是“声明”，不是“定义”！根据C++标准的规定，一个变量是声明，必须同时满足两个条件，否则就是定义：   <br />  (1)声明必须使用extern关键字；(2)不能给变量赋初值   <br />  所以，下面的是声明:   <br />  extern   int   a;   <br />  下面的是定义   <br />  int   a;   int   a   =   0;   extern   int   a   =0;   <br />  B、对于那么编程不是那么严谨的程序员，总是在需要使用变量的文件中随意定义一个全局变量，并且对于变量名也不予考虑，这也往往容易造成变量名重复，而造成LNK2005错误。   <br /><br /><br /> 2．头文件的包含重复。往往需要包含的头文件中含有变量、函数、类的定义，在其它使用的地方又不得不多次包含之，如果头文件中没有相关的宏等防止重复链接 的措施，那么就会产生LNK2005错误。解决办法是在需要包含的头文件中做类似的处理：#ifndef   MY_H_FILE       //如果没有定义这个宏   <br />  #define   MY_H_FILE       //定义这个宏   <br />  …….       //头文件主体内容   <br />  …….   <br />  #endif   <br />  上面是使用宏来做的，也可以使用预编译来做，在头文件中加入：   <br />  #pragma   once   <br />  //头文件主体   <br /><br /><br /> 3．使用第三方的库造成的。这种情况主要是C运行期函数库和MFC的库冲突造成的。具体的办法就是将那个提示出错的库放到另外一个库的前面。另外选择不同 的C函数库，可能会引起这个错误。微软和C有两种C运行期函数库，一种是普通的函数库：LIBC.LIB，不支持多线程。另外一种是支持多线程的： msvcrt.lib。如果一个工程里，这两种函数库混合使用，可能会引起这个错误，一般情况下它需要MFC的库先于C运行期函数库被链接，因此建议使用 支持多线程的msvcrt.lib。所以在使用第三方的库之前首先要知道它链接的是什么库，否则就可能造成LNK2005错误。如果不得不使用第三方的 库，可以尝试按下面所说的方法修改，但不能保证一定能解决问题，前两种方法是微软提供的：   <br />  A、选择VC菜单Project-&gt;Settings-&gt;Link-&gt;Catagory选择Input，再在Ignore   libraries   的Edit栏中填入你需要忽略的库，如：Nafxcwd.lib;Libcmtd.lib。然后在Object/library   Modules的Edit栏中填入正确的库的顺序，这里需要你能确定什么是正确的顺序，呵呵，God   bless   you！   <br />  B、选择VC菜单Project-&gt;Settings-&gt;Link页，然后在Project   Options的Edit栏中输入/verbose:lib，这样就可以在编译链接程序过程中在输出窗口看到链接的顺序了。   <br />  C、选择VC菜单Project-&gt;Settings-&gt;C/C++页，Catagory选择Code   Generation后再在User   Runtime   libraray中选择MultiThread   DLL等其他库，逐一尝试。   <br />  关于编译器的相关处理过程，参考：   <br />  http://www.donews.net/xzwenlan/archive/2004/12/23/211668.aspx   <br />    <br />  这就是我所遇到过的LNK2005错误的几种情况，肯定还有其他的情况也可能造成这种错误，所以我不希望你在看完这篇文章以后，再遇到LNK2005错误 时候，不动脑筋的想对号入座的排除错误。编程的过程就是一个思考的过程，所以还是多多开动你的头脑，那样收获会更多！   <br />  ＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝   <br />  支持,我在社区里也看到了许多LINK   2005错   <br />  补充一点,就是一次在用第三方库时,由于errno被重定义,用多种方法都不能解决,后查找MSDN,发现link有个选项/FORCE可以解决,在IDE下   <br />  Project-&gt;Settings-&gt;Link页,选categroy为custom,将force   file   output前打勾   <br />  但会有警告   <br />  warning   LNK4088:   image   being   generated   due   to   /FORCE   option;   image   may   not   run   <br />  但的确解决了问题,这是由于VC对重定义比较严格,像BCB或GCC在库中的重定义不会有任何警告或错误   <br />  ＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝   <br />  我发现的另外一个出现LINK2005的现象，好像是由于名称空间而引起的。我在dos下写的程序没有问题，但是放在mfc中就出现了这个链接错误。因为 起初图省事，我在一个头文件中写了using   namespace   std,并且这个头文件我多处使用，另外，我还使用了boost库。后来，问题解决的方法非常奇怪，在一个头文件中引用其他头文件，这些头文件的顺序换一 下就通过了，那个出现问题的头文件中我使用了std::map，当我把这种容器使用模板代替后，链接就有没事了。（例如：template&lt; class   coll&gt;）,后来感到模板技术还有这种效果,赚了!哈哈   <br />  ＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝   <br />  Knowledge   Base       <br />  What   are   the   C   and   C++   libraries   my   program   would   link   with?   <br />    <br />  ------------------------------------------------------------------   <br />  |   Compile       Old                 New   IOStream       Libraries                                     |   <br />  |   Option         IOStream       or   STL                   Linked   With                                 |   <br />  |================================================================|   <br />  |   /ML               Yes                 No                           LIBC.LIB,         LIBCI.LIB           |   <br />  |   /MDd             Yes                 No                           MSVCRTD.LIB,   MSVCIRTD.LIB     |   <br />    <br />  你的程序使用了/ML编译选项,而程序依赖的.lib可能使用/MDd选项编译,造成链接冲突.   <br />    <br />  统一编译选项可回避此错误   <br />  Project   Settings-&gt;C/C++   Tab-&gt;Category:CodeGeneration   <br />  Use   run-time   library组合框中选择Multithread   Dll(或Debug   Multithread   Dll   )   <br />    <br />  注意：所有相关工程都应该选择相同编译选项   <br />  ＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝   <br />  微软的MSDN中查到信息的   <br />    <br />  可能的原因     <br />    <br />  不慎同时与单线程库和多线程库链接。确保应用程序项目文件只包括适当的库，并且任何第三方库已适当创建了单线程或多线程版本。     <br />  该符号为封装函数（通过用   /Gy   编译创建），包含在多个文件中，但在各编译间已改变。重新编译所有包含   symbol   的文件。     <br />  以不同的形式在不同库中的两个成员对象中定义了该符号，并且使用了这两个成员对象。     <br />  某个绝对符号被定义两次，而每次定义的值不同。     <br />  头文件声明并定义了变量。可能的解决方案有：     <br />  在   .h   中声明变量：extern   BOOL   MyBool;，然后在   .c   或   .cpp   文件中向它分配：BOOL   MyBool   =   FALSE;。     <br />  将变量声明为   Static。     <br />  将变量声明为   selectany。     <br />  当将   uuid.lib   与定义   GUID   的其他   .lib   文件（例如   oledb.lib   和   adsiid.lib）一起使用时。例如：     <br />  oledb.lib(oledb_i.obj)   :   error   LNK2005:   _IID_ITransactionObject   <br />  already   defined   in   uuid.lib(go7.obj)   <br />  若要修复，请将   /FORCE:MULTIPLE   添加到链接器命令行选项，并确保   uuid.lib   是引用的第一个库。     <br />    <br />  有关更多信息，请参阅知识库文章：     <br />    <br />  Q148652，PRB:   LNK2005   Errors   When   Link   C   Run-Time   Libraries   Are   Linked   Before   MFC   Libraries。     <br />  Q140440，FIX:   Global   Overloaded   Delete   Operator   Causes   LNK2005。     <br />  Q184235，PRB:   LNK2005   Errors   on   New   and   Delete   When   Defining   _ATL_MIN_CRT。     <br />  该错误之后为致命错误   LNK1169。   <br />  ＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝   <br />  有时候因为工程使用了预编译头文件并且是增量编译，所以当你改动以后可能也会出现LNK2005错误，提示“XXXX已经在XXXX.obj文件中定义” 的消息，这时候只要Rebuild   All一般都能解决问题。这是因为头文件的顺序被改动等等操作造成的。   <br />  最后要说明的：事物是在不断变化中的，C++的标准在变化，编译器也在变化，所以并不是所有的LNK2005错误都可以在这里找到答案，但是至少它能给你以提示。学习并思考才是正确的！    <br /><img src ="http://www.blogjava.net/Archangelsy/aggbug/146105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-09-18 11:30 <a href="http://www.blogjava.net/Archangelsy/articles/146105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中的日期和时间 time_t与struct tm（转）</title><link>http://www.blogjava.net/Archangelsy/articles/112829.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Sun, 22 Apr 2007 14:47:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/112829.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/112829.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/112829.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/112829.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/112829.html</trackback:ping><description><![CDATA[
		<div class="tit">C/C++中的日期和时间 time_t与struct tm</div>
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt">
												<p>摘要：</p>
												<p>
														<a href="http://wiseman.cnblogs.com/">http://wiseman.cnblogs.com</a>
														<br />本文从介绍基础概念入手，探讨了在C/C++中对日期和时间操作所用到的数据结构和函数，并对计时、时间的获取、时间的计算和显示格式等方面进行了阐述。本文还通过大量的实例向你展示了time.h头文件中声明的各种函数和数据结构的详细使用方法。<br /><br />关键字：UTC（世界标准时间），Calendar Time（日历时间），epoch（时间点），clock tick（时钟计时单元）<br /><br /><br />1．概念<br />在C/C++中，对字符串的操作有很多值得注意的问题，同样，C/C++对时间的操作也有许多值得大家注意的地方。最近，在技术群中有很多网友也多次问到过C++语言中对时间的操作、获取和显示等等的问题。下面，在这篇文章中，笔者将主要介绍在C/C++中时间和日期的使用方法.<br /><br />通过学习许多C/C++库，你可以有很多操作、使用时间的方法。但在这之前你需要了解一些“时间”和“日期”的概念，主要有以下几个：<br /><br />Coordinated Universal Time（UTC）：协调世界时，又称为世界标准时间，也就是大家所熟知的格林威治标准时间（Greenwich Mean Time，GMT）。比如，中国内地的时间与UTC的时差为+8，也就是UTC+8。美国是UTC-5。<br /><br />Calendar Time：日历时间，是用“从一个标准时间点到此时的时间经过的秒数”来表示的时间。这个标准时间点对不同的编译器来说会有所不同，但对一个编译系统来说，这个标准时间点是不变的，该编译系统中的时间对应的日历时间都通过该标准时间点来衡量，所以可以说日历时间是“相对时间”，但是无论你在哪一个时区，在同一时刻对同一个标准时间点来说，日历时间都是一样的。<br /><br />epoch：时间点。时间点在标准C/C++中是一个整数，它用此时的时间和标准时间点相差的秒数（即日历时间）来表示。<br /><br />clock tick：时钟计时单元（而不把它叫做时钟滴答次数），一个时钟计时单元的时间长短是由CPU控制的。一个clock tick不是CPU的一个时钟周期，而是C/C++的一个基本计时单位。<br /><br />我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法，无论是在结构定义，还是命名，都具有明显的C语言风格。下面，我将说明在C/C++中怎样使用日期的时间功能。<br /><br />2． 计时<br /><br />C/C++中的计时函数是clock()，而与其相关的数据类型是clock_t。在MSDN中，查得对clock函数定义如下：<br /><br />clock_t clock( void );<br /><br />这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元（clock tick）数，在MSDN中称之为挂钟时间（wall-clock）。其中clock_t是用来保存时间的数据类型，在time.h文件中，我们可以找到对它的定义：<br /><br />#ifndef _CLOCK_T_DEFINED<br />typedef long clock_t;<br />#define _CLOCK_T_DEFINED<br />#endif<br /><br />很明显，clock_t是一个长整形数。在time.h文件中，还定义了一个常量CLOCKS_PER_SEC，它用来表示一秒钟会有多少个时钟计时单元，其定义如下：<br /><br />#define CLOCKS_PER_SEC ((clock_t)1000)<br /><br />可以看到可以看到每过千分之一秒（1毫秒），调用clock（）函数返回的值就加1。下面举个例子，你可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间：<br /><br />void elapsed_time()<br />{<br />printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);<br />}<br /><br />当然，你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间：<br /><br />＃i nclude “stdio.h”<br />＃i nclude “stdlib.h”<br />＃i nclude “time.h”<br /><br />int main( void )<br />{<br />   long    i = 10000000L;<br />   clock_t start, finish;<br />   double  duration;<br />   /* 测量一个事件持续的时间*/<br />   printf( "Time to do %ld empty loops is ", i );<br />   start = clock();<br />   while( i-- )      ;<br />   finish = clock();<br />   duration = (double)(finish - start) / CLOCKS_PER_SEC;<br />   printf( "%f seconds\n", duration );<br />   system("pause");<br />}<br /><br />在笔者的机器上，运行结果如下：<br /><br />Time to do 10000000 empty loops is 0.03000 seconds<br /><br />上面我们看到时钟计时单元的长度为1毫秒，那么计时的精度也为1毫秒，那么我们可不可以通过改变CLOCKS_PER_SEC的定义，通过把它定义的大一些，从而使计时精度更高呢？通过尝试，你会发现这样是不行的。在标准C/C++中，最小的计时单位是一毫秒。<br /><br />3．与日期和时间相关的数据结构<br /><br />在标准C/C++中，我们可通过tm结构来获得日期和时间，tm结构在time.h中的定义如下：<br /><br />#ifndef _TM_DEFINED<br />struct tm {<br />        int tm_sec;     /* 秒 – 取值区间为[0,59] */<br />        int tm_min;     /* 分 - 取值区间为[0,59] */<br />        int tm_hour;    /* 时 - 取值区间为[0,23] */<br />        int tm_mday;    /* 一个月中的日期 - 取值区间为[1,31] */<br />        int tm_mon;     /* 月份（从一月开始，0代表一月） - 取值区间为[0,11] */<br />        int tm_year;    /* 年份，其值等于实际年份减去1900 */<br />        int tm_wday;    /* 星期 – 取值区间为[0,6]，其中0代表星期天，1代表星期一，以此类推 */<br />        int tm_yday;    /* 从每年的1月1日开始的天数 – 取值区间为[0,365]，其中0代表1月1日，1代表1月2日，以此类推 */<br />        int tm_isdst;   /* 夏令时标识符，实行夏令时的时候，tm_isdst为正。不实行夏令时的进候，tm_isdst为0；不了解情况时，tm_isdst()为负。*/<br />        };<br />#define _TM_DEFINED<br />#endif<br /><br />ANSI C标准称使用tm结构的这种时间表示为分解时间(broken-down time)。<br /><br />而日历时间（Calendar Time）是通过time_t数据类型来表示的，用time_t表示的时间（日历时间）是从一个时间点（例如：1970年1月1日0时0分0秒）到此时的秒数。在time.h中，我们也可以看到time_t是一个长整型数：<br /><br />#ifndef _TIME_T_DEFINED<br />typedef long time_t;         /* 时间值 */<br />#define _TIME_T_DEFINED     /* 避免重复定义 time_t */<br />#endif<br /><br />大家可能会产生疑问：既然time_t实际上是长整型，到未来的某一天，从一个时间点（一般是1970年1月1日0时0分0秒）到那时的秒数（即日历时间）超出了长整形所能表示的数的范围怎么办？对time_t数据类型的值来说，它所表示的时间不能晚于2038年1月18日19时14分07秒。为了能够表示更久远的时间，一些编译器厂商引入了64位甚至更长的整形数来保存日历时间。比如微软在Visual C++中采用了__time64_t数据类型来保存日历时间，并通过_time64()函数来获得日历时间（而不是通过使用32位字的time()函数），这样就可以通过该数据类型保存3001年1月1日0时0分0秒（不包括该时间点）之前的时间。<br /><br />在time.h头文件中，我们还可以看到一些函数，它们都是以time_t为参数类型或返回值类型的函数：<br /><br />double difftime(time_t time1, time_t time0);<br />time_t mktime(struct tm * timeptr);<br />time_t time(time_t * timer);<br />char * asctime(const struct tm * timeptr);<br />char * ctime(const time_t *timer);<br /><br />此外，time.h还提供了两种不同的函数将日历时间（一个用time_t表示的整数）转换为我们平时看到的把年月日时分秒分开显示的时间格式tm：<br /><br />struct tm * gmtime(const time_t *timer);                                          <br />struct tm * localtime(const time_t * timer);<br /><br />通过查阅MSDN，我们可以知道Microsoft C/C++ 7.0中时间点的值（time_t对象的值）是从1899年12月31日0时0分0秒到该时间点所经过的秒数，而其它各种版本的Microsoft C/C++和所有不同版本的Visual C++都是计算的从1970年1月1日0时0分0秒到该时间点所经过的秒数。<br /><br />4．与日期和时间相关的函数及应用<br />在本节，我将向大家展示怎样利用time.h中声明的函数对时间进行操作。这些操作包括取当前时间、算时间间隔、以不同的形式显示时间等内容。<br /><br />4.1 获得日历时间<br /><br />我们可以通过time()函数来获得日历时间（Calendar Time），其原型为：<br /><br />time_t time(time_t * timer);<br /><br />如果你已经声明了参数timer，你可以从参数timer返回现在的日历时间，同时也可以通过返回值返回现在的日历时间，即从一个时间点（例如：1970年1月1日0时0分0秒）到现在此时的秒数。如果参数为空（NULL），函数将只通过返回值返回现在的日历时间，比如下面这个例子用来显示当前的日历时间：<br /><br />＃i nclude "time.h"<br />＃i nclude "stdio.h"<br />int main(void)<br />{<br />struct tm *ptr;<br />time_t lt;<br />lt =time(NULL);<br />printf("The Calendar Time now is %d\n",lt);<br />return 0;<br />}<br /><br />运行的结果与当时的时间有关，我当时运行的结果是：<br /><br />The Calendar Time now is 1122707619<br /><br />其中1122707619就是我运行程序时的日历时间。即从1970年1月1日0时0分0秒到此时的秒数。<br /><br />4.2 获得日期和时间<br /><br />这里说的日期和时间就是我们平时所说的年、月、日、时、分、秒等信息。从第2节我们已经知道这些信息都保存在一个名为tm的结构体中，那么如何将一个日历时间保存为一个tm结构的对象呢？<br /><br />其中可以使用的函数是gmtime()和localtime()，这两个函数的原型为：<br /><br />struct tm * gmtime(const time_t *timer);                                          <br />struct tm * localtime(const time_t * timer);<br /><br />其中gmtime()函数是将日历时间转化为世界标准时间（即格林尼治时间），并返回一个tm结构体来保存这个时间，而localtime()函数是将日历时间转化为本地时间。比如现在用gmtime()函数获得的世界标准时间是2005年7月30日7点18分20秒，那么我用localtime()函数在中国地区获得的本地时间会比时间标准时间晚8个小时，即2005年7月30日15点18分20秒。下面是个例子：<br /><br />＃i nclude "time.h"<br />＃i nclude "stdio.h"<br />int main(void)<br />{<br />struct tm *local;<br />time_t t;<br />t=time(NULL);<br />local=localtime(&amp;t);<br />printf("Local hour is: %d\n",local-&gt;tm_hour);<br />local=gmtime(&amp;t);<br />printf("UTC hour is: %d\n",local-&gt;tm_hour);<br />return 0;<br />}<br /><br />运行结果是：<br /><br />Local hour is: 15<br />UTC hour is: 7<br /><br />4.3 固定的时间格式<br /><br />我们可以通过asctime()函数和ctime()函数将时间以固定的格式显示出来，两者的返回值都是char*型的字符串。返回的时间格式为：<br /><br />星期几 月份 日期 时:分:秒 年\n\0<br />例如：Wed Jan 02 02:03:55 1980\n\0<br /><br />其中\n是一个换行符，\0是一个空字符，表示字符串结束。下面是两个函数的原型：<br /><br />char * asctime(const struct tm * timeptr);<br />char * ctime(const time_t *timer);<br /><br />其中asctime()函数是通过tm结构来生成具有固定格式的保存时间信息的字符串，而ctime()是通过日历时间来生成时间字符串。这样的话，asctime（）函数只是把tm结构对象中的各个域填到时间字符串的相应位置就行了，而ctime（）函数需要先参照本地的时间设置，把日历时间转化为本地时间，然后再生成格式化后的字符串。在下面，如果lt是一个非空的time_t变量的话，那么：<br /><br />printf(ctime(&lt;));<br /><br />等价于：<br /><br />struct tm *ptr;<br />ptr=localtime(&lt;);<br />printf(asctime(ptr));<br /><br />那么，下面这个程序的两条printf语句输出的结果就是不同的了（除非你将本地时区设为世界标准时间所在的时区）：<br /><br />＃i nclude "time.h"<br />＃i nclude "stdio.h"<br />int main(void)<br />{<br />struct tm *ptr;<br />time_t lt;<br />lt =time(NULL);<br />ptr=gmtime(&lt;);<br />printf(asctime(ptr));<br />printf(ctime(&lt;));<br />return 0;<br />}<br /><br />运行结果：<br /><br />Sat Jul 30 08:43:03 2005<br />Sat Jul 30 16:43:03 2005<br /><br />4.4 自定义时间格式<br /><br />我们可以使用strftime（）函数将时间格式化为我们想要的格式。它的原型如下：<br /><br />size_t strftime(<br />   char *strDest,<br />   size_t maxsize,<br />   const char *format,<br />   const struct tm *timeptr <br />);<br /><br />我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字符串中，最多向strDest中存放maxsize个字符。该函数返回向strDest指向的字符串中放置的字符数。<br /><br />函数strftime()的操作有些类似于sprintf()：识别以百分号(%)开始的格式命令集合，格式化输出结果放在一个字符串中。格式化命令说明串strDest中各种日期和时间信息的确切表示方法。格式串中的其他字符原样放进串中。格式命令列在下面，它们是区分大小写的。<br /><br />%a 星期几的简写 <br />%A 星期几的全称 <br />%b 月分的简写 <br />%B 月份的全称 <br />%c 标准的日期的时间串 <br />%C 年份的后两位数字 <br />%d 十进制表示的每月的第几天 <br />%D 月/天/年 <br />%e 在两字符域中，十进制表示的每月的第几天 <br />%F 年-月-日 <br />%g 年份的后两位数字，使用基于周的年 <br />%G 年分，使用基于周的年 <br />%h 简写的月份名 <br />%H 24小时制的小时 <br />%I 12小时制的小时<br />%j 十进制表示的每年的第几天 <br />%m 十进制表示的月份 <br />%M 十时制表示的分钟数 <br />%n 新行符 <br />%p 本地的AM或PM的等价显示 <br />%r 12小时的时间 <br />%R 显示小时和分钟：hh:mm <br />%S 十进制的秒数 <br />%t 水平制表符 <br />%T 显示时分秒：hh:mm:ss <br />%u 每周的第几天，星期一为第一天 （值从0到6，星期一为0）<br />%U 第年的第几周，把星期日做为第一天（值从0到53）<br />%V 每年的第几周，使用基于周的年 <br />%w 十进制表示的星期几（值从0到6，星期天为0）<br />%W 每年的第几周，把星期一做为第一天（值从0到53） <br />%x 标准的日期串 <br />%X 标准的时间串 <br />%y 不带世纪的十进制年份（值从0到99）<br />%Y 带世纪部分的十制年份 <br />%z，%Z 时区名称，如果不能得到时区名称则返回空字符。<br />%% 百分号<br /><br />如果想显示现在是几点了，并以12小时制显示，就象下面这段程序：<br /><br />＃i nclude “time.h”<br />＃i nclude “stdio.h”<br />int main(void)<br />{<br />struct tm *ptr;<br />time_t lt;<br />char str[80];<br />lt=time(NULL);<br />ptr=localtime(&lt;);<br />strftime(str,100,"It is now %I %p",ptr);<br />printf(str);<br />return 0;<br />}<br /><br />其运行结果为：<br />It is now 4PM<br /><br />而下面的程序则显示当前的完整日期：<br /><br />＃i nclude <stdio.h /><br />＃i nclude <time.h /><br /><br />void main( void )<br />{<br />        struct tm *newtime;<br />        char tmpbuf[128];<br />        time_t lt1;<br />        time( &lt;1 );<br />        newtime=localtime(&lt;1);<br />        strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);<br />        printf(tmpbuf);<br />}<br /><br />运行结果：<br /><br />Today is Saturday, day 30 of July in the year 2005.<br /><br />4.5 计算持续的时间长度<br /><br />有时候在实际应用中要计算一个事件持续的时间长度，比如计算打字速度。在第1节计时部分中，我已经用clock函数举了一个例子。Clock()函数可以精确到毫秒级。同时，我们也可以使用difftime()函数，但它只能精确到秒。该函数的定义如下：<br /><br />double difftime(time_t time1, time_t time0);<br /><br />虽然该函数返回的以秒计算的时间间隔是double类型的，但这并不说明该时间具有同double一样的精确度，这是由它的参数觉得的（time_t是以秒为单位计算的）。比如下面一段程序：<br /><br />＃i nclude “time.h”<br />＃i nclude “stdio.h”<br />＃i nclude “stdlib.h”<br />int main(void)<br />{<br />time_t start,end;<br />start = time(NULL);<br />system("pause");<br />end = time(NULL);<br />printf("The pause used %f seconds.\n",difftime(end,start));//&lt;-<br />system("pause");<br />return 0;<br />}<br /><br />运行结果为：<br />请按任意键继续. . .<br />The pause used 2.000000 seconds.<br />请按任意键继续. . .<br /><br />可以想像，暂停的时间并不那么巧是整整2秒钟。其实，你将上面程序的带有“//&lt;-”注释的一行用下面的一行代码替换：<br /><br />printf("The pause used %f seconds.\n",end-start);<br /><br />其运行结果是一样的。<br /><br />4.6 分解时间转化为日历时间<br /><br />这里说的分解时间就是以年、月、日、时、分、秒等分量保存的时间结构，在C/C++中是tm结构。我们可以使用mktime（）函数将用tm结构表示的时间转化为日历时间。其函数原型如下：<br /><br />time_t mktime(struct tm * timeptr);<br /><br />其返回值就是转化后的日历时间。这样我们就可以先制定一个分解时间，然后对这个时间进行操作了，下面的例子可以计算出1997年7月1日是星期几：<br /><br />＃i nclude "time.h"<br />＃i nclude "stdio.h"<br />＃i nclude "stdlib.h"<br />int main(void)<br />{<br />struct tm t;<br />time_t t_of_day;<br />t.tm_year=1997-1900;<br />t.tm_mon=6;<br />t.tm_mday=1;<br />t.tm_hour=0;<br />t.tm_min=0;<br />t.tm_sec=1;<br />t.tm_isdst=0;<br />t_of_day=mktime(&amp;t);<br />printf(ctime(&amp;t_of_day));<br />return 0;<br />}<br /><br />运行结果：<br /><br />Tue Jul 01 00:00:01 1997<br /><br />现在注意了，有了mktime()函数，是不是我们可以操作现在之前的任何时间呢？你可以通过这种办法算出1945年8月15号是星期几吗？答案是否定的。因为这个时间在1970年1月1日之前，所以在大多数编译器中，这样的程序虽然可以编译通过，但运行时会异常终止。<br /><br />5．总结<br /><br />本文介绍了标准C/C++中的有关日期和时间的概念，并通过各种实例讲述了这些函数和数据结构的使用方法。笔者认为，和时间相关的一些概念是相当重要的，理解这些概念是理解各种时间格式的转换的基础，更是应用这些函数和数据结构的基础。<br /></p>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/Archangelsy/aggbug/112829.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-04-22 22:47 <a href="http://www.blogjava.net/Archangelsy/articles/112829.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>  C++中const总结 （转）</title><link>http://www.blogjava.net/Archangelsy/articles/112828.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Sun, 22 Apr 2007 14:46:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/112828.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/112828.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/112828.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/112828.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/112828.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="98%" align="center" border="0">
				<tbody>
						<tr>
								<td> </td>
						</tr>
						<tr>
								<td>
										<p style="WORD-BREAK: break-all; LINE-HEIGHT: 16pt; WORD-WRAP: break-word">
												<span class="bt">
														<br />一、对于基本声明 <br />1.const int r=100; //标准const变量声明加初始化，因为默认内部连接所以必须被初始化，其作用域为此文件，编译器经过类型检查后直接用100在编译时替换。 <br /><br />2.extend const int r=100; //将const改为外部连接，作用于扩大至全局，编译时会分配内存，并且可以不进行初始化，仅仅作为声明，编译器认为在程序其他地方进行了定义。 <br /><br />3.const int r[ ]={1,2,3,4}; <br />struct S {int a,b;}; <br />const S s[ ]={(1,2),(3.4)}; //以上两种都是常量集合，编译器会为其分配内存，所以不能在编译期间使用其中的值，例如：int temp[r[2]];这样的编译器会报告不能找到常量表达式 <br /><br />二、对于指针 <br />1.const int *r=&amp;x; //声明r为一个指向常量的x的指针，r指向的对象不能被修改，但他可以指向任何地址的常量。 <br /><br />2.int const *r=&amp;x; //与用法1完全等价，没有任何区别。 <br /><br />3.int * const r=&amp;x; //声明r为一个常量指针，他指向x，r这个指针的指向不能被修改，但他指向的地址的内容可以修改。 <br /><br />4.const int * const r=&amp;x; //综合1、3用法，r是一个指向常量的常量型指针。 <br /><br />三、对于类型检查 <br />可以把一个非const对象赋给一个指向const的指针，因为有时候我们不想从这个指针来修改其对象的值；但是不可以把一个const对象赋值给一个非const指针，因为这样可能会通过这个指针改变指向对象的值，但也存在使这种操作通过的合法化写法，使用类型强制转换可以通过指针改变const对象： <br />const int r=100; <br />int * ptr = const_cast(&amp;r); //C++标准，C语言使用：int * ptr =(int*)&amp;r; <br /><br />四、对于字符数组 <br />如char * name = “china”; 这样的语句，在编译时是能够通过的，但是”china”是常量字符数组，任何想修改他的操作也能通过编译但会引起运行时错误，如果我们想修改字符数组的话就要使用char name[ ] = “china”; 这种形式。 <br /><br />五、对于函数 <br />1.void Fuction1 ( const int r ); //此处为参数传递const值，意义是变量初值不能被函数改变 <br /><br />2.const int Fuction1 (int); //此处返回const值，意思指返回的原函数里的变量的初值不能被修改，但是函数按值返回的这个变量被制成副本，能不能被修改就没有了意义，它可以被赋给任何的const或非const类型变量，完全不需要加上这个const关键字。但这只对于内部类型而言（因为内部类型返回的肯定是一个值，而不会返回一个变量，不会作为左值使用），对于用户自定义类型，返回值是常量是非常重要的，见下面条款3。 <br /><br />3.Class CX; //内部有构造函数，声明如CX(int r =0) <br />CX Fuction1 () { return CX(); } <br />const CX Fuction2 () { return CX(); } <br />如有上面的自定义类CX，和函数Fuction1()和Fuction2(),我们进行如下操作时： <br />Fuction1() = CX(1); //没有问题，可以作为左值调用 <br />Fuction2() = CX(1); //编译错误，const返回值禁止作为左值调用。因为左值把返回值作为变量会修改其返回值，const声明禁止这种修改。 <br /><br />4.函数中指针的const传递和返回： <br />int F1 (const char * pstr); //作为传递的时候使用const修饰可以保证不会通过这个指针来修改传递参数的初值，这里在函数内部任何修改*pstr的企图都会引起编译错误。 <br />const char * F2 (); //意义是函数返回的指针指向的对象是一个const对象，它必须赋给一个同样是指向const对象的指针。 <br />const char * const F3(); //比上面多了一个const，这个const的意义只是在他被用作左值时有效，它表明了这个指针除了指向const对象外，它本身也不能被修改，所以就不能当作左值来处理。 <br /><br />5.函数中引用的const传递： <br />void F1 ( const X&amp; px); //这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的，他禁止对引用的对象的一切修改，唯一不同的是按值传递会先建立一个类对象的副本，然后传递过去，而它直接传递地址，所以这种传递比按值传递更有效。 <br />**另外只有引用的const传递可以传递一个临时对象，因为临时对象都是const属性，且是不可见的，他短时间存在一个局部域中，所以不能使用指针，只有引用的const传递能够捕捉到这个家伙。 <br /><br />六、对于类 <br />1.首先，对于const的成员变量，只能在构造函数里使用初始化成员列表来初始化，试图在构造函数体内进行初始化const成员变量会引起编译错误。初始化成员列表形如： <br />2.X:: X ( int ir ): r(ir) {} //假设r是类X的const成员变量 <br /><br />2.const成员函数。提到这个概念首先要谈到const对象，正象内置类型能够定义const对象一样（const int r=10;），用户自定义类型也可以定义const对象(const X px(10);)，编译器要保证这个对象在其生命周期内不能够被改变。如果你定义了这样的一个const对象，那么对于这个对象的一切非const成员函数的调用，编译器为了保证对象的const特性，都会禁止并在编译期间报错。所以如果你想让你的成员函数能够在const对象上进行操作的话，就要把这个函数声明为const成员函数。假如f( )是类中的成员函数的话，它的声明形如： <br />int f( ) const; //const放在函数的最后，编译器会对这个函数进行检查，在这个函数中的任何试图改变成员变量和调用非const成员函数的操作都被视为非法 <br />注意：类的构造和析构函数都不能是const函数。 <br /><br />3.建立了一个const成员函数，但仍然想用这个函数改变对象内部的数据。这样的一个要求也会经常遇到，尤其是在一个苛刻的面试考官那里。首先我们要弄清楚考官的要求，因为有两种方法可以实现，如果这位考官要求不改变原来类的任何东西，只让你从当前这个const成员函数入手，那么你只有使用前面提到的类型强制转换方法。实例如下： <br />//假如有一个叫做X的类，它有一个int成员变量r，我们需要通过一个const成员函数f( )来对这个r进行++r操作，代码如下 <br />void X::f( ) const <br />{ (const_cast(this)) -&gt; ++r; } //通过this指针进行类型强制转换实现 <br />另外一种方法就是使用关键字：mutable。如果你的成员变量在定义时是这个样子的： <br />mutable int r ; <br />那么它就告诉编译器这个成员变量可以通过const成员函数改变。编译器就不会再理会对他的检查了。 <br /><br />：这个总结还不错的。 </span>
												<br />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/Archangelsy/aggbug/112828.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-04-22 22:46 <a href="http://www.blogjava.net/Archangelsy/articles/112828.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于CONST的用法(转)</title><link>http://www.blogjava.net/Archangelsy/articles/112827.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Sun, 22 Apr 2007 14:45:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/112827.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/112827.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/112827.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/112827.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/112827.html</trackback:ping><description><![CDATA[
		<table style="BORDER-COLLAPSE: collapse; WORD-WRAP: break-word" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td align="middle" height="25">
								</td>
						</tr>
						<tr>
								<td bgcolor="#d2dee2" height="1">
								</td>
						</tr>
						<tr>
								<td bgcolor="#ffffff" height="1">
								</td>
						</tr>
						<tr>
								<td align="middle">
										<table style="BORDER-COLLAPSE: collapse; WORD-WRAP: break-word" cellspacing="0" cellpadding="0" width="100%" border="0">
												<tbody>
														<tr>
																<td width="100%">
																		<div id="art" style="MARGIN: 15px" width="100%">
																				<div>const在C语言中算是一个比较新的描述符，我们称之为常量修饰符，意即其所修饰<br />的对象为常量(immutable)。<br /><br />我们来分情况看语法上它该如何被使用。<br /><br />1、函数体内修饰局部变量。<br />例：<br />void func(){<br />const int a=0;<br />}<br /><br />首先，我们先把const这个单词忽略不看，那么a是一个int类型的局部自动变量，<br />我们给它赋予初始值0。<br /><br />然后再看const.<br /><br />const作为一个类型限定词，和int有相同的地位。<br />const int a;<br />int const a;<br />是等价的。于是此处我们一定要清晰的明白，const修饰的对象是谁，是a,和int没<br />有关系。const 要求他所修饰的对象为常量，不可被改变，不可被赋值，不可作为<br />左值（l-value)。<br />这样的写法也是错误的。<br />const int a;<br />a=0;<br />这是一个很常见的使用方式：<br />const double pi=3.14;<br />在程序的后面如果企图对pi再次赋值或者修改就会出错。<br /><br />然后看一个稍微复杂的例子。<br />const int* p;<br />还是先去掉const 修饰符号。<br />注意，下面两个是等价的。<br />int* p;<br />int *p;<br />其实我们想要说的是，*p是int类型。那么显然，p就是指向int的指针。<br />同理<br />const int* p;<br />其实等价于<br />const int (*p);<br />int const (*p);<br />即，*p是常量。也就是说，p指向的数据是常量。<br />于是<br />p+=8; //合法<br />*p=3; //非法，p指向的数据是常量。<br /><br />那么如何声明一个自身是常量指针呢？方法是让const尽可能的靠近p;<br />int* const p;<br />const右面只有p,显然，它修饰的是p,说明p不可被更改。然后把const去掉，可以<br />看出p是一个指向 int形式变量的指针。<br />于是<br />p+=8; //非法<br />*p=3; //合法<br /><br />再看一个更复杂的例子，它是上面二者的综合<br />const int* const p;<br />说明p自己是常量，且p指向的变量也是常量。<br />于是<br />p+=8; //非法<br />*p=3; //非法<br /><br />const 还有一个作用就是用于修饰常量静态字符串。<br />例如：<br />const char* name="David";<br />如果没有const,我们可能会在后面有意无意的写name[4]='x'这样的语句，这样会<br />导致对只读内存区域的赋值，然后程序会立刻异常终止。有了 const,这个错误就<br />能在程序被编译的时候就立即检查出来，这就是const的好处。让逻辑错误在编译<br />期被发现。<br /><br />const 还可以用来修饰数组<br />const char s[]="David";<br />与上面有类似的作用。<br /><br />2、在函数声明时修饰参数<br />来看实际中的一个例子。<br />NAME<br />memmove -- copy byte string<br /><br />LIBRARY<br />Standard C Library (libc, -lc)<br /><br />SYNOPSIS<br />#include &lt;string.h&gt;<br /><br />void *<br />memmove(void *dst, const void *src, size_t len);<br /><br />这是标准库中的一个函数，用于按字节方式复制字符串（内存）。<br />它的第一个参数，是将字符串复制到哪里去（dest),是目的地，这段内存区域必须<br />是可写。<br />它的第二个参数，是要将什么样的字符串复制出去，我们对这段内存区域只做读<br />取，不写。<br />于是，我们站在这个函数自己的角度来看，src 这个指针，它所指向的内存内所存<br />储的数据在整个函数执行的过程中是不变。于是src所指向的内容是常量。于是就<br />需要用const修饰。<br />例如，我们这里这样使用它。<br />const char* s="hello";<br />char buf[100];<br />memmove(buf,s,6); //这里其实应该用strcpy或memcpy更好<br /><br />如果我们反过来写，<br />memmove(s,buf,6);<br />那么编译器一定会报错。事实是我们经常会把各种函数的参数顺序写反。事实是编<br />译器在此时帮了我们大忙。如果编译器静悄悄的不报错，(在函数声明处去掉<br />const即可),那么这个程序在运行的时候一定会崩溃。<br /><br />这里还要说明的一点是在函数参数声明中const一般用来声明指针而不是变量本身。<br />例如，上面的size_t len,在函数实现的时候可以完全不用更改len的值，那么是否<br />应该把len也声明为常量呢？可以，可以这么做。我们来分析这么做有什么优劣。<br />如果加了const,那么对于这个函数的实现者，可以防止他在实现这个函数的时候修<br />改不需要修改的值(len),这样很好。<br />但是对于这个函数的使用者，<br />1。这个修饰符号毫无意义，我们可以传递一个常量整数或者一个非常量整数过<br />去，反正对方获得的只是我们传递的一个copy。<br />2。暴露了实现。我不需要知道你在实现这个函数的时候是否修改过len的值。<br /><br />所以，const一般只用来修饰指针。<br /><br />再看一个复杂的例子<br />int execv(const char *path, char *const argv[]);<br />着重看后面这个，argv.它代表什么。<br />如果去掉const,我们可以看出<br />char * argv[];<br />argv是一个数组，它的每个元素都是char *类型的指针。<br />如果加上const.那么const修饰的是谁呢？他修饰的是一个数组，argv[],意思就是<br />说这个数组的元素是只读的。那么数组的元素的是什么类型呢？是char *类型的指<br />针.也就是说指针是常量，而它指向的数据不是。<br />于是<br />argv[1]=NULL; //非法<br />argv[0][0]='a'; //合法<br /><br /><br />3、全局变量。<br />我们的原则依然是，尽可能少的使用全局变量。<br />我们的第二条规则 则是，尽可能多的使用const。<br />如果一个全局变量只在本文件中使用，那么用法和前面所说的函数局部变量没有什<br />么区别。<br />如果它要在多个文件间共享，那么就牵扯到一个存储类型的问题。<br /><br />有两种方式。<br />1.使用extern<br />例如<br />/* file1.h */<br />extern const double pi;<br />/* file1.c */<br />const double pi=3.14;<br />然后其他需要使用pi这个变量的，包含file1.h<br />#include "file1.h"<br />或者，自己把那句声明复制一遍就好。<br />这样做的结果是，整个程序链接完后，所有需要使用pi这个变量的共享一个存储区域。<br /><br />2.使用static,静态外部存储类<br />/* constant.h */<br />static const pi=3.14;<br />需要使用这个变量的*.c文件中，必须包含这个头文件。<br />前面的static一定不能少。否则链接的时候会报告说该变量被多次定义。<br />这样做的结果是，每个包含了constant.h的*.c文件，都有一份该变量自己的copy,<br />该变量实际上还是被定义了多次，占用了多个存储空间，不过在加了static关键字<br />后，解决了文件间重定义的冲突。<br />坏处是浪费了存储空间，导致链接完后的可执行文件变大。但是通常，这个，小小<br />几字节的变化，不是问题。<br />好处是，你不用关心这个变量是在哪个文件中被初始化的。<br /><br /><br />最后，说说const的作用。<br />const 的好处，是引入了常量的概念，让我们不要去修改不该修改的内存。直接的<br />作用就是让更多的逻辑错误在编译期被发现。所以我们要尽可能的多使用const。<br />但是很多人并不习惯使用它，更有甚者，是在整个程序 编写／调试 完后才补<br />const。如果是给函数的声明补const,尚好。如果是给 全局／局部变量补const,那<br />么……那么，为时已晚，无非是让代码看起来更漂亮了。</div>
																		</div>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/Archangelsy/aggbug/112827.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-04-22 22:45 <a href="http://www.blogjava.net/Archangelsy/articles/112827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>c++全局变量 的问题 可能是～～</title><link>http://www.blogjava.net/Archangelsy/articles/104499.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Sat, 17 Mar 2007 11:25:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/104499.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/104499.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/104499.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/104499.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/104499.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">遇到一个怪现象。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">写的</span>
				<span lang="EN-US">
						<font face="宋体, MS Song">VC</font>
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">程序打开，再关闭，就会提示异常。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">这个程序使用了</span>
				<span lang="EN-US">
						<font face="宋体, MS Song">ADO</font>
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">一开始定义了一个全局的：</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">CAdoConnection <span style="mso-spacerun: yes"> </span>conn;<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?><o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">后面每个类都直接用了</span>
				<span lang="EN-US">
						<font face="宋体, MS Song">conn.</font>
				</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">～～</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">但是</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">全局变量在初时化前引入，</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">在退出时自动释放，无法控制释放的地方。</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US">
						<o:p>
								<font face="宋体, MS Song"> </font>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">但用指针：</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">CAdoConnection <span style="mso-spacerun: yes"> </span>*pConn;<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">pConn = <span style="mso-spacerun: yes"> </span>new <span style="mso-spacerun: yes"> </span>CAdoConnection();<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">pConn-&gt;</span>
				<span style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">～～</span>
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">就可以控制它的释放了：</span>
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: blue; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">delete</span>
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">
						<span style="mso-spacerun: yes"> </span>pConn;<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 宋体; mso-font-kerning: 0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">好像是这个道理吧。</span>
				<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: black; FONT-FAMILY: 'Times New Roman'; mso-font-kerning: 0pt">
						<o:p>
						</o:p>
				</span>
		</p>
<img src ="http://www.blogjava.net/Archangelsy/aggbug/104499.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-03-17 19:25 <a href="http://www.blogjava.net/Archangelsy/articles/104499.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>time函数</title><link>http://www.blogjava.net/Archangelsy/articles/104498.html</link><dc:creator>archangel</dc:creator><author>archangel</author><pubDate>Sat, 17 Mar 2007 11:19:00 GMT</pubDate><guid>http://www.blogjava.net/Archangelsy/articles/104498.html</guid><wfw:comment>http://www.blogjava.net/Archangelsy/comments/104498.html</wfw:comment><comments>http://www.blogjava.net/Archangelsy/articles/104498.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/Archangelsy/comments/commentRss/104498.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Archangelsy/services/trackbacks/104498.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: time												函数																																 																																       time_t    tTime1 = 0;																										       time_t    tT...&nbsp;&nbsp;<a href='http://www.blogjava.net/Archangelsy/articles/104498.html'>阅读全文</a><img src ="http://www.blogjava.net/Archangelsy/aggbug/104498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Archangelsy/" target="_blank">archangel</a> 2007-03-17 19:19 <a href="http://www.blogjava.net/Archangelsy/articles/104498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>