﻿<?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-Calvin's Tech Space-随笔分类-C</title><link>http://www.blogjava.net/lihao336/category/47963.html</link><description>成于坚忍，毁于浮躁</description><language>zh-cn</language><lastBuildDate>Wed, 14 Dec 2011 00:28:04 GMT</lastBuildDate><pubDate>Wed, 14 Dec 2011 00:28:04 GMT</pubDate><ttl>60</ttl><item><title>[转]unix/linux中的dup()系统调用</title><link>http://www.blogjava.net/lihao336/archive/2011/12/13/366231.html</link><dc:creator>calvin</dc:creator><author>calvin</author><pubDate>Tue, 13 Dec 2011 06:20:00 GMT</pubDate><guid>http://www.blogjava.net/lihao336/archive/2011/12/13/366231.html</guid><wfw:comment>http://www.blogjava.net/lihao336/comments/366231.html</wfw:comment><comments>http://www.blogjava.net/lihao336/archive/2011/12/13/366231.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lihao336/comments/commentRss/366231.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lihao336/services/trackbacks/366231.html</trackback:ping><description><![CDATA[   在linux纷繁复杂的内核代码中，sys_dup()的代码也许称得上是最简单的之一了，但是就是这么一个简单的系统调用，却成就了unix/linux系统最著名的一个特性：输入/输出重定向。<br />    sys_dup()的主要工作就是用来“复制”一个打开的文件号，使两个文件号都指向同一个文件。既然说简单，我们就首先来看一下它的代码（定义在fs/fcntl.c中）：<br /><div style="background-color:#eeeeee;font-size:13px;BORDER:1px solid #CCCCCC;PADDING-RIGHT: 5px;PADDING-BOTTOM: 4px;PADDING-left: 4px;PADDING-TOP: 4px;WIDTH: 98%;word-break:break-all"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008080; "> 1</span> <span style="color: #000000; ">asmlinkage </span><span style="color: #0000FF; ">long</span><span style="color: #000000; "> sys_dup(unsigned </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> fildes)<br /></span><span style="color: #008080; "> 2</span> <span style="color: #000000; ">{<br /></span><span style="color: #008080; "> 3</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> ret </span><span style="color: #000000; ">=</span><span style="color: #000000; "> </span><span style="color: #000000; ">-</span><span style="color: #000000; ">EBADF;<br /></span><span style="color: #008080; "> 4</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">struct</span><span style="color: #000000; "> file </span><span style="color: #000000; ">*</span><span style="color: #000000; "> file </span><span style="color: #000000; ">=</span><span style="color: #000000; "> fget(fildes);<br /></span><span style="color: #008080; "> 5</span> <span style="color: #000000; "><br /></span><span style="color: #008080; "> 6</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (file)<br /></span><span style="color: #008080; "> 7</span> <span style="color: #000000; ">        ret </span><span style="color: #000000; ">=</span><span style="color: #000000; "> dupfd(file, </span><span style="color: #000000; ">0</span><span style="color: #000000; ">);<br /></span><span style="color: #008080; "> 8</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> ret;<br /></span><span style="color: #008080; "> 9</span> <span style="color: #000000; ">}<br /></span><span style="color: #008080; ">10</span> <span style="color: #000000; "></span></div><br />而sys_dup()的主体是dupfd()（定义在同一个文件中）：<br /><br /><div style="background-color:#eeeeee;font-size:13px;BORDER:1px solid #CCCCCC;PADDING-RIGHT: 5px;PADDING-BOTTOM: 4px;PADDING-left: 4px;PADDING-TOP: 4px;WIDTH: 98%;word-break:break-all"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008080; "> 1</span> <span style="color: #0000FF; ">static</span><span style="color: #000000; "> </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> dupfd(</span><span style="color: #0000FF; ">struct</span><span style="color: #000000; "> file </span><span style="color: #000000; ">*</span><span style="color: #000000; ">file, </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> start)<br /></span><span style="color: #008080; "> 2</span> <span style="color: #000000; ">{<br /></span><span style="color: #008080; "> 3</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">struct</span><span style="color: #000000; "> files_struct </span><span style="color: #000000; ">*</span><span style="color: #000000; "> files </span><span style="color: #000000; ">=</span><span style="color: #000000; "> current</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">files;<br /></span><span style="color: #008080; "> 4</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">int</span><span style="color: #000000; "> ret;<br /></span><span style="color: #008080; "> 5</span> <span style="color: #000000; "><br /></span><span style="color: #008080; "> 6</span> <span style="color: #000000; ">    ret </span><span style="color: #000000; ">=</span><span style="color: #000000; "> locate_fd(files, file, start);<br /></span><span style="color: #008080; "> 7</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">if</span><span style="color: #000000; "> (ret </span><span style="color: #000000; ">&lt;</span><span style="color: #000000; "> </span><span style="color: #000000; ">0</span><span style="color: #000000; ">)<br /></span><span style="color: #008080; "> 8</span> <span style="color: #000000; ">        </span><span style="color: #0000FF; ">goto</span><span style="color: #000000; "> out_putf;<br /></span><span style="color: #008080; "> 9</span> <span style="color: #000000; ">    allocate_fd(files, file, ret);<br /></span><span style="color: #008080; ">10</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> ret;<br /></span><span style="color: #008080; ">11</span> <span style="color: #000000; "><br /></span><span style="color: #008080; ">12</span> <span style="color: #000000; ">out_putf:<br /></span><span style="color: #008080; ">13</span> <span style="color: #000000; ">    write_unlock(</span><span style="color: #000000; ">&amp;</span><span style="color: #000000; ">files</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">file_lock);<br /></span><span style="color: #008080; ">14</span> <span style="color: #000000; ">    fput(file);<br /></span><span style="color: #008080; ">15</span> <span style="color: #000000; ">    </span><span style="color: #0000FF; ">return</span><span style="color: #000000; "> ret;<br /></span><span style="color: #008080; ">16</span> <span style="color: #000000; ">}<br /></span><span style="color: #008080; ">17</span> <span style="color: #000000; "></span></div><br /><font color="#0000FF">注：dup和dup2的原型如下：<br />#include &lt;unistd.h&gt;<br /><br />int dup(int file_descriptor);<br />int dup2(int file_descriptor1, int file_descriptor2)<br />dup返回的文件描述符总是取最小的可用值<br />dup2返回的文件描述符或者与file_descriptor2相同，或者是第一个大于该参数的可用值。</font><br /><br />    而这么一个简单的系统调用是如何完成重定向这个艰巨的任务的呢？我们不妨先看个例子。<br />   
 当我们在shell下输入如下命令：“echo 
