Where there is a dream ,there is hope

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  64 Posts :: 0 Stories :: 8 Comments :: 0 Trackbacks

常用链接

留言簿(1)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

#

1.引用
2.const member
3.调用base class 的constructor, 它有一组参数
4.调用member class的 constructor,它有一组参数
posted @ 2011-08-30 11:24 IT菜鸟 阅读(84) | 评论 (0)编辑 收藏

1.对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为若符号。
2.强符号规则:一、不允许强符号被多次定义。二、如果一个符号在某目标文件中是强符号,在其他文件中是若符号,那么选择强符号
3.如果一个符号在所有的目标文件中都是若符号,则选择其中占用空间最大的一个如:A定义全局变量GLOBAL为INT型,占4个字节,目标B定义GLOBAL为DOULBE型,占8个字节,那么目标文件A和B链接后,符号GLOBAL占8个字节。

4.强引用,对外部目标文件的符号引用在目标文件被最终链接成可执行文件时,它们必须要被正确决议,如果没有找到该符号的定义,链接器就会报符号未定义错误,这种被称为强引用。与之相对应的还有一种弱引用,原理与强引用一模一样,只不过链接器不认为这是一个错误。
5.链接时符号未定义,导致这个问题的情况非常多,最常见的一般就是链接时缺少某个库。或者输入目标文件路径不正确或符号的声明与定义不一样。
posted @ 2011-08-25 12:24 IT菜鸟 阅读(319) | 评论 (0)编辑 收藏

源码下载完毕,先编译make_versioninfo工程。
之后,将python、pythoncore工程属性general 中的targetName改为$(ProjectName)_d,编译的时候只编译这两个工程即可。
posted @ 2011-08-24 11:24 IT菜鸟 阅读(96) | 评论 (0)编辑 收藏

使用C/C++语言开发软件的程序员经常碰到这样的问题:有时候是程序编译没有问题,但是链接的时候总是报告函数不存在(经典的LNK 2001错误),有时候是程序编译和链接都没有错误,但是只要调用库中的函数就会出现堆栈异常。这些现象通常是出现在CC++的代码混合使用的情况下或 在C++程序中使用第三方的库的情况下(不是用C++语言开发的),其实这都是函数调用约定(Calling Convention)和函数名修饰(Decorated Name)规则惹的祸。函数调用方式决定了函数参数入栈的顺序,是由调用者函数还是被调用函数负责清除栈中的参数等问题,而函数名修饰规则决定了编译器使用何种名字修饰方式来区分不同的函数,如果函数之间的调用约定不匹配或者名字修饰不匹配就会产生以上的问题。本文分别对CC++这两种编程语言的函数调 用约定和函数名修饰规则进行详细的解释,比较了它们的异同之处,并举例说明了以上问题出现的原因。

