2011年9月13日

http://blog.csdn.net/popy007

posted @ 2011-09-13 15:45 Seven_Yuan 阅读(40) 评论(0) 编辑

2010年12月20日

转自:http://www.itcodeworld.com/?p=105

使用结构CRITICAL_SECTION 需加入头文件#include “afxmt.h”

定义一个全局的锁 CRITICAL_SECTION的实例
和一个静态全局变量

CRITICAL_SECTION cs;//可以理解为锁定一个资源
static int n_AddValue = 0;//定义一个静态的全部变量n_AddValue

创建两个线程函数,代码实现如下:

代码
//第一个线程
UINT FirstThread(LPVOID lParam)
{
EnterCriticalSection(
&cs);//加锁 接下来的代码处理过程中不允许其他线程进行操作,除非遇到LeaveCriticalSection
for(int i = 0; i<10; i++){
n_AddValue
++;
cout
<< "n_AddValue in FirstThread is "<<n_AddValue <<endl;
}
LeaveCriticalSection(
&cs);//解锁 到EnterCriticalSection之间代码资源已经释放了,其他线程可以进行操作
return 0;
}
//第二个线程
UINT SecondThread(LPVOID lParam)
{
EnterCriticalSection(
&cs);//加锁
for(int i = 0; i<10; i++){
n_AddValue
++;
cout
<< "n_AddValue in SecondThread is "<<n_AddValue <<endl;

}
LeaveCriticalSection(
&cs);//解锁
return 0;
}

 

在主函数添加以下代码

代码
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

// 初始化 MFC 并在失败时显示错误
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: 更改错误代码以符合您的需要
_tprintf(_T("错误: MFC 初始化失败\n"));
nRetCode
= 1;
}
else
{
InitializeCriticalSection(
&cs);//初始化结构CRITICAL_SECTION

CWinThread
*pFirstThread,*pSecondThread;//存储函数AfxBeginThread返回的CWinThread指针

pFirstThread
= AfxBeginThread(FirstThread,LPVOID(NULL));//启动第一个线程
pSecondThread = AfxBeginThread(SecondThread,LPVOID(NULL));//启动第二个线程

HANDLE hThreadHandle[
2];//
hThreadHandle[0] = pFirstThread->m_hThread;
hThreadHandle[
1] = pSecondThread->m_hThread;

//等待线程返回
WaitForMultipleObjects(2,hThreadHandle,TRUE,INFINITE);
}

return nRetCode;
}

 

输出:

n_AddValue in FirstThread is 1
n_AddValue in FirstThread is 2
n_AddValue in FirstThread is 3
n_AddValue in FirstThread is 4
n_AddValue in FirstThread is 5
n_AddValue in FirstThread is 6
n_AddValue in FirstThread is 7
n_AddValue in FirstThread is 8
n_AddValue in FirstThread is 9
n_AddValue in FirstThread is 10
n_AddValue in SecondThread is 11
n_AddValue in SecondThread is 12
n_AddValue in SecondThread is 13
n_AddValue in SecondThread is 14
n_AddValue in SecondThread is 15
n_AddValue in SecondThread is 16
n_AddValue in SecondThread is 17
n_AddValue in SecondThread is 18
n_AddValue in SecondThread is 19
n_AddValue in SecondThread is 20

如果把两个线程函数中的EnterCriticalSection和LeaveCriticalSection位置移到for循环中去,线程的执行顺序将会改变
输出也就跟着改变,如:

 

代码
//第一个线程
UINT FirstThread(LPVOID lParam)
{

for(int i = 0; i<10; i++){
EnterCriticalSection(
&cs);//加锁 锁移到for循环内部里
n_AddValue ++;
cout
<< "n_AddValue in FirstThread is "<<n_AddValue <<endl;
LeaveCriticalSection(
&cs);//解锁
}
return 0;
}

//第二个线程
UINT SecondThread(LPVOID lParam)
{

for(int i = 0; i<10; i++){
EnterCriticalSection(
&cs);//加锁
n_AddValue ++;
cout
<< "n_AddValue in SecondThread is "<<n_AddValue <<endl;
LeaveCriticalSection(
&cs);//解锁
}
return 0;
}

 

其他代码不变,输出的结果如下:

