??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
创徏一个DLL目Q项目名Uhello,DLL~写采用的是DEV C++中的CZ代码
头文件dll.h
#ifndef _DLL_H_
#define _DLL_H_
#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
DLLIMPORT void HelloWorld (void);
#endif /* _DLL_H_ */
C文g
dllmain.c
#include <stdio.h>
#include <stdlib.h>
DLLIMPORT void HelloWorld ()
{
MessageBox (0, "Hello World from DLL!\n", "Hi", MB_ICONINFORMATION);
}
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
q有要注意的?def文g中指定输出的函数Q编译生成了hello.dll文g
DLL调用部分
dllcall.c
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
HINSTANCE hDLL; //定义DLL包柄
typedef void ( *func)(); //定义函数指针原型
func hello; //定义函数指针
int main()
{
if (hDLL == NULL)
hDLL=LoadLibrary("hello.dll"); //加蝲DLL
hello = (func)GetProcAddress(hDLL,"HelloWorld"); //获取函数指针
hello();
FreeLibrary(hDLL); //释放DLL
return 0;
}
~译执行
integers(整数)~码为:i<整数>e
开始标记iQ结束标Cؓe
例如Q i1234e 表示为整?234
i-1234e 表示为整?1234
整数没有大小限制
i0e 表示为整?
i-0e 为非?br />?开头的为非法如Q?i01234e 为非?/p>
lists(列表)~码为:l<bencoding~码cd>e
开始标Cؓl,l束标记为e
列表里可以包含Q何bencoding~码cdQ包括整敎ͼ字符Ԍ列表Q字典?br />例如Q l4:test5abcdee 表示Z个字W串["test","abcde"]
dictionaries(字典)~码为d<bencoding字符?gt;<bencoding~码cd>e
开始标Cؓd,l束标记为e
关键字必Mؓbencoding字符?br />值可以ؓMbencoding~码cd
例如Q d3:agei20ee 表示为{"age"=20}
d4:path3:C:\8:filename8:test.txte 表示为{"path"="C:\","filename"="test.txt"}
具体文gl构如下Q?br />全部内容必须都ؓbencoding~码cd?br />整个文gZ个字典结?包含如下关键?br />announce:tracker服务器的URL(字符?
announce-list(可?:备用tracker服务器列?列表)
creation date(可?:U子创徏的时_Unix标准旉格式Q从1970 1??00:00:00到创建时间的U数(整数)
comment(可?:备注(字符?
created by(可?:创徏人或创徏E序的信?字符?
info:一个字典结构,包含文g的主要信息,为分二种情况Q单文gl构或多文gl构
单文件结构如下:
length:文g长度Q单位字?整数)
md5sum(可?Q长32个字W的文g的MD5校验和,BT不用这个|只是Z兼容一些程序所保留!(字符?
name:文g?字符?
piece length:每个块的大小Q单位字?整数)
pieces:每个块的20个字节的SHA1 Hash的?二进制格?
多文件结构如下:
files:一个字典结?br /> length:文g长度Q单位字?整数)
md5sum(可?:同单文gl构中相?br /> path:文g的\径和名字Q是一个列表结构,如\test\test.txt 列表为l4:test8test.txte
name:最上层的目录名?字符?
piece length:同单文gl构中相?br /> pieces:同单文gl构中相同?br />实例Q?br />用记事本打开一?torrent可以看来cM如下内容
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 ?w?R排T酆ee
很容易看?br />announceQ?a >http://www.manfen.net:7802/announce
creation dateQ?076675108U?02/13/04 20:25:08)
文g?MICROSOFT.WINDOWS.2000.AND.NT4.SOURCE.CODE-SCENELEADER.torrent
文g大小Q?7799字节
文g块大=32768字节
struct a
{
void (*func)(char *);
};
void hello(char *name)
{
printf ("hello %s\n",name);
}
int main()
{
struct a a1;
a1.func = hello;
a1.func("illusion");
system("PAUSE");
return 0;
}
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
q样成功地创徏了在WIN32E序中访问sqlite所需要的?可以用于链接WIN32E序.
到此所有用sqlite的准备工作已告罄.现在在MSVC6中新Z个Win32 Console Application工程,把sqlite.dll,sqlite.h和sqlite.lib文g复制到工E文件夹?把sqlite.h文g加入到项 目中,然后在Project Setting的Link中的对象库模块中增加sqlite.lib文g. 或者project->add to project->filesQ选择q个lib文g
然后修改加入如下代码卛_:
#include <IOSTREAM>
#include <STRING>
#include <SSTREAM>
#include <stdio.h>
#include "sqlite3.h"
using namespace std;
sqlite3* pDB;
int createTable()
{
char* errMsg;
std::string dropTab="drop table test_tab;";
string strSQL= "create table test_tab (f1 int, f2 long, f3 varchar(20));";
int res = sqlite3_exec(pDB,dropTab.c_str(),0,0, &errMsg);
if (res != SQLITE_OK)
{
std::cout << "执行SQL 出错." << errMsg << std::endl;
//return -1;
}
res = sqlite3_exec(pDB,strSQL.c_str(),0,0, &errMsg);
if (res != SQLITE_OK)
{
std::cout << "执行创徏table的SQL 出错." << errMsg << std::endl;
return -1;
}
else
{
std::cout << "创徏table的SQL成功执行."<< std::endl;
}
return 0;
}
int insert1()
{
char* errMsg;
int res = sqlite3_exec(pDB,"begin transaction;",0,0, &errMsg);
for (int i= 1; i < 100; ++i)
{
std::stringstream strsql;
strsql << "insert into test_tab values(";
strsql << i << ","<< (i+10) <<",'huyi'"<< ");";
std::string str = strsql.str();
cout << str <<endl;
res = sqlite3_exec(pDB,str.c_str(),0,0, &errMsg);
if (res != SQLITE_OK)
{
std::cout << "执行SQL 出错." << errMsg << std::endl;
return -1;
}
}
res = sqlite3_exec(pDB,"commit transaction;",0,0, &errMsg);
std::cout << "SQL成功执行."<< std::endl;
return 0;
}
int select1()
{
char* errMsg;
int nrow = 0, ncolumn = 0;
char **azResult; //二维数组存放l果
string strSQL= "select * from test_tab;";
/*
int res = sqlite3_exec(pDB,strSQL.c_str(),0,0, &errMsg);
if (res != SQLITE_OK)
{
std::cout << "执行SQL 出错." << errMsg << std::endl;
return -1;
}
else
{
std::cout << "SQL成功执行."<< std::endl;
}
*/
sqlite3_get_table(pDB, strSQL.c_str(), &azResult, &nrow, &ncolumn, &errMsg);
int i = 0;
for( i=0 ; i<( nrow + 1 ) * ncolumn ; i++ )
{
if (i>0 && i%ncolumn==0)
printf("\n");
printf( "%s ",azResult[i]);
}
printf("\n");
//释放?azResult 的内存空?br /> sqlite3_free_table( azResult );
sqlite3_close(pDB); //关闭数据?br /> return 0;
}
int main(int argc, char* argv[])
{
if (argc < 2)
{
std::cout << "误入命令行参数Qsqlite数据库名." << std::endl;
return 0;
}
int res = sqlite3_open(argv[1], &pDB);
if( res ){
std::cout << "Can't open database: "<< sqlite3_errmsg(pDB);
sqlite3_close(pDB);
return -1;
}
res = createTable();
if (res != 0)
{
return 0;
}
res = insert1();
if (res != 0)
{
return 0;
}
select1();
return 0;
}
介绍Q?/p>
设计内存池的目标是ؓ了保证服务器长时间高效的q行Q通过对申L间小而申请频J的对象q行有效理Q减内存碎片的产生Q合理分配管理用户内存,从而减系l中出现有效I间_Q而无法分配大块连l内存的情况?/p>
目标Q?/p>
此次设计内存池的基本目标Q需要满线E安全性(多线E)Q适量的内存泄露越界检查,q行效率不太低于malloc/free方式Q实现对4-128字节范围内的内存I间甌的内存池理Q非单一固定大小对象理的内存池Q?/p>
内存池技术设计与实现
本内存池的设计方法主要参考SGI的alloc的设计方案,Z适合一般的应用Qƈ在alloc的基上做一些简单的修改?/p>
Mempool的内存池设计Ҏ如下Q也可参考候捷《深入剖析STL》)
从系l申请大块heap内存Q在此内存上划分不同大小的区块,q把h相同大小的区块连接v来,l成一个链表。比如A大小的块Q组成链表LQ当甌A大小Ӟ直接从链表L头部Q如果不为空Q上取到一块交l申误,当释放A大小的块Ӟ直接挂接到L的头部。内存池的原理比较简单,但是在具体实现过E中大量的细节需要注意?/p>
1Q字节对齐?/p>
Z方便内存池中对象的管理,需要对甌内存I间的进行调_在Mempool中,字节寚w的大ؓ最接近8倍数的字节数。比如,用户甌5个字节,Mempool首先会把它调整ؓ8字节。比如申?2字节Q会调整?4Q对比关pd?/p>
序号 |
寚w字节 |
范围 |
0 |
8 |
1-8 |
1 |
16 |
9-16 |
2 |
24 |
17-24 |
3 |
32 |
25-32 |
4 |
40 |
33-40 |
5 |
48 |
41-48 |
6 |
56 |
49-56 |
7 |
64 |
57-64 |
8 |
72 |
65-72 |
9 |
80 |
73-80 |
10 |
88 |
81-88 |
11 |
96 |
89-96 |
12 |
104 |
97-104 |
13 |
112 |
105-112 |
14 |
120 |
113-120 |
15 |
128 |
121-128 |
Q图1Q?/p>
对于过128字节的申P直接调用malloc函数甌内存I间。这里设计的内存池ƈ不是Ҏ有的对象q行内存理Q只是对甌内存I间,而申请频J的对象q行理Q对于超q?28字节的对象申P不予考虑。这个需要与实际目l合Qƈ不是固定不变的。实现对齐操作的函数如下
static size_t round_up(size_t size)
{
return (((size)+7) &~ 7);// ?字节寚w
}
2Q构建烦引表
内存池中理的对象都是固定大,现在要管?-128字节的范围内的对象申L_除了采用上面提到的字节对齐外Q还需要变通一下,q就是徏立烦引表Q做法如下;
static _obj* free_list[16];
创徏一个包?6个_obj*指针的数l,关于_objl构后面详细讲解。free_list[0]记录所有空闲空间ؓ8字节的链表的首地址Qfree_list[1]对应16字节的链表,free_list[2]对应24字节的列表。free_list中的下标和字节链表对应关pd考图1中的“序号”和“对齐字节”之间的关系。这U关p,我们很容易用法计算出来。如?/p>
static size_t freelist_index(size_t size)
{
return (((size)+7)/7-1);// ?字节寚w
}
所以,q样当用LL间AӞ我们只是通过上面单的转换Q就可以跌{到包含A字节大小的空闲链表上,如下Q?br />_obj** p = free_list[freelist_index(A)];
3:构徏I闲链表
通过索引表,我们知道mempool中维持着16条空闲链表,q些I闲链表中管理的I闲对象大小分别?Q?6Q?4Q?2Q?0?28。这些空闲链表链接v来的方式完全相同。一般情况下我们构徏单链表时需要创建如下的一个结构体?/p>
struct Obj
{
Obj *next;
Char* p;
Int iSize;
}
next指针指向下一个这Ll构Qp指向真正可用I间,iSize用于只是可用I间的大,在其他的一些内存池实现中,q有更复杂的l构体,比如q包括记录此l构体的上l构体的指针Q结构体中当前用空间的变量{,当用LL间时Q把此结构体d的用LL间中去,比如用户甌12字节的空_可以q样?/p>
Obj *p = (Obj*)malloc(12+sizeof(Obj));
p->next = NULL;
p->p = (char*)p+sizeof(Obj);
p->iSize = 12;
但是Q我们ƈ没有采用q种方式Q这U方式的一个缺点就是,用户甌空间时Q内存池加料太多了。比如用L?2字节Ӟ而真实情冉|内存池向内存甌?2+ sizeof(Obj)=12+12=24字节的内存空_q样费大量内存用在标记内存I间上去Qƈ且也没有体现索引表的优势。Mempool采用的是union方式
union Obj
{
Obj *next;
char client_data[1];
}
q里除了把上面的struct修改为unionQƈ把int iSizeLQ同时把char*pQ修改ؓchar client_data[1]Qƈ没有做太多的修改。而优势也恰恰体现在这里。如果采用struct方式Q我们需要维护两条链表,一条链表是Q已分配内存I间链表Q另一条是未分配(I闲Q空间链表。而我们用烦引表和unionl构体,只需要维护一条链表,x分配I间链表。具体如?/p>
索引表的作用有两?Q如上所_l护16条空闲链?Q变相记录每条链表上I间的大,比如下标?的烦引表内维持着是大ؓ24字节的空闲链表。这h们通过索引表减在l构体内记录p所指向I间大小的iSize变量。从而减?个字节?/p>
Union的特性是Q结构内的变量是互斥存在的。再q行状态下Q只是存在一U变量类型。所以在q里sizeof(Obj)的大ؓ4Q难道这里我们也需要把q?字节也加到用LL间中dQ其实不是,如果q样Q我们又Ҏ了union的特性?/p>
当我们构建空闲分配链表时Q我们通过next指向下一个unionl构体,q样我们不用p指针。当把这个结构体分配出去Ӟ我们直接q回client_data的地址Q此时client_data正好指向甌I间的首字节。所以这P我们׃用在用户甌I间上添加Q何东ѝ?/p>
Obj的连接方式如上所C,q样我们无需为用LL间添加Q何内宏V ?/p>
4Q记录申L间字节数
如果采用面向对象方式Q或者我们在释放内存池的I间时能够明知道释攄间的大小Q无需采用q种方式?/p>
在C语言中的free没有传递释攄间大,而可以正释放,在这里也是模仿这U方式,采用q种记录甌I间大小的方式去释放内存。用LL?1操作在字节寚w之前执行Q找到合适空间后Q把首字节改写ؓ甌I间的大,当然1个字节最多纪?56个数Q如果项目需要,可以讄为shortcd或者intcdQ不q这样就需要占用用h较大的空间。当释放内存I间Ӟ首先dq个字节Q获取空间大,q行释放。ؓ了便于对大于128字节对象的大进行合适的释放Q同时也对大?28字节的内存申Pd1字节记录大小。所以现在这里限制了用户内存甌I间不得大于255字节Q不q现在已l满项目要求。当然也可以修改为用shortcd记录甌I间的大?/p>
// 甌
*(( unsigned char *)result) = (size_t)n;
unsigned char * pTemp = (unsigned char*)result;
++pTemp;
result = (_obj*)pTemp;
return result;
// 释放
unsigned char * pTemp = (unsigned char *)ptr;
--pTemp;
ptr = (void*)pTemp;
n = (size_t)(*( unsigned char *)ptr);
5Q内存池的分配原?/p>
在内存池的设计中Q有两个重要的操作过E?Qchunk_allocQ申请大块内存,2Qrefill回填操作Q内存池初始化化时ƈ不是为烦引表中的每一w创徏I闲分配链表Q这个过E会推迟刎ͼ只有用户提取h时才会创L分配链表。详l参考如下代码(在sgi中stl_alloc.h文g中你也可以看到这两个函数Q,主要步骤在注释中已经说明?/p>
/**
* @bri: 甌大块内存Qƈq回size*(*nobjs)大小的内存块
* @param: size,round_up寚w后的大小,nobjs
* @return: q回指向W一个对象内存指?br />*/
static char* chunk_alloc(size_t size, int *nobjs)
{
/**< q回指针 */
char* __result;
/**< 甌内存块大?*/
size_t __total_bytes = size *(*nobjs);
/**< 当前内存可用I间 */
size_t __bytes_left = _end_free - _start_free;
/**< 内存池中q有大片可用内存 */
if (__bytes_left >= __total_bytes)
{
__result = _start_free;
_start_free += __total_bytes;
return (__result);
}
/**< 臛_q有一个对象大的内存I间 */
else if (__bytes_left >= size)
{
*nobjs = (int)(__bytes_left/size);
__total_bytes = size * (*nobjs);
__result = _start_free;
_start_free += __total_bytes;
return (__result);
}
/**< 内存池中没有MI间 */
else
{
/**< 重新甌内存池的大小 */
size_t __bytes_to_get = 2 * __total_bytes + round_up(_heap_size >> 4);
/**< 把内存中剩余的空间添加到freelist?*/
if(__bytes_left > 0)
{
_obj *VOLATILE* __my_free_list =
_free_list + freelist_index(__bytes_left);
((_obj*)_start_free)->free_list_link =
*__my_free_list;
*__my_free_list = (_obj*)_start_free;
}
// 甌新的大块I间
_start_free = (char*)malloc(__bytes_to_get);
/*=======================================================================*/
memset(_start_free,0,__bytes_to_get);
/*=======================================================================*/
// pȝ内存已经无可用内存,那么从内存池中压~内?br /> if(0 == _start_free)
{
size_t __i;
_obj *VOLATILE* __my_free_list;
_obj *__p;
/**< 从freelist中逐项查可用空?此时只收集比size对象大的内存I间) */
for (__i = size; __i <= (size_t)__MAX_BYTES; __i += __ALIGN)
{
__my_free_list = _free_list + freelist_index(__i);
__p = *__my_free_list;
/**< 扑ֈI闲?*/
if (__p != 0)
{
*__my_free_list = __p->free_list_link;
_start_free = (char*)__p;
_end_free = _start_free + __i;
return (chunk_alloc(size,nobjs));
}
}
_end_free = 0;
/**< 再次甌内存Q可能触发一个异?*/
_start_free = (char*)malloc(__bytes_to_get);
}
/**< 记录当前内存池的定w */
_heap_size += __bytes_to_get;
_end_free = _start_free + __bytes_to_get;
return (chunk_alloc(size,nobjs));
}
}
/*=======================================================================*/
/**
* @bri: 填充freelist的连接,默认填充20?br /> * @param: __nQ填充对象的大小Q?字节寚w后的value
* @return: I闲
*/
static void* refill(size_t n)
{
int __nobjs = 20;
char* __chunk = (char*)chunk_alloc(n, &__nobjs);
_obj *VOLATILE* __my_free_list;
_obj *VOLATILE* __my_free_list1;
_obj * __result;
_obj * __current_obj;
_obj * __next_obj;
int __i;
// 如果内存池中仅有一个对?br /> if (1 == __nobjs)
return(__chunk);
__my_free_list = _free_list + freelist_index(n);
/* Build free list in chunk */
__result = (_obj*)__chunk;
*__my_free_list = __next_obj = (_obj*)(__chunk + n);
__my_free_list1 = _free_list + freelist_index(n);
for (__i = 1;; ++__i)
{
__current_obj = __next_obj;
__next_obj = (_obj*)((char*)__next_obj+n);
if(__nobjs - 1 == __i)
{
__current_obj->free_list_link = 0;
break;
}else{
__current_obj->free_list_link = __next_obj;
}
}
return(__result);
}
l过上面操作后,内存池可能会成ؓ如下的一U状态。从图上我们可以看到Q已l构Z8Q?4Q?8Q?28字节的空闲分配链表,而其他没有分配空闲分配链表的他们的指针都指向NULL。我们通过判断索引表中的指针是否ؓNULLQ知道是否已l构建空闲分配表或者空闲分配表是否用完Q如果此处指针ؓNULLQ我们调用refill函数Q重新申?0个这样大的内存I间Qƈ把他们连接v来。在refill函数内,我们要查看大内存中是否有可用内存Q如果有Qƈ且大合适,p回给refill函数?br />
6Q线E安?br /> 采用互斥体,保证U程安全?br />
内存池测?/p>
内存池的试主要分两部分试1Q单U程下malloc与mempool的分配速度Ҏ2Q多U程下malloc和mempool的分配速度ҎQ我们分?Q?0Q?6个线E进行测试了?br /> 试环境Q操作系l:windows2003+sp1QVC7.1+sp1Q硬件环境:intel(R) Celeron(R) CPU 2.53GHz,512M物理内存?br />
甌内存I间讑֮如下
#define ALLOCNUMBER0 4
#define ALLOCNUMBER1 7
#define ALLOCNUMBER2 23
#define ALLOCNUMBER3 56
#define ALLOCNUMBER4 10
#define ALLOCNUMBER5 60
#define ALLOCNUMBER6 5
#define ALLOCNUMBER7 80
#define ALLOCNUMBER8 9
#define ALLOCNUMBER9 100
Malloc方式和mempool方式均用如上数据进行内存空间的甌和释放。申误E,每次循环甌释放上述数据20?br /> 我们对malloc和mempoolQ分别进行了如下甌ơ数的测试(单位ZQ?/p>
2 |
10 |
20 |
30 |
40 |
50 |
80 |
100 |
150 |
200 |
可以看到mempool无论在多U程q是在单U程情况下,mempool的速度都优于malloc方式的直接分配?/p>
Malloc方式debug模式下,在不同的U程下,q行旉如下Q通过囄可知Qmalloc方式Q在debug模式下,甌I间的速度和多U程的关pM大。多U程方式Q要略快于单U程的运行实现?/p>
Malloc方式release模式试l果如下?/p>
多线E的优势Q逐渐体现出来。当执行200wơ申请和释放Ӟ多线E要比单U程?500ms左右Q?Q?0Q?6个线E之间的差别q不是特别大。不q整体感?个线E的q行旉要稍微高?0Q?6个线E的情况下,意味着q程中线E越多用在线E切换上的时间就多?/p>
下面是mempool在debug试l果
下面是mempool在release模式下的试l果
以上所有统计图中所用到的数据,是我们测试三ơ后q_倹{?/p>
通过上面的测试,可以知道mempool的性能基本上超q直接malloc方式Q在200wơ申请和释放的情况下Q单U程release版情况下Qmempool比直接malloc?10倍。而在4个线E情况下Qmempool要比直接malloc?倍左叟뀂以上测试只是申请速度的测试,在不同的压力情况下,试l果可能会不同,试l果也不能说明mempool方式比malloc方式E_?br />
结Q内存池基本上满_期设计目标,但是她ƈ不是完美的,有缺P比如,不能甌大于256字节的内存空_无内存越界检查,无内存自动回~功能等。只是这些对我们的媄响还不是那么重要?/p>
׃q是一个公叔R目,代码涉及版权Q所以不能发布出来。如果你惛_自己的内存池Q可以与我联pugg_xchj#hotmail.com.
#include <stdio.h>
void print_arr (char (*a)[4])
{
int i;
for(i = 0; i < 3; i++)
printf("%s\n", a[i]);
}
int main()
{
char *str_arr[3][4] = {"yes", "no", "uncertain"};
print_arr (str_arr);
return 0;
}
如果要以二指针的Ş式传参,可以在定义ؓ字符串指针数l的形式
char *str_arr[] = {"yes", "no", "uncertain"};
void print_arr (char **a)
{
int i;
for (i=0; i<3;i++)
printf("%s\n", *(a+1));
}
三个函数的申明分别是:
void* realloc(void* ptr, unsigned newsize);
void* malloc(unsigned size);
void* calloc(size_t nelem, size_t elsize);
都在stdlib.h函数库内
它们的返回值都是请求系l分配的地址,如果hp|p回NULL
malloc用于甌一D|的地址,参数size为需要内存空间的长度,?
char* p;
p=(char*)malloc(20);
calloc?span style="FONT-WEIGHT: bold">malloc怼,参数nelem为申请地址的单位元素长?elsize为元素个??
char* p;
p=(char*)calloc(sizeof(char),20);
q个例子与上一个效果相?br />
realloc是给一个已l分配了地址的指针重新分配空?参数ptr为原有的I间地址,newsize是重新申L地址长度
?
char* p;
p=(char*)malloc(sizeof(char)*20);
p=(char*)realloc(p,sizeof(char)*40);
注意Q这里的I间长度都是以字节ؓ单位?br />
C语言的标准内存分配函敎ͼmallocQ?a name="1">callocQreallocQfree{?br />malloc?span style="FONT-WEIGHT: bold">calloc的区别ؓ1块与n块的区别Q?br />malloc调用形式?cd*)malloc(size)Q在内存的动态存储区中分配一块长度ؓ“size”字节的q箋区域Q返回该区域的首地址?br />calloc调用形式?cd*)calloc(nQsize)Q在内存的动态存储区中分配n块长度ؓ“size”字节的q箋区域Q返回首地址?br />realloc调用形式?cd*)realloc(*ptrQsize)Q将ptr内存大小增大到size?br />free的调用Ş式ؓfree(void*ptr)Q释放ptr所指向的一块内存空间?br />C++中ؓnew/delete函数?/p>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1669532
现在不难理解q段E序Z么会出现d@环了。j[8]的位|就是变量i所在的位置。这样当i=8时的j[i]=0语句Q实际上是i的值置?Q然?i 又从0?循环一直下? 如果原句改为int j[8],i; ׃会出现死循环Q而仅仅是一个段界错误.
另一个程?
#include <stdio.h>
int main()
{
int i;
char c;
for(i=0;i<5;i++)
{
scanf("%d",&c);
printf("i=%d ",i);
}
printf("\n");
}
~译后运?br />[foxman@local~]#./a.out
0 (输入0)
i=0 (输出 i ?
1
i=0
2
i=0
3
i=0
4
i=0
...
q样一直@环下厅R?/font>
问题在于Qc被声明ؓcharcdQ而不是intcd。当E序要求scanfd一个整数时Q应该传递给它一个指向整数的指针。而程序中scanf得到的却是一个指向字W的指针Qscanf函数q不能分辨这U情况,只能这个指向字W的指针作ؓ指向整数的指针而接受,q且在指针指向的位置存储一个整数。因为整数所占的存储I间要大于字W所占的存储I间Q所以c附近的内存会被覆?
׃面分析,i ?c 是由高地址C地址存储在栈中,q样在c所在位|尝试存储一?字节变量Q会占用比c高的3个字?覆盖?i 字节的低3?Q即?i L为零Q一直@环下?
如果每次输入Ctrl+D作ؓ字符l止W不存储int到c处,那么׃输出正常i=0..4?
#ifndef lint
static char vcid[] = "$Id: directshow.c,v 1.22 2005/11/12 14:12:39 fuhuizhong Exp $";
#endif /* lint */
/**//*
* 作者:傅惠?
* /modified by xiaoshao_0_0
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <linux/types.h>
#include <linux/videodev.h>
#include "jpeglib.h"
#include <setjmp.h>
#include <SDL/SDL.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#define USB_VIDEO "/dev/video1"
SDL_Surface *screen,*img;
int cam_fd;
int width,height;
const int bpp = 24;
struct video_mmap cam_mm;
struct video_capability cam_cap;
struct video_picture cam_pic;
struct video_mbuf cam_mbuf;
struct video_window win;
char *cam_data = NULL;
int nframe;
void errexit(char *msg)
...{
fputs(msg,stderr);
exit(-1);
}
void e_sig(int signum)
...{
printf(" ERROR:signal %d ",signum);
exit(-1);
}
void setup_sighandler()
...{
signal(SIGTERM,&e_sig);
signal(SIGINT,&e_sig);
signal(SIGSEGV,&e_sig);
signal(SIGILL,&e_sig);
signal(SIGFPE,&e_sig);
}
void init_video(int w,int h,int bpp); /**//* bpp == bits per pixel, 暂时无效*/
void init_screen(int w,int h,int bpp); /**//* like above*/
void read_video(int ,int);
void show_img(char *pixels);
void window_loop();
void exchange_r_b( char * f,long size);
void compress_to_jpeg_file( FILE *outfile, char * image_buffer,int w,int h, int quality);
void save_snapshot();
void free_dev()
...{
printf("free device ");
close(cam_fd);
}
int main(int argc,char *argv[])
...{
int i;
float scale = 1.0;
if( argc == 2 ) ...{
scale = atof(argv[1]);
if(scale<0.3 || scale>1.0)
errexit("scale out of range (0.3 ~ 1.0) ");
}
width = (int)(640*scale);
height = (int)(480*scale);
atexit( &free_dev );
init_video(width,height,bpp);
init_screen(width,height,bpp);
setup_sighandler();
window_loop();
getchar();
munmap(cam_data,cam_mbuf.size);
exit(0);
}
void config_vid_pic()
...{
char *hp = getenv("HOME");
char cfpath[100];
FILE *cf;
int ret;
sprintf( cfpath,"%s/.dshow.conf",hp );
cf = fopen(cfpath,"r");
/**//* The struct video_picture consists of the following fields
brightness Picture brightness
hue Picture hue (colour only)
colour Picture colour (colour only)
contrast Picture contrast
whiteness The whiteness (greyscale only)
depth The capture depth (may need to match the frame buffer depth)
palette Reports the palette that should be used for this image
The following palettes are defined
VIDEO_PALETTE_GREY Linear intensity grey scale (255 is brightest).
VIDEO_PALETTE_HI240 The BT848 8bit colour cube.
VIDEO_PALETTE_RGB565 RGB565 packed into 16 bit words.
VIDEO_PALETTE_RGB555 RGV555 packed into 16 bit words, top bit undefined.
VIDEO_PALETTE_RGB24 RGB888 packed into 24bit words.
VIDEO_PALETTE_RGB32 RGB888 packed into the low 3 bytes of 32bit words. The top 8bits are undefined.
VIDEO_PALETTE_YUV422 Video style YUV422 - 8bits packed 4bits Y 2bits U 2bits V
VIDEO_PALETTE_YUYV Describe me
VIDEO_PALETTE_UYVY Describe me
VIDEO_PALETTE_YUV420 YUV420 capture
VIDEO_PALETTE_YUV411 YUV411 capture
VIDEO_PALETTE_RAW RAW capture (BT848)
VIDEO_PALETTE_YUV422P YUV 4:2:2 Planar
VIDEO_PALETTE_YUV411P YUV 4:1:1 Planar
*/
if (ioctl(cam_fd, VIDIOCGPICT, &cam_pic) < 0) ...{
errexit("ERROR:VIDIOCGPICT ");
}
//cam_pic.palette =VIDEO_PALETTE_RAW;
cam_pic.palette =VIDEO_PALETTE_RGB24;
if( cf==NULL ) ...{
cam_pic.brightness = 44464;
cam_pic.hue = 36000;
cam_pic.colour = 0;
cam_pic.contrast = 43312;
cam_pic.whiteness = 13312;
cam_pic.depth = 24;
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /**//*讄摄像头缓冲中voideo_picture信息*/
if( ret<0 ) ...{
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
return;
}
fscanf(cf,"%d",&cam_pic.brightness);
fscanf(cf,"%d",&cam_pic.hue);
fscanf(cf,"%d",&cam_pic.colour);
fscanf(cf,"%d",&cam_pic.contrast);
fscanf(cf,"%d",&cam_pic.whiteness);
fclose( cf );
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /**//*讄摄像头缓冲中voideo_picture信息*/
if( ret<0 ) ...{
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void init_video(int w,int h,int bpp) /**//* bpp == bytes per pixel*/
...{
int ret;
cam_fd = open( USB_VIDEO, O_RDWR );
if( cam_fd<0 )
errexit("Can't open video device ");
ret = ioctl( cam_fd,VIDIOCGCAP,&cam_cap );
/**//* 摄像头的基本信息
struct video_capability cam_cap;
name[32] Canonical name for this interface
type Type of interface
channels Number of radio/tv channels if appropriate
audios Number of audio devices if appropriate
maxwidth Maximum capture width in pixels
maxheight Maximum capture height in pixels
minwidth Minimum capture width in pixels
minheight Minimum capture height in pixels
*/
if( ret<0 ) ...{
errexit("Can't get device information: VIDIOCGCAP ");
}
print_device_info();
/**//* The struct video_window contains the following fields.
x The X co-ordinate specified in X windows format.
y The Y co-ordinate specified in X windows format.
width The width of the image capture.
height The height of the image capture.
chromakey A host order RGB32 value for the chroma key.
flags Additional capture flags.
clips A list of clipping rectangles. (Set only)
clipcount The number of clipping rectangles. (Set only)
*/
if( ioctl(cam_fd,VIDIOCGWIN,&win)<0 ) ...{
errexit("ERROR:VIDIOCGWIN ");
}
win.x = 0;
win.y = 0;
win.width=width;
win.height=height;
if (ioctl(cam_fd, VIDIOCSWIN, &win) < 0) ...{
errexit("ERROR:VIDIOCSWIN ");
}
config_vid_pic();
ret = ioctl(cam_fd,VIDIOCGMBUF,&cam_mbuf);
/**//*
struct video_mbuf
{
int size; Total memory to map
int frames; Frames
int offsets[VIDEO_MAX_FRAME];
};
*/
/**//*struct video_buffer.
视频~存的信息读取结构,讑֮也是一Ll构。但是一般是X自己讑֮Q你只要d好了?
void *base Base physical address of the buffer
int height Height of the frame buffer
int width Width of the frame buffer
int depth Depth of the frame buffer
int bytesperline Number of bytes of memory between the start of two adjacent lines*/
if( ret<0 ) ...{
errexit("ERROR:VIDIOCGMBUF,Can't get video_mbuf ");
}
printf("Frames:%d ",cam_mbuf.frames);
nframe = cam_mbuf.frames;
cam_data = (char*)mmap(0, cam_mbuf.size, PROT_READ|PROT_WRITE,MAP_SHARED,cam_fd,0);
if( cam_data == MAP_FAILED ) ...{
errexit("ERROR:mmap ");
}
printf("Buffer size:%d Offset:%d ",cam_mbuf.size,cam_mbuf.offsets[0]);
}
void print_device_info()
...{
int type=cam_cap.type;
int i;
char type_info[14][100]=
...{
"VID_TYPE_CAPTURE Can capture to memory",
"VID_TYPE_TUNER Has a tuner of some form",
"VID_TYPE_TELETEXT Has teletext capability",
"VID_TYPE_OVERLAY Can overlay its image onto the frame buffer",
"VID_TYPE_CHROMAKEY Overlay is Chromakeyed",
"VID_TYPE_CLIPPING Overlay clipping is supported",
"VID_TYPE_FRAMERAM Overlay overwrites frame buffer memory",
"VID_TYPE_SCALES The hardware supports image scaling",
"VID_TYPE_MONOCHROME Image capture is grey scale only",
"VID_TYPE_SUBCAPTURE Capture can be of only part of the image",
"VID_TYPE_MPEG_DECODER Can decode MPEG streams",
"VID_TYPE_MPEG_ENCODER Can encode MPEG streams",
"VID_TYPE_MJPEG_DECODER Can decode MJPEG streams",
"VID_TYPE_MJPEG_ENCODER Can encode MJPEG streams",
};
printf("Device name:%s Width:%d ~ %d Height:%d ~ %d ",
cam_cap.name,
cam_cap.maxwidth, cam_cap.minwidth,
cam_cap.maxheight, cam_cap.minheight);
for(i=0;i<14;i++)
...{
if(type&(2^i))
...{ printf(type_info[i]);
printf(" ");
}
else
...{
printf("==not supported==%s",type_info[i]);
printf(" ");
}
}
}
void init_screen(int w,int h,int bpp) // like above
...{
if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) ...{
fprintf(stderr, "无法初始化SDL: %s ", SDL_GetError());
exit(1);
}
img = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, bpp, 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000);
screen = SDL_SetVideoMode(width, height, bpp, SDL_SWSURFACE);
if ( screen == NULL ) ...{
fprintf(stderr, "无法讄视频模式Q?s ", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
}
void read_video(int captrue_frame,int sync_frame)
...{
int ret;
//int frame=0; /*q个g比较关键*/
//q个地方原作者弄错了?
cam_mm.frame=captrue_frame;
/**//* struct video_mmap
{
unsigned int frame; Frame (0 - n) for double buffer
int height,width;
unsigned int format; should be VIDEO_PALETTE_*
};
*/
ret = ioctl(cam_fd,VIDIOCMCAPTURE,&cam_mm);
if( ret<0 ) ...{
errexit("ERROR: VIDIOCMCAPTURE ");
}
//cam_mm.frame=sync_frame;
ret = ioctl(cam_fd,VIDIOCSYNC,&sync_frame);
if( ret<0 ) ...{
errexit("ERROR: VIDIOCSYNC ");
}
}
void show_img(char *pixels)
...{
int row_stride = width*3;
char *pbuf = (char*)img->pixels;
int row;
/**//* for(row=0; row<height; row++) {
memcpy(pbuf, pixels, row_stride);
pbuf += img->pitch;
pixels += row_stride;
}*/
memcpy(pbuf,pixels,row_stride*height);
SDL_BlitSurface(img, NULL, screen, NULL);
SDL_UpdateRect(screen,0,0,width,height);
}
void window_loop()
...{
int ret;
SDL_Event event;
int keystat = 0;
Uint32 ticks = 0;
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,1000);
/**///////////////
cam_mm.height = height;
cam_mm.width = width;
cam_mm.frame=0;
cam_mm.format=VIDEO_PALETTE_RGB24;
ret = ioctl(cam_fd,VIDIOCMCAPTURE,&cam_mm);
if( ret<0 ) ...{
errexit("ERROR: VIDIOCMCAPTURE ");
}
//Here just start caputer frame0,it not in the loop.
/**///////////////
while ( 1 ) ...{
SDL_PollEvent(&event);
switch (event.type) ...{
case SDL_QUIT:
exit(0);
case SDL_KEYDOWN:
if( event.key.keysym.sym == SDLK_F8 && keystat == 0 )
save_snapshot();
if( event.key.keysym.sym == SDLK_F9 && keystat == 0 )
config_vid_pic();
if( event.key.keysym.sym == SDLK_UP && keystat == 0 )
increase_brightness();
if( event.key.keysym.sym == SDLK_DOWN && keystat == 0 )
decrease_birghtness();
if( event.key.keysym.sym == SDLK_LEFT && keystat== 0 )
increase_contrast();
if( event.key.keysym.sym == SDLK_RIGHT && keystat==0 )
decrease_contrast();
keystat = 1;
break;
case SDL_KEYUP:
keystat = 0;
break;
default:
break;
}
if( (SDL_GetTicks()-ticks)<30)
continue;
read_video(1,0);//captrue=1,sync=0
show_img( cam_data+cam_mbuf.offsets[0]);
read_video(0,1);//captrue=0,sync=1
show_img( cam_data+cam_mbuf.offsets[1]);
ticks = SDL_GetTicks();
}
}
void increase_brightness()
...{
int ret=0;
cam_pic.brightness+=1000;
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /**//*讄摄像头缓冲中voideo_picture信息*/
if( ret<0 ) ...{
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void decrease_birghtness()
...{
int ret=0;
cam_pic.brightness-=1000;
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /**//*讄摄像头缓冲中voideo_picture信息*/
if( ret<0 ) ...{
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void increase_contrast()
...{
int ret=0;
cam_pic.contrast+=1000;
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /**//*讄摄像头缓冲中voideo_picture信息*/
if( ret<0 ) ...{
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void decrease_contrast()
...{
int ret=0;
cam_pic.contrast-=1000;
ret = ioctl( cam_fd, VIDIOCSPICT,&cam_pic ); /**//*讄摄像头缓冲中voideo_picture信息*/
if( ret<0 ) ...{
close(cam_fd);
errexit("ERROR: VIDIOCSPICT,Can't set video_picture format ");
}
}
void compress_to_jpeg_file( FILE *outfile, char * image_buffer,int w,int h, int quality)
...{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1]; /**//* pointer to JSAMPLE row[s] */
int row_stride; /**//* physical row width in image buffer */
int image_width;
int image_height;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, outfile);
image_width = w;
image_height = h;
cinfo.image_width = image_width; /**//* image width and height, in pixels */
cinfo.image_height = image_height;
cinfo.input_components = 3; /**//* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /**//* colorspace of input image */
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE /**//* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, TRUE);
row_stride = image_width * 3; /**//* JSAMPLEs per row in image_buffer */
while (cinfo.next_scanline < cinfo.image_height) ...{
row_pointer[0] = (JSAMPROW)& image_buffer[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
/**//* And we're done! */
}
void exchange_r_b( char * f,long size)
...{
char r,b;
long i;
for( i = 0; i < size ; i++)
...{
r = *f;
b = *( f + 2);
*f = b;
*(f + 2) = r;
f = f +3;
}
}
void save_snapshot()
...{
char *basepath = getenv("HOME");
char *basename = "/snapsot";
char *extname = ".jpg";
char filename[100];
int i = 0;
FILE *fo;
for(;;) ...{
sprintf(filename,"%s%s%d%s",basepath,basename,i,extname);
fo = fopen(filename,"rb");
if( fo==NULL )
break;
else
fclose(fo);
i += 1;
}
printf("snapshot:%s ",filename);
fo = fopen(filename,"wb");
exchange_r_b( cam_data, width*height );
compress_to_jpeg_file( fo, cam_data, width, height, 90 );
fclose(fo);
}
一个比较清淅的摄像头参数设|?br />
//loadVariablesNum("schlist.tx", 0);
//fscommand("fullscrean", true);
var my_cam = Camera.get();
//LAN
//Lower image quality, higher motion quality my_cam.setQuality(400000,0)
//Higher image quality, lower motion qualitymy_cam.setQuality(0,100)
//// 保最低品质ؓ 100Q无论采用多大的带宽
my_cam.setQuality(0, 100); //
my_cam.setKeyFrameInterval(15);
my_cam.setLoopback(true);
//my_cam.setMotionLevel(35, 1000);
my_cam.setMode(320, 240, 25, true);
// 捕获摄像?br />myVideo1.attachVideo(my_cam);
myVideo1.smoothing = true;
myVideo1._alpha = 100;
myVideo1._rotation = 360;
// 在舞C?myVideo1 对象的边界内昄视频?br />my_cam.onStatus = function(infoMsg) {
if (infoMsg.code == 'Camera.Muted') {
// 拒绝
trace('User denies access to the camera');
} else if (infoMsg.code == 'Camera.Unmuted') {
// 接受
trace('User allows access to the camera');
}
};
//System.showSettings(3);
// 昄指定?Flash Player"讄"面板
stop();
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#define BUFFER_SIZE 16
struct prodcons
{
int buffer[BUFFER_SIZE];
pthread_mutex_t lock;/*互斥?/
int readpos, writepos;
pthread_cond_t notempty;/*~冲区非IZ?/
pthread_cond_t notfull;/*~冲区非满信?/
};
void init(struct prodcons *b)
{
pthread_mutex_init(&b->lock, NULL);
pthread_cond_init(&b->notempty, NULL);
pthread_cond_init(&b->notfull, NULL);
b->readpos = 0;
b->writepos = 0;
}
void put(struct prodcons *b, int data)
{
pthread_mutex_lock(&b->lock);//获取互斥?br /> while((b->writepos + 1) % BUFFER_SIZE == b->readpos)
{
printf("wait for not full\n");
pthread_cond_wait(&b->notfull, &b->lock);//不满旉出d
}
b->buffer[b->writepos] = data;
b->writepos++;
if(b->writepos >= BUFFER_SIZE) b->writepos = 0;
pthread_cond_signal(&b->notempty);//讄状态变?br /> pthread_mutex_unlock(&b->lock);//释放互斥?br />}
int get(struct prodcons *b)
{
int data;
pthread_mutex_lock(&b->lock);
while(b->writepos == b->readpos)
{
printf("wait for not empty\n");
pthread_cond_wait(&b->notempty, &b->lock);
}
data = b->buffer[b->readpos];
b->readpos++;
if(b->readpos >= BUFFER_SIZE) b->readpos = 0;
pthread_cond_signal(&b->notfull);
pthread_mutex_unlock(&b->lock);
return data;
}
#define OVER (-1)
struct prodcons buffer;
void *producer(void *data)
{
int n;
for(n=0; n<1000; n++)
{
printf("put->%d\n",n);
put(&buffer, n);
}
put(&buffer, OVER);
printf("producer stopped\n");
return NULL;
}
void *consumer(void *data)
{
int d;
while(1)
{
d = get(&buffer);
if(d == OVER)
break;
printf(" %d->get\n",d);
}
printf("consumer stopped!\n");
return NULL;
}
int main(void)
{
pthread_t th_a, th_b;
void *retval;
init(&buffer);
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
return 0;
}
pthread_cond_wait函数Q是U程d在一个条件变量上Q原型ؓQ?br />extern int pthread_cond_wait(pthread_cond_t *_restrict_cond, pthread_mutex_t* _restrict_mutex)
U程解开mutex指向的锁q被条g变量conddQ线E可以被函数pthread_cond_signal和pthread_cond_broadcast唤醒?br />q有另一个函数pthread_cond_timedwait函数Q他比vpthread_cond_wait函数多一个时间参敎ͼl历l定旉後,d被解除?/p>
int **AllocMatrix ( int iRow, int iCol )
void FreeMatrix ( int** p )
CZE序Q?/p>
int *a;
int N;
scanf("%d", &N);
a = (int *) malloc(N * sizeof(int));
....
free(a);
q样动态分配了数组a[N]。数l的长度N可输入确定,也可用程序中的变量确定。但要注意程序结束后要用free()其释放Q否则内存会泄漏?
--------------------------------------------------------------------------------
验证一下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i = 0;
int *a;
int N;
printf("Input array length: ");
scanf("%d", &N);
printf("\n");
a = (int *) malloc(N * sizeof(int));
for(i = 0; i < N; i++)
{
a[i] = i + 1;
printf("%-5d", a[i]);
if ((i + 1) % 10 == 0)
printf("\n");
}
free(a);
printf("\n");
return 0;
}
q行l果(VC):
=========================================================
Input array length: 100?/p>
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
=========================================================