函数调用约定(Calling Convention

    函数调用约定不仅决定了发生函数调用时函数参数的入栈顺序,还决定了是由调用者函数还是被调用函数负责清除栈中的参数,还原堆栈。函数调用约定有很多方式,除了常见的__cdecl__fastcall__stdcall之外,C++的编译器还支持thiscall方式,不少C/C++编译器还支持 naked call方式。这么多函数调用约定常常令许多程序员很迷惑,到底它们是怎么回事,都是在什么情况下使用呢?下面就分别介绍这几种函数调用约定。


1.__cdecl

    编译器的命令行参数是/Gd__cdecl方式是C/C++编译器默认的函数调用约定,所有非C++成员函数和那些没有用__stdcall __fastcall声明的函数都默认是__cdecl方式,它使用C函数调用方式,函数参数按照从右向左的顺序入栈,函数调用者负责清除栈中的参数,由 于每次函数调用都要由编译器产生清除(还原)堆栈的代码,所以使用__cdecl方式编译的程序比使用__stdcall方式编译的程序要大很多,但是 __cdecl调用方式是由函数调用者负责清除栈中的函数参数,所以这种方式支持可变参数,比如printfwindowsAPI wsprintf就是__cdecl调用方式。对于C函数,__cdecl方式的名字修饰约定是在函数名称前添加一个下划线;对于C++函数,除非特别使 用extern "C"C++函数使用不同的名字修饰方式。


2.__fastcall

    编译器的命令行参数是/Gr__fastcall函数调用约定在可能的情况下使用寄存器传递参数,通常是前两个 DWORD类型的参数或较小的参数使用ECXEDX寄存器传递,其余参数按照从右向左的顺序入栈,被调用函数在返回之前负责清除栈中的参数。编译器使用两个@修饰函数名字,后跟十进制数表示的函数参数列表大小,例如:@function_name@number。需要注意的是__fastcall函数调 用约定在不同的编译器上可能有不同的实现,比如16位的编译器和32位的编译器,另外,在使用内嵌汇编代码时,还要注意不能和编译器使用的寄存器有冲突。


3.__stdcall
 
   
编译器的命令行参数是/Gz__stdcallPascal程序的缺省调用方式,大多数WindowsAPI也是__stdcall调用约定。 __stdcall函数调用约定将函数参数从右向左入栈,除非使用指针或引用类型的参数,所有参数采用传值方式传递,由被调用函数负责清除栈中的参数。对于C函数,__stdcall的名称修饰方式是在函数名字前添加下划线,在函数名字后添加@和函数参数的大小,例如:_functionname@number

4.thiscall

    thiscall只用在C++成员函数的调用,函数参数按照从右向左的顺序入栈,类实例的this指针通过ECX寄存器传递。需要注意的是thiscall不是C++的关键字,不能使用thiscall声明函数,它只能由编译器使用。

5.naked call

    采用前面几种函数调用约定的函数,编译器会在必要的时候自动在函数开始添加保存ESIEDIEBXEBP寄存器的代码,在退出函数时恢复这些寄存器 的内容,使用naked call方式声明的函数不会添加这样的代码,这也就是为什么称其为naked的原因吧。naked  call不是类型修饰符,故必须和_declspec共同使用。

    VC的编译环境默认是使用__cdecl调用约定,也可以在编译环境的Project Setting...菜单-》C/C++ =》Code  Generation项选择设置函数调用约定。也可以直接在函数声明前添加关键字__stdcall__cdecl__fastcall等单独确定函数的调用方式。在Windows系统上开发软件常用到WINAPI宏,它可以根据编译设置翻译成适当的函数调用约定,在WIN32中,它被定义为 __stdcall 

 

函数名字修饰(Decorated Name)方式

    函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。LINK程序或其他工具有时需要指定函数的名字修饰来定位函数的正确位置。多数情况下程序员并不需要知道函数的名字修饰,LINK程序或其他工具会自动区分他们。当然,在某些情况下需要指定函数的名字修饰,例如在C++程序中, 为了让LINK程序或其他工具能够匹配到正确的函数名字,就必须为重载函数和一些特殊的函数(如构造函数和析构函数)指定名字装饰。另一种需要指定函数的名字修饰的情况是在汇编程序中调用CC++的函数。如果函数名字,调用约定,返回值类型或函数参数有任何改变,原来的名字修饰就不再有效,必须指定新的名字修饰。CC++程序的函数在内部使用不同的名字修饰方式,下面将分别介绍这两种方式。

1. C编译器的函数名修饰规则

    对于__stdcall调用约定,编译器和链接器会在输出函数名前加上一个下划线前缀,函数名后面加上一个“@”符号和其参数的字节数,例如_functionname@number__cdecl调用约定仅在输出函数名前加上一个下划线前缀,例如_functionname__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,例如@functionname@number   
 
2. C++
编译器的函数名修饰规则

    C++的函数名修饰规则有些复杂,但是信息更充分,通过分析修饰名不仅能够知道函数的调用方式,返回值类型,参数个数甚至参数类型。不管 __cdecl__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字,再后面是参数表的开始标识和按照参数类型代号拼出的参数表。对于__stdcall方式,参数表的开始标识是“@@YG,对于__cdecl方式则是“@@YA,对于__fastcall方式则是“@@YI。参数表的拼写代号如下所示:
X--void   
D--char   
E--unsigned char   
F--short   
H--int   
I--unsigned int   
J--long   
K--unsigned long
DWORD
M--float   
N--double   
_N--bool
U--struct
....
指 针的方式有些特别,用PA表示指针,用PB表示const类型的指针。后面的代号表明指针类型,如果相同类型的指针连续出现,以0”代替,一个0”代 表一次重复。U表示结构类型,通常后跟结构体的类型名,用“@@”表示结构类型名的结束。函数的返回值不作特殊处理,它的描述方式和函数参数一样,紧跟着 参数表的开始标志,也就是说,函数参数表的第一项实际上是表示函数的返回值类型。参数表后以“@Z标识整个名字的结束,如果该函数无参数,则以“Z”标识结束。下面举两个例子,假如有以下函数声明:

int Function1(char *var1,unsigned long);
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

其函数修饰名为“?Function1@@YGHPADK@Z,而对于函数声明:

void Function2();
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

其函数修饰名则为“?Function2@@YGXXZ

 

    对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字符引导的类名;其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是“@@IAE”,私有(private)成员函数的标识是“@@AAE,如果函数声明使用了const关键字,则相应的标识应分别为“@@QBE“@@IBE“@@ABE。如果参数类型是类实例的引用,则使用“AAV1”,对于const类型的引用,则使用“ABV1”。下面就以类CTest为例说明C++成员函数的名字修饰规则:

class CTest
{
......
private:
    void Function(int);
protected:
    void CopyInfo(const CTest &src);
public:
    long DrawText(HDC hdc, long pos, const TCHAR* text, RGBQUAD color, BYTE bUnder, bool bSet);
    long InsightClass(DWORD dwClass) const;
......
};
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

对于成员函数Function,其函数修饰名为“?Function@CTest@@AAEXH@Z,字符串“@@AAE表示这是一个私有函数。成员函数CopyInfo只有一个参数,是对类CTestconst引用参数,其函数修饰名为“?CopyInfo@CTest@@IAEXABV1@@Z DrawText是一个比较复杂的函数声明,不仅有字符串参数,还有结构体参数和HDC句柄参数,需要指出的是HDC实际上是一个HDC__结构类型的指 针,这个参数的表示就是“PAUHDC__@@”,其完整的函数修饰名为“?DrawText@CTest@@QAEJPAUHDC__@@JPBDUtagRGBQUAD@@E_N@ZInsightClass是一个共有的const函数,它的成员函数标识是“@@QBE,完整的修饰名就是“?InsightClass@CTest@@QBEJK@Z

无论是C函数名修饰方式还是C++函数名修饰方式均不改变输出函数名中的字符大小写,这和PASCAL调用约定不同,PASCAL约定输出的函数名无任何修饰且全部大写。

3.查看函数的名字修饰

    有两种方式可以检查你的程序中的函数的名字修饰:使用编译输出列表或使用Dumpbin工具。使用/FAc/FAs/FAcs命令行参数可以让编译器 输出函数或变量名字列表。使用dumpbin.exe /SYMBOLS命令也可以获得obj文件或lib文件中的函数或变量名字列表。此外,还可以使用 undname.exe 将修饰名转换为未修饰形式。

 

函数调用约定和名字修饰规则不匹配引起的常见问题

    函数调用时如果出现堆栈异常,十有八九是由于函数调用约定不匹配引起的。比如动态链接库a有以下导出函数:

long MakeFun(long lFun);
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

动态库生成的时候采用的函数调用约定是__stdcall,所以编译生成的a.dll中函数MakeFun的调用约 定是_stdcall,也就是函数调用时参数从右向左入栈,函数返回时自己还原堆栈。现在某个程序模块b要引用a中的MakeFunba一样使用 C++方式编译,只是b模块的函数调用方式是__cdecl,由于b包含了a提供的头文件中MakeFun函数声明,所以MakeFunb模块中被其它 调用MakeFun的函数认为是__cdecl调用方式,b模块中的这些函数在调用完MakeFun当然要帮着恢复堆栈啦,可是MakeFun已经在结束 时自己恢复了堆栈,b模块中的函数这样多此一举就引起了栈指针错误,从而引发堆栈异常。宏观上的现象就是函数调用没有问题(因为参数传递顺序是一样 的),MakeFun也完成了自己的功能,只是函数返回后引发错误。解决的方法也很简单,只要保证两个模块的在编译时设置相同的函数调用约定就行了。

 

    在了解了函数调用约定和函数的名修饰规则之后,再来看在C++程序中使用C语言编译的库时经常出现的LNK 2001错误就很简单了。还以上面例子的两个模块为例,这一次两个模块在编译的时候都采用__stdcall调用约定,但是a.dll使用C语言的语法编 译的(C语言方式),所以a.dll的载入库a.libMakeFun函数的名字修饰就是“_MakeFun@4b包含了a提供的头文件中MakeFun函数声明,但是由于b采用的是C++语言编译,所以MakeFunb模块中被按照C++的名字修饰规则命名为“?MakeFun@@YGJJ@Z,编译过程相安无事,链接程序时c++的链接器就到a.lib中去找“?MakeFun@@YGJJ@Z,但是a.lib中只有“_MakeFun@4,没有“?MakeFun@@YGJJ@Z,于是链接器就报告:

error LNK2001: unresolved external symbol ?MakeFun@@YGJJ@Z

解决的方法和简单,就是要让b模块知道这个函数是C语言编译的,extern "C"可以做到这一点。一个采用C语言编译的库应该考虑到使用这个库的程序可能是C++程序(使用C++编译器),所以在设计头文件时应该注意这一点。通常应该这样声明头文件:

#ifdef _cplusplus
extern "C" {
#endif

long MakeFun(long lFun);

#ifdef _cplusplus
}
#endif
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

这样C++的编译器就知道MakeFun的修饰名是“_MakeFun@4,就不会有链接错误了。

    许多人不明白,为什么我使用的编译器都是VC的编译器还会产生“error LNK2001”错误?其实,VC的编译器会根据源文件的扩展名选择编译方式,如果文件的扩展名是“.C”,编译器会采用C的语法编译,如果扩展名是 “.cpp”,编译器会使用C++的语法编译程序,所以,最好的方法就是使用extern "C"

posted @ 2011-08-22 18:30 IT菜鸟 阅读(168) | 评论 (0)编辑 收藏

auto_ptr 被销毁时会自动删除它所指之物,所以不能让多个auto_ptr指向同一个对象。所以,通过构造函数或者赋值操作赋值,原对象会变成NULL
tr1::shared_ptr 两者都是在析构函数中做DELETE操作,而不是DELETE[],所以不能在array上用。
两者都重载了operator-> / operator. 操作,经由.get()获得资源指针
用独立的语句将newed对象存储于智能指针内,如果不这样做,一旦异常被抛出,有可能导致无法察觉的资源泄露:
void test(std::tr1::shared_ptr<ClassA> classa, int priority);

std::tr1::shared_ptr
<ClassA> classa;
test
<classa, getPriority()>;//right

std::tr1::shared_ptr
<classA>(new ClassA),getPriority());//wrong