n_AddValue in FirstThread is 1
n_AddValue in SecondThread is 2
n_AddValue in FirstThread is 3
n_AddValue in SecondThread is 4
n_AddValue in FirstThread is 5
n_AddValue in SecondThread is 6
n_AddValue in FirstThread is 7
n_AddValue in SecondThread is 8
n_AddValue in FirstThread is 9
n_AddValue in SecondThread is 10
n_AddValue in FirstThread is 11
n_AddValue in SecondThread is 12
n_AddValue in FirstThread is 13
n_AddValue in SecondThread is 14
n_AddValue in FirstThread is 15
n_AddValue in SecondThread is 16
n_AddValue in FirstThread is 17
n_AddValue in SecondThread is 18
n_AddValue in FirstThread is 19
n_AddValue in SecondThread is 20

个人认为在函数EnterCriticalSection和LeaveCriticalSection中间的代码执行过程不会被其他线程干拢或者这么讲不允许其他线程中
的代码执行。这样可以有效防止一个全局变量在两个线程中同时被操作的可能性

posted @ 2010-12-20 17:45 Seven_Yuan 阅读(1141) 评论(0) 编辑

2010年12月15日

    众多C++书籍都忠告我们C语言宏是万恶之首,但事情总不如我们想象的那么坏,就如同goto一样。宏有
一个很大的作用,就是自动为我们产生代码。如果说模板可以为我们产生各种型别的代码(型别替换),
那么宏其实可以为我们在符号上产生新的代码(即符号替换、增加)。

关于宏的一些语法问题,可以在google上找到。相信我,你对于宏的了解绝对没你想象的那么多。如果你
还不知道#和##,也不知道prescan,那么你肯定对宏的了解不够。

我稍微讲解下宏的一些语法问题(说语法问题似乎不妥,macro只与preprocessor有关,跟语义分析又无关):

1. 宏可以像函数一样被定义,例如:
   #define min(x,y) (x<y?x:y) //事实上这个宏存在BUG
   但是在实际使用时,只有当写上min(),必须加括号,min才会被作为宏展开,否则不做任何处理。
2. 如果宏需要参数,你可以不传,编译器会给你警告(宏参数不够),但是这会导致错误。如C++书籍中所描
   述的,编译器(预处理器)对宏的语法检查不够,所以更多的检查性工作得你自己来做。

3. 很多程序员不知道的#和##
   #符号把一个符号直接转换为字符串,例如:
   #define STRING(x) #x
   const char *str = STRING( test_string ); str的内容就是"test_string",也就是说#会把其后的符号
   直接加上双引号。
   ##符号会连接两个符号,从而产生新的符号(词法层次),例如:
   #define SIGN( x ) INT_##x
   int SIGN( 1 ); 宏被展开后将成为:int INT_1;

4. 变参宏,这个比较酷,它使得你可以定义类似的宏:
   #define LOG( format, ... ) printf( format, __VA_ARGS__ )
   LOG( "%s %d", str, count );
   __VA_ARGS__是系统预定义宏,被自动替换为参数列表。

5. 当一个宏自己调用自己时,会发生什么?例如:
   #define TEST( x ) ( x + TEST( x ) )
   TEST( 1 ); 会发生什么?为了防止无限制递归展开,语法规定,当一个宏遇到自己时,就停止展开,也就是
   说,当对TEST( 1 )进行展开时,展开过程中又发现了一个TEST,那么就将这个TEST当作一般的符号。TEST(1)
   最终被展开为:1 + TEST( 1) 。

6. 宏参数的prescan,
   当一个宏参数被放进宏体时,这个宏参数会首先被全部展开(有例外,见下文)。当展开后的宏参数被放进宏体时,
   预处理器对新展开的宏体进行第二次扫描,并继续展开。例如:
   #define PARAM( x ) x
   #define ADDPARAM( x ) INT_##x
   PARAM( ADDPARAM( 1 ) );
   因为ADDPARAM( 1 ) 是作为PARAM的宏参数,所以先将ADDPARAM( 1 )展开为INT_1,然后再将INT_1放进PARAM。
  
   例外情况是,如果PARAM宏里对宏参数使用了#或##,那么宏参数不会被展开:
   #define PARAM( x ) #x
   #define ADDPARAM( x ) INT_##x
   PARAM( ADDPARAM( 1 ) ); 将被展开为"ADDPARAM( 1 )"。

   使用这么一个规则,可以创建一个很有趣的技术:打印出一个宏被展开后的样子,这样可以方便你分析代码:
   #define TO_STRING( x ) TO_STRING1( x )
   #define TO_STRING1( x ) #x
   TO_STRING首先会将x全部展开(如果x也是一个宏的话),然后再传给TO_STRING1转换为字符串,现在你可以这样:
   const char *str = TO_STRING( PARAM( ADDPARAM( 1 ) ) );去一探PARAM展开后的样子。