hello!”，这条命令要求shell进程执行一个可执行文件echo，参数为“hello!”。当shell接收到命令之后，先找到
bin/echo，然后fork()出一个子进程让他执行bin/echo，并将参数传递给它，<font color="#FF0000">而这个进程从shell继承了三个标准文件，即标准输入
（stdin），标准输出（stdout）和标准出错信息（stderr），他们三个的文件号分别为0、1、2。</font>而至于echo进程的工作很简单，就是将参数“hello!”写到标准输出文件中去，通常都是我们的显示器上。但是如果我们将命令改成“echo hello! &gt; 
foo”，则在执行时输出将会被重定向到磁盘文件foo中<font color="#0000FF">（注：重定向于文件描述符有关）</font>。我们假定在此之前该shell进程只有三个标准文件打开，文件号分别为0、1、2，以上命令行将按如下序列执行：<br />    (1) 打开或创建磁盘文件foo，如果foo中原来有内容，则清除原来内容，其文件号为3。<br />    (2) 通过dup()复制文件stdout，即将文件号1出的file结构指针复制到文件号4处，目的是将stdout的file指针暂时保存一下<br />    (3) 关闭stdout，即1号文件，但是由于4号文件对stdout也同时有个引用，所以stdout文件并未真正关闭，只是腾出1号文件号位置。<br />    (4) 通过dup()，复制3号文件（即磁盘文件foo），由于1号文件关闭，其位置空缺，故3号文件被复制到1号，即进程中原来指向stdout的指针指向了foo。<br />   
 (5) 
通过系统调用fork()和exec()创建子进程并执行echo，子进程在执行echo前夕关闭3号和4号文件，只留下0、1、2三个文件，请注意，这
时的1号文件已经不是stdout而是磁盘文件foo了。当echo想向stdout文件写入“hello!”时自然就写入到了foo中。<br />    (6) 回到shell后，关闭指向foo的1号与3号文件文件，再用dup()和close()将2号恢复至stdout，这样shell就恢复了0、1、2三个标准输入/输出文件。<br /><br />   
 