posted @ 2011-08-10 11:12 IT菜鸟 阅读(144) | 评论 (0)编辑 收藏

定义在其他模块的全局变量和函数,在最终运行的绝对地址都要在最终链接的时候才确定。编译器将源代码编译成目标文件,然后链接器将目标文件链接起来形成可执行文件。
链接的主要内容就是把各个模块之间互相引用的部分都处理好。链接的过程主要包括了:地址和空间分配;符号决议和重定位这些步骤

目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有调整。

.bss段存放的是未初始化的全局变量和局部静态变量,有些编译器会将全觉得未初始化变量存放在目标文件.bss段,有的则不存放,只是预留一个未定义的全局变量符号,等到最终链接的时候再在.bss段分配空间,编译单元内部可见的静态变量的确是存放在.bss段中的
.data段保存初始化了的全局静态变量和局部静态变量

posted @ 2011-08-08 17:54 IT菜鸟 阅读(93) | 评论 (0)编辑 收藏

class GamePlayer {
private:
static const int NumTurns = 5;//声明式
}
;
这个NumTurns是声明式,类中的整数型如果不取指针只需提供声明还无需定义式,如果取某个class的专属常量,就必须也提供定义式
const int GamePlayer::NumTurns;放在实现文件,因为定义式已经赋值,所以不可以再赋值
有的时候编译器不允许在声明的时候提供初值,则可将初值放在定义式,唯一的例外是如果class在编译期间需要一个class常量值,但是编译器错误的报错,那可以用enum 替换.
如果不想让别人获得一个指针或者引用指向某个常量值,则可以用enum实现这个约束。
std::vector<int> vec;
const std::vector<int>::iterator iter = 
vec.begin();
//iter的作用相当于T* iter
*iter = 10;//
++iter ;//
std::vector<int>::const_iterator cIter = vec.begin()//const T*
*cIter = 10 ;//
++iter ;//