7. 一个很重要的补充:就像我在第一点说的那样,如果一个像函数的宏在使用时没有出现括号,那么预处理器只是
   将这个宏作为一般的符号处理(那就是不处理)。


我们来见识一下宏是如何帮助我们自动产生代码的。如我所说,宏是在符号层次产生代码。我在分析Boost.Function
模块时,因为它使用了大量的宏(宏嵌套,再嵌套),导致我压根没看明白代码。后来发现了一个小型的模板库ttl,说的
是开发一些小型组件去取代部分Boost(这是一个好理由,因为Boost确实太大)。同样,这个库也包含了一个function库。
这里的function也就是我之前提到的functor。ttl.function库里为了自动产生很多类似的代码,使用了一个宏:

#define TTL_FUNC_BUILD_FUNCTOR_CALLER(n)  \
 template< typename R, TTL_TPARAMS(n) > \
 struct functor_caller_base##n \
        ///...
该宏的最终目的是:通过类似于TTL_FUNC_BUILD_FUNCTOR_CALLER(1)的调用方式,自动产生很多functor_caller_base模板:
template <typename R, typename T1> struct functor_caller_base1
template <typename R, typename T1, typename T2> struct functor_caller_base2
template <typename R, typename T1, typename T2, typename T3> struct functor_caller_base3
///...
那么,核心部分在于TTL_TPARAMS(n)这个宏,可以看出这个宏最终产生的是:
typename T1
typename T1, typename T2
typename T1, typename T2, typename T3
///...
我们不妨分析TTL_TPARAMS(n)的整个过程。分析宏主要把握我以上提到的一些要点即可。以下过程我建议你翻着ttl的代码,
相关代码文件:function.hpp, macro_params.hpp, macro_repeat.hpp, macro_misc.hpp, macro_counter.hpp。

so, here we go

分析过程,逐层分析,逐层展开,例如TTL_TPARAMS(1):

#define TTL_TPARAMS(n) TTL_TPARAMSX(n,T) 
=> TTL_TPARAMSX( 1, T )
#define TTL_TPARAMSX(n,t) TTL_REPEAT(n, TTL_TPARAM, TTL_TPARAM_END, t)
=> TTL_REPEAT( 1, TTL_TPARAM, TTL_TPARAM_END, T )
#define TTL_TPARAM(n,t) typename t##n,
#define TTL_TPARAM_END(n,t) typename t##n
#define TTL_REPEAT(n, m, l, p) TTL_APPEND(TTL_REPEAT_, TTL_DEC(n))(m,l,p) TTL_APPEND(TTL_LAST_REPEAT_,n)(l,p)
注意,TTL_TPARAM, TTL_TPARAM_END虽然也是两个宏,他们被作为TTL_REPEAT宏的参数,按照prescan规则,似乎应该先将
这两个宏展开再传给TTL_REPEAT。但是,如同我在前面重点提到的,这两个宏是function-like macro,使用时需要加括号,
如果没加括号,则不当作宏处理。因此,展开TTL_REPEAT时,应该为:
=> TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T) TTL_APPEND( TTL_LAST_REPEAT_,1)(
TTL_TPARAM_END,T)
这个宏体看起来很复杂,仔细分析下,可以分为两部分:
TTL_APPEND( TTL_REPEAT_, TTL_DEC(1))(TTL_TPARAM,TTL_TPARAM_END,T)以及
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
先分析第一部分:
#define TTL_APPEND( x, y ) TTL_APPEND1(x,y) //先展开x,y再将x,y连接起来
#define TTL_APPEND1( x, y ) x ## y
#define TTL_DEC(n) TTL_APPEND(TTL_CNTDEC_, n)
根据先展开参数的原则,会先展开TTL_DEC(1)
=> TTL_APPEND(TTL_CNTDEC_,1) => TTL_CNTDEC_1
#define TTL_CNTDEC_1 0  注意,TTL_CNTDEC_不是宏,TTL_CNTDEC_1是一个宏。
=> 0 , 也就是说,TTL_DEC(1)最终被展开为0。回到TTL_APPEND部分:
=> TTL_REPEAT_0 (TTL_TPARAM,TTL_TPARAM_END,T)
#define TTL_REPEAT_0(m,l,p)
TTL_REPEAT_0这个宏为空,那么,上面说的第一部分被忽略,现在只剩下第二部分:
TTL_APPEND( TTL_LAST_REPEAT_,1)(TTL_TPARAM_END,T)
=> TTL_LAST_REPEAT_1 (TTL_TPARAM_END,T) // TTL_APPEND将TTL_LAST_REPEAT_和1合并起来
#define TTL_LAST_REPEAT_1(m,p) m(1,p)
=> TTL_TPARAM_END( 1, T )
#define TTL_TPARAM_END(n,t) typename t##n
=> typename T1  展开完毕。

