﻿<?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-一江春水向东流-文章分类-C/C++</title><link>http://www.blogjava.net/huyi2006/category/21597.html</link><description>                            做一个有思想的人,期待与每一位热爱思考的人交流,您的关注是对我最大的支持。</description><language>zh-cn</language><lastBuildDate>Wed, 06 Jun 2012 19:35:58 GMT</lastBuildDate><pubDate>Wed, 06 Jun 2012 19:35:58 GMT</pubDate><ttl>60</ttl><item><title>C语言中字节对齐</title><link>http://www.blogjava.net/huyi2006/articles/380056.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Tue, 05 Jun 2012 10:00:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/380056.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/380056.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/380056.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/380056.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/380056.html</trackback:ping><description><![CDATA[#pragma pack()
struct test
{
    char m1;
    double m4;
    int m3;
};
#pragma pack()

#pragma pack(8)
struct A
{
    int n;
    char c;
    short s;
};
struct B
{
    char c;
    int n;
    short s;
};
#pragma pack()

sizeof test:16
sizeof a:8
sizeof b:12

编译器默认的成员对齐值是8字节，通过#pragma pack(show)指令，编译的时候在输出栏会限制默认对齐值。以上程序运行完通过调试的内存察看功能得到a和b的内存存储区域如下：
a的存储区域：0x0012FED0  02 00 00 00 31 00 03 00
b的存储区域：0x0012FEBC  31 00 00 00 02 00 00 00 03 00 00 00
最前面的4字节整数是变量的起始地址，后面是变量的整个存储区域。

现在我们按照 align of member = min( pack setting value, sizeof(member) )的公式分析一下a和b的存储。

a的第一个成员n为int，大小为4，align of a.n = min(8,sizeof(int) )，对齐值为4。第一个成员相对于结构体起始地址从0偏移开始，前四个字节02 00 00 00即为n的存储区域，因为x86是Little Endian（低字节在前）的字节顺序，所以第一字节是2，后面三个字节0，我们通常写成0x00000002；

a的第二个成员c为char，大小为1，align of a.c=min(8,sizeof(char))，对齐值为1。c紧接着a后面存储从偏移4开始，满足1字节对齐的要求。它的值为'1'，ASCII码为0x31，共一个字节31；

a的第三个成员为short，大小为2，align of a.s=min(8,sizeof(short))，对齐值为2。如果紧接第二个成员从偏移5开始存储就不满足2字节对齐，因此跳过1个字节，从偏移6字节的地方开始存储，即最后两个字节03 00；

b的第一个成员c为char，大小为1，align of a.c=min(8,sizeof(char))，对齐值为1。第一个成员从偏移起始地址0字节开始存储，它的值为'1'，ASCII码为0x31，共一个字节31；

b的第二个成员n为int，大小为4，align of a.n = min(8,sizeof(int) )，对齐值为4。如果紧接第二个成员后面从偏移1开始存储就不能4字节对齐，因此跳过3个字节，从偏移4字节的地方开始存储，即第5-8的四个字节02 00 00 00；

b的第三个成员为short，大小为2，align of a.s=min(8,sizeof(short))，对齐值为2。紧接第二个成员从偏移8字节的地方开始存储，即9-10两个字节03 00；

这时有人可能要问，b为什么最后多了两个字节00 00呢？这就是我们下面要讲的，整个结构体的对齐。

2、align指令