由此可见，<font color="#FF0000">当echo程序（或其他）在运行的时候并不知道stdout（对于stdin和stderr同样）指向什么，进程与实际输出文件或设备的结合是在运行时由其父进程“包办”的。这样就简化了子进程的程序设计，因为在设计时只要跟三个逻辑上存在的文件打交道就可以了</font>。可能有人会觉得这很像面向对象中
的多态和重载，没有什么新奇之处，但是如果你活在30甚至40年前，可能你会改变你的看法。 
<img src ="http://www.blogjava.net/lihao336/aggbug/366231.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lihao336/" target="_blank">calvin</a> 2011-12-13 14:20 <a href="http://www.blogjava.net/lihao336/archive/2011/12/13/366231.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>小端存储和大端存储</title><link>http://www.blogjava.net/lihao336/archive/2011/11/15/363850.html</link><dc:creator>calvin</dc:creator><author>calvin</author><pubDate>Tue, 15 Nov 2011 07:36:00 GMT</pubDate><guid>http://www.blogjava.net/lihao336/archive/2011/11/15/363850.html</guid><wfw:comment>http://www.blogjava.net/lihao336/comments/363850.html</wfw:comment><comments>http://www.blogjava.net/lihao336/archive/2011/11/15/363850.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lihao336/comments/commentRss/363850.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lihao336/services/trackbacks/363850.html</trackback:ping><description><![CDATA[所有网络协议都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时，在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。
<p><wbr> <wbr> <wbr> 判断小端还是大端规则的方法：</wbr></wbr></wbr></p><p><wbr>int x = 1;<br /><wbr>if(*(char *)&amp;x == 1)//取x指针强制转换为char*类型再取值，此时取到的值是int最低字节值<br /><wbr> <wbr> <wbr> <wbr> printf(“little-endian\n”);<br /><wbr>else<br /><wbr> <wbr> <wbr> <wbr> printf(“big-endian\n”);</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></p><p><wbr></wbr></p><p><wbr> <wbr> <wbr> <wbr> 另外补充：</wbr></wbr></wbr></wbr></p><p>1．BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有关的，每一种CPU不是BIG-ENDIAN就是LITTLE-
ENDIAN、。IA架构的CPU中是Little-Endian，而PowerPC 
、SPARC和Motorola处理器。这其实就是所谓的主机字节序。而网络字节序是指数据在网络上传输时是大头还是小头的，在Internet的网络字
节序是BIG-ENDIAN。所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序，JAVA字节序也是BIG-ENDIAN。</p><p>2．所以在用C/C++写通信程序时，在发送数据前务必用htonl和htons去把整型和短整型的数据进行从主机字节序到网络字节序的转换，而接
收数据后对于整型和短整型数据则必须调用ntohl和ntohs实现从网络字节序到主机字节序的转换。如果通信的一方是JAVA程序、一方是C/C++程
序时，则需要在C/C++一侧使用以上几个方法进行字节序的转换，而JAVA一侧，则不需要做任何处理，因为JAVA字节序与网络字节序都是BIG-
ENDIAN，只要C/C++一侧能正确进行转换即可（发送前从主机序到网络序，接收时反变换）。如果通信的双方都是JAVA，则根本不用考虑字节序的问
题了。</p><p><br /></p><p>转自<a href="http://www.embest.net/index.php/archives/259">http://www.embest.net/index.php/archives/259</a><br /></p><img src ="http://www.blogjava.net/lihao336/aggbug/363850.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lihao336/" target="_blank">calvin</a> 2011-11-15 15:36 <a href="http://www.blogjava.net/lihao336/archive/2011/11/15/363850.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>socket端口bind问题</title><link>http://www.blogjava.net/lihao336/archive/2011/09/11/358464.html</link><dc:creator>calvin</dc:creator><author>calvin</author><pubDate>Sun, 11 Sep 2011 15:47:00 GMT</pubDate><guid>http://www.blogjava.net/lihao336/archive/2011/09/11/358464.html</guid><wfw:comment>http://www.blogjava.net/lihao336/comments/358464.html</wfw:comment><comments>http://www.blogjava.net/lihao336/archive/2011/09/11/358464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lihao336/comments/commentRss/358464.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lihao336/services/trackbacks/358464.html</trackback:ping><description><![CDATA[参照unp，试着运行一个简单的时间打印服务器程序，设置bind端口微13,运行起来后，却发现13端口总不能bind上，查看netstat:<br />$ netstat -tln<br />Active Internet connections (only servers)<br />Proto Recv-Q Send-Q Local Address           Foreign Address         State         <br />tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN     <br /><b>tcp        0      0 0.0.0.0:40700           0.0.0.0:*               LISTEN </b>    <br />tcp6       0      0 ::1:631                 :::*                    LISTEN<br /><br />显示没bind到13端口，却bind到了一个随机的端口40700上。<br />后来忽然意识到1-1023是保留端口号，是不是因为13属于reserved port的原因？<br />修改bind端口为8000后，果然可以bind了。<br /><br />原来1-1023是reserved port是以这种方式实现的，而以root权限运行程序，则可以bind上1023以下的端口。<br /><br />这篇文章不错的：<br /><a href="http://www.cnblogs.com/zhangleiccst/archive/2011/05/14/2046465.html">http://www.cnblogs.com/zhangleiccst/archive/2011/05/14/2046465.html</a><br /><img src ="http://www.blogjava.net/lihao336/aggbug/358464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lihao336/" target="_blank">calvin</a> 2011-09-11 23:47 <a href="http://www.blogjava.net/lihao336/archive/2011/09/11/358464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]sprintf snprintf strcpy memcpy 区别及应用</title><link>http://www.blogjava.net/lihao336/archive/2011/09/11/358442.html</link><dc:creator>calvin</dc:creator><author>calvin</author><pubDate>Sun, 11 Sep 2011 06:45:00 GMT</pubDate><guid>http://www.blogjava.net/lihao336/archive/2011/09/11/358442.html</guid><wfw:comment>http://www.blogjava.net/lihao336/comments/358442.html</wfw:comment><comments>http://www.blogjava.net/lihao336/archive/2011/09/11/358442.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lihao336/comments/commentRss/358442.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lihao336/services/trackbacks/358442.html</trackback:ping><description><![CDATA[http://hi.baidu.com/liziyun537/blog/item/ae84671a6a75e9d9ac6e754e.html<br /><br />关于sprintf和snprintf的正确使用<br /><br />
考虑以下有缺陷的例子：<br />
void f(const char *p)<br />
{<br />
char buf[11]={0};<br />
sprintf(buf,"%10s",p); // very dangerous<br />
printf("%sn",buf);<br />
}<br /><br />
不要让格式标记“%10s”误导你。如果p的长度大于10个字符，那么 sprintf() 的写操作就会越过buf的边界，从而产生一个缓冲区溢出。<br />
检测这类缺陷并不容易，因为它们只在 p  的长度大于10个字符的时候才会发生。黑客通常利用这类脆弱的代码来入侵看上去安全的系统。<br /><br />
要修正这一缺陷，<b>可以使用函数 snprintf()代替函数sprintf()</b>。<br /><br />
函数原型：int snprintf(char *dest, size_t n,  const char *fmt, ...);<br />
函数说明:  最多从源串中拷贝n－1个字符到目标串中，然后再在后面加一个0。所以如果目标串的大小为n的话，将不会溢出。<br />
函数返回值:  若成功则返回存入数组的字符数，若编码出错则返回负值。<br /><br />
推荐的用法：<br />
void f(const char *p)<br />
{<br />
char buf[11]={0};<br />
snprintf(buf, sizeof(buf), "%10s", p); //  注意：这里第2个参数应当用sizeof(str)，而不要使用硬编码11，也不应当使用sizeof(str)-1或10<br />
printf("%sn",buf);<br />
}<br /><br />
strcpy 函数操作的对象是 <font color="blue">字符串</font>，完成 <font color="red">从  源字符串 到 目的字符串 的 拷贝</font> 功能。<br /><br />
snprintf 函数操作的对象 <font color="blue">不限于字符串</font>：虽然目的对象是字符串，但是源对象可以是字符串、也可以是任意基本类型的数据。这个函数主要用来实现 <font color="red">（字符串或基本数据类型）向 字符串 的转换</font>功能。如果源对象是字符串，并且指定 %s 格式符，也可实现字符串拷贝功能。<br /><br />
memcpy 函数顾名思义就是 <font color="red">内存拷贝</font>，实现 <font color="red">将一个  内存块 的内容复制到另一个 内存块</font>  这一功能。内存块由其首地址以及长度确定。程序中出现的实体对象，不论是什么类型，其最终表现就是在内存中占据一席之地（一个内存区间或块）。因此，memcpy 的操作对象不局限于某一类数据类型，或者说可 <font color="blue">适用于任意数据类型</font>，只要能给出对象的起始地址和内存长度信息、并且对象具有可操作性即可。鉴于 memcpy 函数等长拷贝的特点以及数据类型代表的物理意义，memcpy  函数通常限于同种类型数据或对象之间的拷贝，其中当然也包括字符串拷贝以及基本数据类型的拷贝。<br /><br />
对于字符串拷贝来说，用上述三个函数都可以实现，但是其实现的效率和使用的方便程度不同：<br /><ul><li>strcpy 无疑是最合适的选择：效率高且调用方便。</li><li>snprintf 要额外指定格式符并且进行格式转化，麻烦且效率不高。</li><li>memcpy  
虽然高效，但是需要额外提供拷贝的内存长度这一参数，易错且使用不便；并且如果长度指定过大的话（最优长度是源字符串长度 +  
1），还会带来性能的下降。其实 strcpy 函数一般是在内部调用 memcpy 函数或者用汇编直接实现的，以达到高效的目的。因此，使用  
memcpy 和 strcpy 拷贝字符串在性能上应该没有什么大的差别。</li></ul><br />
对于非字符串类型的数据的复制来说，strcpy 和 snprintf 一般就无能为力了，可是对 memcpy  
却没有什么影响。但是，对于基本数据类型来说，尽管可以用 memcpy  
进行拷贝，由于有赋值运算符可以方便且高效地进行同种或兼容类型的数据之间的拷贝，所以这种情况下 memcpy 几乎不被使用。memcpy  
的长处是用来实现（通常是内部实现居多）对结构或者数组的拷贝，其目的是或者高效，或者使用方便，甚或两者兼有。<br /><br />
strcpy和memcpy功能上也有些差别：<br />
比如:<br />
const char *str1="abc\0def";<br />
char str2[7];<br /><br />
首先用strcpy实现：<br />
strcpy（str2,str1）<br />
得到结果：str2＝"abc";也就是说，strcpy是以'\0'为结束标志的。<br /><br />
再用memcpy实现：<br />
memset(str2,7);<br />
memcpy(str2,str1,7);<br />
得到结果：str2="abc\0def";<br />
也就是说，memcpy是对内存区域的复制。当然，不仅能够复制字符串数组，而且能够复制整型数组等其他数组。<img src ="http://www.blogjava.net/lihao336/aggbug/358442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lihao336/" target="_blank">calvin</a> 2011-09-11 14:45 <a href="http://www.blogjava.net/lihao336/archive/2011/09/11/358442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]C/C++ void 理解</title><link>http://www.blogjava.net/lihao336/archive/2011/09/08/358280.html</link><dc:creator>calvin</dc:creator><author>calvin</author><pubDate>Thu, 08 Sep 2011 03:12:00 GMT</pubDate><guid>http://www.blogjava.net/lihao336/archive/2011/09/08/358280.html</guid><wfw:comment>http://www.blogjava.net/lihao336/comments/358280.html</wfw:comment><comments>http://www.blogjava.net/lihao336/archive/2011/09/08/358280.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lihao336/comments/commentRss/358280.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lihao336/services/trackbacks/358280.html</trackback:ping><description><![CDATA[C/C++ void 理解<br /><br />　1.概述<br />　　许多初学者对C/C++语言中的void及void指针类型不甚理解，因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说，并详述void及void指针类型的使用方法与技巧。<br /><br />　　2.void的含义<br /><br />        void的字面意思是“无类型”，void *则为“无类型指针”，void *可以指向任何类型的数据。<br /><br />　　void几乎只有“注释”和限制程序的作用，因为从来没有人会定义一个void变量，让我们试着来定义：<br /><br />        void a;<br /><br />　　这行语句编译时会出错，提示“illegal use of type 'void'”。不过，即使void a的编译不会出错，它也没有任何实际意义。<br /><br />　　void真正发挥的作用在于：<br /><b>　　（1） 对函数返回的限定；<br />　　（2） 对函数参数的限定。</b><br /><br />　　我们将在第三节对以上二点进行具体说明。<br /><br />　　众所周知，如果指针p1和p2的类型相同，那么我们可以直接在p1和p2间互相赋值；如果p1和p2指向不同的数据类型，则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。<br /><br />　　例如：<br /><br />float *p1;<br />int *p2;<br />p1 = p2;<br /><br />　　其中p1 = p2语句会编译出错，提示“'=' : cannot convert from 'int *' to 'float *'”，必须改为：<br /><br />p1 = (float *)p2;<br /><br />而void *则不同，任何类型的指针都可以直接赋值给它，无需进行强制类型转换：<br /><br />void *p1;<br />int *p2;<br />p1 = p2;<br /><br />但这并不意味着，void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”，而“有类型”则不能包容“无类型”。道理很简单，我们可以说“男人和女人都是人”，但不能说“人是男人”或者“人是女人”。下面的语句编译出错：<br /><br />void *p1;<br />int *p2;<br />p2 = p1;<br /><br />　　提示“'=' : cannot convert from 'void *' to 'int *'”。<br /><br />3.void的使用<br /><br />　　下面给出void关键字的使用规则：<br />　　<b>规则一 如果函数没有返回值，那么应声明为void类型</b><br /><br />　　<b>在C语言中，凡不加返回值类型限定的函数，就会被编译器作为返回整型值处理。</b>但是许多程序员却误以为其为void类型。例如：<br /><br />add ( int a, int b )<br />{<br />return a + b;<br />}<br />int main(int argc, char* argv[])<br />{<br />printf ( "2 + 3 = %d", add ( 2, 3) );<br />}<br />　　程序运行的结果为输出：<br />　　2 + 3 = 5<br />　　这说明不加返回值说明的函数的确为int函数。<br /><br />　　林锐博士《高质量C/C++编程》中提到：“C++语言有很严格的类型安全检查，不允许上述情况（指函数不加类型声明）发生”。可是编译器并不一定这么认定，譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确，所以不能寄希望于编译器会做严格的类型检查。<br /><br />　　因此，为了避免混乱，我们在编写C/C++程序时，<b>对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值，一定要声明为void类型</b>。这既是程序良好可读性的需要，也是编程规范性的要求。另外，加上void类型声明后，也可以发挥代码的“自注释”作用。代码的“自注释”即代码能自己注释自己。<br /><br />在C++语言中声明一个这样的函数：<br /><br />int function(void)<br />{<br />return 1;<br />}<br /><br />　　则进行下面的调用是不合法的：<br /><br />function(2);<br /><br />因为在C++中，函数参数为void的意思是这个函数不接受任何参数。<br /><br />　　我们在Turbo C 2.0中编译：<br /><br />#include "stdio.h"<br />fun()<br />{<br />return 1;<br />}<br />main()<br />{<br />printf("%d",fun(2));<br />getchar();<br />}<br /><br />　　编译正确且输出1，这说明，在C语言中，可以给无参数的函数传送任意类型的参数，但是在C++编译器中编译同样的代码则会出错。在C++中，不能向无参数的函数传送任何参数，出错提示“'fun' : function does not take 1 parameters”。<br /><br />　　所以，无论在C还是C++中，若函数不接受任何参数，一定要指明参数为void。<br /><br />　　<b>规则二 小心使用void指针类型</b><br /><br />　　按照ANSI(American National Standards Institute)标准，不能对void指针进行算法操作，即下列操作都是不合法的：<br /><br />void * pvoid;<br />pvoid++; //ANSI：错误<br />pvoid += 1; //ANSI：错误<br />//ANSI标准之所以这样认定，是因为它坚持：进行算法操作的指针必须是确定知道其指向数据类型大小的。<br />//例如：<br />int *pint;<br />pint++; //ANSI：正确<br /><br />　　pint++的结果是使其增大sizeof(int)。<br /><br />　　但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定，它指定void *的算法操作与char *一致。<br /><br />因此下列语句在GNU编译器中皆正确：<br /><br />pvoid++; //GNU：正确<br />pvoid += 1; //GNU：正确<br /><br /><b>pvoid++的执行结果是其增大了1</b>。<br /><br />　　在实际的程序设计中，为迎合ANSI标准，并提高程序的可移植性，我们可以这样编写实现同样功能的代码：<br /><br />void * pvoid;<br />(char *)pvoid++; //ANSI：正确；GNU：正确<br />(char *)pvoid += 1; //ANSI：错误；GNU：正确<br /><br />　　GNU和ANSI还有一些区别，总体而言，GNU较ANSI更“开放”，提供了对更多语法的支持。但是我们在真实设计时，还是应该尽可能地迎合ANSI标准。<br /><br />　　<b>规则四 如果函数的参数可以是任意类型指针，那么应声明其参数为void *</b><br /><br />　　典型的如内存操作函数memcpy和memset的函数原型分别为：<br /><br />void * memcpy(void *dest, const void *src, size_t len);<br />void * memset ( void * buffer, int c, size_t num );<br /><br /><br />　　这样，任何类型的指针都可以传入memcpy和memset中，这也真实地体现了内存操作函数的意义，因为它操作的对象仅仅是一片内存，而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *，而是char *，那才叫真的奇怪了！这样的memcpy和memset明显不是一个“纯粹的，脱离低级趣味的”函数！<br /><br />下面的代码执行正确：<br /><br />//示例：memset接受任意类型指针<br />int intarray[100];<br />memset ( intarray, 0, 100*sizeof(int) ); //将intarray清0<br /><br />//示例：memcpy接受任意类型指针<br />int intarray1[100], intarray2[100];<br /><br />memcpy ( intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1<br />　　有趣的是，memcpy和memset函数返回的也是void *类型，标准库函数的编写者是多么地富有学问啊！<br /><br />　　<b>规则五 void不能代表一个真实的变量</b><br /><br />　　下面代码都企图让void代表一个真实的变量，因此都是错误的代码：<br /><br />void a; //错误<br />function(void a); //错误<br /><br />　　void体现了一种抽象，这个世界上的变量都是“有类型”的，譬如一个人不是男人就是女人（还有人妖？）。<br /><br />　　void的出现只是为了一种抽象的需要，如果你正确地理解了面向对象中“抽象基类”的概念，也很容易理解void数据类型。正如不能给抽象基类定义一个实例，我们也不能定义一个void（让我们类比的称void为“抽象数据类型”）变量。<br /><br />4.总结<br />　　小小的void蕴藏着很丰富的设计哲学，作为一名程序设计人员，对问题进行深一个层次的思考必然使我们受益匪浅。 <br /><img src ="http://www.blogjava.net/lihao336/aggbug/358280.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lihao336/" target="_blank">calvin</a> 2011-09-08 11:12 <a href="http://www.blogjava.net/lihao336/archive/2011/09/08/358280.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>main函数参数的意义</title><link>http://www.blogjava.net/lihao336/archive/2011/03/02/345526.html</link><dc:creator>calvin</dc:creator><author>calvin</author><pubDate>Wed, 02 Mar 2011 13:33:00 GMT</pubDate><guid>http://www.blogjava.net/lihao336/archive/2011/03/02/345526.html</guid><wfw:comment>http://www.blogjava.net/lihao336/comments/345526.html</wfw:comment><comments>http://www.blogjava.net/lihao336/archive/2011/03/02/345526.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lihao336/comments/commentRss/345526.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lihao336/services/trackbacks/345526.html</trackback:ping><description><![CDATA[int&nbsp;&nbsp; main( int&nbsp;&nbsp; argc ,&nbsp; char&nbsp; *argv[] ,&nbsp;&nbsp; char&nbsp; *envp[] )&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; main()函数一般用int或者void形的。我比较喜欢用int型定义main。因为在结束的时候可以返回给操作系统一个值以表示执行情况。&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; argc&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个东东用来表示你在命令行下输入命令的时候，一共有多少个参数。比方说你的程序编译后，可执行文件是test.exe&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; D:\tc2&gt;test&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个时候，argc的值是1&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 但是&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; D:\tc2&gt;test.exe&nbsp;&nbsp; myarg1&nbsp;&nbsp; myarg2&nbsp; 的话，argc的值是3。也就是命令名加上两个参数，一共三个参数&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp; *argv[]&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个东东用来取得你所输入的参数&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; D:\tc2&gt;test&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个时候，argc的值是1，argv[0]的值是&nbsp;&nbsp; "test"&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; D:\tc2&gt;test&nbsp;&nbsp; myarg1&nbsp;&nbsp; myarg2&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个时候，argc的值是3，argc[0]的值是"test"，argc[1]的值是"myarg1"，argc[2]的值是"myarg2"。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个东东一般用来为程序提供非常重要的信息，如：数据文件名，等等。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 如：copy&nbsp;&nbsp; a.c&nbsp;&nbsp; b.txt&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个时候，a.c和b.txt就是所谓的&#8220;非常重要的信息&#8221;。不指定这两个文件，你没法进行拷贝。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 当你的程序用到argc和argv这两个参数的时候，可以简单地通过判断argc的值，来看看程序的参数是否符合要求&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp; *envp[]&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 这个东东相对来说用得比较少。它是用来取得系统的环境变量的。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 如：在DOS下，有一个PATH变量。当你在DOS提示符下输入一个命令（当然，这个命令不是dir一类的内部命令）的时候，DOS会首先在当前目录下找这个命令的执行文件。如果找不到，则到PATH定义的路径下去找，找到则执行，找不到返回Bad&nbsp;&nbsp; command&nbsp;&nbsp; or&nbsp;&nbsp; file&nbsp;&nbsp; name&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 在DOS命令提示符下键入set可查看系统的环境变量&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 同样，在UNIX或者LINUX下，也有系统环境变量，而且用得比DOS要多。如常用的$PATH,$USER,$HOME等等。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; envp保存所有的环境变量。其格式为（UNIX下）&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; PATH=/usr/bin;/local/bin;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; HOME=/home/shuui&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 即：&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 环境变量名=值&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; DOS下大概也一样。&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 环境变量一般用来为程序提供附加信息。如，你做了一个显示文本的内容的程序。你想控制其一行中显示的字符的个数。你可以自己定义一个环境变量（UNIX下）&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; %setenv&nbsp;&nbsp; NUMBER&nbsp;&nbsp; =&nbsp;&nbsp; 10&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; %echo&nbsp;&nbsp; $NUMBER&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 10&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 然后你可以在程序中读入这个环境变量。然后根据其值决定一行输出多少个字符。这样，如果你不修改环境变量的话，你每次执行这个程序，一行中显示的字符数都是不一样的&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 下面是一个例子程序&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; /* argtest.c */&nbsp;&nbsp; <br />
#include&lt;stdio.h&gt;&nbsp;&nbsp; <br />
int main(&nbsp;&nbsp; int&nbsp;&nbsp; argc&nbsp;&nbsp; ,&nbsp;&nbsp; char&nbsp;&nbsp; *argv[]&nbsp;&nbsp; ,&nbsp;&nbsp; char&nbsp;&nbsp; *envp[]&nbsp;&nbsp; )&nbsp;&nbsp; <br />
{&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp; i;&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; printf(&nbsp;&nbsp; "You&nbsp;&nbsp; have&nbsp;&nbsp; inputed&nbsp;&nbsp; total&nbsp;&nbsp; %d&nbsp;&nbsp; argments\n"&nbsp;&nbsp; ,&nbsp;&nbsp; argc&nbsp;&nbsp; );&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; for(&nbsp;&nbsp; i=0&nbsp;&nbsp; ;&nbsp;&nbsp; i&lt;argc&nbsp;&nbsp; ;&nbsp;&nbsp; i++)&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf(&nbsp;&nbsp; "arg%d&nbsp;&nbsp; :&nbsp;&nbsp; %s\n"&nbsp;&nbsp; ,&nbsp;&nbsp; i&nbsp;&nbsp; ,&nbsp;&nbsp; argv[i]&nbsp;&nbsp; );&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br />
<br />
&nbsp;&nbsp;&nbsp; printf(&nbsp;&nbsp; "The&nbsp;&nbsp; follow&nbsp;&nbsp; is&nbsp;&nbsp; envp&nbsp;&nbsp; :\n"&nbsp;&nbsp; );&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; for(&nbsp;&nbsp; i=0&nbsp;&nbsp; ;&nbsp;&nbsp; *envp[i]!='\0'&nbsp;&nbsp; ;&nbsp;&nbsp; i++&nbsp;&nbsp; )&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; printf(&nbsp;&nbsp; "%s\n"&nbsp;&nbsp; ,&nbsp;&nbsp; envp[i]&nbsp;&nbsp; );&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; 0;&nbsp;&nbsp; <br />
}&nbsp;&nbsp; <br />
<br />
<br />
D:\&gt;argtest&nbsp;&nbsp; this&nbsp;&nbsp; is&nbsp;&nbsp; a&nbsp;&nbsp; test&nbsp;&nbsp; programe&nbsp;&nbsp; of&nbsp;&nbsp; main()'s&nbsp;&nbsp; argments&nbsp;&nbsp; <br />
You&nbsp;&nbsp; have&nbsp;&nbsp; inputed&nbsp;&nbsp; total&nbsp;&nbsp; 9&nbsp;&nbsp; argments&nbsp;&nbsp; <br />
arg0&nbsp;&nbsp; :&nbsp;&nbsp; D:\TC\NONAME.EXE&nbsp;&nbsp; <br />
arg1&nbsp;&nbsp; :&nbsp;&nbsp; this&nbsp;&nbsp; <br />
arg2&nbsp;&nbsp; :&nbsp;&nbsp; is&nbsp;&nbsp; <br />
arg3&nbsp;&nbsp; :&nbsp;&nbsp; a&nbsp;&nbsp; <br />
arg4&nbsp;&nbsp; :&nbsp;&nbsp; test&nbsp;&nbsp; <br />
arg5&nbsp;&nbsp; :&nbsp;&nbsp; programe&nbsp;&nbsp; <br />
arg6&nbsp;&nbsp; :&nbsp;&nbsp; of&nbsp;&nbsp; <br />
arg7&nbsp;&nbsp; :&nbsp;&nbsp; main()'s&nbsp;&nbsp; <br />
arg8&nbsp;&nbsp; :&nbsp;&nbsp; argments&nbsp;&nbsp; <br />
The&nbsp;&nbsp; follow&nbsp;&nbsp; is&nbsp;&nbsp; envp&nbsp;&nbsp; :&nbsp;&nbsp; <br />
TMP=C:\WINDOWS\TEMP&nbsp;&nbsp; <br />
TEMP=C:\WINDOWS\TEMP&nbsp;&nbsp; <br />
PROMPT=$p$g&nbsp;&nbsp; <br />
winbootdir=C:\WINDOWS&nbsp;&nbsp; <br />
PATH=C:\WINDOWS;C:\WINDOWS\COMMAND&nbsp;&nbsp; <br />
COMSPEC=C:\WINDOWS\COMMAND.COM&nbsp;&nbsp; <br />
SBPCI=C:\SBPCI&nbsp;&nbsp; <br />
windir=C:\WINDOWS&nbsp;&nbsp; <br />
BLASTER=A220&nbsp;&nbsp; I7&nbsp;&nbsp; D1&nbsp;&nbsp; H7&nbsp;&nbsp; P330&nbsp;&nbsp; T6&nbsp;&nbsp; <br />
CMDLINE=noname&nbsp;&nbsp; this&nbsp;&nbsp; is&nbsp;&nbsp; a&nbsp;&nbsp; test&nbsp;&nbsp; programe&nbsp;&nbsp; of&nbsp;&nbsp; main()'s&nbsp;&nbsp; argments&nbsp;&nbsp;&nbsp;&nbsp; <br />
-----------------------------------------------------------------------------------------<br />
命令行参数啊 argc&nbsp;&nbsp; 是参数的个数，argv[]是参数，argv[0]是文件名，argv[1]是第一个参数...&nbsp;&nbsp; <br />
如你得exe文件名是:myprog.exe，那么&nbsp;&nbsp; <br />
myprog&nbsp;&nbsp; 12&nbsp;&nbsp; 22&nbsp;&nbsp; 32&nbsp;&nbsp; <br />
则argv[0]="myprog"，argv[1]="12"，argv[2]="22"...&nbsp;&nbsp; <br />
<br />
<br />
<img src ="http://www.blogjava.net/lihao336/aggbug/345526.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lihao336/" target="_blank">calvin</a> 2011-03-02 21:33 <a href="http://www.blogjava.net/lihao336/archive/2011/03/02/345526.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>