虽然我们分析出来了,但是这其实并不是我们想要的。我们应该从那些宏里去获取作者关于宏的编程思想。很好地使用宏
看上去似乎是一些偏门的奇技淫巧,但是他确实可以让我们编码更自动化。

 

转自:http://www.kuqin.com/language/20080319/4797.html

posted @ 2010-12-15 21:43 Seven_Yuan 阅读(366) 评论(0) 编辑

第一、四个用途

用途一:

定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,
// 和一个字符变量;
以下则可行:
typedef char* PCHAR; // 一般用大写
PCHAR pa, pb; // 可行,同时声明了两个指向字符变量的指针
虽然:
char *pa, *pb;
也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

用途二:

用在旧的C的代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名,如:

struct tagPOINT1
{
int x;
int y;
};
struct tagPOINT1 p1;

 

 

 

而在C++中,则可以直接写:结构名 对象名,即:
tagPOINT1 p1;

估计某人觉得经常多写一个struct太麻烦了,于是就发明了:

 

typedef struct tagPOINT
{
int x;
int y;
}POINT;

POINT p1;
// 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候

 

 

或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。

用途三:

用typedef来定义与平台无关的类型。
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL;
在不支持 long double 的平台二上,改为:
typedef double REAL;
在连 double 都不支持的平台三上,改为:
typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

用途四:

为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:

1. 原声明:int *(*a[5])(int, char*);
变量名为a,直接用一个新别名pFun替换a就可以了:
typedef int *(*pFun)(int, char*);
原声明的最简化版:
pFun a[5];

2. 原声明:void (*b[10]) (void (*)());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];

3. 原声明:doube(*)() (*e)[9];
变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e;

理解复杂声明可用的“右左法则”:
从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int (*func)(int *p);
首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。
int (*func[5])(int *);
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

也可以记住2个模式:
type (*)(....)函数指针
type (*)[]数组指针

第二、两大陷阱

陷阱一:

记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:
typedef char* PSTR;
然后:
int mystrcmp(const PSTR, const PSTR);

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

陷阱二:

typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。

以上资料出自:http://blog.sina.com.cn/s/blog_4826f7970100074k.html 作者:赤龙

第三、typedef 与 #define的区别

案例一:

通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:

 

typedef char *pStr1;
#define pStr2 char *;
pStr1 s1, s2;
pStr2 s3, s4;

 

 

在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。

案例二:

下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?

 

typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1
++;
p2
++;

 

 

是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。

第四部分资料:使用 typedef 抑制劣质代码

作者:Danny Kalev
编译:MTT 工作室

原文出处:Using typedef to Curb Miscreant Code

摘要:Typedef 声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用 typedef 避免缺欠,从而使代码更健壮。

typedef 声明,简称 typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。所谓美观,意指 typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱。

Q:如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法?

A: 使用 typedefs 为现有类型创建同义字。

定义易于记忆的类型名
  typedef 使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于 ''typedef'' 关键字右边。例如:

typedef int size;

此声明定义了一个 int 的同义字,名字为 size。注意 typedef 并不创建新的类型。它仅仅为现有类型添加一个同义字。你可以在任何需要 int 的上下文中使用 size:

void measure(size * psz); size array[4];size len = file.getlength();std::vector <size> vs; 

typedef 还可以掩饰符合类型,如指针和数组。例如,你不用象下面这样重复定义有 81 个字符元素的数组:

char line[81];char text[81];

定义一个 typedef,每当要用到相同类型和大小的数组时,可以这样:

typedef char Line[81]; Line text, secondline;getline(text);

同样,可以象下面这样隐藏指针语法:

typedef char * pstr;int mystrcmp(pstr, pstr);

这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个‘const char *’类型的参数。因此,它可能会误导人们象下面这样声明 mystrcmp():

int mystrcmp(const pstr, const pstr); 

这是错误的,按照顺序,‘const pstr’被解释为‘char * const’(一个指向 char 的常量指针),而不是‘const char *’(指向常量 char 的指针)。这个问题很容易解决:

typedef const char * cpstr; int mystrcmp(cpstr, cpstr); // 现在是正确的

记住:不管什么时候,只要为指针声明 typedef,那么都要在最终的 typedef 名称中加一个 const,以使得该指针本身是常量,而不是对象。

代码简化
  上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是 typedef 在编译时被解释,因此让编译器来应付超越预处理器能力的文本替换。例如:

typedef int (*PF) (const char *, const char *);

这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的:

PF Register(PF pf);

Register() 的参数是一个 PF 类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的:

int (*Register (int (*pf)(const char *, const char *))) (const char *, const char *); 

很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权,而是一种必需。持怀疑态度的人可能会问:“OK,有人还会写这样的代码吗?”,快速浏览一下揭示 signal()函数的头文件 <csinal>,一个有同样接口的函数。

typedef 和存储类关键字(storage class specifier)
  这种说法是不是有点令人惊讶,typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。这并是说 typedef 会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。下面将带到第二个陷阱:

typedef register int FAST_COUNTER; // 错误

编译通不过。问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置,在 typedef 声明中不能用 register(或任何其它存储类关键字)。

促进跨平台开发
  typedef 有另外一个重要的用途,那就是定义机器无关的类型,例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以i获得最高的精度:

typedef long double REAL; 

在不支持 long double 的机器上,该 typedef 看起来会是下面这样:

typedef double REAL; 

并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样: 、

typedef float REAL; 

你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法,例如:basic_string<char, char_traits<char>,allocator<char>> 和 basic_ofstream<char, char_traits<char>>。

 

以上转自:http://www.kuqin.com/language/20090322/41866.html

 

typedef & 结构的问题
  

1)、typedef的最简单使用
  typedef long byte_4;
  给已知数据类型long起个新名字,叫byte_4。
(2)、 typedef与结构结合使用
  typedef struct tagMyStruct
  {
  int iNum;
  long lLength;
  } MyStruct;
  这语句实际上完成两个操作:
1) 定义一个新的结构类型
  struct tagMyStruct
  {
  int iNum;
  long lLength;
  };
  分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,struct 关键字和tagMyStruct一起,构成了这个结构类型,不论是否有typedef,这个结构都存在。
  我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
2) typedef为这个新的结构起了一个名字,叫MyStruct。
  typedef struct tagMyStruct MyStruct;
  因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。
3)、规范做法:
  struct tagNode
  {
  char *pItem;
  struct tagNode *pNext;
  };
  typedef struct tagNode *pNode;
3. typedef & #define的问题
  有下面两种定义pStr数据类型的方法,两者有什么不同?哪一种更好一点?
  typedef char* pStr;
  #define pStr char*;

  答案与分析:
  通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:
  typedef char* pStr1;
  #define pStr2 char *
  pStr1 s1, s2;
  pStr2 s3, s4;
  在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。
  上例中define语句必须写成 pStr2 s3, *s4; 这这样才能正常执行。
  #define用法例子:
  #define f(x) x*x
  main( )
  {
  int a=6,b=2,c;
  c=f(a) / f(b);
  printf("%d \\n",c);
  }
  以下程序的输出结果是: 36。
  因为如此原因,在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,则上述定义应该如下定义才对:
  #define f(x) (x*x)
  当然,如果你使用typedef就没有这样的问题。