初始化序列:如果成员变量是const或者references 他们就一定要初值,所以要再初始化列表中
posted @ 2011-08-08 15:46 IT菜鸟 阅读(94) | 评论 (0)编辑 收藏

C++中有两种模板:类模板和函数模板
类模板可以偏特化
函数模板可以重载,不叫偏特化,可以全特化

模板函数的调用规则:
1.非模板函数,为一等公民,首先调用
2.主函数模板(即 非特化的模板)中参数匹配最好的
3.如果选中的主模板还有针对这个类型的特化,则特化版本会被选中,否则根据模板实例化相应的函数

templete <class T>
void f(T);//主模板

template<class T>
void f(T*)//主模板


template<>
void f<int>(int*)//上一个的特化
posted @ 2011-07-28 14:58 IT菜鸟 阅读(103) | 评论 (0)编辑 收藏

boost::lexical_cast
从一个可流化的数据转化为另一个可流化的数据
posted @ 2011-07-20 14:28 IT菜鸟 阅读(58) | 评论 (0)编辑 收藏

C++位操作包括两种:传统的C语言方式的位操作和C++中利用bitset容器的位操作 1 d# J8 P2 o6 O' P
一、传统的C方式位操作:: O3 i" ^) [* I0 F
1.基本操作:
  使用一个unsigned int变量来作为位容器。. _' i* b: K7 }7 X* K
