什么二进制操作能够被用于置位?solidworks模板位置中应该有什么位模式?

当前访客身份:游客 [
简洁,高效,赏心悦目
:引用来自“ChunJackie”的评论请问仁兄,用的是什...
:请问仁兄,用的是什么语言,(Clear, Dump),在...
:引用来自“Dream0303”的评论 引用来自“叶秀兰”...
:引用来自“叶秀兰”的评论 为什么图片看不到呢?...
:为什么图片看不到呢?
:好东西,果断收藏
:好文,收藏了。
:寄存器传参数的规则相对堆栈传还是很简单的。看看...
:当年的delphi就支持
今日访问:0
昨日访问:11
本周访问:30
本月访问:11
所有访问:8581
列表模式: |
文档生成器,类似于可以生成如右格式的文档:HTML,LATEX,PDF,MAN手册页,RTF(MS-WORD),CHM
You can configure doxygen to extract the code structure from undocumented source files. This is very useful to quickly find your way in large source distributions(这句话意思不明...)
自动可视化元素之间的关系,包括include依赖图,类的继承图,函数的协作图(collaboration)
一些必须的软件:
flex,bison,texlive-binaries,graphviz,texlive-latex-base
以上软件均可以通过apt-get install xxx获取
以下也是文档中列出但是已经存在的:
libiconv,可以通过'whereis iconv'判断该包是否存在
strip,perl,同上判断
qt4.3及其以上(qt5.x不支持),用于构建doxywizard,doxygen的图形界面版本
gunzip doxygen-$VERSION.src.tar.gz
tar xf doxygen-$VERSION.src.tar
cd doxygen-$VERSION
./configure #可以运行'./configure –help'查看可用选项,都是一些英文短语,很容易理解
make install
eclipse中插件使用
通过eclipse的''下载安装插件eclox
配置eclox,首选项-&Doxygen-&选择版本
安装eclox后,工具栏会出现'@'图标,点击可以进行Doxygen配置文件的生成与调用doxygen工具
配置文件:每一个项目都应该有自己的配置文件,配置文件决定了doxygen的所有设置
模板配置文件的生成:doxygen -g 配置文件名;几种特殊情况:
'配置文件名'为空,则默认:Doxygen
'配置文件名'已经存在,如:'doxygen -g setting.txt',并且'setting.txt'已经存在,则'setting.txt'会被重命名为'setting.txt.bak';
配置文件组成:由一系列名值对组成
大小写敏感
Environment variables can be expanded using the pattern $(ENV_VARIABLE_NAME),展开环境变量
最终被解析文件:
INPUT,RECURSIVE,FILE_PATTERNS,EXCLUDE,EXLUDE_PATTERNS;这些标签用于决定最终的被解析文件;
INPUT,RECURSIVE决定了参与解析的文件名列表,如:
INPUT=/root/CProject/HelloWRECURSIVE=YES;则产生的文件名列表如下:
/root/Cproject/HelloWorld:
&&&&main.cc
&&&&dlove_Dir.h
&&&&dlove_File.h
&&&&dlove_FileStat.h
/root/Cproject/HelloWorld/Debug:
&&&&main.mk
&&&&test1.pdf
&&&&dlove_File.d
FILE_PATTERNS,EXCLUDE,EXCLUDE_PATTERNS决定从上述文件名列表中去除那些文件/目录,如: FILE_PATTERNS=*.cc *.h;则此时文件名列表变为: /root/Cproject/HelloWorld:
/root/Cproject/HelloWorld:
&&&&main.cc
&&&&dlove_Dir.h
&&&&dlove_File.h
&&&&dlove_FileStat.h
/root/Cproject/HelloWorld/Debug:
EXCLUDE=/root/CProject/HelloWorld/Debug dlove_Dir.h;则表示去除'/root/CProject/HelloWorld/Debug'目录下的所有文件,与dlove_Dir.h文件
使用配置文件:
'doxygen 配置文件名',doxygen会在output directory下创建目录html,rtf,latex,xml,man,docbook,其中目录html包含了doxygen生成的格式为HTML的文档,以此类推...
OUTPUT_DIRECTORY,HTML_OUTPUT,RTF_OUTPUT,LATEX_OUTPUT,XML_OUTPUT,MAN_OUTPUT,DOCBOOK_OUTPUT标签指定了相应目录的名称:
如果目录不存在,则doxygen会自动创建目录,但不会递归创建目录
OUTPUT_DIRECTORY,如果是个相对路径,则相对于doxygen的工作目录
XXXX_OUTPUT,如果是个相对路径,则相对于OUTPUT_DIRECTORY
doxygen解析步骤:
Markdown格式的语法会被替换为相应的HTML与special command
执行注释块中的special command
如果一行由一些空白字符开头,并且其后跟着一个或多个星号,然后又跟着空白字符;则所有的空白与星号都会被移除,由此产生的所有空行被视为一个段落分隔符.
INPUT tag is used to specify the files and/or directories that contain documented source files.
If this tag is empty the current directory is searched.
RECURSIVE:
指定子目录是否会被参与进搜索,即是否递归搜索;
YES,进行递归搜索
FILE_PATTERNS:
当'INPUT'中包含目录时,对于该目录下的文件,仅当其扩展名属于FILE_PATTERNS时才会被parser
标签为空,则表明使用默认的文件名扩展
指明了一些文件,目录;这些文件或目录在parser时会从INPUT中排除
EXCLUDE_PATTERNS:
指定了一种模式,'INPUT'中满足'EXCLUDE_PATTERNS'指定模式的目录将会被排除,如: EXCLUDE_PATTERNS=*/test/* 则会从INPUT中排除以下目录:
/test/子目录名
/root/.../test/..
EXTRACT_ALL:
NO:则只解析有doxygen注释的代码
YES:解析所有的代码,即使没有doxygen注释
发布于 1年前,
阅读(80) | 评论(0) |
投票(0) | 收藏(0)
问题:char ch[3];则ch指向着数组的第一个元素,那么ch是以怎样的形式存在的呢?一个位于堆栈中的变量?答案:根据编译后的汇编文件,可以看出ch并不存在,他就像汇编中的标号!
发布于 1年前,
阅读(49) | 评论(0) |
投票(0) | 收藏(0)
RTTI运行时类型识别,根据基类指针或引用检索指针或引用所指对象的实际类型要使用RTTI,要求基类中具有虚函数两种方法支持RTTI:typeid操作符dynamic_cast操作符dynamic_cast动态类型转换当需要通过基类指针或引用调用不是基类组成部分的派生类成员,就需要将基类指针或引用转换为派生类的指针或引用;当然也可以通过虚函数的方式,但是某些情况下是不可能使用虚函数的,比如静态成员函数;见下: /*
&*&main.cc
&*&&Created&on:&日
&*&&&&&&Author:&root
#include&&stdio.h&
&&&&static&void&print(){&printf("A\n");&}
&&&&virtual&~A(){&;&}
&&&&/*&基类必须要用虚函数才能使用RTTI&*/
class&B:public&A{
&&&&static&void&print(){&printf("B\n");&}
&&&&virtual&~B(){&;&}
int&main(int&argc,char&*argv[]){
&&&&A&*a=new&B;
&&&&a-&print();
&&&&/*&如果要调用B版本的此时不可能通过虚函数
&&&&&*&所以必须使用动态类型转换,即dynamic_cast操作符&*/
&&&&/*&此时b的作用域为if块与if匹配的else块中&*/
&&&&if(B&*b=dynamic_cast&B*&(a))
&&&&&&&&b-&print();
&&&&&&&&a-&print();
&&&&delete&a;
&&&&return&0;
} dynamic_cast语法dynamic_cast&派生类指针或引用&(基类指针或引用),将基类指针或引用转换为同一继承层次中派生类的指针或引用;转换失败dynamic_cast会进行运行时检查,如果基类指针或引用所指对象的实际类型不是'&派生类指针或引用&'类型,如: class&A{&virtual&~A(){}&};
class&B:public&A{};
class&C:public&A{};
int&main(int&argc,char&*argv[]){
&&&&A&*a=new&B;
&&&&dynamic_cast&C*&(a);
&&&&/*&此时就会转换失败&*/
&&&&delete&a;
&&&&return&0;
}对于 dynamic_cast&派生类指针&(基类指针): 转换失败的话,返回0;对于 dynamic_cast&派生类引用&(基类引用):由于不存在空引用,所以转换失败的话,抛出bad_cast异常;typeidtypeid(表达式),返回type_info类型,type_info类是对表达式的类型的一个包装,如果有虚函数支持的话,则会返回表达式的运行时实际类型;type_info支持以下操作: /*&必须包含&&typeinfo&&头文件才能使用&*/
bool&operator==(const&type_info&&__type);
bool&operator!=(const&type_info&&__type);
/*&判断两个类型是否相等&*/
const&char&*name()
/*&返回类型的字符串表示&*/ /*
&*&main.cc
&*&&Created&on:&日
&*&&&&&&Author:&root
#include&&stdio.h&
#include&&typeinfo&
static&void&print(){&printf("A\n");&}
virtual&~A(){&;&}
class&B:public&A{
static&void&print(){&printf("B\n");&}
virtual&~B(){&;&}
int&main(int&argc,char&*argv[]){
if(typeid(*a)&==typeid(B)&)
printf("B:&%s\n",typeid(*a).name());
printf("AA\n");
发布于 1年前,
阅读(108) | 评论(0) |
投票(0) | 收藏(0)
嵌套类就是在一个类(可以称为外围类)中又定义一个类,如: class&A{
&&&&int&a;
&&&&class&B{int&b;};
/*&A就是外围类
&*&B就是嵌套类&*/ 嵌套类与外围类相互独立这个主要体现在两个方面:嵌套类对象中不会含有外围类的成员,外围类的对象也不会含有嵌套类的成员,如:A a1,B b1;则a1中不会有B::b这个成员,b1中也不会有A::a这个成员嵌套类对外围类成员的访问并没有特殊权限,也是只能访问外围类的public成员,外围类对嵌套类成员的访问也是如此嵌套类是外围类的类型成员所以具有数据成员与函数成员的性质:定义在外围类public区域的嵌套类可以在程序的任何地方使用定义在外围类protected区域的嵌套类仅能在外围类及其子类以及友元中使用定义在外围类private区域的嵌套类仅能在外围类及其友元中使用 #include&&stdio.h&
friend&int&main(int&argc,char&*argv[]);
protected:
class&B{};
int&main(int&argc,char&*argv[]){
&&&&/*&main是A的友元函数,所以可以使用B类类型
&&&&&*&使用B类类型需要加上A限定符,因为B是A的静态成员嘛..&*/
} 嵌套类与模板若外围类是模板类,则其内部类也隐式的是模板类,但由于内部类并没有用'template&..&'修饰,所以类似'内部类名&Type&'是非法的!!事实上只要确定了外围类的模板形参,则内部类内部使用的模板形参也即确定了.所以没有必要出现'内部类&Type&'template&typename&ElemT&
class&Queue{
struct&&_QueueItem{
&&&&ElemT&&&&&&&&&&&&&&&_/*&使用模板形参&ElemT&定义数据&*/
&&&&_QueueItem&&&&&&&&&&*_
&&&&/**&value_type&也是&Queue&的类型成员.&*/
&&&&typedef&&&&&ElemT&&&&&&&value_
&&&&_QueueItem&&*_&&&&/*&并不是&_QueueItem&ElemT&&*/
&&&&_QueueItem&&*_
template&typename&Type&
inline&const&char*&getTypeName(&const&Type&&&)&&{&return&typeid(Type).name();&}
#define&PrintType(var)&&&&&&Println(#var&":&%s",getTypeName(var));
int&main(&int&argc,char&*argv[]&){
&&&&Queue&int&&&i;
&&&&i._head=new&Queue&int&::_QueueI&/*&并不是&Queue&int&::_QueueItem&int&&*/
&&&&PrintType(i._head);
&&&&PrintType(i._tail);
&&&&PrintType(i._head-&_next);
&&&&Println("_QueueItem&int&*:&%s",typeid(Queue&int&::_QueueItem*).name());
&&&&PrintType(i._head-&_data);
/*&---&程序输出&---&*/
i._head:&PN5QueueIiE10_QueueItemE
i._tail:&PN5QueueIiE10_QueueItemE
i._head-&_next:&PN5QueueIiE10_QueueItemE
_QueueItem&int&*:&PN5QueueIiE10_QueueItemE
i._head-&_data:&i可以看出 _head,_tail,_head-&_next 的类型为: _QueueItem&int*& 类型_head-&_data 的类型为 int 类型类外定义内部类的成员 /*&A.h&*/
#ifndef&AAAA_H_
#define&AAAA_H_
static&int&a;
void&print();
static&int&a;
void&print();
/*&A.cc&*/
#include&"A.h"
#include&"stdio.h"
int&A::a=33;
int&A::B::a=77;
A::A(){&;&}
void&A::print(){&printf("A\n");&return&;&}
/*&与常规类的外部定义也是一样的,只不过对于内部类的访问需要外部类限定符
&*&因为内部类是外部类的类型成员&*/
A::B::B(){&;&}
void&A::B::print(){&printf("B\n");&return&;&} 类外定义内部类将整个内部类都放在类的外部进行定义,如: /*
&*&&Created&on:&日
&*&&&&&&Author:&Dreamlove
#ifndef&AAAA_H_
#define&AAAA_H_
static&int&a;
void&print();
class&A::B{
static&int&a;
void&print();
#endif要注意以下: 由于在A中只是前沿声明了B,所以B是不完全类型!不能使用B来定义对象(因为B是不完全类型,所以编译器不知道为B分配多少字节的空间);内部类的完整定义必须要在外部类的完整定义下面,并且内部类名之前要有外部类名来限定,也可以把内部类与外部类的定义分别放在两个文件中: /*&A.h&*/&
#ifndef&AAAA_H_
#define&AAAA_H_
&&&&class&B;
&&&&static&int&a;
&&&&void&print();
#ifndef&&&&BBBB_H_
#define&&&&BBBB_H_
#include&"A.h"
/*&因为外围类的完整定义必须要在内部类之前&*/
class&A::B{
&&&&static&int&a;
&&&&void&print();
发布于 1年前,
阅读(119) | 评论(0) |
投票(0) | 收藏(1)
问题抛出异常时发生了什么?解答class&ExceptionItem{
&string&_ItemN
&ExceptionItem(const&string&&itemName):
&_ItemName(itemName)
&printf("构造:&%s\n",_ItemName.c_str());
&ExceptionItem(const&ExceptionItem&&item):
&_ItemName(item._ItemName)
&printf("复制:&%s-&%s\n",item._ItemName.c_str(),this-&_ItemName.c_str());
&~ExceptionItem(){
&printf("析构:&%s\n",_ItemName.c_str());
&string&toString()const{
&return&_ItemN
void&func1(){
&ExceptionItem&item("func1");
void&func2(){
&ExceptionItem&item("func2");
&try{&func1();&}
&catch(int&e){&;&}
void&func3(){
&ExceptionItem&item("func3");
&try{&func2();&}
&catch(const&ExceptionItem&&item){
&printf("捕捉:&%s\n",item.toString().c_str());
int&main(int&argc,char&*argv[]){
&}catch(const&ExceptionItem&&item){
&printf("捕捉:&%s\n",item.toString().c_str());
&printf("结束\n");
}所以,抛出异常(执行throw语句)时发生的事情:在全局区创建异常对象的副本(对于类类型,调用复制构造函数,所以用作异常的类型复制构造函数必须可用) 即上方的'Exce: exce1 -& _exce1';如果throw表达式没有处在try块中或者没有匹配的catch子句,则执行堆栈展开:释放函数所占的内存空间对于类类型的局部对象调用她们的析钩函数(所以析钩函数应该不抛出任何异常)来清理对象否则执行第一个匹配catch子句,并且一般情况下执行完catch子句后,处在全局区的异常对象会被释放(除非catch子句中使用了重新抛出')抛出时对指针解引用 #include&&stdio.h&
A(){&printf("A\n");&}
A(const&A&&__a){&printf("A&-&&");&}
virtual&~A(){&;&}
class&B:public&A{
B():A(){&printf("B\n");&}
B(const&B&&__b):A(){&printf("B&-&");&}
virtual&~B(){&;&}
int&main(int&argc,char&*argv[]){
throw&*a;&&&&
}catch(const&B&&__b){
printf("Catch:&B\n");
}catch(const&A&&__a){
printf("Catch:&A\n");
}throw *a;对a解引用,无论指针指向的实际类型是什么, 抛出的异常对象(也即在全局区创建的异常对象类型)总与指针的静态类型相匹配;
所以此时执行 A::A(const A&) 创建抛出的异常对象,然后匹配 catch(const A &__a) 子句. 匹配catch子句一般情况下,异常对象必须与catch子句的形参类型完全匹配才会进入相应的catch子句中,除了下列三种情况:允许非const到const的转换,非const对象的throw可以与指定接受const引用的catch子句匹配;允许派生类型到基类型的转换允许数组转换为数组类型的指针,函数转换为函数类型的指针,如: &&&&B&b;
//&&&&try{
//&&&&&&&&throw&b;
//&&&&}catch(const&A&&__b){&&/*&被匹配&*/
//&&&&&&&&printf("Catch:&B\n");
//&&&&}catch(const&B&&__a){
//&&&&&&&&printf("Catch:&A\n");
&&&& throw&&b;
&&&&}catch(A&*a){&/*&被匹配&*/
&&&& puts("catch1");
&&&&}catch(B&*b){
&&&& puts("catch2");
&&&&} 重新抛出当catch子句中使用了重新抛出时,处在全局区的异常对象不会被释放,如: #include&&stdio.h&
&&&&int&a;
&&&&A():a(0){&printf("A:%p\n",this);&}
&&&&A(const&A&&__a){&printf("A:%p-&%p\n",&__a,this);&}
&&&&~A(){&printf("A:~%p\n",this);&}
&&&&throw&a1;
&&&&return&;
void&f1(){
&&&&try{&f();&}
&&&&catch(A&&__a1){&++__a1.a;&&}
&&&&/*&throw与throw&__a1是不同&*/
&&&&return&;
void&f2(){
&&&&try{&f1();&}
&&&&catch(A&&__a){&printf("%d\n",__a.a);&}
int&main(int&argc,char&*argv[]){
&&&&return&0;
}:重新抛出,是将全局区的异常对象继续沿着函数调用链向上传递;不会释放该异常对象;throw __a1:此时根据__a1重新在全局区创建一个新的异常对象,然后将处在全局区的__a1释放;然后在堆栈展开...balabala匹配所有类型catch(...){};匹配所有类型的异常对象,如果'catch(...)'处在catch子句的第一位,那么其他catch是不会得到机会的;函数测试块用于捕获构造函数初始化列表中的异常: 不过测试发现:会在捕获处理后将初始化列表中发生的异常重新抛出(''那种) #include&&stdio.h&
A():a(0){&printf("A:%p\n",this);&}
A(const&A&&__a){&printf("A:%p-&%p\n",&__a,this);&}
~A(){&printf("A:~%p\n",this);&}
B()try:a(f()){/*&注意try的位置,初始化列表之前&*/
}catch(...){/*&会捕获初始化列表与构造函数体中抛出的异常,不过在处理后又会重新抛出&*/
printf("B:Catch\n");
int&main(int&argc,char&*argv[]){
try{&B&b;&}
catch(...){&printf("main:Catch\n");&}
/*&执行结果:&*/
A:0x7fffc45fbae0
A:0x7fffc45fbae0-&0x1afd090
A:~0x7fffc45fbae0
main:Catch&&&&
A:~0x1afd090&/*&确定是重新抛出&*/RAII资源分配即初始化,即通过一个类来包装资源的分配与释放,这样可以保证异常发生时资源会被释放;异常说明 void&f()throw(Type)
/*&f&会抛出Type类型或其派生类型的异常&*/
void&f()throw()
/*&f()不会抛出任何异常,此时编译器可能会执行一些被可能抛出异常的代码抑制的优化&*/
/*&f()会抛出任何类型的异常&*/ 违反了异常说明如果抛出了不在异常说明列表中的异常,则会执行堆栈展开退出当前函数后直接调用标准库函数 unexcepted()[默认调用 terminate()终止程序 ] ;而不会沿着函数调用链向上...如: #include&&stdio.h&
A():a(0){&printf("A:%p\n",this);&}
A(const&A&&__a){&printf("A:%p-&%p\n",&__a,this);&}
~A(){&printf("A:~%p\n",this);&}
int&f()throw(){
void&f1(){
int&main(int&argc,char&*argv[]){
try{&f1();&}
catch(...){&printf("main:Catch\n");&}
}继承层次中的异常说明派生类虚函数异常说明中的异常列表?基类虚函数异常说明中的异常列表,这个主要是为了:当通过基类指针调用派生类虚函数,这条限制可以保证派生类虚函数不会抛出新的异常#include&&stdio.h&
class&A1{&virtual&~A1(){&;&}&};
class&B1:public&A1{&&};
virtual&void&print()throw(A1){
virtual&~A(){&;&}
class&B:public&A{
virtual&void&print()throw(B1){
virtual&~B(){&;&}
int&main(int&argc,char&*argv[]){
B&b;&b.print();
}只要满足批注中的条件即可,如上例也编译通过 异常说明与析钩函数 class&A{
virtual&void&print()throw(A1){
virtual&~A()throw(){&;&}
class&B:public&A{
virtual&void&print()throw(B1){
virtual&~B(){&;&}
};因为 ~A() 不会抛出任何类型的异常,所以 ~B() 也不能抛出任何类型的异常,如上例编译不会通过; 异常说明与函数指针 int&(*fptr)()throw(int,double);
/*&fptr作为一个函数指针,指向着一个函数:
&*&该函数没有参数,返回类型为int
&*&并且可能抛出int,double类型的异常&*/
int&(*fptr1)()throw(); 当给函数指针赋值的时候,源指针异常声明的类型列表?目的指针异常声明的类型列表,这样主要是为了保证:当通过目的指针调用函数时,函数抛出的异常不会多于目的函数指针异常列表中的异常但实际上,下列代码编译成功了: #include&&stdio.h&
int&f()throw(int,double){
int&main(int&argc,char&*argv[]){
int&(*fptr1)()throw();
fptr1=f;/*&这里赋值应该是失败的...&*/
try{&fptr1();&}
&&&&/*&fptr1的异常说明不会抛出任何异常,所以这里抛出异常时应该是
&&&&&*&调用unexpected()的;但实际上异常被捕获了&*/
catch(...){&;&}
发布于 1年前,
阅读(74) | 评论(0) |
投票(0) | 收藏(1)
new与delete表达式作了什么对于new来说计算所需内存的字节数,然后以此为参数调用标准库的operator new(size_t)函数在operator new()返回的内存上调用类的适当的构造函数初始化一个对象将operator new() 返回的指针作为表达式的运算结果也即 new 把内存分配与对象构造合在一起了对于delete来说调用指针所指对象的析够函数释放对象本身调用标准库的operator delete()函数将指针所指内存返还给系统也即 delete 把释放对象本身与释放对象所占的内存合在一起new[] 与 delete[] 行为与上类似allocator类很多时候并不需要立即在分配的内存上初始化一个对象,与此同时也希望在释放内存之前不调用对象的析钩函数;allocator类提供了这种机制,其成员:allocator&T&&a;&&&/*&a可以用来分配原始内存以及在原始内存上构造T类型对象&*/
a.allocate(n);&&&&&/*&分配原始的内存,可以保存n个Type类型的对象&*/
a.deallocate(p,n);/*&释放p所指的原始内存,p必须合法;并且不会调用p所指对象的析钩函数,n表示p所指的内存可以保存n个T类型对象&*/
a.construct(p,t);/*&在p所指的内存上调用T类型的复制构造函数(以t为参数)初始化一个对象&*/
a.destory(p);/*&调用p所指对象的析钩函数&*/若干个用于在原始内存上构造对象的算法:uninitialized_copy(begin,end,begin2);
/*&从迭代器[begin,end)指出的输入范围将元素复制到从迭代起begin2
&*&开始的未构造的原始内存中,该函数是在目的地构造对象,而不是赋值&*/&&
uninitialized_fill(begin,end,t)
/*&将由迭代器[begin,end)指出的范围中的对象初始化为t的副本
&*&[begin,end)范围是未构造的原始内存,使用复制构造函数构造对象&*/
uninitialized_fill_n(begin,end,t,n)
/*&同上,不过至多构造n个对象&*/operator new/operator delete标准库提供了下列函数用于分配/释放原始的未构造的内存:void*&operator&new(size_t)&&&&&&/*&new表达式调用&*/
void*&operator&new[](size_t)&&&&/*&new[]表达式调用&*/
void&operator&delete(void*)
void&operator&delete[](void*)定位new表达式用来在已经分配好的,原始的内存上调用构造函数初始化对象,与其他new表达式不同,定位new表达式不会分配新的内存,其语法:/*&在已经分配的原始内存上调用对象的默认构造函数构造对象&*/
new(原始内存指针)&Type&&&&
/*&根据参数类型与参数个数调用合适的构造函数构造对象&*/
new(原始内存指针)&Type(初始化参数)#include&&stdio.h&
#include&&new&
#include&&malloc.h&
class&Test{
Test(){&printf("1\n");&}
Test(int&__a,int&__b){&printf("2\n");&}
~Test(){&;&}
int&main(int&argc,char&*argv[]){
Test&*ptr=(Test*)malloc(sizeof(Test));
ptr-&~Test();
new(ptr)Test(3,3);
ptr-&~Test();
free(ptr);
}自定义new与delete不能重定义new与delete表达式的行为,但是可以自定义new与delete表达式的行为
当编译器看到类类型的new或delete表达式时,她查看该类是否有operator new 或operator delete成员类定义(或继承)了自己的成员new和delete函数,则使用这些函数为对象分配/释放原始内存否则,调用这些函数的标准库版本#include&&stdio.h&
#include&&new&
#include&&malloc.h&
class&Test{
&&&&&&&&void*&operator&new(size_t&__size){
printf("1\n");
return&::operator&new(__size);
int&main(int&argc,char&*argv[]){
Test&*test=new&T
&&&&&&&&delete&&&&&
&&&&&&&&return&0;
}自定义operator new与operator delete时只要注意参数类型,返回值类型即可#include&&stdio.h&
#include&&new&
#include&&malloc.h&
class&Test{
void&operator&delete(void*&__p,size_t&__size){
printf("Size:&%zd\n",__size);
/*&__size可有可无,若__size存在,编译器会把'delete&p'
&*&p所指对象所占内存字节传递给__size&*/
::operator&delete(__p);
int&main(int&argc,char&*argv[]){
Test&*test=new&T
}在类继承层次中自定义new与delete对于operator new:#include&&stdio.h&
#include&&new&
#include&&malloc.h&
void*&operator&new(size_t&__size){
printf("A\n");
return&::operator&new(__size);
class&B:public&A{
void*&operator&new(size_t&__size){
printf("B\n");
return&::operator&new(__size);
int&main(int&argc,char&*argv[]){
A&*a=new&A;/*&调用A的operator&new&*/
A&*b=new&B;/*&调用B的operator&new&*/
&&&&&&&/*&分析new表达式的行为-&与&B&*b=new&B&是一样的情况&*/&
&&&&&&&delete&a;
}对于 operator delete,B继承A,当B中未定义operator delete时:#include&&stdio.h&
#include&&new&
#include&&malloc.h&
void&operator&delete(void&*__p,size_t&__size){
printf("Size:&%zd\n",__size);
::operator&delete(__p);
virtual&~A(){&;&}
class&B:public&A{
int&main(int&argc,char&*argv[]){
A&*a=new&A;
A&*b=new&B;
delete&a;/*&a-&A类型的对象,所以__size=sizeof(A)&*/
/*&由于A中存在虚析够函数,所以编译器会将b所指动态类型的
&*&尺寸传递给__size,即__size=sizeof(B)&*/
}当B中也定义了operator delete时:#include&&stdio.h&
#include&&new&
#include&&malloc.h&
void&operator&delete(void&*__p,size_t&__size){
printf("A:Size:&%zd\n",__size);
::operator&delete(__p);
virtual&~A(){&;&}
class&B:public&A{
void&operator&delete(void&*__p,size_t&__size){
printf("B:Size:&%zd\n",__size);
::operator&delete(__p);
int&main(int&argc,char&*argv[]){
A&*a=new&A;
A&*b=new&B;
delete&a; /*&调用A::operator&delete&*/
delete&b; /*&调用的是B::operator&delete&*/
}在delete b 中调用的 B::operator delete 有一点迷惑不解,由于 operator new()与operator delete()在类中默认是静态成员函数(因为当 A *a=new A时,分配内存会先于对象构造,所以应该是静态函数) 所以她们 不可能同时又是虚函数,所以delete b应该调用的是A::operator delete;但是确确实实调用的是B::operator delete;想来大概是编译器的作用,记住就行自定义new[]/delete[]与上非常相似,如不在类继承层次中的自定义:#include&&stdio.h&
#include&&new&
#include&&malloc.h&
A(){&printf("A\n");&}
void*&operator&new[](size_t&__size){
printf("A:new&Size:&%zd\n",__size);
return&::operator&new[](__size);
void&operator&delete[](void&*__p,size_t&__size){
printf("A:delete&Size:&%zd\n",__size);
::operator&delete[](__p);
virtual&~A(){&printf("~A\n");&}
int&main(int&argc,char&*argv[]){
A&*a=new&A[4];
delete[]&a;/*&会把a所指内存的字节(即数组大小)传递给__size&*/
}在类继承层次:#include&&stdio.h&
#include&&new&
#include&&malloc.h&
A(){&printf("A\n");&}
void*&operator&new[](size_t&__size){
printf("A:new&Size:&%zd\n",__size);
return&::operator&new[](__size);
void&operator&delete[](void&*__p,size_t&__size){
printf("A:delete&Size:&%zd\n",__size);
::operator&delete[](__p);
void&operator&delete(void&*__p,size_t&__size){
printf("A:delete&Size:&%zd\n",__size);
::operator&delete(__p);
virtual&~A(){&printf("~A\n");&}
class&B:public&A{
B():b(0){&printf("B\n");&}
void*&operator&new[](size_t&__size){
printf("B:new&Size:&%zd\n",__size);
return&::operator&new[](__size);
void&operator&delete[](void&*__p,size_t&__size){
printf("B:delete&Size:&%zd\n",__size);
::operator&delete[](__p);
void&operator&delete(void&*__p,size_t&__size){
printf("B:delete&Size:&%zd\n",__size);
::operator&delete(__p);
virtual&~B(){&printf("~B\n");&}
int&main(int&argc,char&*argv[]){
A&*b=new&B;
delete&b;/*&正确释放&*/
B&*b2=new&B[4];
delete[]&b2;/*&可以正确释放&*/
A&*b1=new&B[4];
delete[]&b1;/*&此时提示段错误,编译器不能识别了...&*/
//&&&&&&delete[]&(B*)b1&&&&/*&即可&*/
&&&&&&&&return&0;
}注意 delete[] b1 出错!
发布于 1年前,
阅读(81) | 评论(0) |
投票(1) | 收藏(6)
FPU中的寄存器8个80位的数据寄存器分别名为R0...R7;以堆栈的形式组织在一起,不能通过R0...R7来引用这些寄存器;只能通过ST(X)来引用浮点寄存器 当前栈顶寄存器存放在FPU状态寄存器中TOP域内;如:若FPU状态寄存器TOP域为:011;则说明R3是栈顶寄存器,即R3是ST(0);则此时ST(X)与RX的对应方式为: R7&&&&ST(4)
R6&&&&ST(3)
R5&&&&ST(2)
R4&&&&ST(1)
R3&&&&ST(0)
R2&&&&ST(7)
R1&&&&ST(6)
R0&&&&ST(5)压栈:TOP域中的值首先减1;然后再将数据复制到'新'ST(0)中;与 push 操作类似 出栈:首先把数据从ST(0)中复制到目的内存,然后TOP域的值加1,与 pop 操作类似6个特殊寄存器只介绍比较关心的状态寄存器,初始值0x0000 11-13位也即TOP域控制寄存器,初始值0x037f控制着FPU对异常的处理,运算精度,舍入方式。 运算精度,默认是11;00----单精度(24位有效位)01----未使用10----双精度(53位有效位)11----扩展双精度(64位有效位)舍入控制,默认是0000----舍入到最近值01----向下舍入10----向上舍入11----向零舍入标记寄存器,初始值0xffff标记着8个数据寄存器中值的状态,每一个数据寄存器使用2位标识00----一个合法的扩展双精度值01----零值10----特殊的浮点值11----空 浮点数比较FCOM系列fcom后面跟几个p,表示进行几次出栈操作,如 fcompp 进行两次出栈操作; fcom最多只有一个操作数fcom st(i)/mem 比较 st(0) 与 source 的值,并根据结果设置状态寄存器的C0,C2,C3位fcom 没有操作数时比较 st(0) 与 st(1) 的值,然后相应的设置 C0,C2,C3位; C3(相当于ZF) C2(相当于PF) C0(相当于CF) ST(0)&source 0 0 0 ST(0)&source 0 0 1 ST(0)=source 1 0 0 正常的比较流程(以比较st(0)与st(1)为例):fcom
%ax /*&将状态寄存器-&ax&*/
/*&此时&C3-ZF,C2-&PF,C0-&CF&*/
/*&此后就可以使用&JA,JB,JE&等来控制流程&*/
/*&JA&&-&&CF=0&&ZF=0&
&*&JB&&-&&CF=1&
&*&JE&&-&&ZF=1&*/FCOMI系列FCOMI %st(i) %st(0) 比较ST(0)与ST(i);将相应设置ZF,PF,CF(设置规则同上);FCOMIP ...同上,比较后执行一次出栈操作FUCOMI/FUCOMIP 同上,比较前会首先根据标记寄存器的值检测操作数是否合法FCMOV系列FCMOV是根据EFLAGS寄存器的值判断是否进行传送,所以一般是在FCOMI后使用 FCMOVXX %st(i),%st(0);目的操作数总是 ST(0);
#include&&stdio.h&
#include&&math.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
#define _mMaxDouble(__a,__b,__max) &&&&&&&\
_mBeginASM
%2,%%st(0)\n\t" \
%2,%%st(0)" \
:"=t"(__max)
:"0"(__a),"u"(__b)
}while(0);
int&main(int&argc,char&*argv[]){
double&a,b,c;
while(scanf("%lf&%lf",&a,&b)&=2){
_mMaxDouble(a,b,c);
printf("%f\n",c);
} FPU备份与恢复FSTENV/FLDENVfstenv mem----保存FPU 特殊的寄存器到一个28字节的内存空间中fldenv mem----将指定的内存地址的值加载FPU 特殊寄存器中FSAVE/FRSTORfsave mem----保存FPU 所有的寄存器到一个108字节的内存空间中frstor mem----从制定内存地址加载FPU的数据与设置FPU异常与同步FPU可以检测6种异常:无效操作,除零,反规格化操作数,数值溢出,下溢,无效精度;每一种异常在状态寄存器与控制寄存器中都有相应的标志位; 当FPU检测到异常时:首先将状态寄存器中相应标志位置位,然后根据控制寄存器相应异常位是否置位作不同反应控制寄存器上相应异常标志位置位,则表明由处理器处理异常FPU会终止当前指令,然后给结果赋一个默认值(如除0时,给结果赋一个无穷大值)继续安静地执行后续指令控制寄存器上相应异常标志位清零,则:FPU终止当前指令,然后产生一个异常事件信号一般情况下,浮点指令在执行之前会首先检测有无异常事件信号,若有则执行异常处理程序;但是整数运算指令不会在执行之前检测,所以:fild&&&int1
/*&如果fild指令的执行产生了异常,FPU设置异常事件信号
&*&但由于下一条指令在之前之前不会检测有无异常信号
&*&所以下一条指令是未定义的,可能会产生错误&*/
inc&&&&int1
/*&正确情况下:&*/
fild&&&&&int1
fwait&&&&/*&用于异常同步&*/
inc&&&&&&int1浮点指令优化优先使用 FCOMI 而不是 FCOM把整数加载到FPU寄存器然后执行浮点运算,这样比直接对整数使用浮点运算指令要快,如不使用FIDIV;而是先FILD,然后FDIVP
发布于 1年前,
阅读(196) | 评论(1) |
投票(0) | 收藏(6)
外联汇编64位下的CPU由于新增了很多寄存器,所以不开启任何优化级别的G++都开始使用寄存器传递函数参数了,比如 printf() 函数使用的就是寄存器传递参数 所以如果一定要以外联汇编的形式优化程序的,那么估计又要记住参数传递使用寄存器规则索性使用内联汇编的形式优化,更加自然内联汇编G++中的内联汇编分为基本形式的内联汇编与扩展形式的内联汇编;毫无疑问,扩展形式的内联汇编更加复杂,也更加强大__asm__与asm两者是一样的,只不过ANSI C标准将asm作为关键字用于其他用途;所以为了与ANSIC兼容,还是使用__asm__;__volatile__于volatile告诉编译器,此处禁止优化,与__asm__一同使用,表示不优化内联汇编段基本形式的内联汇编可以使用宏定义,来更加方便得使用内联汇编,如#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM& );基本形式的内联汇编语法如: _mBeginASM
"汇编代码"
_mEndASMG++会将"汇编代码"部分逐字插入到为程序生成的汇编代码中,所以应该在每一条指令后面手动添加'\n\t';如#include&stdio.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
int&main(int&argc,char&*argv[]){
_mBeginASM
a,%eax\n\t"
b,%eax\n\t"
printf("%d\n",c);/*&运行可以看出c为77&*/
/*&使用&g++&-S&-o&Hello.s&Hello.cpp&查看编译器生成的汇编文件*/
/*&全局变量就是直接定义在&.data&段中的数据,而且编译器对变量名并没有使用名称修饰&*/
&&&&.align&4
&&&&.type&&&&a,&@object
&&&&.size&&&&a,&4
&&&&.long&&&&44
&&&&.globl&&&&b
&&&&.align&4
&&&&.type&&&&b,&@object
&&&&.size&&&&b,&4
&&&&.long&&&&33
&&&&.globl&&&&c
&&&&.align&4
&&&&.type&&&&c,&@object
&&&&.size&&&&c,&4
&&&&.zero&&&&4
/*&生成的内联汇编&*/
#&15&"Hello.cpp"&1
&&&&movl&&&&&&&&a,%eax
&&&&addl&&&&&&&&b,%eax
&&&&movl&&&&&&&&%eax,c
/*&为了验证G++是将"汇编代码"逐字插入到为程序生成的汇编代码中,可以试着去掉每一条汇编指令中的'\n\t';&*/扩展形式的内联汇编语法形式:_mBeginASM
"汇编代码模板"&&&&
:输出参数列表
:输入参数列表
:改动的寄存器列表
_mEndASM总的作用机理就是:g++ 首先根据'输入参数列表'部分将输入参数复制到指定的寄存器(使用mov,fld..等指令);替换一下汇编代码模板中的占位符,然后将该部分逐字插入到为程序生成的汇编代码中根据'输出参数列表'部分将指定寄存器中的值mov到C++变量中感觉就像一个函数调用,'汇编代码模板'就是函数体,'输入参数列表'部分指定了函数的参数,'输出参数列表'部分指定了函数的返回值汇编代码模板与基本形式的内联汇编相似,除了多了占位符(%0,%1,...表示着输入参数输出参数),寄存器之前要用两个%%(为了与占位符区分),其他也没什么输入,输出参数列表如果有多个输入,输出参数,则相互之间用','隔开;每一个参数形式如/*&对于输入参数&*/
"描述符"(C++表达式/C++局部|全局变量)
/*&对于输出参数&*/
"描述符"(左值)对于输入参数来说,描述符只有一个字符,用于说明在调用'函数体'之前,应该将变量复制到哪里;对于输出参数来说,描述符一般有两个字符,说明了结束调用后,从哪里对变量赋值;如:
#include&stdio.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
int&main(int&argc,char&*argv[]){
int&a=44,b=33,c;
_mBeginASM
%%ebx,%%eax"
:"=a"(c)&&&&&&&&/*&说明了调用'函数体'之后,应该把eax中的值赋值该变量c&*/
:"b"(b),"a"(a)&&/*&表明了在调用'函数体'之前,应该把变量a复制到eax中,b复制到ebx中&*/
printf("%d\n",c);
/*&生成的汇编代码段&*/
&&&&movl&&&&$44,&-28(%rbp)
&&&&movl&&&&$33,&-24(%rbp)&&&&/*&变量a存放在&-28(%rbp)&中,b&存放在-24(%rbp)中&*/
&&&&movl&&&&-24(%rbp),&%ebx&&&/*&b-&ebx&*/
&&&&movl&&&&-28(%rbp),&%eax&&&/*&a-&eax&*/&#APP&&&&&&&&&&&&&&&&&&&&&&&&&&/*&调用函数体...&*/
#&16&"Hello.cpp"&1
&&&&addl&&&&&&&&%ebx,%eax
&&&&movl&&&&%eax,&-20(%rbp)&&&/*&eax-&c;c&存放在-20(%rbp)&&*/完整的描述符表可以自行搜索...r && && 表示任何可用的通用寄存器;m &&&&表示直接使用内存位置,即内存操作数占位符:_mBeginASM
%1,%2\n\t"
:"r"(a),"r"(b)
_mEndASM作用机理就是:
r表示任何可用的通用寄存器,所以G++首先会为a,b,c选择一个通用寄存器;如对于a选择使用b选择使用c选择使用那么 %0 就是 ecx %1 就是 eax %2 就是 ebx 生成的汇编代码如下:movl&&&&&&&&-24(%rbp),eax
movl&&&&&&&&-28(%rbp),ebx
imul&&&&&&&&%eax,%ebx
movl&&&&&&&&%ebx,%ecx
movl&&&&&&&&%ecx,-20(%rbp)数字描述符 _mBeginASM
:"r"(a),"0"(b)&&&&/*&"0"(b)表示为b分配的寄存器与%0(即c)一样,即&%2==%0&*/
_mEndASM 使用内存操作数 #include&stdio.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
int&main(int&argc,char&*argv[]){
int&a=44,b=33,c;
printf("%d\t%d\n",a,b);
_mBeginASM
:"0"(a),"m"(b)&&&
printf("%d\t%d",a,b);
/*&生成的汇编代码:&*/
&&&&movl&&&&-4(%rbp),&%eax&&&&&&&&/*&a-&eax&*/
#&15&"Hello.cpp"&1
&&&&xchgl&&&&&&&&%eax,-8(%rbp)&&&&/*&-8(%rbp)就是b,m的意思就是使用内存操作数&*/
&&&&movl&&&&%eax,&-4(%rbp)&&&&&&&&/*&eax-&a&*/
/*&由于b使用内存操作数,所以不需要在调用之前把b移到某个寄存器中,也不需要在调用之后从某个寄存器中为b赋值&*/ 使用FPU描述符,输出值不能用f来描述;
f&&&&&&& 表示任何可用的浮点寄存器,即st(0)-st(7);t&&&&&&& 表示 st(0)u&&&&&& 表示st(1) #include&&stdio.h&
#include&&math.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
#define&_mGetSinCos(d,sind,cosd) \&&&&/*&内联汇编通常与宏连载一起&*/
_mBeginASM
"fsincos" \
:"=t"(cosd),"=u"(sind) \
_mEndASM \
}while(0);
int&main(int&argc,char&*argv[]){
double&sind,
while(scanf("%lf",&d)&&=&1){
printf("%f\t%f\n",sin(d),cos(d));
_mGetSinCos(d,sind,cosd);
printf("%f\t%f\n",sind,cosd);
}注意:不要遗忘被修改的FPU寄存器
因为FPU的数据寄存器是以堆栈的形式工作的,所以不要忘记被修改的寄存器,如: _mBeginASM
"fild&%1\n\t"
"fimul&%1\n\t"
"fldpi\n\t"
"fmul&&%%st(1),%%st(0)\n\t"
:"=t"(area)
:"st(1)"&&&&
/*&手动运行一下内联汇编指令的话,可以发现退出'函数体'时,st(1)被修改了&
&*&并且st(1)并没有出现在输入,输出列表中,所以应该在'改动的寄存器列表'部分声明一下&*/
改动的寄存器列表如果我们在内联汇编代码中,修改了某些寄存器的值,并且这些寄存器没有在输入,输出部分出现;那么我们应该在改动的寄存器列表中登记一下该寄存器#include&stdio.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
int&main(int&argc,char&*argv[]){
int&a=44,b=33,c;
printf("%d\t%d\n",a,b);
_mBeginASM
%2,%%r8d\n\t"
%3,%2\n\t"
:"=r"(a),"=r"(b)
:"0"(a),"1"(b)
/*&因为r8d被修改了,也即r8被修改了,所以要在'改动的寄存器列表'里声明一下
&*&实际上..g++提示:r8d是未知的寄存器名,不过r8可以..&*/
printf("%d\t%d",a,b);
}不过如果寄存器已经在输入,输出部分出现过一次了,则不需要在改动的寄存器列表中重新声明;数字标签条件跳转指令与无条件跳转指令都允许指定一个数字加上方向标志作为标签;处理器会根据方向标志向前后向后搜索,第一个遇到的数字标签会被采用。如:#include&&stdio.h&
#include&&math.h&
#define&_mBeginASM& __asm__&__volatile__&(
#define&_mEndASM );
#define&_mMax(a,b,r) \
_mBeginASM
%1,%2\n\t" \
%1,%0\n\t"
:"r"(a),"r"(b) \
_mEndASM \
}while(0);
int&main(int&argc,char&*argv[]){
while(scanf("%d&%d",&a,&b)&&=&2){
_mMax(a,b,c);
printf("%d\n",c);
/*&生成的指令&*/
&&&&movl&&&&-12(%rbp),&%eax
&&&&movl&&&&-8(%rbp),&%edx
#&27&"Hello.cpp"&1
&&&&cmp&&&&&&&&%eax,%edx
&&&&jge&&&&&&&&0f
&&&&mov&&&&&&&&%eax,%eax
&&&&jmp&&&&&&&&1f
&&&&mov&&&&&&&&%edx,%eax
&&&&movl&&&&%eax,&-4(%rbp)注意:
由于G++是直接将内联汇编插入到为程序生成的汇编代码中,所以标签'1'...'f'表示向前搜索,'b'表示向后搜索;
发布于 1年前,
阅读(3030) | 评论(7) |
投票(8) | 收藏(92)
三字符序列主要是因为以前键盘上没有某些字符,所以为了使用这些字符,必须使用多个可以打印字符来标识这个不能打印的字符,三字符的替换发生在所有操作之前;??= # ??( [ ??& { ??/ \ ??) ] ??& } ??' ^ ??! | ??- ~ 不过 现在编译器默认是关掉替换的,替换会造成很多有趣的事情,如:int&ci=1;
//接下来的??/
printf("%d\n",ci);
/*&??/会被替换为'\';而'\'作为一个行连接符会让"++ci"被注释掉
&*&不过现在编译器默认是关掉替换的&*/ 宏展开的流程首先根据','将实际参数分割成一个个记号,此时不会被实际参数进行扩展;#define&cat(x,y) x;y;
cat(int&a;,int&b;);/*&宏展开:&int&a;;int&b;;&*/
/*&根据','分割记号&*/用实参产生的记号替换宏中未用引号引起来的相应形式参数的标识符如果标识符前面没有'#',或者前者或后面都没有'##'符号;则检查实际参数产生的记号,并在必要的时刻进行宏扩展根据','将实际参数分成记号,并匹配宏定义中的形参,此时位于引号或者嵌套括号的逗号不会用于分割,如/**
&*&&根据&func&的返回值测试&func&是否成功执行,若出错,则抛出&std::runtime_error()&类型的异常,异常字符串为:文件名;行号;format..
&*&&@param&func&若&func&返回&int&类型,则当返回值不为0时,认为&func&出错;若&func&返回指针类型,则当返回值为&0&时,认为出错.
#define&TF(func,format,...)&&testSystemCall(func,__FILE__,LINE_TO_STR(__LINE__),#func,format,##__VA_ARGS__)
TF(socket(AF_INET,SOCK_STREAM,0),"HELLOWORLD");
//&此时根据','进行分割,匹配情况为:
//&func&=&socket(AF_INET,SOCK_STREAM,0)
//&format&=&"HELLOWORLD"
//&所以此时替换后内容为:testSystemCall(socket(2,1,0),"main.cc","13","socket(AF_INET,SOCK_STREAM,0)","HELLOWORLD");
//&即&socket()&内的","不会用于分割.#如果在上述替换时发现形式参数标识符之前存在'#'符号,则
首先在实际参数产生的记号两端添加"符号,使得记号变成一个字符串字面值然后再用该记号替换 '#'与其后的标志符实际参数中的字符串字面值,字符常量两边或内部的每个双引号( " )或反斜杠( \ )前面都会由预编译器自动插入一个反斜杠( \ )。 #define&mStr(x) &#x
printf("%s\n",mStr("3\3"));
/*&此时宏展开&"\"3\\3\""&*/ ##在形式参数都被替换后都要把##及其前后的空白符都删除掉,以便将相邻记号连接起来形成一个新记号。#define&cat(x,y)&x&##&y
printf("%d\n",cat(1,2)); 多次扫描重复扫描替换记号序列以查找更多的己定义标识符。但是, 当某个标识符在某个扩展中被替换后,再次扫描并再次遇到此标识符时不再对其执行替换,而是保持不变 。printf("%d\n",mtest(m,test)(3,3));
/*&第一次扫描:mtest(3,3);此时因为mtest()
&*&在上一层扫描中已经被展开了,所以此时不会再展开&*/
printf("%d\n",mtest(m,test1)(3,3));
/*&第一次扫描:mtest1(3,3);然后
&*&下一次扫描再展开mtest1&*/ include#include 把该行(直接在当前行)替换为文件名指定的文件的内容 ,三种形式:#include &文件&#include "文件"#include 宏调用#define&incFile(file)&#file
#include&incFile(___Dlove_HTTPSend.h);
/*&就是直接&#inlcude&"___Dlove_HTTPSend.h"&*/if elif else endif#if&整数表达式
#endif"文本" 是指任何不属于条件编译指令结构的程序代码,它可以包含预处理指令,也可以为空 。因为预编译器不进行计算,所以#if后只能用整数表达式(必须是整型,并且其中不包含sizeof,强制类型转换运算符或枚举常量);defined ifdef&defined 标志符 或者 defined(标志符); &&&&如果该标识符在预处理器中已经定义,则用 1 替换它,否则,用 0 替换。预处理器进行宏扩展之后仍然存在的任何标识符都将用0来替换 。 &&&&&&& 加粗字体。。我不甚了解,大概就是因为在宏的多次扫描中已经扩展过的标志符不会在被扩展,差不多这个意思;error#error 描述
&&&&将终止编译并打印指定的诊断信息。可变参数宏语法通过在宏定义中使用"...",在宏拓展中使用"__VA_ARGS__"来实现具有可变参数的宏,如#define&errRep(func,...)&&&\
&&&fprintf(stderr,func,__VA_ARGS__);当只需要一个参数的时候#define&err(format,...)&fprintf(stderr,format,__VA_ARGS__)
int&main(int&argc,char&*argv[]){
&&&&err("HelloWord");
&&&&err("H%d;%f",33,3.3);
&&&&return&0;
}$&g++&-E&main.cc
#&1&"main.cc"
#&1&"&built-in&"
#&1&"&命令行&"
#&1&"main.cc"
int&main(int&argc,char&*argv[]){
&&&&fprintf(stderr,"HelloWord",);&#&当没有参数时,__VA_ARGS__&相当于空,所以此时会多了一个逗号.
&&&&fprintf(stderr,"H%d",360);
&&&&return&0;
}使用 ##__VA_ARGS__ 来消除,#define&err(format,...)&fprintf(stderr,format,##__VA_ARGS__)$&g++&-E&main.cc&
#&1&"main.cc"
#&1&"&built-in&"
#&1&"&命令行&"
#&1&"main.cc"
int&main(int&argc,char&*argv[]){
&&&&fprintf(stderr,"HelloWord");&#&多余的逗号不见了
&&&&fprintf(stderr,"H%d",360);
&&&&return&0;
}标准宏__LINE__当前行号__FILE__当前源文件名__DATE__当前编译日期__TIME__当前编译时间__STDC__当程序严格遵循STDC时为1__cplusplus当程序使用C++语言编写为1#include&iostream&
using&namespace&
int&main(){
cout&&"源文件为:&"&&__FILE__&&endl&&"编译于:"__DATE__&&"&"&&__TIME__&&
cout&&"使用";
#ifdef&&__cplusplus
cout&&"C++";
cout&&"编写&"&&
#ifndef&__STDC__
cout&&"不";
cout&&"遵循ANSC标准"&&
}do{}while(0)1.一般用在宏定义中,如:/*
&*&功能:往stderr流中写入出错信息,以退出程序
&*&参数:与&fprintf()&参数一致
#define&mErrRep(errfuc,...)&&&&&&&&\
&&&&do{&&&&\
&&&&&&&&fprintf(stderr,errfuc,##__VA_ARGS__);&&&&\
&&&&&&&&exit(1);&&&&&&&&\
&&&&}while(0);此时 mErrRep() 宏有两条语句,并且经常出现在 if() 后面;此时使用 do{}while(0) 可以很安全的这样:
if((dirs=opendir("/root"))&==&NULL)
&&&mErrRep("main:opendir:&%s\n",strerror(errno));
/*&会被替换为:&*/&
&if(dirs=opendir("/root")&==NULL&)
&&&&&&&&&&fprintf(stderr,"main:opendir:&%s\n",strerror(errno));
&&&&&&&&&&exit(1);
&&&&&&}while(0)
如果不使用&do{}while(0)&的话在每一个if()后面都应该加上一个{}以括起宏定义中可能含有的多条语句
发布于 2年前,
阅读(147) | 评论(0) |
投票(0) | 收藏(12)
记录着计算机硬件组成一些非常浅显的东西; 数据的存储数据在计算机中的存储;计算机中只能存储二进制数据;对于各种类型的数据需要经过编码成二进制数据才能被计算机接受与运算数值:浮点数:按照IEEE规定编码成二进制整数:按照补码规则编码成二进制存放在计算机中字符: &&& 字符类型有多种编码规则,经过这些编码规则可以将字符转换为整数,从而以二进制形式存放在计算机中 &&&&常见编码规则:UTF-8(最通用),ASCII,UNICODE。。与外设连接很显然只凭一个CPU是什么事情都做不了的;CPU需要与其它设备一同配合; 但由于CPU的速度与其它设备的速度的不同步(CPU的速度要远远高于外设的速度); 设备通过中断来引起CPU的注意;CPU与外设之间通过接口连接,接口中有若干端口(端口即寄存器);用于存放设备的数据,状态。 CPU通过in,out指令往设备的端口发送数据(O),从设备的端口读取数据(I);怪不得设备都称为IO设备;硬盘简单介绍硬盘的主要物理组成有:盘片,磁头,见下图:盘片,由坚硬金属材料制成,并涂以磁性介质用来存放数据,一个盘片的两面都可以用与存放数据,是硬盘最基本的组成,一个硬盘中可能含有多个盘片.磁头,用与从盘片上读取数据,一个盘片有2个面,每一面都具有一个磁头.磁道,盘面会以特殊方式磁化一些磁化区,这些磁化区是以盘面中心为圆心的同心圆,每一个同心圆都被称为一个磁道.柱面,由不同盘片,相同半径的磁道组成扇区,盘面上的每个磁道被等分为若干个扇形的区域,即扇区,扇区的大小固定 521 byte.&&&&同一个盘片的不同磁道具有相同数目的扇区数,而且不同磁道具有不一样的周长,最外围磁道的周长最大,所以存储密度低,安全性高.硬盘的存储容量 = 盘面(不是盘片,一个盘片具有2面)数*每一个盘面的存储容量,而:盘面数 = 磁头数.每一个盘面的存储容量 = 盘面上的磁道数*磁道上的扇区数*扇区的字节大小.扇区的定位通过柱面号,磁头号,扇区号可以为整个硬盘上所有扇区进行定位.对硬盘进行抽象,即将硬盘看作由若干个扇区组成.
发布于 2年前,
阅读(38) | 评论(0) |
投票(0) | 收藏(0)

我要回帖

更多关于 office2003模板位置 的文章

 

随机推荐