4. typedef & #define的另一例
  下面的代码中编译器会报一个错误,你知道是哪个语句错了吗?
  typedef char * pStr;
  char string[4] = "abc";
  const char *p1 = string;
  const pStr p2 = string;
  p1++;
  p2++;
  答案与分析:
  是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。
  #define与typedef引申谈
  1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
  2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。
5. typedef & 复杂的变量声明
  在编程实践中,尤其是看别人代码的时候,常常会遇到比较复杂的变量声明,使用typedef作简化自有其价值,比如:
  下面是三个变量的声明,我想使用typdef分别给它们定义一个别名,请问该如何做?
  >1:int *(*a[5])(int, char*);
  >2:void (*b[10]) (void (*)());
  >3. double(*)() (*pa)[9];
  答案与分析:
  对复杂变量建立一个类型别名的方法很简单,你只要在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头就行了。
  >1:int *(*a[5])(int, char*);
  //pFun是我们建的一个类型别名
  typedef int *(*pFun)(int, char*);
  //使用定义的新类型来声明对象,等价于int* (*a[5])(int, char*);
  pFun a[5];
  >2:void (*b[10]) (void (*)());
  //首先为上面表达式蓝色部分声明一个新类型
  typedef void (*pFunParam)();
  //整体声明一个新类型
  typedef void (*pFun)(pFunParam);
  //使用定义的新类型来声明对象,等价于void (*b[10]) (void (*)());
  pFun b[10];
  >3. double(*(*pa)[9])();
  //首先为上面表达式蓝色部分声明一个新类型
  typedef double(*pFun)();
  //整体声明一个新类型
  typedef pFun (*pFunParam)[9];
  //使用定义的新类型来声明对象,等价于double(*(*pa)[9])();
  pFunParam pa;

posted @ 2010-12-15 21:41 Seven_Yuan 阅读(488) 评论(0) 编辑

2010年12月10日

转自:http://www.91linux.com/html/article/program/python/20090804/17759.htm

在python有各种各样的string操作函数。在历史上string类在python中经历了一段轮回的历史。在最开始的时候,python有一个专门的string的module,要使用string的方法要先import,但后来由于众多的python使用者的建议,从python2.0开始,string方法改为用S.method()的形式调用,只要S是一个字符串对象就可以这样使用,而不用import。同时为了保持向后兼容,现在的python中仍然保留了一个string的module,其中定义的方法与S.method()是相同的,这些方法都最后都指向了用S.method()调用的函数。要注意,S.method()能调用的方法比string的module中的多,比如isdigit()、istitle()等就只能用S.method()的方式调用。

 

对一个字符串对象,首先想到的操作可能就是计算它有多少个字符组成,很容易想到用S.len(),但这是错的,应该是len(S)。因为len()是内置函数,包括在__builtin__模块中。python不把len()包含在string类型中,乍看起来好像有点不可理解,其实一切有其合理的逻辑在里头。len()不仅可以计算字符串中的字符数,还可以计算list的成员数,tuple的成员数等等,因此单单把len()算在string里是不合适,因此一是可以把len()作为通用函数,用重载实现对不同类型的操作,还有就是可以在每种有len()运算的类型中都要包含一个len()函数。python选择的是第一种解决办法。类似的还有str(arg)函数,它把arg用string类型表示出来。

字符串中字符大小写的变换:


S.lower() #小写
S.upper() #大写
S.swapcase() #大小写互换
S.capitalize() #首字母大写
String.capwords(S)
#这是模块中的方法。它把S用split()函数分开,然后用capitalize()把首字母变成大写,最后用join()合并到一起
S.title() #只有首字母大写,其余为小写,模块中没有这个方法


字符串在输出时的对齐:


S.ljust(width,[fillchar])
#输出width个字符,S左对齐,不足部分用fillchar填充,默认的为空格。
S.rjust(width,[fillchar]) #右对齐
S.center(width, [fillchar]) #中间对齐
S.zfill(width) #把S变成width长,并在右对齐,不足部分用0补足

字符串中的搜索和替换:


S.find(substr, [start, [end]])
#返回S中出现substr的第一个字母的标号,如果S中没有substr则返回-1。start和end作用就相当于在S[start:end]中搜索
S.index(substr, [start, [end]])
#与find()相同,只是在S中没有substr时,会返回一个运行时错误
S.rfind(substr, [start, [end]])
#返回S中最后出现的substr的第一个字母的标号,如果S中没有substr则返回-1,也就是说从右边算起的第一次出现的substr的首字母标号
S.rindex(substr, [start, [end]])
S.count(substr, [start, [end]]) #计算substr在S中出现的次数
S.replace(oldstr, newstr, [count])
#把S中的oldstar替换为newstr,count为替换次数。这是替换的通用形式,还有一些函数进行特殊字符的替换
S.strip([chars])
#把S中前后chars中有的字符全部去掉,可以理解为把S前后chars替换为None
S.lstrip([chars])
S.rstrip([chars])
S.expandtabs([tabsize])
#把S中的tab字符替换没空格,每个tab替换为tabsize个空格,默认是8个
字符串的分割和组合:

S.split([sep, [maxsplit]])
#以sep为分隔符,把S分成一个list。maxsplit表示分割的次数。默认的分割符为空白字符
S.rsplit([sep, [maxsplit]])
S.splitlines([keepends])
#把S按照行分割符分为一个list,keepends是一个bool值,如果为真每行后而会保留行分割符。
S.join(seq) #把seq代表的序列──字符串序列,用S连接起来

 

字符串的mapping,这一功能包含两个函数:


String.maketrans(from, to)
#返回一个256个字符组成的翻译表,其中from中的字符被一一对应地转换成to,所以from和to必须是等长的。
S.translate(table[,deletechars])
#使用上面的函数产后的翻译表,把S进行翻译,并把deletechars中有的字符删掉。需要注意的是,如果S为unicode字符串,那么就不支持deletechars参数,可以使用把某个字符翻译为None的方式实现相同的功能。此外还可以使用codecs模块的功能来创建更加功能强大的翻译表。
字符串还有一对编码和解码的函数:


S.encode([encoding,[errors]])
#其中encoding可以有多种值,比如gb2312 gbk gb18030 bz2 zlib big5 bzse64等都支持。errors默认值为"strict",意思是UnicodeError。可能的值还有'ignore', 'replace', 'xmlcharrefreplace', 'backslashreplace' 和所有的通过codecs.register_error注册的值。这一部分内容涉及codecs模块,不是特明白

S.decode([encoding,[errors]])
字符串的测试函数,这一类函数在string模块中没有,这些函数返回的都是bool值:


S.startwith(prefix[,start[,end]])
#是否以prefix开头
S.endwith(suffix[,start[,end]])
#以suffix结尾
S.isalnum()
#是否全是字母和数字,并至少有一个字符
S.isalpha() #是否全是字母,并至少有一个字符
S.isdigit() #是否全是数字,并至少有一个字符
S.isspace() #是否全是空白字符,并至少有一个字符
S.islower() #S中的字母是否全是小写
S.isupper() #S中的字母是否便是大写
S.istitle() #S是否是首字母大写的

字符串类型转换函数,这几个函数只在string模块中有:


string.atoi(s[,base])
#base默认为10,如果为0,那么s就可以是012或0x23这种形式的字符串,如果是16那么s就只能是0x23或0X12这种形式的字符串
string.atol(s[,base]) #转成long
string.atof(s[,base]) #转成float

这里再强调一次,字符串对象是不可改变的,也就是说在python创建一个字符串后,你不能把这个字符中的某一部分改变。任何上面的函数改变了字符串后,都会返回一个新的字符串,原字串并没有变。其实这也是有变通的办法的,可以用S=list(S)这个函数把S变为由单个字符为成员的list,这样的话就可以使用S[3]='a'的方式改变值,然后再使用S=" ".join(S)还原成字符串

posted @ 2010-12-10 11:55 Seven_Yuan 阅读(628) 评论(0) 编辑

保证你要转换的字符串编码为UTF8,如果不是,请iconv cnStr成utf8

  1. $cnStr = "中"; //utf8的中文
  2.  
  3. //unicode
  4. $code = unpack("H6codes", $cnStr);
  5.  
  6. //汉字
  7. $cnStr = pack("H6", $code['codes']);

恩, pack/unpack很强大,, 和c语言交换数据, 二进制方式的序列化,操作二进制文件.. etc…