2.操作符:' ^( z$ `% a7 V
|   按位或操作符:result=exp1|exp2;当exp1和exp2中对应位中至少有一个为1时,result中对应位为1,否则为0。
&  按位与操作符::result=exp1&exp2;当exp1和exp2中对应位全为1时,result中对应位为1,否则为0。* Z# D  t( ^2 ], g6 n
^  按位异或或操作符:result=exp1^exp2;当exp1和exp2中对应位不相同时,result中对应位为1,否则为0。
~  反转操作符:将位容器中的所有位都反转,1变为0,0变为1。( U, d, A  o9 I. _
<< 按位左移操作符:exp<<n,将容器中所有的位向左移n位,空出的位用0填充。8 N! b5 J' E* N9 Z
>> 按位右移操作符:exp>>n,将容器中所有的位向右移n位,空出的位用0填充。/ v5 ^7 B3 R$ b, {/ C
|=,&=,^= 分别对应|&^三种操作符的复合操作符。
3.常用操作
这里我们假设有一个result的unsigned int变量用来储存32个学生的成绩(通过和不通过分别用0和1),这样result就有33位(result从右至左,从0开始计算位数,在这个例子中0位被浪费)。; a6 A' q! }# W7 H6 Y& H$ C
(a) 将第27位设置为及格(设作1)其他位不变:8 a8 e/ n2 ~! d7 m/ s
   result|=(1<<27) //任意的位值与1作按位或操作其值为1,而与0作按位与操作其值不变
(b) 将第27位设置成不及格(设为0)。8 f: b2 W8 F; r4 U: Q
   result&=~(1<<27) //任意的位值与0作按位与操作其值为0,而与1作按位与操作其值不变$ j! n3 C$ U" k* t+ F0 ~
(c) 反转第27位的值。. m5 V2 |5 O) F8 Y) ~7 ?3 A9 K
   result^=(1<<27) //任意的位值与1作按位异或操作其值为1,而与0作按位异与操作其值不变# L' ^9 V) J9 p# i4 q: Y

二、C++中的bitset容器
 M1.头文件:
+ I% l) R- \, j" I* t
  #include <bitset>