align指令可以用于设置各种内置类型、自定义类型如struct、union或class的的对齐方式。指令格式为：__declspec(align(#)) ，#是对齐值，取值为2的1次方至2的8192次方。在声明自定义类型或内置变量时，如果指定了对齐值，则对应变量的起始地址必须是该值的整数倍。除此外，它还会影响结构体的大小。下面引用两段MSDN关于align的描述：

Without __declspec(align(#)), Visual C++ aligns data on natural boundaries based on the size of the data, for example 4-byte integers on 4-byte boundaries and 8-byte doubles on 8-byte boundaries. Data in classes or structures is aligned within the class or structure at the minimum of its natural alignment and the current packing setting (from #pragma pack or the /Zp compiler option).

从这段可以看出，如果没有设置align(#)值，变量x按照sizeof(x)来对齐起始地址。类或结构体内的成员在类或结构体内部按照min( pack setting value,sizeof(member))来对齐。这个我们在pack指令部分已经分析过。

The sizeof value for any structure is the offset of the final member, plus that member's size, rounded up to the nearest multiple of the largest member alignment value or the whole structure alignment value, whichever is greater.

从这段可以看出，align(#)指令会影响结构体或类的大小。总结公式为：
sizeof(structure) = (结构体最后一个成员的偏移 + sizeof(结构体最后一个成员) ) 上取整 ( n* max( 结构体各成员的对齐值,align(#)设置的值 ) ); 其中n为正整数。

根据该公式我们分析一下b为什么后面会多两个填充字节0。
b的最后一个成s偏移为8，大小为2，b中各成员对齐值最大的为4，因为未设置align(#)，所以上取整的数值为4n。8+2按4的倍数上取整为12。因此后面需要填充两个字节，这样才能使sizeof(b) == 12。


<img src ="http://www.blogjava.net/huyi2006/aggbug/380056.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2012-06-05 18:00 <a href="http://www.blogjava.net/huyi2006/articles/380056.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>win32环境C语言实现最基本的DLL编写及调用实例，测试通过[原]</title><link>http://www.blogjava.net/huyi2006/articles/264109.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Mon, 06 Apr 2009 05:46:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/264109.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/264109.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/264109.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/264109.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/264109.html</trackback:ping><description><![CDATA[
		<p>开发环境是DEV C++,采用C语言编写<br />创建一个DLL项目，项目名称hello,DLL编写采用的是DEV C++中的示例代码<br /><br />头文件dll.h<br />#ifndef _DLL_H_<br />#define _DLL_H_</p>
		<p>#if BUILDING_DLL<br /># define DLLIMPORT __declspec (dllexport)<br />#else /* Not BUILDING_DLL */<br /># define DLLIMPORT __declspec (dllimport)<br />#endif /* Not BUILDING_DLL */</p>
		<p>
				<br />DLLIMPORT void HelloWorld (void);</p>
		<p>
				<br />#endif /* _DLL_H_ */<br /><br />C文件<br />dllmain.c<br />#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;</p>
		<p>DLLIMPORT void HelloWorld ()<br />{<br />    MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);<br />}</p>
		<p>
				<br />BOOL APIENTRY DllMain (HINSTANCE hInst     /* Library instance handle. */ ,<br />                       DWORD reason        /* Reason this function is being called. */ ,<br />                       LPVOID reserved     /* Not used. */ )<br />{<br />    switch (reason)<br />    {<br />      case DLL_PROCESS_ATTACH:<br />        break;</p>
		<p>      case DLL_PROCESS_DETACH:<br />        break;</p>
		<p>      case DLL_THREAD_ATTACH:<br />        break;</p>
		<p>      case DLL_THREAD_DETACH:<br />        break;<br />    }</p>
		<p>    /* Returns TRUE on success, FALSE on failure */<br />    return TRUE;<br />}<br /><br />还有要注意的在.def文件中指定输出的函数，编译生成了hello.dll文件<br /><br />DLL调用部分<br />dllcall.c<br />#include &lt;windows.h&gt;<br />#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;</p>
		<p>HINSTANCE hDLL; //定义DLL包柄<br />typedef void ( *func)();    //定义函数指针原型 <br />func hello;  //定义函数指针<br />int main()<br />{<br /> if (hDLL == NULL)<br />    hDLL=LoadLibrary("hello.dll");  //加载DLL<br /> hello = (func)GetProcAddress(hDLL,"HelloWorld"); //获取函数指针<br /> hello();<br /> FreeLibrary(hDLL);  //释放DLL<br /> return 0;<br />}</p>
		<p>编译执行<br /><img src="http://www.blogjava.net/images/blogjava_net/huyi2006/38822/r_dllcall.jpg" /><br /> </p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/264109.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2009-04-06 13:46 <a href="http://www.blogjava.net/huyi2006/articles/264109.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>linux下C语言实现文件传输的简单实例</title><link>http://www.blogjava.net/huyi2006/articles/263836.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 03 Apr 2009 15:06:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/263836.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/263836.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/263836.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/263836.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/263836.html</trackback:ping><description><![CDATA[
		<p>实例来自互联网，这段测试代码实现了基本的文件传输原理，没有实现错误处理。<br /><br />//////////////////////////////////////////////////////////////////////////////////////<br />// file_server.c  文件传输顺序服务器示例<br />//////////////////////////////////////////////////////////////////////////////////////<br />//本文件是服务器的代码<br />#include &lt;netinet/in.h&gt;    // for sockaddr_in<br />#include &lt;sys/types.h&gt;    // for socket<br />#include &lt;sys/socket.h&gt;    // for socket<br />#include &lt;stdio.h&gt;        // for printf<br />#include &lt;stdlib.h&gt;        // for exit<br />#include &lt;string.h&gt;        // for bzero<br />/*<br />#include &lt;sys/types.h&gt;<br />#include &lt;sys/stat.h&gt;<br />#include &lt;fcntl.h&gt;<br />#include &lt;unistd.h&gt;<br />*/<br />#define HELLO_WORLD_SERVER_PORT    6666 <br />#define LENGTH_OF_LISTEN_QUEUE  20<br />#define BUFFER_SIZE 1024<br />#define FILE_NAME_MAX_SIZE 512<br /><br />int main(int argc, char **argv)<br />{<br />    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口<br />    struct sockaddr_in server_addr;<br />    bzero(&amp;server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0<br />    server_addr.sin_family = AF_INET;<br />    server_addr.sin_addr.s_addr = htons(INADDR_ANY);<br />    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);<br /><br />    //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket<br />    int server_socket = socket(PF_INET,SOCK_STREAM,0);<br />    if( server_socket &lt; 0)<br />    {<br />        printf("Create Socket Failed!");<br />        exit(1);<br />    }<br />    <br />    //把socket和socket地址结构联系起来<br />    if( bind(server_socket,(struct sockaddr*)&amp;server_addr,sizeof(server_addr)))<br />    {<br />        printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT); <br />        exit(1);<br />    }<br />    <br />    //server_socket用于监听<br />    if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) )<br />    {<br />        printf("Server Listen Failed!"); <br />        exit(1);<br />    }<br />    while (1) //服务器端要一直运行<br />    {<br />        //定义客户端的socket地址结构client_addr<br />        struct sockaddr_in client_addr;<br />        socklen_t length = sizeof(client_addr);<br /><br />        //接受一个到server_socket代表的socket的一个连接<br />        //如果没有连接请求,就等待到有连接请求--这是accept函数的特性<br />        //accept函数返回一个新的socket,这个socket(new_server_socket)用于同连接到的客户的通信<br />        //new_server_socket代表了服务器和客户端之间的一个通信通道<br />        //accept函数把连接到的客户端信息填写到客户端的socket地址结构client_addr中<br />        int new_server_socket = accept(server_socket,(struct sockaddr*)&amp;client_addr,&amp;length);<br />        if ( new_server_socket &lt; 0)<br />        {<br />            printf("Server Accept Failed!\n");<br />            break;<br />        }<br />        <br />        char buffer[BUFFER_SIZE];<br />        bzero(buffer, BUFFER_SIZE);<br />        length = recv(new_server_socket,buffer,BUFFER_SIZE,0);//<font color="#ff0000">这里先接收客户端发来的要获取的文件名</font><br />        if (length &lt; 0)<br />        {<br />            printf("Server Recieve Data Failed!\n");<br />            break;<br />        }<br />        char file_name[FILE_NAME_MAX_SIZE+1];<br />        bzero(file_name, FILE_NAME_MAX_SIZE+1);<br />        strncpy(file_name, buffer, strlen(buffer)&gt;FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));<br />//        int fp = open(file_name, O_RDONLY);<br />//        if( fp &lt; 0 )<br />        FILE * fp = fopen(file_name,"r");<br />        if(NULL == fp )<br />        {<br />            printf("File:\t%s Not Found\n", file_name);<br />        }<br />        else<br />        {<br />            bzero(buffer, BUFFER_SIZE);<br />            int file_block_length = 0;<br />//            while( (file_block_length = read(fp,buffer,BUFFER_SIZE))&gt;0)<br /><font color="#0000cc">            while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE,fp))&gt;0)<br />            {<br />                printf("file_block_length = %d\n",file_block_length);<br />                //发送buffer中的字符串到new_server_socket,实际是给客户端<br />                if(send(new_server_socket,buffer,file_block_length,0)&lt;0)<br />                {<br />                    printf("Send File:\t%s Failed\n", file_name);<br />                    break;<br />                }<br />                bzero(buffer, BUFFER_SIZE);<br />            }                                                                 //这段代码是循环读取文件的一段数据，在循环调用send，发送到客户端，这里强调一点的TCP每次接受最多是1024字节，多了就会分片，因此每次发送时尽量不要超过1024字节。</font><br />//            close(fp);<br />            fclose(fp);<br />            printf("File:\t%s Transfer Finished\n",file_name);<br />        }<br />        //关闭与客户端的连接<br />        close(new_server_socket);<br />    }<br />    //关闭监听用的socket<br />    close(server_socket);<br />    return 0;<br />}<br /><br /><br />//////////////////////////////////////////////////////////////////////////////////////<br />// file_client.c  文件传输客户端程序示例<br />//////////////////////////////////////////////////////////////////////////////////////<br />//本文件是客户机的代码<br />#include &lt;netinet/in.h&gt;    // for sockaddr_in<br />#include &lt;sys/types.h&gt;    // for socket<br />#include &lt;sys/socket.h&gt;    // for socket<br />#include &lt;stdio.h&gt;        // for printf<br />#include &lt;stdlib.h&gt;        // for exit<br />#include &lt;string.h&gt;        // for bzero<br />/*<br />#include &lt;sys/types.h&gt;<br />#include &lt;sys/stat.h&gt;<br />#include &lt;fcntl.h&gt;<br />#include &lt;unistd.h&gt;<br />*/<br /><br />#define HELLO_WORLD_SERVER_PORT    6666 <br />#define BUFFER_SIZE 1024<br />#define FILE_NAME_MAX_SIZE 512<br /><br />int main(int argc, char **argv)<br />{<br />    if (argc != 2)<br />    {<br />        printf("Usage: ./%s ServerIPAddress\n",argv[0]);<br />        exit(1);<br />    }<br /><br />    //设置一个socket地址结构client_addr,代表客户机internet地址, 端口<br />    struct sockaddr_in client_addr;<br />    bzero(&amp;client_addr,sizeof(client_addr)); //把一段内存区的内容全部设置为0<br />    client_addr.sin_family = AF_INET;    //internet协议族<br />    client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址<br />    client_addr.sin_port = htons(0);    //0表示让系统自动分配一个空闲端口<br />    //创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket<br />    int client_socket = socket(AF_INET,SOCK_STREAM,0);<br />    if( client_socket &lt; 0)<br />    {<br />        printf("Create Socket Failed!\n");<br />        exit(1);<br />    }<br />    //把客户机的socket和客户机的socket地址结构联系起来<br />    if( bind(client_socket,(struct sockaddr*)&amp;client_addr,sizeof(client_addr)))<br />    {<br />        printf("Client Bind Port Failed!\n"); <br />        exit(1);<br />    }<br /><br />    //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口<br />    struct sockaddr_in server_addr;<br />    bzero(&amp;server_addr,sizeof(server_addr));<br />    server_addr.sin_family = AF_INET;<br />    if(inet_aton(argv[1],&amp;server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数<br />    {<br />        printf("Server IP Address Error!\n");<br />        exit(1);<br />    }<br />    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);<br />    socklen_t server_addr_length = sizeof(server_addr);<br />    //向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接<br />    if(connect(client_socket,(struct sockaddr*)&amp;server_addr, server_addr_length) &lt; 0)<br />    {<br />        printf("Can Not Connect To %s!\n",argv[1]);<br />        exit(1);<br />    }<br /><br />    char file_name[FILE_NAME_MAX_SIZE+1];<br />    bzero(file_name, FILE_NAME_MAX_SIZE+1);<br />    printf("Please Input File Name On Server:\t");<br />    scanf("%s", file_name);<br />    <br />    char buffer[BUFFER_SIZE];<br />    bzero(buffer,BUFFER_SIZE);<br />    strncpy(buffer, file_name, strlen(file_name)&gt;BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));<br />    //向服务器发送buffer中的数据<br />    send(client_socket,buffer,BUFFER_SIZE,0);<br /><br />//    int fp = open(file_name, O_WRONLY|O_CREAT);<br />//    if( fp &lt; 0 )<br />    FILE * fp = fopen(file_name,"w");<br />    if(NULL == fp )<br />    {<br />        printf("File:\t%s Can Not Open To Write\n", file_name);<br />        exit(1);<br />    }<br />    <br />    //从服务器接收数据到buffer中<br />    bzero(buffer,BUFFER_SIZE);<br />    int length = 0;<br /><font color="#0000cc">    while( length = recv(client_socket,buffer,BUFFER_SIZE,0))       //循环接收，再写到文件<br />    {<br />        if(length &lt; 0)<br />        {<br />            printf("Recieve Data From Server %s Failed!\n", argv[1]);<br />            break;<br />        }<br />//        int write_length = write(fp, buffer,length);<br />        int write_length = fwrite(buffer,sizeof(char),length,fp);<br />        if (write_length&lt;length)<br />        {<br />            printf("File:\t%s Write Failed\n", file_name);<br />            break;<br />        }<br />        bzero(buffer,BUFFER_SIZE);    <br />    }</font><br />    printf("Recieve File:\t %s From Server[%s] Finished\n",file_name, argv[1]);<br />    <br />    close(fp);<br />    //关闭socket<br />    close(client_socket);<br />    return 0;<br />}</p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/263836.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2009-04-03 23:06 <a href="http://www.blogjava.net/huyi2006/articles/263836.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多种语言socket编程集锦—win32</title><link>http://www.blogjava.net/huyi2006/articles/263831.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 03 Apr 2009 14:34:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/263831.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/263831.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/263831.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/263831.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/263831.html</trackback:ping><description><![CDATA[
		<p>借此地方整理以下socket编程的多种语言的实现，socket可以跨平台的通信，因此多语言、多平台下的实现有必要了解的。代码都是源自网上，感谢网友的分享。<br /><br />socket编程在windows下的C语言实现，dev C++下编译通过<br />编译时设置编译选项 工具--&gt;编译选项--&gt;编译器，勾选在连接器命令行加入以下命令，在对话框中填-lwsock32<br />//server.c<br />#include &lt;stdio.h&gt;<br />#include &lt;Winsock2.h&gt;<br />int main()<br />{<br />    WORD wVersionRequested;<br />    WSADATA wsaData;<br />    int err;<br />    <br />    wVersionRequested = MAKEWORD( 1, 1 );<br />    <br />    err = WSAStartup( wVersionRequested, &amp;wsaData );<br />    if ( err != 0 ) {<br />        return;<br />    }<br />    <br />    if ( LOBYTE( wsaData.wVersion ) != 1 ||<br />    HIBYTE( wsaData.wVersion ) != 1 ) {<br />        WSACleanup();<br />        return;<br />    }<br />    SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);<br />    <br />    SOCKADDR_IN addrSrv;<br />    addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);<br />    addrSrv.sin_family=AF_INET;<br />    addrSrv.sin_port=htons(6000);<br />    <br />    bind(sockSrv,(SOCKADDR*)&amp;addrSrv,sizeof(SOCKADDR));<br />    <br />    listen(sockSrv,5);<br />    <br />    SOCKADDR_IN addrClient;<br />    int len=sizeof(SOCKADDR);<br />    while(1)<br />    {<br />        SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&amp;addrClient,&amp;len);<br />        char sendBuf[50];<br />        sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));<br />        send(sockConn,sendBuf,strlen(sendBuf)+1,0);<br />        char recvBuf[50];<br />        recv(sockConn,recvBuf,50,0);<br />        printf("%s\n",recvBuf);<br />        closesocket(sockConn);<br />    }<br />}<br /><br />//客户端代码<br />#include &lt;stdio.h&gt;<br />#include &lt;Winsock2.h&gt;<br />int main()<br />{<br />    WORD wVersionRequested;<br />    WSADATA wsaData;<br />    int err;<br />    <br />    wVersionRequested = MAKEWORD( 1, 1 );<br />    <br />    err = WSAStartup( wVersionRequested, &amp;wsaData );<br />    if ( err != 0 ) {<br />        return;<br />    }<br />    <br />    if ( LOBYTE( wsaData.wVersion ) != 1 ||<br />        HIBYTE( wsaData.wVersion ) != 1 ) {<br />        WSACleanup( );<br />        return;<br />    }<br />    SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);<br />    <br />    SOCKADDR_IN addrSrv;<br />    addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");<br />    addrSrv.sin_family=AF_INET;<br />    addrSrv.sin_port=htons(6000);<br />    connect(sockClient,(SOCKADDR*)&amp;addrSrv,sizeof(SOCKADDR));<br />    send(sockClient,"hello",strlen("hello")+1,0);<br />    char recvBuf[50];<br />    recv(sockClient,recvBuf,50,0);<br />    printf("%s\n",recvBuf); <br />    closesocket(sockClient);<br />    WSACleanup();<br />}<br /></p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/263831.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2009-04-03 22:34 <a href="http://www.blogjava.net/huyi2006/articles/263831.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux下的多线程编程</title><link>http://www.blogjava.net/huyi2006/articles/256617.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Wed, 25 Feb 2009 06:53:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/256617.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/256617.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/256617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/256617.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/256617.html</trackback:ping><description><![CDATA[一篇介绍Linux下的多线程编程的文章 <br />1 引言<br />　　线程（thread）技术早在60年代就被提出，但真正应用多线程到操作系统中去，是在80年代中期，solaris是这方面的佼佼者。传统的<br />Unix也支持线程的概念，但是在一个进程（process）中只允许有一个线程，这样多线程就意味着多进程。现在，多线程技术已经被许多操作系<br />统所支持，包括Windows/NT，当然，也包括Linux。<br />　　为什么有了进程的概念后，还要再引入线程呢？使用多线程到底有哪些好处？什么的系统应该选用多线程？我们首先必须回答这些问题。<br />　　使用多线程的理由之一是和进程相比，它是一种非常"节俭"的多任务操作方式。我们知道，在Linux系统下，启动一个新的进程必须分配给<br />它独立的地址空间，建立众多的数据表来维护它的代码段、堆栈段和数据段，这是一种"昂贵"的多任务工作方式。而运行于一个进程中的多个<br />线程，它们彼此之间使用相同的地址空间，共享大部分数据，启动一个线程所花费的空间远远小于启动一个进程所花费的空间，而且，线程间<br />彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计，总的说来，一个进程的开销大约是一个线程开销的30倍左右，当然，在具<br />体的系统上，这个数据可能会有较大的区别。<br />　　使用多线程的理由之二是线程间方便的通信机制。对不同进程来说，它们具有独立的数据空间，要进行数据的传递只能通过通信的方式进<br />行，这种方式不仅费时，而且很不方便。线程则不然，由于同一进程下的线程之间共享数据空间，所以一个线程的数据可以直接为其它线程所<br />用，这不仅快捷，而且方便。当然，数据的共享也带来其他一些问题，有的变量不能同时被两个线程所修改，有的子程序中声明为static的数<br />据更有可能给多线程程序带来灾难性的打击，这些正是编写多线程程序时最需要注意的地方。<br />　　除了以上所说的优点外，不和进程比较，多线程程序作为一种多任务、并发的工作方式，当然有以下的优点：<br />　　1)<br />提高应用程序响应。这对图形界面的程序尤其有意义，当一个操作耗时很长时，整个系统都会等待这个操作，此时程序不会响应键盘、鼠标、<br />菜单的操作，而使用多线程技术，将耗时长的操作（time consuming）置于一个新的线程，可以避免这种尴尬的情况。<br />　　2) 使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时，不同的线程运行于不同的CPU上。<br />　　3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程，成为几个独立或半独立的运行部分，这样的程序会利于理解和修改。<br />　　下面我们先来尝试编写一个简单的多线程程序。<br />2 简单的多线程编程<br />　　Linux系统下的多线程遵循POSIX线程接口，称为pthread。编写Linux下的多线程程序，需要使用头文件pthread.h，连接时需要使用库libp<br />thread.a。顺便说一下，Linux下pthread的实现是通过系统调用clone（）来实现的。clone（）是Linux所特有的系统调用，它的使用方式类似<br />fork，关于clone（）的详细情况，有兴趣的读者可以去查看有关文档说明。下面我们展示一个最简单的多线程程序 example1.c。<br />/* example.c*/<br />#include &lt;stdio.h&gt;<br />#include &lt;pthread.h&gt;<br />void thread(void)<br />{<br />int i;<br />for(i=0;i&lt;3;i++)<br />printf("This is a pthread.n");<br />}<br />int main(void)<br />{<br />pthread_t id;<br />int i,ret;<br />ret=pthread_create(&amp;id,NULL,(void *) thread,NULL);<br />if(ret!=0){<br />printf ("Create pthread error!n");<br />exit (1);<br />}<br />for(i=0;i&lt;3;i++)<br />printf("This is the main process.n");<br />pthread_join(id,NULL);<br />return (0);<br />}<br />我们编译此程序：<br />gcc example1.c -lpthread -o example1<br />运行example1，我们得到如下结果：<br />This is the main process.<br />This is a pthread.<br />This is the main process.<br />This is the main process.<br />This is a pthread.<br />This is a pthread.<br />再次运行，我们可能得到如下结果：<br />This is a pthread.<br />This is the main process.<br />This is a pthread.<br />This is the main process.<br />This is a pthread.<br />This is the main process.<br />　　前后两次结果不一样，这是两个线程争夺CPU资源的结果。上面的示例中，我们使用到了两个函数，　　pthread_create和pthread_join，<br />并声明了一个pthread_t型的变量。<br />　　pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义：<br />　　typedef unsigned long int pthread_t;<br />　　它是一个线程的标识符。函数pthread_create用来创建一个线程，它的原型为：<br />　　extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,<br />　　void *(*__start_routine) (void *), void *__arg));<br />　　第一个参数为指向线程标识符的指针，第二个参数用来设置线程属性，第三个参数是线程运行函数的起始地址，最后一个参数是运行函数<br />的参数。这里，我们的函数thread不需要参数，所以最后一个参数设为空指针。第二个参数我们也设为空指针，这样将生成默认属性的线程。<br />对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时，函数返回0，若不为0则说明创建线程失败，常见的错误返回代码为EAGAIN<br />和EINVAL。前者表示系统限制创建新的线程，例如线程数目过多了；后者表示第二个参数代表的线程属性值非法。创建线程成功后，新创建的<br />线程则运行参数三和参数四确定的函数，原来的线程则继续运行下一行代码。<br />　　函数pthread_join用来等待一个线程的结束。函数原型为：<br />　　extern int pthread_join __P ((pthread_t __th, void **__thread_return));<br />　　第一个参数为被等待的线程标识符，第二个参数为一个用户定义的指针，它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞<br />的函数，调用它的函数将一直等待到被等待的线程结束为止，当函数返回时，被等待线程的资源被收回。一个线程的结束有两种途径，一种是<br />象我们上面的例子一样，函数结束了，调用它的线程也就结束了；另一种方式是通过函数pthread_exit来实现。它的函数原型为：<br />　　extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));<br />　　唯一的参数是函数的返回代码，只要pthread_join中的第二个参数thread_return不是NULL，这个值将被传递给<br />thread_return。最后要说明的是，一个线程不能被多个线程等待，否则第一个接收到信号的线程成功返回，其余调用pthread_join的线程则返<br />回错误代码ESRCH。<br />　　在这一节里，我们编写了一个最简单的线程，并掌握了最常用的三个函数pthread_create，pthread_join和pthread_exit。下面，我们来<br />了解线程的一些常用属性以及如何设置这些属性。<br />3 修改线程的属性<br />　　在上一节的例子里，我们用pthread_create函数创建了一个线程，在这个线程中，我们使用了默认参数，即将该函数的第二个参数设为NUL<br />L。的确，对大多数程序来说，使用默认属性就够了，但我们还是有必要来了解一下线程的有关属性。<br />　　属性结构为pthread_attr_t，它同样在头文件/usr/include/pthread.h中定义，喜欢追根问底的人可以自己去查看。属性值不能直接设置<br />，须使用相关函数进行操作，初始化的函数为pthread_attr_init，这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定<br />、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。<br />　　关于线程的绑定，牵涉到另外一个概念：轻进程（LWP：Light Weight<br />Process）。轻进程可以理解为内核线程，它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的，一个轻<br />进程可以控制一个或多个线程。默认状况下，启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的，这种状况即称为非绑定的。绑<br />定状况下，则顾名思义，即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度，这是因为CPU时间片的调度是面向轻进<br />程的，绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实<br />时反应之类的要求。<br />　　设置线程绑定状态的函数为pthread_attr_setscope，它有两个参数，第一个是指向属性结构的指针，第二个是绑定类型，它有两个取值：<br />PTHREAD_SCOPE_SYSTEM（绑定的）和PTHREAD_SCOPE_PROCESS（非绑定的）。下面的代码即创建了一个绑定的线程。<br />#include &lt;pthread.h&gt;<br />pthread_attr_t attr;<br />pthread_t tid;<br />/*初始化属性值，均设为默认值*/<br />pthread_attr_init(&amp;attr);<br />pthread_attr_setscope(&amp;attr, PTHREAD_SCOPE_SYSTEM);<br />pthread_create(&amp;tid, &amp;attr, (void *) my_function, NULL);<br />　　线程的分离状态决定一个线程以什么样的方式来终止自己。在上面的例子中，我们采用了线程的默认属性，即为非分离状态，这种情况下<br />，原有的线程等待创建的线程结束。只有当pthread_join（）函数返回时，创建的线程才算终止，才能释放自己占用的系统资源。而分离线程<br />不是这样子的，它没有被其他的线程所等待，自己运行结束了，线程也就终止了，马上释放系统资源。程序员应该根据自己的需要，选择适当<br />的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate（pthread_attr_t *attr, int<br />detachstate）。第二个参数可选为PTHREAD_CREATE_DETACHED（分离线程）和 PTHREAD<br />_CREATE_JOINABLE（非分离线程）。这里要注意的一点是，如果设置一个线程为分离线程，而这个线程运行又非常快，它很可能在<br />pthread_create函数返回之前就终止了，它终止以后就可能将线程号和系统资源移交给其他的线程使用，这样调用pthread_create的线程就得<br />到了错误的线程号。要避免这种情况可以采取一定的同步措施，最简单的方法之一是可以在被创建的线程里调用<br />pthread_cond_timewait函数，让这个线程等待一会儿，留出足够的时间让函数pthread_create返回。设置一段等待时间，是在多线程编程里常<br />用的方法。但是注意不要使用诸如wait（）之类的函数，它们是使整个进程睡眠，并不能解决线程同步的问题。<br />　　另外一个可能常用的属性是线程的优先级，它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数<br />pthread_attr_setschedparam进行存放，一般说来，我们总是先取优先级，对取得的值修改后再存放回去。下面即是一段简单的例子。<br />#include &lt;stdio.h&gt;<br />#include &lt;pthread.h&gt;<br />pthread_attr_t attr;<br />pthread_t tid;<br />sched_param param;<br />int newprio=20;<br />pthread_attr_init(&amp;attr);<br />pthread_attr_getschedparam(&amp;attr, &amp;para;m);<br />param.sched_priority=newprio;<br />pthread_attr_setschedparam(&amp;attr, &amp;para;m);<br />pthread_create(&amp;tid, &amp;attr, (void *)myfunction, myarg);<br />　　<br />4 线程的数据处理<br />　　和进程相比，线程的最大优点之一是数据的共享性，各个进程共享父进程处沿袭的数据段，可以方便的获得、修改数据。但这也给多线程<br />编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的，即同时不能运行一个函数的多个拷贝（除非<br />使用不同的数据段）。在函数中声明的静态变量常常带来问题，函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址<br />，则在一个线程调用该函数得到地址后使用该地址指向的数据时，别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用<br />关键字volatile来定义，这是为了防止编译器在优化时（如gcc中使用-OX参数）改变它们的使用方式。为了保护变量，我们必须使用信号量、<br />互斥等方法来保证我们对变量的正确使用。下面，我们就逐步介绍处理线程数据时的有关知识。<br />4.1 线程数据<br />　　在单线程的程序里，有两种基本的数据：全局变量和局部变量。但在多线程程序里，还有第三种数据类型：线程数据（TSD:<br />Thread-Specific<br />Data）。它和全局变量很象，在线程内部，各个函数可以象使用全局变量一样调用它，但它对线程外部的其它线程是不可见的。这种数据的必<br />要性是显而易见的。例如我们常见的变量errno，它返回标准的出错信息。它显然不能是一个局部变量，几乎每个函数都应该可以调用它；但它<br />又不能是一个全局变量，否则在<br />A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量，我们就必须使用线程数据。我们为每个线程数据创建一个键，它和这个键<br />相关联，在各个线程里，都使用这个键来指代线程数据，但在不同的线程里，这个键代表的数据是不同的，在同一个线程里，它代表同样的数<br />据内容。<br />　　和线程数据相关的函数主要有4个：创建一个键；为一个键指定线程数据；从一个键读取线程数据；删除键。<br />　　创建键的函数原型为：<br />　　extern int pthread_key_create __P ((pthread_key_t *__key,<br />　　void (*__destr_function) (void *)));<br />　　第一个参数为指向一个键值的指针，第二个参数指明了一个destructor函数，如果这个参数不为空，那么当每个线程结束时，系统将调用<br />这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine)<br />(void)))一起使用，为了让这个键只被创建一次。函数pthread_once声明一个初始化函数，第一次调用pthread_once时它执行这个函数，以后<br />的调用将被它忽略。<br />　　在下面的例子中，我们创建一个键，并将它和某个数据相关联。我们要定义一个函数createWindow，这个函数定义一个图形窗口（数据类<br />型为Fl_Window *，这是图形界面开发工具FLTK中的数据类型）。由于各个线程都会调用这个函数，所以我们使用线程数据。<br />/* 声明一个键*/<br />pthread_key_t myWinKey;<br />/* 函数 createWindow */<br />void createWindow ( void ) {<br />Fl_Window * win;<br />static pthread_once_t once= PTHREAD_ONCE_INIT;<br />/* 调用函数createMyKey，创建键*/<br />pthread_once ( &amp; once, createMyKey) ;<br />/*win指向一个新建立的窗口*/<br />win=new Fl_Window( 0, 0, 100, 100, "MyWindow");<br />/* 对此窗口作一些可能的设置工作，如大小、位置、名称等*/<br />setWindow(win);<br />/* 将窗口指针值绑定在键myWinKey上*/<br />pthread_setpecific ( myWinKey, win);<br />}<br />/* 函数 createMyKey，创建一个键，并指定了destructor */<br />void createMyKey ( void ) {<br />pthread_keycreate(&amp;myWinKey, freeWinKey);<br />}<br />/* 函数 freeWinKey，释放空间*/<br />void freeWinKey ( Fl_Window * win){<br />delete win;<br />}<br />　　这样，在不同的线程中调用函数createMyWin，都可以得到在线程内部均可见的窗口变量，这个变量通过函数<br />pthread_getspecific得到。在上面的例子中，我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原<br />型如下：<br />　　extern int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer));<br />　　extern void *pthread_getspecific __P ((pthread_key_t __key));<br />　　这两个函数的参数意义和使用方法是显而易见的。要注意的是，用pthread_setspecific为一个键指定新的线程数据时，必须自己释放原有<br />的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键，这个键占用的内存将被释放，但同样要注意的是，它只释放键占<br />用的内存，并不释放该键关联的线程数据所占用的内存资源，而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据<br />的释放必须在释放键之前完成。<br />4.2 互斥锁<br />　　互斥锁用来保证一段时间内只有一个线程在执行一段代码。必要性显而易见：假设各个线程向同一个文件顺序写入数据，最后得到的结果<br />一定是灾难性的。<br />　　我们先看下面一段代码。这是一个读/写程序，它们公用一个缓冲区，并且我们假定一个缓冲区只能保存一条信息。即缓冲区只有两个状态<br />：有信息或没有信息。<br />void reader_function ( void );<br />void writer_function ( void );<br />char buffer;<br />int buffer_has_item=0;<br />pthread_mutex_t mutex;<br />struct timespec delay;<br />void main ( void ){<br />pthread_t reader;<br />/* 定义延迟时间*/<br />delay.tv_sec = 2;<br />delay.tv_nec = 0;<br />/* 用默认属性初始化一个互斥锁对象*/<br />pthread_mutex_init (&amp;mutex,NULL);<br />pthread_create(&amp;reader, pthread_attr_default, (void *)&amp;reader_function), NULL);<br />writer_function( );<br />}<br />void writer_function (void){<br />while(1){<br />/* 锁定互斥锁*/<br />pthread_mutex_lock (&amp;mutex);<br />if (buffer_has_item==0){<br />buffer=make_new_item( );<br />buffer_has_item=1;<br />}<br />/* 打开互斥锁*/<br />pthread_mutex_unlock(&amp;mutex);<br />pthread_delay_np(&amp;delay);<br />}<br />}<br />void reader_function(void){<br />while(1){<br />pthread_mutex_lock(&amp;mutex);<br />if(buffer_has_item==1){<br />consume_item(buffer);<br />buffer_has_item=0;<br />}<br />pthread_mutex_unlock(&amp;mutex);<br />pthread_delay_np(&amp;delay);<br />}<br />}<br />　　这里声明了互斥锁变量mutex，结构pthread_mutex_t为不公开的数据类型，其中包含一个系统分配的属性对象。函数<br />pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁，须调用函数<br />pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数<br />pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared，它有两个取值，<br />PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步，后者用于同步本进程的不同线程。在上面的例子中，<br />我们使用的是默认属性PTHREAD_PROCESS_<br />PRIVATE。后者用来设置互斥锁类型，可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、<br />PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制，一般情况下，选用最后一个默认属性。<br />　　pthread_mutex_lock声明开始用互斥锁上锁，此后的代码直至调用pthread_mutex_unlock为止，均被上锁，即同一时间只能被一个线程调<br />用执行。当一个线程执行到pthread_mutex_lock处时，如果该锁此时被另一个线程使用，那此线程被阻塞，即程序将等待到另一个线程释放此<br />互斥锁。在上面的例子中，我们使用了pthread_delay_np函数，让线程睡眠一段时间，就是为了防止一个线程始终占据此函数。<br />　　上面的例子非常简单，就不再介绍了，需要提出的是在使用互斥锁的过程中很有可能会出现死锁：两个线程试图同时占用两个资源，并按<br />不同的次序锁定相应的互斥锁，例如两个线程都需要锁定互斥锁1和互斥锁2，a线程先锁定互斥锁1，b线程先锁定互斥锁2，这时就出现了死锁<br />。此时我们可以使用函数<br />pthread_mutex_trylock，它是函数pthread_mutex_lock的非阻塞版本，当它发现死锁不可避免时，它会返回相应的信息，程序员可以针对死锁<br />做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样，但最主要的还是要程序员自己在程序设计注意这一点。<br />4.3 条件变量<br />　　前一节中我们讲述了如何使用互斥锁来实现线程间数据的共享和通信，互斥锁一个明显的缺点是它只有两种状态：锁定和非锁定。而条件<br />变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足，它常和互斥锁一起使用。使用时，条件变量被用来阻塞一个线<br />程，当条件不满足时，线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量，它将通知相应的条件变量唤醒<br />一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来，条件变量被用来进行线承间的同步<br />。<br />　　条件变量的结构为pthread_cond_t，函数pthread_cond_init（）被用来初始化一个条件变量。它的原型为：<br />　　extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));<br />　　其中cond是一个指向结构pthread_cond_t的指针，cond_attr是一个指向结构pthread_condattr_t的指针。结构pthread_condattr_t是条件<br />变量的属性结构，和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用，默认值是 PTHREAD_<br />PROCESS_PRIVATE，即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件<br />变量的函数为pthread_cond_ destroy（pthread_cond_t cond）。　<br />　　函数pthread_cond_wait（）使线程阻塞在一个条件变量上。它的函数原型为：<br />　　extern int pthread_cond_wait __P ((pthread_cond_t *__cond,<br />　　pthread_mutex_t *__mutex));<br />　　线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数<br />pthread_cond_broadcast唤醒，但是要注意的是，条件变量只是起阻塞和唤醒线程的作用，具体的判断条件还需用户给出，例如一个变量是否<br />为0等等，这一点我们从后面的例子中可以看到。线程被唤醒后，它将重新检查判断条件是否满足，如果还不满足，一般说来线程应该仍阻塞在<br />这里，被等待被下一次唤醒。这个过程一般用while语句实现。<br />　　另一个用来阻塞线程的函数是pthread_cond_timedwait（），它的原型为：<br />　　extern int pthread_cond_timedwait __P ((pthread_cond_t *__cond,<br />　　pthread_mutex_t *__mutex, __const struct timespec *__abstime));<br />　　它比函数pthread_cond_wait（）多了一个时间参数，经历abstime段时间后，即使条件变量不满足，阻塞也被解除。<br />　　函数pthread_cond_signal（）的原型为：<br />　　extern int pthread_cond_signal __P ((pthread_cond_t *__cond));<br />　　它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件变量上时，哪一个线程被唤醒是由线程的调度策略所决定的。要<br />注意的是，必须用保护条件变量的互斥锁来保护这个函数，否则条件满足信号又可能在测试条件和调用pthread_cond_wait函数之间被发出，从<br />而造成无限制的等待。下面是使用函数pthread_cond_wait（）和函数pthread_cond_signal（）的一个简单的例子。<br />pthread_mutex_t count_lock;<br />pthread_cond_t count_nonzero;<br />unsigned count;<br />decrement_count　() {<br />pthread_mutex_lock (&amp;count_lock);<br />while(count==0)<br />pthread_cond_wait( &amp;count_nonzero, &amp;count_lock);<br />count=count -1;<br />pthread_mutex_unlock (&amp;count_lock);<br />}<br />increment_count(){<br />pthread_mutex_lock(&amp;count_lock);<br />if(count==0)<br />pthread_cond_signal(&amp;count_nonzero);<br />count=count+1;<br />pthread_mutex_unlock(&amp;count_lock);<br />}<br />　　count值为0时， decrement函数在pthread_cond_wait处被阻塞，并打开互斥锁count_lock。此时，当调用到函数<br />increment_count时，pthread_cond_signal（）函数改变条件变量，告知decrement_count（）停止阻塞。读者可以试着让两个线程分别运行这<br />两个函数，看看会出现什么样的结果。<br />　　函数pthread_cond_broadcast（pthread_cond_t<br />*cond）用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁，所以必须小心使用这个函数。<br />4.4 信号量<br />　　信号量本质上是一个非负的整数计数器，它被用来控制对公共资源的访问。当公共资源增加时，调用函数sem_post（）增加信号量。只有<br />当信号量值大于０时，才能使用公共资源，使用后，函数sem_wait（）减少信号量。函数sem_trywait（）和函数pthread_<br />mutex_trylock（）起同样的作用，它是函数sem_wait（）的非阻塞版本。下面我们逐个介绍和信号量有关的一些函数，它们都在头文件<br />/usr/include/semaphore.h中定义。<br />　　信号量的数据类型为结构sem_t，它本质上是一个长整型的数。函数sem_init（）用来初始化一个信号量。它的原型为：<br />　　extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));<br />　　sem为指向信号量结构的一个指针；pshared不为０时此信号量在进程间共享，否则只能为当前进程的所有线程共享；value给出了信号量的<br />初始值。<br />　　函数sem_post( sem_t *sem<br />)用来增加信号量的值。当有线程阻塞在这个信号量上时，调用这个函数会使其中的一个线程不在阻塞，选择机制同样是由线程的调度策略决定<br />的。<br />　　函数sem_wait( sem_t *sem<br />)被用来阻塞当前线程直到信号量sem的值大于0，解除阻塞后将sem的值减一，表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem<br />)是函数sem_wait（）的非阻塞版本，它直接将信号量sem的值减一。<br />　　函数sem_destroy(sem_t *sem)用来释放信号量sem。<br />　　下面我们来看一个使用信号量的例子。在这个例子中，一共有4个线程，其中两个线程负责从文件读取数据到公共的缓冲区，另两个线程从<br />缓冲区读取数据作不同的处理（加和乘运算）。<br />/* File sem.c */<br />#include<br />#include<br />#include<br />#define MAXSTACK 100<br />int stack[MAXSTACK][2];<br />int size=0;<br />sem_t sem;<br />/* 从文件1.dat读取数据，每读一次，信号量加一*/<br />void ReadData1(void){<br />FILE *fp=fopen("1.dat","r");<br />while(!feof(fp)){<br />fscanf(fp,"%d %d",&amp;stack[size][0],&amp;stack[size][1]);<br />sem_post(&amp;sem);<br />++size;<br />}<br />fclose(fp);<br />}<br />/*从文件2.dat读取数据*/<br />void ReadData2(void){<br />FILE *fp=fopen("2.dat","r");<br />while(!feof(fp)){<br />fscanf(fp,"%d %d",&amp;stack[size][0],&amp;stack[size][1]);<br />sem_post(&amp;sem);<br />++size;<br />}<br />fclose(fp);<br />}<br />/*阻塞等待缓冲区有数据，读取数据后，释放空间，继续等待*/<br />void HandleData1(void){<br />while(1){<br />sem_wait(&amp;sem);<br />printf("Plus:%d+%d=%dn",stack[size][0],stack[size][1],<br />stack[size][0]+stack[size][1]);<br />--size;<br />}<br />}<br />void HandleData2(void){<br />while(1){<br />sem_wait(&amp;sem);<br />printf("Multiply:%d*%d=%dn",stack[size][0],stack[size][1],<br />stack[size][0]*stack[size][1]);<br />--size;<br />}<br />}<br />int main(void){<br />pthread_t t1,t2,t3,t4;<br />sem_init(&amp;sem,0,0);<br />pthread_create(&amp;t1,NULL,(void *)HandleData1,NULL);<br />pthread_create(&amp;t2,NULL,(void *)HandleData2,NULL);<br />pthread_create(&amp;t3,NULL,(void *)ReadData1,NULL);<br />pthread_create(&amp;t4,NULL,(void *)ReadData2,NULL);<br />/* 防止程序过早退出，让它在此无限期等待*/<br />pthread_join(t1,NULL);<br />}<br />　　在Linux下，我们用命令gcc -lpthread sem.c -o sem生成可执行文件sem。<br />我们事先编辑好数据文件1.dat和2.dat，假设它们的内容分别为1 2 3 4 5 6 7 8 9 10和 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10<br />，我们运行sem，得到如下的结果：<br />Multiply:-1*-2=2<br />Plus:-1+-2=-3<br />Multiply:9*10=90<br />Plus:-9+-10=-19<br />Multiply:-7*-8=56<br />Plus:-5+-6=-11<br />Multiply:-3*-4=12<br />Plus:9+10=19<br />Plus:7+8=15<br />Plus:5+6=11<br />　　从中我们可以看出各个线程间的竞争关系。而数值并未按我们原先的顺序显示出来这是由于size这个数值被各个线程任意修改的缘故。这<br />也往往是多线程编程要注意的问题。<br />5 小结<br />　　多线程编程是一个很有意思也很有用的技术，使用多线程技术的网络蚂蚁是目前最常用的下载工具之一，使用多线程技术的grep比单线程<br />的grep要快上几倍，类似的例子还有很多。希望大家能用多线程技术写出高效实用的好程序来。<br />本文出自:http://www.china-pub.com 作者: 姚继锋 (2001-08-11 09:05:00)<img src ="http://www.blogjava.net/huyi2006/aggbug/256617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2009-02-25 14:53 <a href="http://www.blogjava.net/huyi2006/articles/256617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>BT种子文件格式</title><link>http://www.blogjava.net/huyi2006/articles/241632.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Thu, 20 Nov 2008 06:08:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/241632.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/241632.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/241632.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/241632.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/241632.html</trackback:ping><description><![CDATA[
		<p align="left">BT种子文件使用了一种叫bencoding的编码方法来保存数据。<br />bencoding现有四种类型的数据：srings(字符串)，integers(整数)，lists(列表)，dictionaries(字典)<br />编码规则如下：<br />strings(字符串)编码为：&lt;字符串长度&gt;：&lt;字符串&gt;<br />例如： 4:test 表示为字符串"test"<br /> 4:例子 表示为字符串“例子”<br />字符串长度单位为字节<br />没开始或结束标记</p>
		<p>integers(整数)编码为：i&lt;整数&gt;e<br />开始标记i，结束标记为e<br />例如： i1234e 表示为整数1234<br /> i-1234e 表示为整数-1234<br />整数没有大小限制<br /> i0e 表示为整数0<br /> i-0e 为非法<br />以0开头的为非法如： i01234e 为非法</p>
		<p>lists(列表)编码为：l&lt;bencoding编码类型&gt;e<br />开始标记为l,结束标记为e<br />列表里可以包含任何bencoding编码类型，包括整数，字符串，列表，字典。<br />例如： l4:test5abcdee 表示为二个字符串["test","abcde"]</p>
		<p>dictionaries(字典)编码为d&lt;bencoding字符串&gt;&lt;bencoding编码类型&gt;e<br />开始标记为d,结束标记为e<br />关键字必须为bencoding字符串<br />值可以为任何bencoding编码类型<br />例如： d3:agei20ee 表示为{"age"=20}<br /> d4:path3:C:\8:filename8:test.txte 表示为{"path"="C:\","filename"="test.txt"}</p>
		<p>具体文件结构如下：<br />全部内容必须都为bencoding编码类型。<br />整个文件为一个字典结构,包含如下关键字<br />announce:tracker服务器的URL(字符串)<br />announce-list(可选):备用tracker服务器列表(列表)<br />creation date(可选):种子创建的时间，Unix标准时间格式，从1970 1月1日 00:00:00到创建时间的秒数(整数)<br />comment(可选):备注(字符串)<br />created by(可选):创建人或创建程序的信息(字符串)<br />info:一个字典结构，包含文件的主要信息，为分二种情况：单文件结构或多文件结构<br />单文件结构如下：<br />          length:文件长度，单位字节(整数)<br />          md5sum(可选)：长32个字符的文件的MD5校验和，BT不使用这个值，只是为了兼容一些程序所保留!(字符串)<br />          name:文件名(字符串)<br />          piece length:每个块的大小，单位字节(整数)<br />          pieces:每个块的20个字节的SHA1 Hash的值(二进制格式)<br />多文件结构如下：<br />          files:一个字典结构<br />                 length:文件长度，单位字节(整数)<br />                 md5sum(可选):同单文件结构中相同<br />                 path:文件的路径和名字，是一个列表结构，如\test\test.txt 列表为l4:test8test.txte<br />          name:最上层的目录名字(字符串)<br />          piece length:同单文件结构中相同<br />          pieces:同单文件结构中相同 <br />实例：<br />用记事本打开一个.torrent可以看来类似如下内容<br />d8:announce35:http://www.manfen.net:7802/announce13:creation datei1076675108e4:infod6:lengthi17799e4:name62:MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent12:piece lengthi32768e6:pieces20:?W &#x6;?躐?緕排T酆ee</p>
		<p>很容易看出<br />announce＝<a href="http://www.chinaitpower.com/A/2002-03-25/&quot;http://www.manfen.net:7802/announce&quot;">http://www.manfen.net:7802/announce</a><br />creation date＝1076675108秒(02/13/04 20:25:08)<br />文件名=MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent<br />文件大小＝17799字节<br />文件块大小＝32768字节<br /></p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/241632.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-11-20 14:08 <a href="http://www.blogjava.net/huyi2006/articles/241632.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>函数指针作为结构体成员，实现函数注册</title><link>http://www.blogjava.net/huyi2006/articles/233710.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 11 Oct 2008 02:24:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/233710.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/233710.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/233710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/233710.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/233710.html</trackback:ping><description><![CDATA[
		<p>有时我们需要将函数作为结构体的成员，模拟C++类的情形，可应用于方法注册。<br />#include &lt;stdio.h&gt;</p>
		<p>struct a<br />{<br />    void (*func)(char *);<br />};</p>
		<p>void hello(char *name)<br />{<br />    printf ("hello %s\n",name);<br />}</p>
		<p>int main()<br />{<br />    struct a a1;<br />    a1.func = hello;<br />    a1.func("illusion");<br />    system("PAUSE");<br />    return 0;<br />}</p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/233710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-10-11 10:24 <a href="http://www.blogjava.net/huyi2006/articles/233710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在VC6.0中使用sqlite数据库</title><link>http://www.blogjava.net/huyi2006/articles/199720.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 10 May 2008 08:21:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/199720.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/199720.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/199720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/199720.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/199720.html</trackback:ping><description><![CDATA[
		<p>在sqlite.org 上下载得到Windows版本的sqlite,它是以sqlitedll.zip文件提供的,其中有sqlite3.def和 sqlite3.dll文件,当然可以直接通过LoadLibrary等WIN32API来操作dll,查找其中包含的函数,并使用这些函数,但是一般都 不这么做,原因很简单:这样太麻烦,所以一般先使用LIB命令生成用于链接的lib,然后把sqlite头文件sqlite3.h包含进程序中,<br />这样直接调用 sqlite的API就方便多了.当然sqlite3.h文件得从sqlite源代码（以sqlite-source-3_3_4.zip文件提供）中搞到,在源码中sqlite3.h这个头文件是sqlite3.h.in存在的，解压出来改成sqlite.h就可以了. <br />使用VC++的LIB命令有以下步骤： <br />（1）设置VC98中LIB.exe所在的路径： <br />D:\MyDoc\db\capi&gt;set path=%path%;"D:\Program Files\Microsoft Visual Studio\VC98\Bin" <br />(2)生成SQLite的lib文件： <br />D:\MyDoc\db\capi&gt;LIB /DEF:SQLITE3.DEF /MACHINE:IX86 </p>
		<p>Microsoft (R) Library Manager Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. Creating library SQLITE.lib and object SQLITE.exp <br />  这样就成功地创建了在WIN32程序中访问sqlite所需要的库,可以用于链接WIN32程序. <br />    到此所有使用sqlite的准备工作已告罄.现在在MSVC6中新建一个Win32 Console Application工程,把sqlite.dll,sqlite.h和sqlite.lib文件复制到工程文件夹中,把sqlite.h文件加入到项 目中,然后在Project Setting的Link中的对象库模块中增加sqlite.lib文件. 或者project-&gt;add to project-&gt;files，选择这个lib文件<br />然后修改加入如下代码即可: </p>
		<p>#include &lt;IOSTREAM&gt;<br />#include &lt;STRING&gt;<br />#include &lt;SSTREAM&gt;<br />#include &lt;stdio.h&gt;<br />#include "sqlite3.h"</p>
		<p>using namespace std;</p>
		<p>sqlite3* pDB;</p>
		<p>int createTable()<br />{<br />  char* errMsg;<br />  std::string dropTab="drop table test_tab;";<br />  string strSQL= "create table test_tab (f1 int, f2 long, f3 varchar(20));";<br />  int res = sqlite3_exec(pDB,dropTab.c_str(),0,0, &amp;errMsg);<br />  if (res != SQLITE_OK)<br />  {<br />   std::cout &lt;&lt; "执行SQL 出错." &lt;&lt; errMsg &lt;&lt; std::endl;<br />   //return -1;<br />  } </p>
		<p>  res = sqlite3_exec(pDB,strSQL.c_str(),0,0, &amp;errMsg);</p>
		<p>  if (res != SQLITE_OK)<br />  {<br />    std::cout &lt;&lt; "执行创建table的SQL 出错." &lt;&lt; errMsg &lt;&lt; std::endl;<br />    return -1;<br />  }<br />  else<br />  {<br />      std::cout &lt;&lt; "创建table的SQL成功执行."&lt;&lt; std::endl;<br />  }</p>
		<p>  return 0;<br />}</p>
		<p>int insert1()<br />{<br />  char* errMsg;</p>
		<p>  int res = sqlite3_exec(pDB,"begin transaction;",0,0, &amp;errMsg);</p>
		<p>  for (int i= 1; i &lt; 100; ++i)<br />  {<br />    std::stringstream strsql;<br />    strsql &lt;&lt; "insert into test_tab  values(";<br />    strsql  &lt;&lt; i &lt;&lt; ","&lt;&lt; (i+10) &lt;&lt;",'huyi'"&lt;&lt; ");";<br />    std::string str = strsql.str();<br />    cout &lt;&lt; str &lt;&lt;endl;<br />    res = sqlite3_exec(pDB,str.c_str(),0,0, &amp;errMsg);<br />    if (res != SQLITE_OK)<br />    {<br />      std::cout &lt;&lt; "执行SQL 出错." &lt;&lt; errMsg &lt;&lt; std::endl;<br />      return -1;<br />    }<br />  }<br />  res = sqlite3_exec(pDB,"commit transaction;",0,0, &amp;errMsg);<br />  std::cout &lt;&lt; "SQL成功执行."&lt;&lt; std::endl;<br />  return 0; <br />}</p>
		<p>
				<br />int select1()<br />{<br />  char* errMsg;<br /> int nrow = 0, ncolumn = 0;<br /> char **azResult; //二维数组存放结果<br />  string strSQL= "select * from test_tab;";<br />     /*<br />  int res = sqlite3_exec(pDB,strSQL.c_str(),0,0, &amp;errMsg);</p>
		<p>  if (res != SQLITE_OK)<br />  {<br />    std::cout &lt;&lt; "执行SQL 出错." &lt;&lt; errMsg &lt;&lt; std::endl;<br />    return -1;<br />  }<br />  else<br />  {<br />       std::cout &lt;&lt; "SQL成功执行."&lt;&lt; std::endl;<br />  }<br />  */<br />  sqlite3_get_table(pDB, strSQL.c_str(), &amp;azResult, &amp;nrow, &amp;ncolumn, &amp;errMsg);<br />     int i = 0;<br />  for( i=0 ; i&lt;( nrow + 1 ) * ncolumn ; i++ )<br />  {<br />   if (i&gt;0 &amp;&amp; i%ncolumn==0)<br />    printf("\n");<br />      printf( "%s ",azResult[i]);<br />  }<br />         printf("\n");<br />  //释放掉 azResult 的内存空间<br />  sqlite3_free_table( azResult );<br />  sqlite3_close(pDB); //关闭数据库<br />  return 0;<br />}</p>
		<p>int main(int argc, char* argv[])<br />{<br />  if (argc &lt; 2)<br />  {<br />    std::cout &lt;&lt; "请输入命令行参数：sqlite数据库名." &lt;&lt; std::endl;<br />    return 0;<br />  }</p>
		<p>  int res = sqlite3_open(argv[1], &amp;pDB);</p>
		<p>  if( res ){<br />    std::cout &lt;&lt; "Can't open database: "&lt;&lt; sqlite3_errmsg(pDB);<br />    sqlite3_close(pDB);<br />    return -1;<br />  }<br />  res = createTable();<br />  if (res != 0)<br />  {<br />      return 0;<br />  }<br />  res = insert1();<br />  if (res != 0)<br />  {<br />      return 0;<br />  }<br />  select1();<br />   return 0;<br />}<br /></p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/199720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-05-10 16:21 <a href="http://www.blogjava.net/huyi2006/articles/199720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>判断一个数是否是素数</title><link>http://www.blogjava.net/huyi2006/articles/192024.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Thu, 10 Apr 2008 15:39:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/192024.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/192024.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/192024.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/192024.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/192024.html</trackback:ping><description><![CDATA[int prime(int m) <br />{<br />   int i,k;   <br />   k=sqrt(m); <br />   for(i = 2; i &lt; k; i++) <br />   if(m % i == 0)<br />      return 0; <br />return 1; <br />} <img src ="http://www.blogjava.net/huyi2006/aggbug/192024.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-04-10 23:39 <a href="http://www.blogjava.net/huyi2006/articles/192024.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于C语言的内存池设计与实现[转]</title><link>http://www.blogjava.net/huyi2006/articles/192023.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Thu, 10 Apr 2008 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/192023.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/192023.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/192023.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/192023.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/192023.html</trackback:ping><description><![CDATA[
		<div class="content" id="fontzoom">
				<p>
				</p>
				<p>介绍：</p>
				<p>       设计内存池的目标是为了保证服务器长时间高效的运行，通过对申请空间小而申请频繁的对象进行有效管理，减少内存碎片的产生，合理分配管理用户内存，从而减少系统中出现有效空间足够，而无法分配大块连续内存的情况。</p>
				<p>目标：</p>
				<p>    此次设计内存池的基本目标，需要满足线程安全性（多线程），适量的内存泄露越界检查，运行效率不太低于malloc/free方式，实现对4-128字节范围内的内存空间申请的内存池管理（非单一固定大小对象管理的内存池）。</p>
				<p>内存池技术设计与实现</p>
				<p>    本内存池的设计方法主要参考SGI的alloc的设计方案，为了适合一般的应用，并在alloc的基础上做一些简单的修改。</p>
				<p>    Mempool的内存池设计方案如下（也可参考候捷《深入剖析STL》）</p>
				<p>    从系统申请大块heap内存，在此内存上划分不同大小的区块，并把具有相同大小的区块连接起来，组成一个链表。比如A大小的块，组成链表L，当申请A大小时，直接从链表L头部（如果不为空）上取到一块交给申请者，当释放A大小的块时，直接挂接到L的头部。内存池的原理比较简单，但是在具体实现过程中大量的细节需要注意。</p>
				<p>    1：字节对齐。</p>
				<p>    为了方便内存池中对象的管理，需要对申请内存空间的进行调整，在Mempool中，字节对齐的大小为最接近8倍数的字节数。比如，用户申请5个字节，Mempool首先会把它调整为8字节。比如申请22字节，会调整为24，对比关系如下</p>
				<table cellspacing="0" cellpadding="0" border="1">
						<tbody>
								<tr>
										<td valign="top" width="56">
												<p>序号</p>
										</td>
										<td valign="top" width="108">
												<p>对齐字节</p>
										</td>
										<td valign="top" width="177">
												<p>范围</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>0</p>
										</td>
										<td valign="top" width="108">
												<p>8</p>
										</td>
										<td valign="top" width="177">
												<p>1-8</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>1</p>
										</td>
										<td valign="top" width="108">
												<p>16</p>
										</td>
										<td valign="top" width="177">
												<p>9-16</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>2</p>
										</td>
										<td valign="top" width="108">
												<p>24</p>
										</td>
										<td valign="top" width="177">
												<p>17-24</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>3</p>
										</td>
										<td valign="top" width="108">
												<p>32</p>
										</td>
										<td valign="top" width="177">
												<p>25-32</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>4</p>
										</td>
										<td valign="top" width="108">
												<p>40</p>
										</td>
										<td valign="top" width="177">
												<p>33-40</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>5</p>
										</td>
										<td valign="top" width="108">
												<p>48</p>
										</td>
										<td valign="top" width="177">
												<p>41-48</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>6</p>
										</td>
										<td valign="top" width="108">
												<p>56</p>
										</td>
										<td valign="top" width="177">
												<p>49-56</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>7</p>
										</td>
										<td valign="top" width="108">
												<p>64</p>
										</td>
										<td valign="top" width="177">
												<p>57-64</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>8</p>
										</td>
										<td valign="top" width="108">
												<p>72</p>
										</td>
										<td valign="top" width="177">
												<p>65-72</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>9</p>
										</td>
										<td valign="top" width="108">
												<p>80</p>
										</td>
										<td valign="top" width="177">
												<p>73-80</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>10</p>
										</td>
										<td valign="top" width="108">
												<p>88</p>
										</td>
										<td valign="top" width="177">
												<p>81-88</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>11</p>
										</td>
										<td valign="top" width="108">
												<p>96</p>
										</td>
										<td valign="top" width="177">
												<p>89-96</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>12</p>
										</td>
										<td valign="top" width="108">
												<p>104</p>
										</td>
										<td valign="top" width="177">
												<p>97-104</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>13</p>
										</td>
										<td valign="top" width="108">
												<p>112</p>
										</td>
										<td valign="top" width="177">
												<p>105-112</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>14</p>
										</td>
										<td valign="top" width="108">
												<p>120</p>
										</td>
										<td valign="top" width="177">
												<p>113-120</p>
										</td>
								</tr>
								<tr>
										<td valign="top" width="56">
												<p>15</p>
										</td>
										<td valign="top" width="108">
												<p>128</p>
										</td>
										<td valign="top" width="177">
												<p>121-128</p>
										</td>
								</tr>
						</tbody>
				</table>
				<p align="center">（图1）</p>
				<p>对于超过128字节的申请，直接调用malloc函数申请内存空间。这里设计的内存池并不是对所有的对象进行内存管理，只是对申请内存空间小，而申请频繁的对象进行管理，对于超过128字节的对象申请，不予考虑。这个需要与实际项目结合，并不是固定不变的。实现对齐操作的函数如下</p>
				<p align="left">static size_t round_up(size_t size)<br />{<br />        return (((size)+7) &amp;~ 7);// 按8字节对齐<br />}</p>
				<p>2：构建索引表</p>
				<p>内存池中管理的对象都是固定大小，现在要管理0-128字节的范围内的对象申请空间，除了采用上面提到的字节对齐外，还需要变通一下，这就是建立索引表，做法如下；<br />static _obj*  free_list[16];<br />创建一个包含16个_obj*指针的数组，关于_obj结构后面详细讲解。free_list[0]记录所有空闲空间为8字节的链表的首地址；free_list[1]对应16字节的链表，free_list[2]对应24字节的列表。free_list中的下标和字节链表对应关系参考图1中的“序号”和“对齐字节”之间的关系。这种关系，我们很容易用算法计算出来。如下</p>
				<p align="left">static size_t freelist_index(size_t size)<br />{<br />        return (((size)+7)/7-1);// 按8字节对齐<br />}</p>
				<p>    所以，这样当用户申请空间A时，我们只是通过上面简单的转换，就可以跳转到包含A字节大小的空闲链表上,如下；<br />_obj** p = free_list[freelist_index(A)];<br /><br />3:构建空闲链表</p>
				<p>通过索引表，我们知道mempool中维持着16条空闲链表，这些空闲链表中管理的空闲对象大小分别为8，16，24，32，40…128。这些空闲链表链接起来的方式完全相同。一般情况下我们构建单链表时需要创建如下的一个结构体。</p>
				<p>struct Obj<br />{<br />    Obj *next;<br />    Char* p;<br />    Int iSize;<br />}</p>
				<p>next指针指向下一个这样的结构，p指向真正可用空间,iSize用于只是可用空间的大小，在其他的一些内存池实现中，还有更复杂的结构体，比如还包括记录此结构体的上级结构体的指针，结构体中当前使用空间的变量等，当用户申请空间时，把此结构体添加的用户申请空间中去，比如用户申请12字节的空间，可以这样做</p>
				<p>Obj *p = (Obj*)malloc(12+sizeof(Obj));<br />p-&gt;next = NULL;<br />p-&gt;p = (char*)p+sizeof(Obj);<br />p-&gt;iSize = 12;</p>
				<p>但是，我们并没有采用这种方式，这种方式的一个缺点就是，用户申请小空间时，内存池加料太多了。比如用户申请12字节时，而真实情况是内存池向内存申请了12+ sizeof(Obj)=12+12=24字节的内存空间，这样浪费大量内存用在标记内存空间上去，并且也没有体现索引表的优势。Mempool采用的是union方式</p>
				<p>union Obj<br />{<br />    Obj *next;<br />    char client_data[1];<br />}</p>
				<p>这里除了把上面的struct修改为union，并把int iSize去掉，同时把char*p，修改为char client_data[1]，并没有做太多的修改。而优势也恰恰体现在这里。如果采用struct方式，我们需要维护两条链表，一条链表是，已分配内存空间链表，另一条是未分配（空闲）空间链表。而我们使用索引表和union结构体，只需要维护一条链表，即未分配空间链表。具体如下</p>
				<p>索引表的作用有两条1：如上所说，维护16条空闲链表2：变相记录每条链表上空间的大小，比如下标为3的索引表内维持着是大小为24字节的空闲链表。这样我们通过索引表减少在结构体内记录p所指向空间大小的iSize变量。从而减少4个字节。</p>
				<p>Union的特性是，结构内的变量是互斥存在的。再运行状态下，只是存在一种变量类型。所以在这里sizeof(Obj)的大小为4，难道这里我们也需要把这4字节也加到用户申请空间中去嘛？其实不是，如果这样，我们又抹杀了union的特性。</p>
				<p>当我们构建空闲分配链表时，我们通过next指向下一个union结构体，这样我们不使用p指针。当把这个结构体分配出去时，我们直接返回client_data的地址，此时client_data正好指向申请空间的首字节。所以这样，我们就不用在用户申请空间上添加任何东西。</p>
				<p align="center">
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000px8" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000px8" border="0" />
						</a>
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000px8" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000px8" border="0" />
						</a>
						<br />图2</p>
				<p>    Obj的连接方式如上所示，这样我们无需为用户申请空间添加任何内容。   </p>
				<p>4：记录申请空间字节数</p>
				<p>如果采用面向对象方式，或者我们在释放内存池的空间时能够明确知道释放空间的大小，无需采用这种方式。</p>
				<p align="center">
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000px9" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000px9" border="0" />
						</a>
						<br />图3</p>
				<p>在C语言中的free没有传递释放空间大小，而可以正确释放，在这里也是模仿这种方式，采用这种记录申请空间大小的方式去释放内存。用户申请空间+1操作将在字节对齐之前执行，找到合适空间后，把首字节改写为申请空间的大小，当然1个字节最多纪录256个数，如果项目需要，可以设置为short类型或者int类型，不过这样就需要占用用户比较大的空间。当释放内存空间时，首先读取这个字节，获取空间大小，进行释放。为了便于对大于128字节对象的大小进行合适的释放，同时也对大于128字节的内存申请，添加1字节记录大小。所以现在这里限制了用户内存申请空间不得大于255字节，不过现在已经满足项目要求。当然也可以修改为用short类型记录申请空间的大小。</p>
				<p>    // 申请<br />    *(( unsigned char *)result) = (size_t)n;<br />    unsigned char * pTemp = (unsigned char*)result;<br />    ++pTemp;<br />    result = (_obj*)pTemp;<br />    return result;</p>
				<p>    // 释放<br />    unsigned char * pTemp = (unsigned char *)ptr;<br />    --pTemp;<br />    ptr = (void*)pTemp;<br />    n = (size_t)(*( unsigned char *)ptr);</p>
				<p>5：内存池的分配原理</p>
				<p>在内存池的设计中，有两个重要的操作过程1：chunk_alloc，申请大块内存，2：refill回填操作，内存池初始化化时并不是为索引表中的每一项都创建空闲分配链表，这个过程会推迟到，只有用户提取请求时才会创建这样的分配链表。详细参考如下代码（在sgi中stl_alloc.h文件中你也可以看到这两个函数），主要步骤在注释中已经说明。</p>
				<p align="left">/**<br />* @bri: 申请大块内存，并返回size*(*nobjs)大小的内存块<br />* @param: size,round_up对齐后的大小,nobjs<br />* @return: 返回指向第一个对象内存指针<br />*/<br />static char* chunk_alloc(size_t size, int *nobjs)<br />{<br />     /**&lt; 返回指针 */<br />     char* __result;<br />     /**&lt; 申请内存块大小 */<br />     size_t __total_bytes = size *(*nobjs);<br />     /**&lt; 当前内存可用空间 */<br />     size_t __bytes_left = _end_free - _start_free;<br /></p>
				<p align="left">     /**&lt; 内存池中还有大片可用内存 */<br />     if (__bytes_left &gt;= __total_bytes)<br />     {<br />         __result = _start_free;<br />         _start_free += __total_bytes;<br />         return (__result);<br />     }<br />     /**&lt; 至少还有一个对象大小的内存空间 */<br />     else if (__bytes_left &gt;= size)<br />     {<br />         *nobjs = (int)(__bytes_left/size);<br />         __total_bytes = size * (*nobjs);<br />         __result = _start_free;<br />         _start_free += __total_bytes;<br />         return (__result);<br />     }<br />     /**&lt; 内存池中没有任何空间 */<br />     else<br />     {<br />         /**&lt; 重新申请内存池的大小 */<br />         size_t __bytes_to_get = 2 * __total_bytes + round_up(_heap_size &gt;&gt; 4);<br />         /**&lt; 把内存中剩余的空间添加到freelist中 */<br />         if(__bytes_left &gt; 0)<br />         {<br />              _obj *VOLATILE* __my_free_list = <br />                   _free_list + freelist_index(__bytes_left);<br />              ((_obj*)_start_free)-&gt;free_list_link =<br />*__my_free_list;<br />              *__my_free_list = (_obj*)_start_free;<br />         }<br />         // 申请新的大块空间<br />         _start_free = (char*)malloc(__bytes_to_get);<br />         /*=======================================================================*/<br />         memset(_start_free,0,__bytes_to_get);<br />         /*=======================================================================*/<br />         // 系统内存已经无可用内存，那么从内存池中压缩内存<br />         if(0 == _start_free)<br />         {<br />              size_t __i;<br />              _obj *VOLATILE* __my_free_list;<br />              _obj *__p;<br />              /**&lt; 从freelist中逐项检查可用空间(此时只收集比size对象大的内存空间) */<br />              for (__i = size; __i &lt;= (size_t)__MAX_BYTES; __i += __ALIGN)<br />              {<br />                   __my_free_list = _free_list + freelist_index(__i);<br />                   __p = *__my_free_list;<br />                   /**&lt; 找到空闲块 */<br />                   if (__p != 0)<br />                   {<br />                       *__my_free_list = __p-&gt;free_list_link;<br />                       _start_free = (char*)__p;<br />                       _end_free = _start_free + __i;<br />                       return (chunk_alloc(size,nobjs));<br />                   }<br />              }<br />              _end_free = 0;<br />              /**&lt; 再次申请内存，可能触发一个异常 */<br />              _start_free = (char*)malloc(__bytes_to_get);<br />         }<br />         /**&lt; 记录当前内存池的容量 */<br />         _heap_size += __bytes_to_get;<br />         _end_free = _start_free + __bytes_to_get;<br />         return (chunk_alloc(size,nobjs));<br />     }<br />}</p>
				<p align="left">/*=======================================================================*/<br />/**<br /> * @bri: 填充freelist的连接，默认填充20个<br /> * @param: __n，填充对象的大小，8字节对齐后的value<br /> * @return: 空闲<br /> */<br />static void* refill(size_t n)<br />{<br />     int __nobjs = 20;<br />     char* __chunk = (char*)chunk_alloc(n, &amp;__nobjs);<br />     _obj *VOLATILE* __my_free_list;<br />     _obj *VOLATILE* __my_free_list1;<br />     _obj * __result;<br />     _obj * __current_obj;<br />     _obj * __next_obj;<br />     int __i;<br />     // 如果内存池中仅有一个对象<br />     if (1 == __nobjs) <br />         return(__chunk);<br />     __my_free_list = _free_list + freelist_index(n);<br />     /* Build free list in chunk */<br />     __result = (_obj*)__chunk;<br />     *__my_free_list = __next_obj = (_obj*)(__chunk + n);<br />     __my_free_list1 = _free_list + freelist_index(n);<br />     for (__i = 1;; ++__i)<br />     {<br />         __current_obj = __next_obj;<br />         __next_obj = (_obj*)((char*)__next_obj+n);<br />         if(__nobjs - 1 == __i)<br />         {<br />              __current_obj-&gt;free_list_link = 0;<br />              break;<br />         }else{<br />              __current_obj-&gt;free_list_link = __next_obj;<br />         }<br />     }<br />     return(__result);<br />}<br /></p>
				<p>经过上面操作后，内存池可能会成为如下的一种状态。从图上我们可以看到，已经构建了8，24，88，128字节的空闲分配链表，而其他没有分配空闲分配链表的他们的指针都指向NULL。我们通过判断索引表中的指针是否为NULL，知道是否已经构建空闲分配表或者空闲分配表是否用完，如果此处指针为NULL，我们调用refill函数，重新申请20个这样大小的内存空间，并把他们连接起来。在refill函数内，我们要查看大内存中是否有可用内存，如果有，并且大小合适，就返回给refill函数。<br /></p>
				<p align="center">
						<a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxg" target="_blank">
								<img alt="" src="http://album.sina.com.cn/pic/3fe3099c02000pxg" border="0" />
						</a>
						<br />图4</p>
				<p> </p>
				<p>    6：线程安全<br />    采用互斥体，保证线程安全。<br /><br />内存池测试</p>
				<p>    内存池的测试主要分两部分测试1：单线程下malloc与mempool的分配速度对比2：多线程下malloc和mempool的分配速度对比，我们分为4，10，16个线程进行测试了。<br />    测试环境：操作系统：windows2003+sp1，VC7.1+sp1，硬件环境：intel(R) Celeron(R) CPU 2.53GHz,512M物理内存。<br /><br />    申请内存空间设定如下<br />#define ALLOCNUMBER0 4<br />#define ALLOCNUMBER1 7<br />#define ALLOCNUMBER2 23<br />#define ALLOCNUMBER3 56<br />#define ALLOCNUMBER4 10<br />#define ALLOCNUMBER5 60<br />#define ALLOCNUMBER6 5<br />#define ALLOCNUMBER7 80<br />#define ALLOCNUMBER8 9<br />#define ALLOCNUMBER9 100</p>
				<p>    Malloc方式和mempool方式均使用如上数据进行内存空间的申请和释放。申请过程，每次循环申请释放上述数据20次<br />    我们对malloc和mempool，分别进行了如下申请次数的测试（单位为万）</p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="0" width="325" border="0">
						<tbody>
								<tr>
										<td nowrap="" width="25">
												<p align="right">2</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">10</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">20</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">30</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">40</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">50</p>
										</td>
										<td nowrap="" width="30">
												<p align="right">80</p>
										</td>
										<td nowrap="" width="38">
												<p align="right">100</p>
										</td>
										<td nowrap="" width="38">
												<p align="right">150</p>
										</td>
										<td nowrap="" width="41">
												<p align="right">200</p>
										</td>
								</tr>
						</tbody>
				</table>malloc和mempool在单线程，多线程，release，debug版的各种测试数据，形成如下的统计图 
<p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxh" target="_blank"></a><br />图5</p><p>可以看到mempool无论在多线程还是在单线程情况下，mempool的速度都优于malloc方式的直接分配。</p><p>    Malloc方式debug模式下，在不同的线程下，运行时间如下，通过图片可知，malloc方式，在debug模式下，申请空间的速度和多线程的关系不大。多线程方式，要略快于单线程的运行实现。</p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxi" target="_blank"></a><br />图6</p><p>    Malloc方式release模式测试结果如下。</p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxj" target="_blank"></a><br />图7</p><p>多线程的优势，逐渐体现出来。当执行200w次申请和释放时，多线程要比单线程快1500ms左右，而4，10，16个线程之间的差别并不是特别大。不过整体感觉4个线程的运行时间要稍微高于10，16个线程的情况下，意味着进程中线程越多用在线程切换上的时间就越多。</p><p>下面是mempool在debug测试结果<br /></p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxk" target="_blank"></a><br />图8</p><p>    下面是mempool在release模式下的测试结果</p><p align="center"><a href="http://blog.sina.com.cn/main/html/showpic.html#url=http://album.sina.com.cn/pic/3fe3099c02000pxl" target="_blank"></a><br />图9</p><p>    以上所有统计图中所用到的数据，是我们测试三次后平均值。</p><p>通过上面的测试，可以知道mempool的性能基本上超过直接malloc方式，在200w次申请和释放的情况下，单线程release版情况下，mempool比直接malloc快110倍。而在4个线程情况下，mempool要比直接malloc快7倍左右。以上测试只是申请速度的测试，在不同的压力情况下，测试结果可能会不同，测试结果也不能说明mempool方式比malloc方式稳定。<br /><br />    小结：内存池基本上满足初期设计目标，但是她并不是完美的，有缺陷，比如,不能申请大于256字节的内存空间，无内存越界检查，无内存自动回缩功能等。只是这些对我们的影响还不是那么重要。</p><p>由于这是一个公司项目，代码涉及版权，所以不能发布出来。如果你想做自己的内存池，可以与我联系ugg_xchj#hotmail.com.</p></div>
<img src ="http://www.blogjava.net/huyi2006/aggbug/192023.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-04-10 23:26 <a href="http://www.blogjava.net/huyi2006/articles/192023.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[总结]软件工程师笔试题目（C++） </title><link>http://www.blogjava.net/huyi2006/articles/191083.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sun, 06 Apr 2008 11:02:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/191083.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/191083.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/191083.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/191083.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/191083.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 发布之前先申明两点：    1 所有资料来自网络(主要是CSDN)，本人只是收集和转发。    2 所有问题解答(尤其是代码)只是参考，不保证正确。		先发基本问题，再发编程问题..........4.    static有什么用途？（请至少说明两种）1.限制变量的作用域2.设置变量的存储域7.    引用与指针有什么区别？1) 引用必须被初始化，指针不必。2) 引用初始化以后不能被改变，指针...&nbsp;&nbsp;<a href='http://www.blogjava.net/huyi2006/articles/191083.html'>阅读全文</a><img src ="http://www.blogjava.net/huyi2006/aggbug/191083.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-04-06 19:02 <a href="http://www.blogjava.net/huyi2006/articles/191083.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>cache和volatie变量的关系</title><link>http://www.blogjava.net/huyi2006/articles/189444.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 29 Mar 2008 05:30:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/189444.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/189444.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/189444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/189444.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/189444.html</trackback:ping><description><![CDATA[有volatie修饰的变量，每次操作时遵循下面动作： <br />从内存取值 ---&gt; 放入寄存器 ----&gt; 操作 ----&gt;写回内存 <br />没有volatie修饰的变量，操作<b>可能</b>遵循（可能就是不是所有情况都如此）： <br />从内存取值 ---&gt; 放入寄存器 ----&gt; 第一次操作 -----&gt; 第二次操作（此时仍操作寄存器中的值） …… ----&gt;第N次操作 ----&gt;写回内存 <br /><br /><br />volatile变量的修改要求即时被所有cpu可见，所以会要求cache一致性。对一些weak memory model的cpu，还有对memory access order的限制。<br /><img src ="http://www.blogjava.net/huyi2006/aggbug/189444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-03-29 13:30 <a href="http://www.blogjava.net/huyi2006/articles/189444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>二维数组传递参数</title><link>http://www.blogjava.net/huyi2006/articles/187992.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sun, 23 Mar 2008 02:02:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/187992.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/187992.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/187992.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/187992.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/187992.html</trackback:ping><description><![CDATA[
		<p>二维数组的传参<br />二级数组传递参数时，不能以一个二级指针的方式，以二级指针的方式只传递了这个二级数组的首地址，但没有带出这个二维数组各元素的地址，编译器不能通过二级指针的移动寻找其元素。对于二级数组要用数组指针的形式传参</p>
		<p>#include &lt;stdio.h&gt;<br />void print_arr (char (*a)[4])<br />{<br />    int i;<br />    for(i = 0; i &lt; 3; i++)<br />    printf("%s\n", a[i]);<br />}</p>
		<p>int main()<br />{<br />    char *str_arr[3][4] = {"yes", "no", "uncertain"};<br />    print_arr (str_arr);<br />    return 0;<br />}</p>
		<p>如果要以二级指针的形式传参，可以在定义为字符串指针数组的形式<br />char *str_arr[] = {"yes", "no", "uncertain"};<br />void print_arr (char **a)<br />{<br />    int i;<br />    for (i=0; i&lt;3;i++)<br />    printf("%s\n", *(a+1));<br />}<br /></p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/187992.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-03-23 10:02 <a href="http://www.blogjava.net/huyi2006/articles/187992.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络套接字结构体</title><link>http://www.blogjava.net/huyi2006/articles/187777.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 21 Mar 2008 11:13:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/187777.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/187777.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/187777.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/187777.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/187777.html</trackback:ping><description><![CDATA[netdb.h:<br /><br />/* Description of data base entry for a single host. */<br />struct hostent<br />{<br />char *h_name; /* Official name of host. */<br />char **h_aliases; /* Alias list. */<br />int h_addrtype; /* Host address type. */<br />int h_length; /* Length of address. */<br />char **h_addr_list; /* List of addresses from name server. */<br />#define h_addr h_addr_list[0] /* Address, for backward compatibility. */<br />};<br /><br /><br />struct sockaddr{<br />        unisgned short  as_family;<br />        char            sa_data[14];<br />};<br /><br />struct sockaddr_in{<br />        unsigned short          sin_family;     <br />         unsigned short int      sin_port;<br />         struct in_addr          sin_addr;<br />         unsigned char           sin_zero[8];<br />}<br /><br />typedef   uint32_t   in_addr_t;   <br />struct   in_addr   <br />{   <br />        in_addr_t   s_addr;   <br />};<br /><br /><br /><img src ="http://www.blogjava.net/huyi2006/aggbug/187777.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-03-21 19:13 <a href="http://www.blogjava.net/huyi2006/articles/187777.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探讨C语言中的回调函数</title><link>http://www.blogjava.net/huyi2006/articles/180169.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 16 Feb 2008 04:00:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/180169.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/180169.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/180169.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/180169.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/180169.html</trackback:ping><description><![CDATA[ 回调函数是一个程序员不能显式调用的函数；通过将回调函数的地址传给 <br />调用者从而实现调用。回调函数使用是必要的，在我们想通过一个统一接口实现不 <br />同的内容，这时用回掉函数非常合适。比如，我们为几个不同的设备分别写了不同 <br />的显示函数：void TVshow(); void ComputerShow(); void NoteBookShow()...等 <br />等。这是我们想用一个统一的显示函数，我们这时就可以用回掉函数了。void sho <br />w(void (*ptr)()); 使用时根据所传入的参数不同而调用不同的回调函数。 <br />      不同的编程语言可能有不同的语法，下面举一个c语言中回调函数的例子， <br />其中一个回调函数不带参数，另一个回调函数带参数。 <br />      例子1： <br />//Test.c <br />#include &lt;stdlib.h&gt; <br />#include &lt;stdio.h&gt; <br />int Test1() <br />{ <br />  int i; <br />  for (i=0; i&lt;30; i++) <br />  { <br />    printf("The %d th charactor is: %c\n", i, (char)('a' + i%26)); <br /><br />  } <br />  return 0; <br />} <br />int Test2(int num) <br />{ <br />  int i; <br />  for (i=0; i&lt;num; i++) <br />  { <br />   printf("The %d th charactor is: %c\n", i, (char)('a' + i%26)); <br /><br />  } <br />  return 0; <br />} <br />void Caller1(void (*ptr)())//指向函数的指针作函数参数 <br />{ <br />  (*ptr)(); <br />} <br />void Caller2(int n, int (*ptr)())//指向函数的指针作函数参数,这里第一个参数是为指向函数的指针服务的， <br />{                                               //不能写成void Caller2(int (*ptr)(int n))，这样的定义语法错误。 <br />  (*ptr)(n); <br />  return; <br />} <br />int main() <br />{ <br />  printf("************************\n"); <br />  Caller1(Test1); //相当于调用Test2(); <br />  printf("&amp;&amp;&amp;&amp;&amp;&amp;************************\n"); <br />  Caller2(30, Test2); //相当于调用Test2(30); <br />  return 0; <br />} <br />      以上通过将回调函数的地址传给调用者从而实现调用，但是需要注意的是带 <br />参回调函数的用法。要实现回调，必须首先定义函数指针。函数指针的定义这里稍 <br />微提一下。比如： <br />    int (*ptr)(); 这里ptr是一个函数指针，其中(*ptr)的括号不能省略，因为 <br />括号的优先级高于星号，那样就成了一个返回类型为整型的函数声明了<img src ="http://www.blogjava.net/huyi2006/aggbug/180169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-02-16 12:00 <a href="http://www.blogjava.net/huyi2006/articles/180169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>realloc,malloc,calloc的区别及free(zz)</title><link>http://www.blogjava.net/huyi2006/articles/180131.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 15 Feb 2008 14:01:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/180131.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/180131.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/180131.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/180131.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/180131.html</trackback:ping><description><![CDATA[
		<div class="diaryContent" id="diary3461548" style="DISPLAY: block">
				<p>三个函数的申明分别是:<br />void*<strong> realloc</strong>(void* ptr, unsigned newsize);<br />void* <span style="FONT-WEIGHT: bold">malloc</span>(unsigned size);<br />void* <span style="FONT-WEIGHT: bold">calloc</span>(size_t nelem, size_t elsize);<br />都在stdlib.h函数库内<br /><br />它们的返回值都是请求系统分配的地址,如果请求失败就返回NULL<br /><br /><span style="FONT-WEIGHT: bold">malloc</span>用于申请一段新的地址,参数size为需要内存空间的长度,如:<br />char* p;<br />p=(char*)<span style="FONT-WEIGHT: bold">malloc</span>(20);<br /><br /><span style="FONT-WEIGHT: bold">calloc</span>与<span style="FONT-WEIGHT: bold">malloc</span>相似,参数nelem为申请地址的单位元素长度,elsize为元素个数,如:<br />char* p;<br />p=(char*)<span style="FONT-WEIGHT: bold">calloc</span>(sizeof(char),20);<br />这个例子与上一个效果相同<br /><br />realloc是给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度<br />如:<br />char* p;<br />p=(char*)<span style="FONT-WEIGHT: bold">malloc</span>(sizeof(char)*20);<br />p=(char*)<strong>realloc</strong>(p,sizeof(char)*40);<br /><br />注意，这里的空间长度都是以字节为单位。<br /></p>
				<p>C语言的标准内存分配函数：<a name="0"></a><span style="FONT-WEIGHT: bold">malloc</span>，<a name="1"></a><span style="FONT-WEIGHT: bold">calloc</span>，realloc，free等。<br /><span style="FONT-WEIGHT: bold">malloc</span>与<span style="FONT-WEIGHT: bold">calloc</span>的区别为1块与n块的区别：<br /><span style="FONT-WEIGHT: bold">malloc</span>调用形式为(类型*)<span style="FONT-WEIGHT: bold">malloc</span>(size)：在内存的动态存储区中分配一块长度为“size”字节的连续区域，返回该区域的首地址。<br /><span style="FONT-WEIGHT: bold">calloc</span>调用形式为(类型*)<span style="FONT-WEIGHT: bold">calloc</span>(n，size)：在内存的动态存储区中分配n块长度为“size”字节的连续区域，返回首地址。<br /><strong>realloc</strong>调用形式为(类型*)<strong>realloc</strong>(*ptr，size)：将ptr内存大小增大到size。<br /><strong>free</strong>的调用形式为<strong>free</strong>(void*ptr)：释放ptr所指向的一块内存空间。<br />C++中为new/delete函数。</p>
		</div>
<img src ="http://www.blogjava.net/huyi2006/aggbug/180131.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-02-15 22:01 <a href="http://www.blogjava.net/huyi2006/articles/180131.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux C编程——线程同步</title><link>http://www.blogjava.net/huyi2006/articles/180025.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 15 Feb 2008 02:15:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/180025.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/180025.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/180025.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/180025.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/180025.html</trackback:ping><description><![CDATA[
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt">使用线程编写程序需要技巧，而多线程的程序中的bug非常难以跟踪、调试，因为这些bug经常是难以再现的。<br /><strong><br />竞争条件</strong>：<br />当一个线程访问一个数据结构的时候，另一个线程也访问同一个数据结构，这时就出现了竞争条件——两个线程（也可能是多个）竞争对同一个资源的访问。<br />当其中一个线程处理到一部分的时候，另外的线程可能进入了对同一数据的处理，而且出于调度的原因，它运行的比前一个更快；这时，同样的处理可能就出现了多次。<br />例如，一个代表任务列表的单向链表（队列），一个线程从当前元素中读出了下一个任务，并发现下一个任务不是空，准备将此一个任务置为NULL并执行任务；这时，调度使得这个线程停下来，另一个线程也从当前元素中读出了下一个任务，当然下个任务仍然不是空值，这个线程也将要执行下一个任务。这样，在某些不幸的情况下，这个任务被执行了两次。<br />更为不幸的情况下，执行任务的过程中线程被中断，此时另一个线程释放了任务的内存，那么执行中的线程会导致段错误。<br />在比较“幸运”的情况下，这些事情可能从来也不发生；但是当程序运行在负荷很高的系统中时，这个bug就会凸现出来。<br /><br /><strong>互斥锁</strong>：<br />为了排除竞争条件的影响，应该使一些操作变成“原子的”——这些操作既不能分割也不能中断。<br />当一个线程锁定了互斥锁后，其他线程也要求锁定这个互斥锁的时候，就会被阻塞；直到前面的线程解除锁定后，其他线程才可以解除阻塞恢复运行。<br />GNU/Linux保证线程在锁定互斥锁的过程中不会发生竞争条件，只有一个线程可以锁定互斥锁，其他线程的锁定请求都会被阻塞。<br /><br />创建互斥锁（Mutex），需要：<br />｀创建一个pthread_mutex_t类型的变量，将其指针传入函数pthread_mutex_init中；该函数的第二个参数是指向一个mutex属性对象（这个对象用来指定mutex的属性）的指针。mutex对象只能初始化一次。<br />｀更简单的办法是，使用PTHREAD_MUTEX_INITIALIZER来获得默认属性的mutex对象：<br />pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;<br /><br />使用mutex：<br />｀调用pthread_mutex_lock函数，如果mutex对象未被锁定，则此函数立即返回；如果已被锁定，则此函数阻塞此线程的执行，直到mutex被解除锁定。<br />｀每次解除锁定后，只有一个线程可以解除阻塞恢复执行，其他调用线程都会继续阻塞。选定的解除阻塞的线程是不可预知的。<br />｀调用pthread_mutex_unlock函数，可以解除调用线程对mutex对象的锁定。在锁定mutex的线程中，必须调用此函数以解除锁定。<br /><br />mutex提供了解决竞争条件的方案，但是它带来了一种新的bug——<strong>死锁</strong>（<strong>deadlock</strong>）。<br />所谓死锁，就是说一个线程在等待永远不会发生的条件。<br />一个简单的死锁：一个线程锁定它自己已经锁定的mutex。这时程序的表现依赖于mutex的类型：<br />｀快速排他锁（fast mutex）——导致死锁。<br />｀递归排他锁（recursive mutex）——不导致死锁。同一个线程可以安全的多次锁定同一个已锁定的mutex，但是锁定的次数会被记录下来，该线程还必须调用相应次数的pthread_mutex_unlock才能真正解除对mutex的锁定。<br />｀检错排他锁（error-checking mutex）——GNU/Linux视此操作为死锁，但是对锁定的mutex调用pthread_mutex_lock函数，函数立即返回错误码EDEADLK。<br /><br />mutex的锁定函数会阻塞，有时需要一个不阻塞就能知道mutex是否已锁定的函数，以在发现mutex已锁的情况下去完成其他工作并在以后再来检查。这样的函数是：<br />pthread_mutex_trylock<br />如果传入的mutex已经被其他线程锁定，那么这个函数返回错误码EBUSY；如果未被锁定，此函数会锁定mutex，并返回0。这个函数不会阻塞。</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/huyi2006/aggbug/180025.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-02-15 10:15 <a href="http://www.blogjava.net/huyi2006/articles/180025.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[原]linux下调试C程序中的段错误，死循环经验。</title><link>http://www.blogjava.net/huyi2006/articles/178436.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Tue, 29 Jan 2008 16:45:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/178436.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/178436.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/178436.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/178436.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/178436.html</trackback:ping><description><![CDATA[    在linux下进行C程序时，难免会出现这里那里的错误，对于像段错误的调试，对于生手可能比较麻烦，无从下手，我想这是一个C程序员的必经的经历。一旦出现这样的错误是相当棘手的。相信调试过几次样的错误后再次遇到就能很轻松的解决了。<br />    段错误的产生主要是由于内存操作有误，如访问的数组越界，向空地址赋值，取值等。调试段错误是要借助gdb调试工具。对于那些立即出现的错误很好调试，如果是很时间才出现的错误，就可以通过生成core文件。通过调试core文件定位错误产生的地方，而有些错误并非是一眼能看出来。像在多线程情况下，在没有保护的情况下操作数据库时也会产生段错误。一个不可重入函数在单线程下运行没有问题。可能到多线程情况下就会出现越界的情况。因而意识到多线程下的并发机制。<br />    对于死循环的调试，也可以借助gdb，当程序出现死循环时，将gdb attach到程序上，如果 是主进程序出现死循环，可以通过设断点方法跟踪，而对于多线程程序可以通过查看哪个线程在运行，基本上能定位错误出现的地方。<img src ="http://www.blogjava.net/huyi2006/aggbug/178436.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-01-30 00:45 <a href="http://www.blogjava.net/huyi2006/articles/178436.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是core文件，怎么产生core文件 ----from csdn</title><link>http://www.blogjava.net/huyi2006/articles/176851.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Mon, 21 Jan 2008 14:18:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/176851.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/176851.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/176851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/176851.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/176851.html</trackback:ping><description><![CDATA[
		<p>1. 前言:<br />有的程序可以通过编译, 但在运行时会出现Segment fault(段错误). 这通常都是指针错误引起的.<br />但这不像编译错误一样会提示到文件-&gt;行, 而是没有任何信息, 使得我们的调试变得困难起来.<br /><br />2. gdb:<br />有一种办法是, 我们用gdb的step, 一步一步寻找. <br />这放在短小的代码中是可行的, 但要让你step一个上万行的代码, 我想你会从此厌恶程序员这个名字, 而把他叫做调试员.<br />我们还有更好的办法, 这就是core file.<br /><br />3. ulimit:<br />如果想让系统在信号中断造成的错误时产生core文件, 我们需要在shell中按如下设置:<br /><span class="Code">#设置core大小为无限<br />ulimit -c unlimited<br />#设置文件大小为无限<br />ulimit unlimited</span><br /><br />这些需要有root权限, 在ubuntu下每次重新打开中断都需要重新输入上面的第一条命令, 来设置core大小为无限.<br /><br />4. 用gdb查看core文件:<br />下面我们可以在发生运行时信号引起的错误时发生core dump了.<br />发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.<br /><span class="Code">gdb [exec file] [core file]</span><br />如:<br /><span class="Code">gdb ./test test.core</span><br />在进入gdb后, 用bt命令查看backtrace以检查发生程序运行到哪里, 来定位core dump的文件-&gt;行.</p>
		<p id="TBPingURL">Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1669532</p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/176851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2008-01-21 22:18 <a href="http://www.blogjava.net/huyi2006/articles/176851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>程序实例分析-为什么会陷入死循环</title><link>http://www.blogjava.net/huyi2006/articles/170368.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Tue, 25 Dec 2007 10:18:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/170368.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/170368.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/170368.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/170368.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/170368.html</trackback:ping><description><![CDATA[
		<font size="2">看似简单的一段程序如下:<br /> int main()<br />{<br />   int i,j[8];<br />   for(i=0;i&lt;=8;i++)<br />    j[i]=0;<br />   return 0;<br />} <br />    gcc编译运行会陷入死循环.  <br /> <br />    因为变量 i 和数组 j[8]是保存在栈中，默认是由高地址向低地址方向存储. 输出变量地址可以发现: i 存储位置在0xbfd90dec, j[0]、j[1]...j[7]在内存的地址分别是0xbfdab05c、0xbfdab060,...0xbfdab078. 如下所示：<br />          高地址 &lt;---------------------------------------&gt;低地址<br />                           i,j[7],j[6],j[5],j[4],j[3],j[2],j[1],j[0]<br /> <br />    如果在int i,j[8]后面再定义变量int c,那么c就存放在j[0]的往低方向的下一个地址0xbfdab058 .</font>
		<p>
				<font size="2">      现在不难理解这段程序为什么会出现死循环了。j[8]的位置就是变量i所在的位置。这样当i=8时的j[i]=0语句，实际上就是将i的值置为0，然后 i 又从0到8循环一直下去.  </font>
				<font size="2">如果将原句改为int j[8],i; 就不会出现死循环，而仅仅是一个段越界错误.</font>
		</p>
		<p>
				<font size="2">另一个程序:<br />#include &lt;stdio.h&gt;<br />int main()<br />{<br />        int i;<br />        char c;<br />        for(i=0;i&lt;5;i++)<br />        {<br />                scanf("%d",&amp;c); <br />                printf("i=%d ",i);<br />        }<br />        printf("\n");<br />}<br />编译后运行<br />[foxman@local~]#./a.out<br />0    (输入0)<br />i=0  (输出 i 值)<br />1<br />i=0<br />2<br />i=0<br />3<br />i=0<br />4<br />i=0<br />...<br />这样一直循环下去。</font>
		</p>
		<p>
				<font size="2">问题在于，c被声明为char类型，而不是int类型。当程序要求scanf读入一个整数时，应该传递给它一个指向整数的指针。而程序中scanf得到的却是一个指向字符的指针，scanf函数并不能分辨这种情况，只能将这个指向字符的指针作为指向整数的指针而接受，并且在指针指向的位置存储一个整数。因为整数所占的存储空间要大于字符所占的存储空间，所以c附近的内存会被覆盖.</font>
		</p>
		<p>
				<font size="2">由上面分析，i 和 c 是由高地址到低地址存储在栈中，这样在c所在位置尝试存储一个4字节变量，会占用比c高的3个字节(覆盖掉 i 字节的低3位)，即使 i 总是为零，一直循环下去.</font>
		</p>
		<p>
				<font size="2">如果每次输入Ctrl+D作为字符终止符不存储int到c处，那么就会输出正常i=0..4了.<br /></font>
		</p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/170368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-12-25 18:18 <a href="http://www.blogjava.net/huyi2006/articles/170368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言面试题大汇总之微软亚洲技术中心面试题</title><link>http://www.blogjava.net/huyi2006/articles/116444.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Thu, 10 May 2007 04:27:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/116444.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/116444.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/116444.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/116444.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/116444.html</trackback:ping><description><![CDATA[　1．进程和线程的差别。 <br /><br />　　线程是指进程内的一个执行单元,也是进程内的可调度实体. <br /><br />　　与进程的区别: <br /><br />　　(1)调度：线程作为调度和分配的基本单位，进程作为拥有资源的基本单位 <br /><br />　　(2)并发性：不仅进程之间可以并发执行，同一个进程的多个线程之间也可并发执行 <br /><br />　　(3)拥有资源：进程是拥有资源的一个独立单位，线程不拥有系统资源，但可以访问隶属于进程的资源. <br /><br />　　(4)系统开销：在创建或撤消进程时，由于系统都要为之分配和回收资源，导致系统的开销明显大于创建或撤消线程时的开销。 <br />　 <br />　　2.测试方法 <br /><br />　　人工测试：个人复查、抽查和会审 <br /><br />　　机器测试：黑盒测试和白盒测试 <br /><br />　　2．Heap与stack的差别。 <br /><br />　　Heap是堆，stack是栈。 <br /><br />　　Stack的空间由操作系统自动分配/释放，Heap上的空间手动分配/释放。 <br /><br />　　Stack空间有限，Heap是很大的自由存储区 <br /><br />　　C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。 <br /><br />　　程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。 <br /><br />　　3．Windows下的内存是如何管理的？ <br /><br />　　4．介绍.Net和.Net的安全性。 <br /><br />　　5．客户端如何访问.Net组件实现Web Service？ <br /><br />　　6．C/C++编译器中虚表是如何完成的？ <br /><br />　　7．谈谈COM的线程模型。然后讨论进程内/外组件的差别。 <br /><br />　　8．谈谈IA32下的分页机制 <br /><br />　　小页(4K)两级分页模式，大页(4M)一级 <br /><br />　　9．给两个变量，如何找出一个带环单链表中是什么地方出现环的？ <br /><br />　　一个递增一，一个递增二，他们指向同一个接点时就是环出现的地方 <br /><br />　　10．在IA32中一共有多少种办法从用户态跳到内核态？ <br /><br />　　通过调用门，从ring3到ring0，中断从ring3到ring0，进入vm86等等 <br /><br />　　11．如果只想让程序有一个实例运行，不能运行两个。像winamp一样，只能开一个窗口，怎样实现？ <br /><br />　　用内存映射或全局原子（互斥变量）、查找窗口句柄.. <br /><br />　　FindWindow，互斥，写标志到文件或注册表,共享内存。.　 <br /><br />　　12．如何截取键盘的响应，让所有的‘a’变成‘b’？ <br /><br />　　键盘钩子SetWindowsHookEx <br />　 <br />　　13．Apartment在COM中有什么用？为什么要引入？ <br />　 <br />　　14．存储过程是什么？有什么用？有什么优点？ <br /><br />　　我的理解就是一堆sql的集合，可以建立非常复杂的查询，编译运行，所以运行一次后，以后再运行速度比单独执行SQL快很多 <br />　 <br />　　15．Template有什么特点？什么时候用？ <br /><br />　　16．谈谈Windows DNA结构的特点和优点。 <br /><br />　　17.网络编程中设计并发服务器，使用多进程 与 多线程 ，请问有什么区别？ <br /><br />　　1，进程：子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。 <br /><br />　　2，线程：相对与进程而言，线程是一个更加接近与执行体的概念，它可以与同进程的其他线程共享数据，但拥有自己的栈空间，拥有独立的执行序列。 <br /><br />　　两者都可以提高程序的并发度，提高程序运行效率和响应时间。 <br /><br />　　线程和进程在使用上各有优缺点：线程执行开销小，但不利于资源管理和保护；而进程正相反。同时，线程适合于在SMP机器上运行，而进程则可以跨机器迁移。 <br /><img src ="http://www.blogjava.net/huyi2006/aggbug/116444.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-05-10 12:27 <a href="http://www.blogjava.net/huyi2006/articles/116444.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C语言面试题大汇总之华为面试题</title><link>http://www.blogjava.net/huyi2006/articles/116428.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Thu, 10 May 2007 03:25:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/116428.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/116428.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/116428.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/116428.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/116428.html</trackback:ping><description><![CDATA[1、局部变量能否和全局变量重名？ <br /><br />　　答：能，局部会屏蔽全局。要用全局变量，需要使用"::" <br /><br />　　局部变量可以与全局变量同名，在函数内引用这个变量时，会用到同名的局部变量，而不会用到全局变量。对于有些编译器而言，在同一个函数内可以定义多个同名的局部变量，比如在两个循环体内都定义一个同名的局部变量，而那个局部变量的作用域就在那个循环体内。 <br /><br />　　2、如何引用一个已经定义过的全局变量？ <br /><br />　　答：extern <br /><br />　　可以用引用头文件的方式，也可以用extern关键字，如果用引用头文件方式来引用某个在头文件中声明的全局变理，假定你将那个变写错了，那么在编译期间会报错，如果你用extern方式引用时，假定你犯了同样的错误，那么在编译期间不会报错，而在连接期间报错。 <br /><br />　　3、全局变量可不可以定义在可被多个.C文件包含的头文件中？为什么？ <br /><br />　　答：可以，在不同的C文件中以static形式来声明同名全局变量。 <br /><br />　　可以在不同的C文件中声明同名的全局变量，前提是其中只能有一个C文件中对此变量赋初值，此时连接不会出错 <br /><br />　　4、语句for( ；1 ；)有什么问题？它是什么意思？ <br /><br />　　答：和while(1)相同。 <br /><br />　　5、do……while和while……do有什么区别？ <br /><br />　　答：前一个循环一遍再判断，后一个判断以后再循环 <br /><br />　　6、请写出下列代码的输出内容 <br /><br />　　#include&lt;stdio.h&gt; <br />　　main() <br />　　{ <br />　　　int a,b,c,d; <br />　　　a=10; <br />　　　b=a++; <br />　　　c=++a; <br />　　　d=10*a++; <br />　　　printf("b，c，d：%d，%d，%d"，b，c，d）; <br />　　　return 0; <br />　　} <br /><br />　　答：10，12，120 <br /><br />　　7、static全局变量与普通的全局变量有什么区别？static局部变量和普通局部变量有什么区别？static函数与普通函数有什么区别？ <br /><br />　　全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式， 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序， 当一个源程序由多个源文件组成时，非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域， 即只在定义该变量的源文件内有效， 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内，只能为该源文件内的函数公用， 因此可以避免在其它源文件中引起错误。 <br /><br />　　从以上分析可以看出， 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域， 限制了它的使用范围。 <br /><br />　　static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static)，内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数，应该在一个头文件中说明，要使用这些函数的源文件要包含这个头文件 <br /><br />　　static全局变量与普通的全局变量有什么区别：static全局变量只初使化一次，防止在其他文件单元中被引用; <br /><br />　　static局部变量和普通局部变量有什么区别：static局部变量只被初始化一次，下一次依据上一次结果值； <br /><br />　　static函数与普通函数有什么区别：static函数在内存中只有一份，普通函数在每个被调用中维持一份拷贝 <br /><br />　　8、程序的局部变量存在于（堆栈）中，全局变量存在于（静态区 ）中，动态申请数据存在于（ 堆）中。 <br /><br />　　9、设有以下说明和定义： <br /><br />　　typedef union {long i; int k[5]; char c;} DATE; <br />　　struct data { int cat; DATE cow; double dog;} too; <br />　　DATE max; <br /><br />　　则语句 printf("%d",sizeof(struct date)+sizeof(max));的执行结果是：___52____ <br /><br />　　答：DATE是一个union, 变量公用空间. 里面最大的变量类型是int[5], 占用20个字节. 所以它的大小是20 <br />data是一个struct, 每个变量分开占用空间. 依次为int4 + DATE20 + double8 = 32. <br />所以结果是 20 + 32 = 52. <br /><br />　　当然...在某些16位编辑器下, int可能是2字节,那么结果是 int2 + DATE10 + double8 = 20 <br /><br />　　10、队列和栈有什么区别？ <br />　　 <br />　　队列先进先出，栈后进先出 <br /><br />　　11、写出下列代码的输出内容 <br /><br />　　#include&lt;stdio.h&gt; <br />　　int inc(int a) <br />　　{ <br />　　　return(++a); <br />　　} <br />　　int multi(int*a,int*b,int*c) <br />　　{ <br />　　　return(*c=*a**b); <br />　　} <br />　　typedef int(FUNC1)(int in); <br />　　typedef int(FUNC2) (int*,int*,int*); <br /><br />　　void show(FUNC2 fun,int arg1, int*arg2) <br />　　{ <br />　　　INCp=&amp;inc; <br />　　　int temp =p(arg1); <br />　　　fun(&amp;temp,&amp;arg1, arg2); <br />　　　printf("%d\n",*arg2); <br />　　} <br /><br />　　main() <br />　　{ <br />　　　int a; <br />　　　show(multi,10,&amp;a); <br />　　　return 0; <br />　　} <br /><br />　　答：110 <br /><br />　　12、请找出下面代码中的所以错误 <br /><br />　　说明：以下代码是把一个字符串倒序，如“abcd”倒序后变为“dcba” <br /><br />　　1、#include"string.h" <br />　　2、main() <br />　　3、{ <br />　　4、 char*src="hello,world"; <br />　　5、 char* dest=NULL; <br />　　6、 int len=strlen(src); <br />　　7、 dest=(char*)malloc(len); <br />　　8、 char* d=dest; <br />　　9、 char* s=src[len]; <br />　　10、 while(len--!=0) <br />　　11、 d++=s--; <br />　　12、 printf("%s",dest); <br />　　13、 return 0; <br />　　14、} <br /><br />　　答： <br /><br />　　方法1： <br /><br />　　int main() <br />　　{ <br />　　　char* src = "hello,world"; <br />　　　int len = strlen(src); <br />　　　char* dest = (char*)malloc(len+1);//要为\0分配一个空间 <br />　　　char* d = dest; <br />　　　char* s = &amp;src[len-1];//指向最后一个字符 <br />　　　while( len-- != 0 ) <br />　　　*d++=*s--; <br />　　　*d = 0;//尾部要加\0 <br />　　　printf("%s\n",dest); <br />　　　free(dest);// 使用完，应当释放空间，以免造成内存汇泄露 <br />　　　return 0; <br />　　} <br /><img src ="http://www.blogjava.net/huyi2006/aggbug/116428.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-05-10 11:25 <a href="http://www.blogjava.net/huyi2006/articles/116428.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 对摄像头编程的研究小节 </title><link>http://www.blogjava.net/huyi2006/articles/116035.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Tue, 08 May 2007 12:28:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/116035.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/116035.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/116035.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/116035.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/116035.html</trackback:ping><description><![CDATA[
		<p>**//* $Id: directshow.c,v 1.22 2005/11/12 14:12:39 fuhuizhong Exp $ */ </p>
		<p>#ifndef lint <br />static char vcid[] = "$Id: directshow.c,v 1.22 2005/11/12 14:12:39 fuhuizhong Exp $"; <br />#endif /* lint */ </p>
		<p>
				<br />/**//* <br />* 作者：傅惠忠 <br />* /modified by xiaoshao_0_0 <br />*/ <br />#include &lt;unistd.h&gt; <br />#include &lt;sys/types.h&gt; <br />#include &lt;sys/stat.h&gt; <br />#include &lt;fcntl.h&gt; <br />#include &lt;stdio.h&gt; <br />#include &lt;sys/ioctl.h&gt; <br />#include &lt;sys/mman.h&gt; <br />#include &lt;stdlib.h&gt; <br />#include &lt;linux/types.h&gt; <br />#include &lt;linux/videodev.h&gt; <br />#include "jpeglib.h" <br />#include &lt;setjmp.h&gt; <br />#include &lt;SDL/SDL.h&gt; <br />#include &lt;string.h&gt; <br />#include &lt;signal.h&gt; <br />#include &lt;errno.h&gt; <br />#define USB_VIDEO "/dev/video1" </p>
		<p>SDL_Surface *screen,*img; <br />int cam_fd; <br />int width,height; <br />const int bpp = 24; <br />struct video_mmap cam_mm; <br />struct video_capability cam_cap; <br />struct video_picture cam_pic; <br />struct video_mbuf cam_mbuf; <br />struct video_window win; <br />char *cam_data = NULL; <br />int nframe; </p>
		<p>void errexit(char *msg) <br />...{ <br />fputs(msg,stderr); <br />exit(-1); <br />} </p>
		<p>void e_sig(int signum) <br />...{ <br />printf(" ERROR:signal %d ",signum); <br />exit(-1); <br />} </p>
		<p>void setup_sighandler() <br />...{ <br />signal(SIGTERM,&amp;e_sig); <br />signal(SIGINT,&amp;e_sig); <br />signal(SIGSEGV,&amp;e_sig); <br />signal(SIGILL,&amp;e_sig); <br />signal(SIGFPE,&amp;e_sig); <br />} </p>
		<p>void init_video(int w,int h,int bpp); /**//* bpp == bits per pixel, 暂时无效*/ <br />void init_screen(int w,int h,int bpp); /**//* like above*/ <br />void read_video(int ,int); <br />void show_img(char *pixels); <br />void window_loop(); <br />void exchange_r_b( char * f,long size); <br />void compress_to_jpeg_file( FILE *outfile, char * image_buffer,int w,int h, int quality); <br />void save_snapshot(); </p>
		<p>void free_dev() <br />...{ <br />printf("free device "); <br />close(cam_fd); <br />} </p>
		<p>int main(int argc,char *argv[]) <br />...{ <br />int i; <br />float scale = 1.0; <br />if( argc == 2 ) ...{ <br />scale = atof(argv[1]); <br />if(scale&lt;0.3 || scale&gt;1.0) <br />errexit("scale out of range (0.3 ~ 1.0) "); <br />} <br />width = (int)(640*scale); <br />height = (int)(480*scale); </p>
		<p>atexit( &amp;free_dev ); <br />init_video(width,height,bpp); <br />init_screen(width,height,bpp); <br />setup_sighandler(); </p>
		<p>window_loop(); </p>
		<p>getchar(); <br />munmap(cam_data,cam_mbuf.size); </p>
		<p>exit(0); <br />} </p>
		<p>void config_vid_pic() <br />...{ <br />char *hp = getenv("HOME"); <br />char cfpath[100]; <br />FILE *cf; <br />int ret; <br />sprintf( cfpath,"%s/.dshow.conf",hp ); <br />cf = fopen(cfpath,"r"); <br />/**//* The struct video_picture consists of the following fields </p>
		<p>brightness Picture brightness <br />hue Picture hue (colour only) <br />colour Picture colour (colour only) <br />contrast Picture contrast <br />whiteness The whiteness (greyscale only) <br />depth The capture depth (may need to match the frame buffer depth) <br />palette Reports the palette that should be used for this image </p>
		<p>The following palettes are defined </p>
		<p>VIDEO_PALETTE_GREY Linear intensity grey scale (255 is brightest). <br />VIDEO_PALETTE_HI240 The BT848 8bit colour cube. <br />VIDEO_PALETTE_RGB565 RGB565 packed into 16 bit words. <br />VIDEO_PALETTE_RGB555 RGV555 packed into 16 bit words, top bit undefined. <br />VIDEO_PALETTE_RGB24 RGB888 packed into 24bit words. <br />VIDEO_PALETTE_RGB32 RGB888 packed into the low 3 bytes of 32bit words. The top 8bits are undefined. <br />VIDEO_PALETTE_YUV422 Video style YUV422 - 8bits packed 4bits Y 2bits U 2bits V <br />VIDEO_PALETTE_YUYV Describe me <br />VIDEO_PALETTE_UYVY Describe me <br />VIDEO_PALETTE_YUV420 YUV420 capture <br />VIDEO_PALETTE_YUV411 YUV411 capture <br />VIDEO_PALETTE_RAW RAW capture (BT848) <br />VIDEO_PALETTE_YUV422P YUV 4:2:2 Planar <br />VIDEO_PALETTE_YUV411P YUV 4:1:1 Planar <br />*/ <br />if (ioctl(cam_fd, VIDIOCGPICT, &amp;cam_pic) &lt; 0) ...{ <br />errexit("ERROR:VIDIOCGPICT "); <br />} <br />//cam_pic.palette =VIDEO_PALETTE_RAW; <br />cam_pic.palette =VIDEO_PALETTE_RGB24; </p>
		<p>if( cf==NULL ) ...{ <br />cam_pic.brightness = 44464; <br />cam_pic.hue = 36000; <br />cam_pic.colour = 0; <br />cam_pic.contrast = 43312; <br />cam_pic.whiteness = 13312; <br />cam_pic.depth = 24; <br />ret = ioctl( cam_fd, VIDIOCSPICT,&amp;cam_pic ); /**//*设置摄像头缓冲中voideo_picture信息*/ <br />if( ret&lt;0 ) ...{ <br />close(cam_fd); <br />errexit("ERROR: VIDIOCSPICT,Can't set video_picture format "); <br />} <br />return; <br />} </p>
		<p>fscanf(cf,"%d",&amp;cam_pic.brightness); <br />fscanf(cf,"%d",&amp;cam_pic.hue); <br />fscanf(cf,"%d",&amp;cam_pic.colour); <br />fscanf(cf,"%d",&amp;cam_pic.contrast); <br />fscanf(cf,"%d",&amp;cam_pic.whiteness); <br />fclose( cf ); <br />ret = ioctl( cam_fd, VIDIOCSPICT,&amp;cam_pic ); /**//*设置摄像头缓冲中voideo_picture信息*/ <br />if( ret&lt;0 ) ...{ <br />close(cam_fd); <br />errexit("ERROR: VIDIOCSPICT,Can't set video_picture format "); <br />} </p>
		<p>} </p>
		<p>void init_video(int w,int h,int bpp) /**//* bpp == bytes per pixel*/ <br />...{ <br />int ret; </p>
		<p>cam_fd = open( USB_VIDEO, O_RDWR ); <br />if( cam_fd&lt;0 ) <br />errexit("Can't open video device "); </p>
		<p>ret = ioctl( cam_fd,VIDIOCGCAP,&amp;cam_cap ); <br />/**//* 摄像头的基本信息 <br />struct video_capability cam_cap; <br />name[32] Canonical name for this interface <br />type Type of interface <br />channels Number of radio/tv channels if appropriate <br />audios Number of audio devices if appropriate <br />maxwidth Maximum capture width in pixels <br />maxheight Maximum capture height in pixels <br />minwidth Minimum capture width in pixels <br />minheight Minimum capture height in pixels <br />*/ <br />if( ret&lt;0 ) ...{ <br />errexit("Can't get device information: VIDIOCGCAP "); <br />} </p>
		<p>print_device_info(); </p>
		<p>/**//* The struct video_window contains the following fields. </p>
		<p>x The X co-ordinate specified in X windows format. <br />y The Y co-ordinate specified in X windows format. <br />width The width of the image capture. <br />height The height of the image capture. <br />chromakey A host order RGB32 value for the chroma key. <br />flags Additional capture flags. <br />clips A list of clipping rectangles. (Set only) <br />clipcount The number of clipping rectangles. (Set only) <br />*/ <br />if( ioctl(cam_fd,VIDIOCGWIN,&amp;win)&lt;0 ) ...{ <br />errexit("ERROR:VIDIOCGWIN "); <br />} <br />win.x = 0; <br />win.y = 0; <br />win.width=width; <br />win.height=height; <br />if (ioctl(cam_fd, VIDIOCSWIN, &amp;win) &lt; 0) ...{ <br />errexit("ERROR:VIDIOCSWIN "); <br />} </p>
		<p>config_vid_pic(); </p>
		<p>ret = ioctl(cam_fd,VIDIOCGMBUF,&amp;cam_mbuf); <br />/**//* <br />struct video_mbuf <br />{ <br />int size; Total memory to map <br />int frames; Frames <br />int offsets[VIDEO_MAX_FRAME]; <br />}; <br />*/ </p>
		<p>/**//*struct video_buffer. <br />视频缓存的信息读取结构，设定也是一样的结构。但是一般是X自己设定，你只要读取就好了。 <br />void *base Base physical address of the buffer <br />int height Height of the frame buffer <br />int width Width of the frame buffer <br />int depth Depth of the frame buffer <br />int bytesperline Number of bytes of memory between the start of two adjacent lines*/ </p>
		<p>if( ret&lt;0 ) ...{ <br />errexit("ERROR:VIDIOCGMBUF,Can't get video_mbuf "); <br />} <br />printf("Frames:%d ",cam_mbuf.frames); <br />nframe = cam_mbuf.frames; <br />cam_data = (char*)mmap(0, cam_mbuf.size, PROT_READ|PROT_WRITE,MAP_SHARED,cam_fd,0); <br />if( cam_data == MAP_FAILED ) ...{ <br />errexit("ERROR:mmap "); <br />} <br />printf("Buffer size:%d Offset:%d ",cam_mbuf.size,cam_mbuf.offsets[0]); <br />} </p>
		<p>void print_device_info() <br />...{ <br />int type=cam_cap.type; <br />int i; <br />char type_info[14][100]= <br />...{ <br />"VID_TYPE_CAPTURE Can capture to memory", <br />"VID_TYPE_TUNER Has a tuner of some form", <br />"VID_TYPE_TELETEXT Has teletext capability", <br />"VID_TYPE_OVERLAY Can overlay its image onto the frame buffer", <br />"VID_TYPE_CHROMAKEY Overlay is Chromakeyed", <br />"VID_TYPE_CLIPPING Overlay clipping is supported", <br />"VID_TYPE_FRAMERAM Overlay overwrites frame buffer memory", <br />"VID_TYPE_SCALES The hardware supports image scaling", <br />"VID_TYPE_MONOCHROME Image capture is grey scale only", <br />"VID_TYPE_SUBCAPTURE Capture can be of only part of the image", <br />"VID_TYPE_MPEG_DECODER Can decode MPEG streams", <br />"VID_TYPE_MPEG_ENCODER Can encode MPEG streams", <br />"VID_TYPE_MJPEG_DECODER Can decode MJPEG streams", <br />"VID_TYPE_MJPEG_ENCODER Can encode MJPEG streams", <br />}; <br />printf("Device name:%s Width:%d ~ %d Height:%d ~ %d ", <br />cam_cap.name, <br />cam_cap.maxwidth, cam_cap.minwidth, <br />cam_cap.maxheight, cam_cap.minheight); <br />for(i=0;i&lt;14;i++) <br />...{ <br />if(type&amp;(2^i)) <br />...{ printf(type_info[i]); <br />printf(" "); <br />} <br />else <br />...{ <br />printf("==not supported==%s",type_info[i]); <br />printf(" "); <br />} <br />} <br />} </p>
		<p>void init_screen(int w,int h,int bpp) // like above <br />...{ <br />if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) &lt; 0 ) ...{ <br />fprintf(stderr, "无法初始化SDL: %s ", SDL_GetError()); <br />exit(1); <br />} <br />img = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, bpp, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000); <br />screen = SDL_SetVideoMode(width, height, bpp, SDL_SWSURFACE); <br />if ( screen == NULL ) ...{ <br />fprintf(stderr, "无法设置视频模式：%s ", SDL_GetError()); <br />exit(1); <br />} <br />atexit(SDL_Quit); <br />} </p>
		<p>void read_video(int captrue_frame,int sync_frame) <br />...{ <br />int ret; <br />//int frame=0; /*这个似乎比较关键*/ <br />//这个地方原作者弄错了。 <br />cam_mm.frame=captrue_frame; <br />/**//* struct video_mmap <br />{ <br />unsigned int frame; Frame (0 - n) for double buffer <br />int height,width; <br />unsigned int format; should be VIDEO_PALETTE_* <br />}; <br />*/ <br />ret = ioctl(cam_fd,VIDIOCMCAPTURE,&amp;cam_mm); <br />if( ret&lt;0 ) ...{ <br />errexit("ERROR: VIDIOCMCAPTURE "); <br />} <br />//cam_mm.frame=sync_frame; <br />ret = ioctl(cam_fd,VIDIOCSYNC,&amp;sync_frame); <br />if( ret&lt;0 ) ...{ <br />errexit("ERROR: VIDIOCSYNC "); <br />} <br />} </p>
		<p>void show_img(char *pixels) <br />...{ <br />int row_stride = width*3; <br />char *pbuf = (char*)img-&gt;pixels; <br />int row; </p>
		<p>/**//* for(row=0; row&lt;height; row++) { <br />memcpy(pbuf, pixels, row_stride); <br />pbuf += img-&gt;pitch; <br />pixels += row_stride; <br />}*/ <br />memcpy(pbuf,pixels,row_stride*height); <br />SDL_BlitSurface(img, NULL, screen, NULL); <br />SDL_UpdateRect(screen,0,0,width,height); <br />} </p>
		<p>void window_loop() <br />...{ <br />int ret; <br />SDL_Event event; <br />int keystat = 0; <br />Uint32 ticks = 0; <br />SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,1000); <br />/**/////////////// <br />cam_mm.height = height; <br />cam_mm.width = width; <br />cam_mm.frame=0; <br />cam_mm.format=VIDEO_PALETTE_RGB24; <br />ret = ioctl(cam_fd,VIDIOCMCAPTURE,&amp;cam_mm); </p>
		<p>if( ret&lt;0 ) ...{ <br />errexit("ERROR: VIDIOCMCAPTURE "); <br />} <br />//Here just start caputer frame0,it not in the loop. <br />/**/////////////// <br />while ( 1 ) ...{ <br />SDL_PollEvent(&amp;event); <br />switch (event.type) ...{ <br />case SDL_QUIT: <br />exit(0); <br />case SDL_KEYDOWN: <br />if( event.key.keysym.sym == SDLK_F8 &amp;&amp; keystat == 0 ) <br />save_snapshot(); <br />if( event.key.keysym.sym == SDLK_F9 &amp;&amp; keystat == 0 ) <br />config_vid_pic(); <br />if( event.key.keysym.sym == SDLK_UP &amp;&amp; keystat == 0 ) <br />increase_brightness(); <br />if( event.key.keysym.sym == SDLK_DOWN &amp;&amp; keystat == 0 ) <br />decrease_birghtness(); <br />if( event.key.keysym.sym == SDLK_LEFT &amp;&amp; keystat== 0 ) <br />increase_contrast(); <br />if( event.key.keysym.sym == SDLK_RIGHT &amp;&amp; keystat==0 ) <br />decrease_contrast(); <br />keystat = 1; <br />break; <br />case SDL_KEYUP: <br />keystat = 0; <br />break; <br />default: <br />break; <br />} <br />if( (SDL_GetTicks()-ticks)&lt;30) <br />continue; <br />read_video(1,0);//captrue=1,sync=0 <br />show_img( cam_data+cam_mbuf.offsets[0]); <br />read_video(0,1);//captrue=0,sync=1 <br />show_img( cam_data+cam_mbuf.offsets[1]); <br />ticks = SDL_GetTicks(); <br />} <br />} <br />void increase_brightness() <br />...{ <br />int ret=0; <br />cam_pic.brightness+=1000; <br />ret = ioctl( cam_fd, VIDIOCSPICT,&amp;cam_pic ); /**//*设置摄像头缓冲中voideo_picture信息*/ <br />if( ret&lt;0 ) ...{ <br />close(cam_fd); <br />errexit("ERROR: VIDIOCSPICT,Can't set video_picture format "); <br />} <br />} <br />void decrease_birghtness() <br />...{ <br />int ret=0; <br />cam_pic.brightness-=1000; <br />ret = ioctl( cam_fd, VIDIOCSPICT,&amp;cam_pic ); /**//*设置摄像头缓冲中voideo_picture信息*/ <br />if( ret&lt;0 ) ...{ <br />close(cam_fd); <br />errexit("ERROR: VIDIOCSPICT,Can't set video_picture format "); <br />} <br />} <br />void increase_contrast() <br />...{ <br />int ret=0; <br />cam_pic.contrast+=1000; <br />ret = ioctl( cam_fd, VIDIOCSPICT,&amp;cam_pic ); /**//*设置摄像头缓冲中voideo_picture信息*/ <br />if( ret&lt;0 ) ...{ <br />close(cam_fd); <br />errexit("ERROR: VIDIOCSPICT,Can't set video_picture format "); <br />} <br />} <br />void decrease_contrast() <br />...{ <br />int ret=0; <br />cam_pic.contrast-=1000; <br />ret = ioctl( cam_fd, VIDIOCSPICT,&amp;cam_pic ); /**//*设置摄像头缓冲中voideo_picture信息*/ <br />if( ret&lt;0 ) ...{ <br />close(cam_fd); <br />errexit("ERROR: VIDIOCSPICT,Can't set video_picture format "); <br />} <br />} </p>
		<p>void compress_to_jpeg_file( FILE *outfile, char * image_buffer,int w,int h, int quality) <br />...{ <br />struct jpeg_compress_struct cinfo; <br />struct jpeg_error_mgr jerr; <br />JSAMPROW row_pointer[1]; /**//* pointer to JSAMPLE row[s] */ <br />int row_stride; /**//* physical row width in image buffer */ <br />int image_width; <br />int image_height; <br />cinfo.err = jpeg_std_error(&amp;jerr); <br />jpeg_create_compress(&amp;cinfo); <br />jpeg_stdio_dest(&amp;cinfo, outfile); </p>
		<p>image_width = w; <br />image_height = h; <br />cinfo.image_width = image_width; /**//* image width and height, in pixels */ <br />cinfo.image_height = image_height; <br />cinfo.input_components = 3; /**//* # of color components per pixel */ <br />cinfo.in_color_space = JCS_RGB; /**//* colorspace of input image */ <br />jpeg_set_defaults(&amp;cinfo); <br />jpeg_set_quality(&amp;cinfo, quality, TRUE /**//* limit to baseline-JPEG values */); <br />jpeg_start_compress(&amp;cinfo, TRUE); <br />row_stride = image_width * 3; /**//* JSAMPLEs per row in image_buffer */ <br />while (cinfo.next_scanline &lt; cinfo.image_height) ...{ <br />row_pointer[0] = (JSAMPROW)&amp; image_buffer[cinfo.next_scanline * row_stride]; <br />(void) jpeg_write_scanlines(&amp;cinfo, row_pointer, 1); <br />} </p>
		<p>jpeg_finish_compress(&amp;cinfo); </p>
		<p>jpeg_destroy_compress(&amp;cinfo); </p>
		<p>/**//* And we're done! */ <br />} <br />void exchange_r_b( char * f,long size) <br />...{ <br />char r,b; <br />long i; <br />for( i = 0; i &lt; size ; i++) <br />...{ <br />r = *f; <br />b = *( f + 2); <br />*f = b; <br />*(f + 2) = r; <br />f = f +3; <br />} </p>
		<p>} </p>
		<p>void save_snapshot() <br />...{ <br />char *basepath = getenv("HOME"); <br />char *basename = "/snapsot"; <br />char *extname = ".jpg"; <br />char filename[100]; <br />int i = 0; <br />FILE *fo; <br />for(;;) ...{ <br />sprintf(filename,"%s%s%d%s",basepath,basename,i,extname); <br />fo = fopen(filename,"rb"); <br />if( fo==NULL ) <br />break; <br />else <br />fclose(fo); <br />i += 1; <br />} <br />printf("snapshot:%s ",filename); <br />fo = fopen(filename,"wb"); <br />exchange_r_b( cam_data, width*height ); <br />compress_to_jpeg_file( fo, cam_data, width, height, 90 ); <br />fclose(fo); <br />} <br /><br />一个比较清淅的摄像头参数设置<br /></p>
		<p>//loadVariablesNum("schlist.tx", 0);<br />//fscommand("fullscrean", true);<br />var my_cam = Camera.get();<br />//LAN <br />//Lower image quality, higher motion quality my_cam.setQuality(400000,0) <br />//Higher image quality, lower motion qualitymy_cam.setQuality(0,100)<br />//// 确保最低品质为 100，无论采用多大的带宽<br />my_cam.setQuality(0, 100); //<br />my_cam.setKeyFrameInterval(15);<br />my_cam.setLoopback(true);<br />//my_cam.setMotionLevel(35, 1000);<br />my_cam.setMode(320, 240, 25, true);</p>
		<p>
				<br />// 捕获摄像头<br />myVideo1.attachVideo(my_cam);<br />myVideo1.smoothing = true;<br />myVideo1._alpha = 100;<br />myVideo1._rotation = 360;<br />// 在舞台上的 myVideo1 对象的边界内显示视频流<br />my_cam.onStatus = function(infoMsg) {<br /> if (infoMsg.code == 'Camera.Muted') {<br />  // 拒绝<br />  trace('User denies access to the camera');<br /> } else if (infoMsg.code == 'Camera.Unmuted') {<br />  // 接受<br />  trace('User allows access to the camera');<br /> }</p>
		<p>};<br />//System.showSettings(3);<br />// 显示指定的 Flash Player"设置"面板<br />stop(); </p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/116035.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-05-08 20:28 <a href="http://www.blogjava.net/huyi2006/articles/116035.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个生产者-消费者的模型</title><link>http://www.blogjava.net/huyi2006/articles/115132.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Thu, 03 May 2007 05:44:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/115132.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/115132.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/115132.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/115132.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/115132.html</trackback:ping><description><![CDATA[
		<p>一个很好的多线程和互斥锁学习例程，是一个生产者-消费者的模型</p>
		<p>
				<br />#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;<br />#include &lt;time.h&gt;<br />#include &lt;pthread.h&gt;<br />#define BUFFER_SIZE 16</p>
		<p>struct prodcons<br />{<br />   int buffer[BUFFER_SIZE];<br />   pthread_mutex_t lock;/*互斥锁*/<br />   int readpos, writepos;<br />   pthread_cond_t notempty;/*缓冲区非空信号*/<br />   pthread_cond_t notfull;/*缓冲区非满信号*/<br />};</p>
		<p>void init(struct prodcons *b)<br />{<br />   pthread_mutex_init(&amp;b-&gt;lock, NULL);<br />   pthread_cond_init(&amp;b-&gt;notempty, NULL);<br />   pthread_cond_init(&amp;b-&gt;notfull, NULL);<br />   b-&gt;readpos = 0;<br />   b-&gt;writepos = 0;<br />}<br />void put(struct prodcons *b, int data)<br />{<br />   pthread_mutex_lock(&amp;b-&gt;lock);//获取互斥锁<br />   while((b-&gt;writepos + 1) % BUFFER_SIZE == b-&gt;readpos)<br />   {<br />      printf("wait for not full\n");<br />      pthread_cond_wait(&amp;b-&gt;notfull, &amp;b-&gt;lock);//不满时逃出阻塞<br />   }<br />   b-&gt;buffer[b-&gt;writepos] = data;<br />   b-&gt;writepos++;<br />   if(b-&gt;writepos &gt;= BUFFER_SIZE) b-&gt;writepos = 0;<br />   pthread_cond_signal(&amp;b-&gt;notempty);//设置状态变量<br />   pthread_mutex_unlock(&amp;b-&gt;lock);//释放互斥锁<br />}</p>
		<p>int get(struct prodcons *b)<br />{<br />   int data;<br />   pthread_mutex_lock(&amp;b-&gt;lock);<br />   while(b-&gt;writepos == b-&gt;readpos)<br />   {<br />     printf("wait for not empty\n");<br />     pthread_cond_wait(&amp;b-&gt;notempty, &amp;b-&gt;lock);<br />   }<br />   data = b-&gt;buffer[b-&gt;readpos];<br />   b-&gt;readpos++;<br />   if(b-&gt;readpos &gt;= BUFFER_SIZE) b-&gt;readpos = 0;<br />   pthread_cond_signal(&amp;b-&gt;notfull);<br />   pthread_mutex_unlock(&amp;b-&gt;lock);<br />   return data;<br />}</p>
		<p>#define OVER (-1)<br />struct prodcons buffer;</p>
		<p>void *producer(void *data)<br />{<br />   int n;<br />   for(n=0; n&lt;1000; n++)<br />   {<br />      printf("put-&gt;%d\n",n);<br />      put(&amp;buffer, n);<br />   }<br />   put(&amp;buffer, OVER);<br />   printf("producer stopped\n");<br />   return NULL;<br />}</p>
		<p>void *consumer(void *data)<br />{<br />   int d;<br />   while(1)<br />   {<br />      d = get(&amp;buffer);<br />      if(d == OVER)<br />      break;<br />      printf("             %d-&gt;get\n",d);<br />   }<br />   printf("consumer stopped!\n");<br />   return NULL;<br />}<br />int main(void)<br />{<br />   pthread_t th_a, th_b;<br />   void *retval;<br />   init(&amp;buffer);<br />   pthread_create(&amp;th_a, NULL, producer, 0);<br />   pthread_create(&amp;th_b, NULL, consumer, 0);<br />   pthread_join(th_a, &amp;retval);<br />   pthread_join(th_b, &amp;retval);<br />   return 0;<br />} <br />pthread_cond_wait函数，是线程阻塞在一个条件变量上，原型为：<br />extern int pthread_cond_wait(pthread_cond_t *_restrict_cond, pthread_mutex_t* _restrict_mutex)<br />线程解开mutex指向的锁并被条件变量cond阻塞，线程可以被函数pthread_cond_signal和pthread_cond_broadcast唤醒。<br />还有另一个函数pthread_cond_timedwait函数，他比vpthread_cond_wait函数多一个时间参数，经历给定时间後，阻塞将被解除。</p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/115132.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-05-03 13:44 <a href="http://www.blogjava.net/huyi2006/articles/115132.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>C/C++中的日期和时间 </title><link>http://www.blogjava.net/huyi2006/articles/114844.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Mon, 30 Apr 2007 10:50:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/114844.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/114844.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/114844.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/114844.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/114844.html</trackback:ping><description><![CDATA[C/C++中的日期和时间 <br /><br /><br />摘要： <br />本文从介绍基础概念入手，探讨了在C/C++中对日期和时间操作所用到的数据结构和函数，并对计时、时间的获取、时间的计算和显示格式等方面进行了阐述。本文还通过大量的实例向你展示了time.h头文件中声明的各种函数和数据结构的详细使用方法。 <br /><br />关键字： <br />UTC（世界标准时间），Calendar Time（日历时间），epoch（时间点），clock tick（时钟计时单元） <br /><br />1．概念 <br />在C/C++中，对字符串的操作有很多值得注意的问题，同样，C/C++对时间的操作也有许多值得大家注意的地方。最近，在技术群中有很多网友也多次问到过C++语言中对时间的操作、获取和显示等等的问题。下面，在这篇文章中，笔者将主要介绍在C/C++中时间和日期的使用方法. <br /><br />通过学习许多C/C++库，你可以有很多操作、使用时间的方法。但在这之前你需要了解一些“时间”和“日期”的概念，主要有以下几个： <br />Coordinated Universal Time（UTC）：协调世界时，又称为世界标准时间，也就是大家所熟知的格林威治标准时间（Greenwich Mean Time，GMT）。比如，中国内地的时间与UTC的时差为+8，也就是UTC+8。美国是UTC-5。 <br />Calendar Time：日历时间，是用“从一个标准时间点到此时的时间经过的秒数”来表示的时间。这个标准时间点对不同的编译器来说会有所不同，但对一个编译系统来说，这个标准时间点是不变的，该编译系统中的时间对应的日历时间都通过该标准时间点来衡量，所以可以说日历时间是“相对时间”，但是无论你在哪一个时区，在同一时刻对同一个标准时间点来说，日历时间都是一样的。 <br />epoch：时间点。时间点在标准C/C++中是一个整数，它用此时的时间和标准时间点相差的秒数（即日历时间）来表示。 <br />clock tick：时钟计时单元（而不把它叫做时钟滴答次数），一个时钟计时单元的时间长短是由CPU控制的。一个clock tick不是CPU的一个时钟周期，而是C/C++的一个基本计时单位。 <br /><br />我们可以使用ANSI标准库中的time.h头文件。这个头文件中定义的时间和日期所使用的方法，无论是在结构定义，还是命名，都具有明显的C语言风格。下面，我将说明在C/C++中怎样使用日期的时间功能。 <br /><br />2． 计时 <br />C/C++中的计时函数是clock()，而与其相关的数据类型是clock_t。在MSDN中，查得对clock函数定义如下： <br />clock_t clock( void ); <br />这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元（clock tick）数，在MSDN中称之为挂钟时间（wal-clock）。其中clock_t是用来保存时间的数据类型，在time.h文件中，我们可以找到对它的定义： <br />#ifndef _CLOCK_T_DEFINED <br />typedef long clock_t; <br />#define _CLOCK_T_DEFINED <br />#endif <br />很明显，clock_t是一个长整形数。在time.h文件中，还定义了一个常量CLOCKS_PER_SEC，它用来表示一秒钟会有多少个时钟计时单元，其定义如下： <br />#define CLOCKS_PER_SEC ((clock_t)1000) <br />可以看到每过千分之一秒（1毫秒），调用clock（）函数返回的值就加1。下面举个例子，你可以使用公式clock()/CLOCKS_PER_SEC来计算一个进程自身的运行时间： <br />void elapsed_time() <br />{ <br />printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC); <br />} <br />当然，你也可以用clock函数来计算你的机器运行一个循环或者处理其它事件到底花了多少时间： <br />#include “stdio.h” <br />#include “stdlib.h” <br />#include “time.h” <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 />Time to do 10000000 empty loops is 0.03000 seconds <br />上面我们看到时钟计时单元的长度为1毫秒，那么计时的精度也为1毫秒，那么我们可不可以通过改变CLOCKS_PER_SEC的定义，通过把它定义的大一些，从而使计时精度更高呢？通过尝试，你会发现这样是不行的。在标准C/C++中，最小的计时单位是一毫秒。 <br /><br />3．与日期和时间相关的数据结构 <br /><br />在标准C/C++中，我们可通过tm结构来获得日期和时间，tm结构在time.h中的定义如下： <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)。而日历时间（Calendar Time）是通过time_t数据类型来表示的，用time_t表示的时间（日历时间）是从一个时间点（例如：1970年1月1日0时0分0秒）到此时的秒数。在time.h中，我们也可以看到time_t是一个长整型数： <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 />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 /><br />在本节，我将向大家展示怎样利用time.h中声明的函数对时间进行操作。这些操作包括取当前时间、计算时间间隔、以不同的形式显示时间等内容。 <br /><br />4.1 获得日历时间 <br /><br />我们可以通过time()函数来获得日历时间（Calendar Time），其原型为：time_t time(time_t * timer); <br />如果你已经声明了参数timer，你可以从参数timer返回现在的日历时间，同时也可以通过返回值返回现在的日历时间，即从一个时间点（例如：1970年1月1日0时0分0秒）到现在此时的秒数。如果参数为空（NUL），函数将只通过返回值返回现在的日历时间，比如下面这个例子用来显示当前的日历时间： <br /><br />#include "time.h" <br />#include "stdio.h" <br />int main(void) <br />{ <br />struct tm *ptr; <br />time_t lt; <br />lt =time(NUL); <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 />#include "time.h" <br />#include "stdio.h" <br />int main(void) <br />{ <br />struct tm *local; <br />time_t t; <br />t=time(NUL); <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 />char * asctime(const struct tm * timeptr); <br />char * ctime(const time_t *timer); <br />其中asctime()函数是通过tm结构来生成具有固定格式的保存时间信息的字符串，而ctime()是通过日历时间来生成时间字符串。这样的话，asctime（）函数只是把tm结构对象中的各个域填到时间字符串的相应位置就行了，而ctime（）函数需要先参照本地的时间设置，把日历时间转化为本地时间，然后再生成格式化后的字符串。在下面，如果t是一个非空的time_t变量的话，那么： <br />printf(ctime(&amp;t)); <br />等价于： <br />struct tm *ptr; <br />ptr=localtime(&amp;t); <br />printf(asctime(ptr)); <br />那么，下面这个程序的两条printf语句输出的结果就是不同的了（除非你将本地时区设为世界标准时间所在的时区）： <br /><br />#include "time.h" <br />#include "stdio.h" <br />int main(void) <br />{ <br />struct tm *ptr; <br />time_t lt; <br />lt =time(NUL); <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 />#include “time.h” <br />#include “stdio.h” <br />int main(void) <br />{ <br />struct tm *ptr; <br />time_t lt; <br />char str[80]; <br />lt=time(NUL); <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 />#include &lt;stdio.h&gt; <br />#include &lt;time.h&gt; <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 />#include "time.h" <br />#include "stdio.h" <br />#include "stdlib.h" <br />int main(void) <br />{ <br />time_t start,end; <br />start = time(NUL); <br />system("pause"); <br />end = time(NUL); <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 />time_t mktime(struct tm * timeptr); <br />其返回值就是转化后的日历时间。这样我们就可以先制定一个分解时间，然后对这个时间进行操作了，下面的例子可以计算出1997年7月1日是星期几： <br />#include "time.h" <br />#include "stdio.h" <br />#include "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 />Tue Jul 01 00:00:01 1997 <br /><br />现在注意了，有了mktime()函数，是不是我们可以操作现在之前的任何时间呢？你可以通过这种办法算出1945年8月15号是星期几吗？答案是否定的。因为这个时间在1970年1月1日之前，所以在大多数编译器中，这样的程序虽然可以编译通过，但运行时会异常终止。 <br /><br />5．总结 <br /><br />本文介绍了标准C/C++中的有关日期和时间的概念，并通过各种实例讲述了这些函数和数据结构的使用方法。笔者认为，和时间相关的一些概念是相当重要的，理解这些概念是理解各种时间格式的转换的基础，更是应用这些函数和数据结构的基础。<img src ="http://www.blogjava.net/huyi2006/aggbug/114844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-04-30 18:50 <a href="http://www.blogjava.net/huyi2006/articles/114844.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>动态二维数组的内存分配问题</title><link>http://www.blogjava.net/huyi2006/articles/110592.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Sat, 14 Apr 2007 02:01:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/110592.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/110592.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/110592.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/110592.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/110592.html</trackback:ping><description><![CDATA[
		<p>一个关于动态二维数组分配内存的问题，以前也有做遇见过这样的，今天在网上参考了一个可以分配任意类型的示例，写了一个仅分配整型的二维数组小测试程序，在VC++6.0上测试通过</p>
		<p>int **AllocMatrix ( int iRow, int iCol )<br />void FreeMatrix ( int** p )</p>
		<p>示例程序：</p>
		<p> </p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #000000">#include </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000">stdio.h</span>
				<span style="COLOR: #000000">&gt;</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />#include </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #000000">stdlib.h</span>
				<span style="COLOR: #000000">&gt;</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />#include </span>
				<span style="COLOR: #000000">&lt;</span>
				<span style="COLOR: #0000ff">string</span>
				<span style="COLOR: #000000">.h</span>
				<span style="COLOR: #000000">&gt;</span>
				<span style="COLOR: #000000">
						<br />
						<img id="Codehighlighter1_59_121_Open_Image" onclick="this.style.display='none'; Codehighlighter1_59_121_Open_Text.style.display='none'; Codehighlighter1_59_121_Closed_Image.style.display='inline'; Codehighlighter1_59_121_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" />
						<img id="Codehighlighter1_59_121_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_59_121_Closed_Text.style.display='none'; Codehighlighter1_59_121_Open_Image.style.display='inline'; Codehighlighter1_59_121_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />
				</span>
				<span id="Codehighlighter1_59_121_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">/**/</span>
				<span id="Codehighlighter1_59_121_Open_Text">
						<span style="COLOR: #808080">////////////////////////////////////////////////////////////</span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #808080">
						</span>
				</span>
				<br />
				<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> FreeMatrix(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">**</span>
				<span style="COLOR: #000000">p);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000">**</span>
				<span style="COLOR: #000000"> AllocMatrix(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> iRow, </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> iCol);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">**</span>
				<span style="COLOR: #000000">AllocMatrix(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> iRow, </span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> iCol)<br /><img id="Codehighlighter1_226_777_Open_Image" onclick="this.style.display='none'; Codehighlighter1_226_777_Open_Text.style.display='none'; Codehighlighter1_226_777_Closed_Image.style.display='inline'; Codehighlighter1_226_777_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_226_777_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_226_777_Closed_Text.style.display='none'; Codehighlighter1_226_777_Open_Image.style.display='inline'; Codehighlighter1_226_777_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_226_777_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_226_777_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">**</span>
						<span style="COLOR: #000000"> tdarray </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> NULL;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000"> tmparray </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> NULL;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000"> i </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000">allocate pointer array</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    </span>
						<span style="COLOR: #0000ff">if</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">!</span>
						<span style="COLOR: #000000">(tdarray </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> (</span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">**</span>
						<span style="COLOR: #000000">)malloc(</span>
						<span style="COLOR: #0000ff">sizeof</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #0000ff">char</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">iRow)))<br /><img id="Codehighlighter1_365_415_Open_Image" onclick="this.style.display='none'; Codehighlighter1_365_415_Open_Text.style.display='none'; Codehighlighter1_365_415_Closed_Image.style.display='inline'; Codehighlighter1_365_415_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_365_415_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_365_415_Closed_Text.style.display='none'; Codehighlighter1_365_415_Open_Image.style.display='inline'; Codehighlighter1_365_415_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span>
						<span id="Codehighlighter1_365_415_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
								<img src="http://www.blogjava.net/images/dot.gif" />
						</span>
						<span id="Codehighlighter1_365_415_Open_Text">
								<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        printf(</span>
								<span style="COLOR: #000000">"</span>
								<span style="COLOR: #000000">allocate iRow wrong\n</span>
								<span style="COLOR: #000000">"</span>
								<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        exit(</span>
								<span style="COLOR: #000000">1</span>
								<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span>
						</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000">allocate actual array space</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    </span>
						<span style="COLOR: #0000ff">if</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #000000">!</span>
						<span style="COLOR: #000000">(tmparray </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> (</span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">)malloc(</span>
						<span style="COLOR: #0000ff">sizeof</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(iRow </span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000"> iCol))))<br /><img id="Codehighlighter1_508_581_Open_Image" onclick="this.style.display='none'; Codehighlighter1_508_581_Open_Text.style.display='none'; Codehighlighter1_508_581_Closed_Image.style.display='inline'; Codehighlighter1_508_581_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_508_581_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_508_581_Closed_Text.style.display='none'; Codehighlighter1_508_581_Open_Image.style.display='inline'; Codehighlighter1_508_581_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span>
						<span id="Codehighlighter1_508_581_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
								<img src="http://www.blogjava.net/images/dot.gif" />
						</span>
						<span id="Codehighlighter1_508_581_Open_Text">
								<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        FreeMatrix(tdarray);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        printf(</span>
								<span style="COLOR: #000000">"</span>
								<span style="COLOR: #000000">allocate iRow wrong\n</span>
								<span style="COLOR: #000000">"</span>
								<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        exit(</span>
								<span style="COLOR: #000000">1</span>
								<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span>
						</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000">initialize memory</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    memset(tmparray, </span>
						<span style="COLOR: #000000">0x00</span>
						<span style="COLOR: #000000">, </span>
						<span style="COLOR: #0000ff">sizeof</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000">(iRow </span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000"> iCol));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #008000">//</span>
						<span style="COLOR: #008000">evaluate the pointer array</span>
						<span style="COLOR: #008000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />
						</span>
						<span style="COLOR: #000000">    </span>
						<span style="COLOR: #0000ff">for</span>
						<span style="COLOR: #000000">(i</span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">; i</span>
						<span style="COLOR: #000000">&lt;</span>
						<span style="COLOR: #000000">iRow; i</span>
						<span style="COLOR: #000000">++</span>
						<span style="COLOR: #000000">)<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        tdarray[i] </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> (tmparray </span>
						<span style="COLOR: #000000">+</span>
						<span style="COLOR: #000000"> (i</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #000000"> iCol)</span>
						<span style="COLOR: #000000">*</span>
						<span style="COLOR: #0000ff">sizeof</span>
						<span style="COLOR: #000000">(</span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">return</span>
						<span style="COLOR: #000000"> tdarray;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
						<br />
						<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> FreeMatrix(</span>
				<span style="COLOR: #0000ff">int</span>
				<span style="COLOR: #000000"> </span>
				<span style="COLOR: #000000">**</span>
				<span style="COLOR: #000000">p)<br /><img id="Codehighlighter1_805_845_Open_Image" onclick="this.style.display='none'; Codehighlighter1_805_845_Open_Text.style.display='none'; Codehighlighter1_805_845_Closed_Image.style.display='inline'; Codehighlighter1_805_845_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_805_845_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_805_845_Closed_Text.style.display='none'; Codehighlighter1_805_845_Open_Image.style.display='inline'; Codehighlighter1_805_845_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_805_845_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_805_845_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">if</span>
						<span style="COLOR: #000000">(p)<br /><img id="Codehighlighter1_815_843_Open_Image" onclick="this.style.display='none'; Codehighlighter1_815_843_Open_Text.style.display='none'; Codehighlighter1_815_843_Closed_Image.style.display='inline'; Codehighlighter1_815_843_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_815_843_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_815_843_Closed_Text.style.display='none'; Codehighlighter1_815_843_Open_Image.style.display='inline'; Codehighlighter1_815_843_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />    </span>
						<span id="Codehighlighter1_815_843_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
								<img src="http://www.blogjava.net/images/dot.gif" />
						</span>
						<span id="Codehighlighter1_815_843_Open_Text">
								<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        free(p[</span>
								<span style="COLOR: #000000">0</span>
								<span style="COLOR: #000000">]);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        free(p);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />    }</span>
						</span>
						<span style="COLOR: #000000">
								<br />
								<img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
				<span style="COLOR: #000000">
						<br />
						<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
						<br />
						<img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />
				</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000"> main(</span>
				<span style="COLOR: #0000ff">void</span>
				<span style="COLOR: #000000">)<br /><img id="Codehighlighter1_864_991_Open_Image" onclick="this.style.display='none'; Codehighlighter1_864_991_Open_Text.style.display='none'; Codehighlighter1_864_991_Closed_Image.style.display='inline'; Codehighlighter1_864_991_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_864_991_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_864_991_Closed_Text.style.display='none'; Codehighlighter1_864_991_Open_Image.style.display='inline'; Codehighlighter1_864_991_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" /></span>
				<span id="Codehighlighter1_864_991_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff">
						<img src="http://www.blogjava.net/images/dot.gif" />
				</span>
				<span id="Codehighlighter1_864_991_Open_Text">
						<span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    </span>
						<span style="COLOR: #0000ff">int</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">**</span>
						<span style="COLOR: #000000">arr </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> AllocMatrix(</span>
						<span style="COLOR: #000000">2</span>
						<span style="COLOR: #000000">,</span>
						<span style="COLOR: #000000">2</span>
						<span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    arr[</span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">][</span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">] </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    arr[</span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">][</span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">] </span>
						<span style="COLOR: #000000">=</span>
						<span style="COLOR: #000000"> </span>
						<span style="COLOR: #000000">4</span>
						<span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    printf(</span>
						<span style="COLOR: #000000">"</span>
						<span style="COLOR: #000000">%d, %d \n</span>
						<span style="COLOR: #000000">"</span>
						<span style="COLOR: #000000">,arr[</span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">][</span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">], arr[</span>
						<span style="COLOR: #000000">0</span>
						<span style="COLOR: #000000">][</span>
						<span style="COLOR: #000000">1</span>
						<span style="COLOR: #000000">]);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />    FreeMatrix(arr);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />}</span>
				</span>
		</div>
		<p> </p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/110592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-04-14 10:01 <a href="http://www.blogjava.net/huyi2006/articles/110592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> C语言中动态分配数组(一维)</title><link>http://www.blogjava.net/huyi2006/articles/110533.html</link><dc:creator>allic</dc:creator><author>allic</author><pubDate>Fri, 13 Apr 2007 13:36:00 GMT</pubDate><guid>http://www.blogjava.net/huyi2006/articles/110533.html</guid><wfw:comment>http://www.blogjava.net/huyi2006/comments/110533.html</wfw:comment><comments>http://www.blogjava.net/huyi2006/articles/110533.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/huyi2006/comments/commentRss/110533.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/huyi2006/services/trackbacks/110533.html</trackback:ping><description><![CDATA[
		<p> C语言中动态分配数组(一维)<br /> <br />    当初学Pascal的时候就想过这个问题：如何动态的定义及使用数组呢？记得一般用数组的时候都是先指定大小的。当时问老师，老师说是不可以的。后来又问了一位教C++的老师，他告诉我在C++里用new可以做到，一直不用C++，所以也不明白。今天在逛论坛时终于找到了C语言中的用法(看原贴)：</p>
		<p>    int *a;<br />    int N;<br />    scanf("%d", &amp;N);<br />    a = (int *) malloc(N * sizeof(int));<br />    ....<br />    free(a);</p>
		<p>    这样就动态分配了数组a[N]。数组的长度N可输入确定，也可用程序中的变量确定。但要注意程序结束后要用free()将其释放，否则内存会泄漏。 </p>
		<p>
				<br />--------------------------------------------------------------------------------<br />验证一下：</p>
		<p>#include &lt;stdio.h&gt;<br />#include &lt;stdlib.h&gt;<br />int main()<br />{<br />    int i = 0; </p>
		<p>    int *a;<br />    int N;</p>
		<p>    printf("Input array length: ");<br />    scanf("%d", &amp;N);<br />    printf("\n");</p>
		<p>    a = (int *) malloc(N * sizeof(int));<br />    <br />    for(i = 0; i &lt; N; i++)<br />    {<br />        a[i] = i + 1;</p>
		<p>        printf("%-5d", a[i]);<br />        if ((i + 1) % 10 == 0)<br />            printf("\n");<br />    }</p>
		<p>    free(a);</p>
		<p>    printf("\n");    <br />    return 0;<br />}</p>
		<p>运行结果(VC):<br />=========================================================<br />Input array length: 100↙</p>
		<p>1    2    3    4    5    6    7    8    9    10<br />11   12   13   14   15   16   17   18   19   20<br />21   22   23   24   25   26   27   28   29   30<br />31   32   33   34   35   36   37   38   39   40<br />41   42   43   44   45   46   47   48   49   50<br />51   52   53   54   55   56   57   58   59   60<br />61   62   63   64   65   66   67   68   69   70<br />71   72   73   74   75   76   77   78   79   80<br />81   82   83   84   85   86   87   88   89   90<br />91   92   93   94   95   96   97   98   99   100</p>
		<p>=========================================================</p>
		<p> </p>
<img src ="http://www.blogjava.net/huyi2006/aggbug/110533.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/huyi2006/" target="_blank">allic</a> 2007-04-13 21:36 <a href="http://www.blogjava.net/huyi2006/articles/110533.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>