附上format参数的说明:

  1. a      NUL-padded string, 即"\0"作为"空字符"的表示形式
  2. A      SPACE-padded string, 空格作为"空字符"的表示形式
  3. h      Hex string, low nibble first,升序位顺序
  4. H      Hex string, high nibble first,降序位顺序
  5. c      signed char, 有符号单字节
  6. C      unsigned char, 无符号单字节
  7. s      signed short (always 16 bit, machine byte order)
  8. S      unsigned short (always 16 bit, machine byte order)
  9. n      unsigned short (always 16 bit, big endian byte order)
  10. v      unsigned short (always 16 bit, little endian byte order)
  11. i      signed integer (machine dependent size and byte order)
  12. I      unsigned integer (machine dependent size and byte order)
  13. l      signed long (always 32 bit, machine byte order)
  14. L      unsigned long (always 32 bit, machine byte order)
  15. N      unsigned long (always 32 bit, big endian byte order)
  16. V      unsigned long (always 32 bit, little endian byte order)
  17. f      float (machine dependent size and representation)
  18. d      double (machine dependent size and representation)
  19. x      NUL byte, 实际使用的时候作为跳过多少字节用,很有用
  20. X      Back up one byte, 后退1字节
  21. @      NUL-fill to absolute position,实际使用的时候作为从开头跳到某字节用.
posted @ 2010-12-10 01:20 Seven_Yuan 阅读(673) 评论(0) 编辑

2010年12月8日

摘要: 1.  前言偶然看到Erlang vs. Stackless python: a first benchmark,对Erlang和Stackless Python的并发处理性能进行了实验比较,基本结论认为二者有比较相近的性能。我看完产生的问题是,Stackless Python与Python的其他并发实现机制性能又会有多大区别呢,比如线程和进程。因此我采用与这篇文章相同的办法来对Stackless Python、普通Python的thread模块、普通Python的threading模块、普通Python的processing模块这四种并发实现方案进行了性能实验,并将实验过程和基本结果记录在这阅读全文
posted @ 2010-12-08 16:37 Seven_Yuan 阅读(974) 评论(0) 编辑

2010年12月6日

摘要: 有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用struct来处理c语言中的结构体.struct模块中最重要的三个函数是pack(), unpack(), calcsize()pack(fmt, v1, v2, ...) 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)unpack(fmt, string) 按照给定的格式(fmt)解析字节流string,返回解析出来的tuplecalcsize(fmt) 计算给定的格式(fmt)占用多少字节的内存struct中支持的格式如下阅读全文
posted @ 2010-12-06 22:43 Seven_Yuan 阅读(607) 评论(0) 编辑
摘要: 代码初探正确的答案是:[代码]如果您已经猜对了,那么可以不看我这篇博文了。如果你没有猜对或者心里有所疑问,那我的这篇博文正是为您所准备的。一切由为什么会输出“A.__private()”开始。但要讲清楚为什么,我们就有必要了解一下Python的命名机制。据 Python manual,变量名(标识符)是Python的一种原子元素。当变量名被绑定到一个对象的时候,变量名就指代这个对象,就像人类社会一样,不是吗?当变量名出现在代码块中,那它就是本地变量;当变量名出现在模块中,它就是全局变量。模块相信大家都有很好的理解,但代码块可能让人费解些。在这里解释一下:代码块就是可作为可执行单元的一段Pyth阅读全文
posted @ 2010-12-06 20:10 Seven_Yuan 阅读(575) 评论(0) 编辑
摘要: 今天学习了Python中有关正则表达式的知识。关于正则表达式的语法,不作过多解释,网上有许多学习的资料。这里主要介绍Python中常用的正则表达式处理函数。re.match  re.match 尝试从字符串的开始匹配一个模式,如:下面的例子匹配第一个单词。 [代码]re.match的函数原型为:re.match(pattern, string, flags)第一个参数是正则表达式,这里为"(\w+)\s",如果匹配成功,则返回一个Match,否则返回一个None;第二个参数表示要匹配的字符串;第三个参数是标致位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。re.search阅读全文
posted @ 2010-12-06 20:04 Seven_Yuan 阅读(1841) 评论(1) 编辑

导航

公告

昵称:Seven_Yuan
园龄:2年5个月
粉丝:20
关注:0
<2012年2月>
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910

统计

搜索

 
 

常用链接

随笔分类

随笔档案

最新评论

阅读排行榜

评论排行榜

推荐排行榜