声明一个容器:/ V5 W- ]+ |2 r8 B
(a)声明一个指定位数的空容器(所有位设为0): bitset<int> bits;4 ]" z) l( l% E0 g  d
(b)声明一个指定位数并将指定的几个位初始化为相应值的容器: bitset<n> bits(int);
  bitdet<int> bits(string&)3 [4 r" _4 ^/ I2 O6 X9 m
总结:bitset模板类中类型参数传递容器的位数,而构造函数参数通过一个int或一个string&值来从右至左初始化容器中的相应值。
bitset的基本用法:
操作                            功能                                   用法+ N0 X! i' w+ u+ b
test(pos)                       pos位是否为1                    a.test(4)
 Wany()                            任意位是否为1                   a.any()
none()                          是否没有位为1                   a.none()+ c4 i7 d/ A  h5 W  k4 L
count()                         值是1的位的小数              a.count()6 k2 z9 V  o  K, U8 _+ O( X# r
size()                           位元素的个数                     a.size()
[pos]                            访问pos位                         a[4]
flip()                            翻转所有位                         a.flip()
flip(pos)                       翻转pos位                         a.flip(4)
set()                             将所有位置1                      a.set()& [. _2 I- j8 I6 s2 M( N2 U1 Z
set(pos)                        将pos位置1                       a.set(4)
reset()                          将所有位置0                      a.reset()
reset(pos)                            将pos位置0                       a.reset(4)$ e$ A( w+ ~# M
4.bitset与传统C位操作及字符串的转换
可以通过to_string()成员将容器转输出为一个string字符串,另外还可以用to_long()成员将容器输出到传统的用于C风格的位容器中。如:
 unsigned long bits = bits.to_long();
 sting str(bits.to_string());& p/ \! \' s  K4 ]*
Q  G$ Q/ m

如何对某一位置0或者置1?
_方法一:
写成宏,方便移植
#define setbit(x,y) x|=(1<<y) //将X的第Y位置1, a" d7 y' }7 {; |4 n$ z: j
#define clrbit(x,y) x&=!(1<<y) //将X的第Y位清0

方法二:
C语言位运算除了可以提高运算效率外,在嵌入式系统的编程中,它的另一个最典型的应用,而且十分广泛地正在被使用着的是位间的与(&)、或(|)、非(~)操作,这跟嵌入式系统的编程特点有很大关系。我们通常要对硬件寄存器进行位设置& w" ]4 {9 S# E' S$ y
) A8 O7 s( v1 p
譬如,我们通过将AM186ER型80186处理器的中断屏蔽控制寄存器的4 ^8 w2 ?. q, v" k0 l& {+ U7 n7 ]! d
第低6位设置为0(开中断2),最通用的做法是:3 U/ h7 @  f: ]# k# ~
#define INT_I2_MASK 0x0040 5 J# s5 n& N0 f# ]: I* c+ ^. v
wTemp = inword(INT_MASK);0 T5 Y: h4 w, }4 C
outword(INT_MASK, wTemp &~INT_I2_MASK);
" N+ o1 F, _) o+ k0 `
而将该位设置为1的做法是:& a, W/ p6 r  T, M9 @
#define INT_I2_MASK 0x0040
Temp = inword(INT_MASK);5 G3 \* k6 `4 A1 t( r3 @& M
outword(INT_MASK, wTemp | INT_I2_MASK);
( ~  ^- [6 N8 K% X* ~2 M8 o
判断该位是否为1的做法是:
#define INT_I2_MASK 0x0040   P/ `9 E  @3 }
wTemp = inword(INT_MASK);( w7 C9 S! x, l6 n! r
if(wTemp & INT_I2_MASK)
 {{
… /* 该位为1 */
}
. E' F6 a' z- x3 o( ?
方法三:( h+ C7 j5 U1 g- [  L! L& d9 z
int a|=(1<<x) //X就是某位需要置1的数字,如第四位置1为: a|=(1<<4): R- A4 {. s4 Y8 T3 t7 `
int b&=~(1<<x) //把某位置05 j6 A* v! e* ~/ V& [0 H$ v8 y8 x
x=x|0x0100    //把第三位置1
x=x&0x1011    //把第三位置0* l6 Q# N0 |# i. `2 s3 _
#define BitGet(Number,pos) ((Number) >> (pos)&1)) //用宏得到某数的某位
#define BitGet(Number,pos) ((Number) |= 1<<(pos)) //把某位置17 P: G/ R3 _7 B- ?& s
#define BitGet(Number,pos) ((Number) &= ~(1<<(pos)) //把某位置0
#define BitGet(Number,pos) ((Number) ^= 1<<(pos)) //把Number的POS位取反% u) p5 E+ @# C" c' c# P( I$ \' W
典型操作有:! r: a" r+ x4 l% s3 k; d
WTCON |=  (1 << 5) //WTCON的第五位清1 ! P; E% m% m+ P# Q# N
WTCON &= ~(1 << 5) //WTCON的第五位清0  . E;
 

a

posted @ 2011-07-12 13:37 IT菜鸟 阅读(510) | 评论 (0)编辑 收藏

仅列出标题
共7页: 1 2 3 4 5 6 7