c_c _tips - leeqx/leeqx.github.io GitHub Wiki

C/C++ 知识点

  1. 子进程正常退出会不会触发SIGCHLD\SIGSEV信号
 1 #include <sys/wait.h>
 2 #include <signal.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <unistd.h>
 6 void sig_handler(int sig)
 7 {
 8     printf("catch signal:%d SIGCHILD=%d getpid=%d getppid=%d\n",sig,SIGCHLD,getpid(),getppid());
 9     int status;
10     pid_t pid = wait(&status);
11     printf("pid %d exit\n",pid);
12     kill(getpid(),SIGABRT);
13 }
14 int main( int argc,char* argv[])
15 {
16     struct sigaction action;
17     action.sa_handler = sig_handler;
18     sigaction(SIGCHLD,&action,NULL);
19 
20     pid_t pid = 0;
21     if((pid=fork()) == 0){
22         printf("this is child process:%d %d\n",getpid(),getppid());
23         exit(-1); // 这里如果是exit(0) 那么将不会触发SIGCHLD 信号,这里也有可能是需要exit(EXIT_FAILURE)产生SIGSEV;才会触发信号
24     }
25     while(1) sleep(100000);
26     return 0;
27 }
output:
  this is child process:881 880
  catch signal:17 SIGCHILD=17 getpid=880 getppid=6871
  pid 881 exit
  Aborted
  1. 编译期的factorial
#include <stdio.h>
#include <unistd.h>
#include <time.h>


template<typename ResultType,int N>
class Factorial {
  public:
      enum { value = (ResultType)N*Factorial<ResultType,N-1>::value };
};

template<typename ResultType>
class Factorial<ResultType,1> {
  public:
      enum { value =(ResultType)1 };
};


template<typename ResultType>
ResultType factorial(int n)
{
  if(n != 1)
  {
      return n * factorial<ResultType>(n-1);
  } else {
      return 1;
  }
}

#define PRIME    100
int main(int argc,char* argv[])
{
#define PRINTTYPE "%llu"
  printf("Factorial:" PRINTTYPE "\n",Factorial<unsigned long long,PRIME>::value);

  time_t btime,etime;
  btime = time(NULL);
  printf("Factorial2:" PRINTTYPE "\n",factorial<unsigned long long>(PRIME));
  etime = time(NULL);
  printf("time cost:%lu\n",etime - btime);
  return 0;
}
  1. select 1024 限制问题

KERNEL部分
1.首先我需要重新修改
/usr/src/linux/include/linux/posix_types.h中__FD_SETSIZE的大小
2.重新编译KERNEL并启动进入这个新的KERNEL
用户应用部分
3.修改
/usr/include/linux/posix_types.h中__FD_SETSIZE的大小
/usr/include/bits/typesizes.h中__FD_SETSIZE的大小
4.重新编译应用程序
5.修改可以打开的最大文件数
(1)通过ulimit -n 修改 或者
(2)在自己应用程序中调用setrlimit修改
这里我有几个小问题:
1.有文档提到还要同时修改
/proc/sys/fs/file-max
/proc/sys/fs/file-nr
/proc/sys/fs/inode-nr
这样类似的文件,但是以我的理解,好像对2.6的KERNEL不再需要
2.有些文档好像还提到了修改glibc库
按我的理解,似乎也不需要
3.还有文档提到编译自己的应用程序的时候需要加上
-DFD_SETSIZE=nnn
按我的理解,也不需要
4./proc下的文件有没有详细的说明,我是说对内涵2.6来说

  1. map 循环删除元素

    for(map<int,int>::iterator it = m.begin(); it != m.end();it++)
       m.erase(it++); // 或者 it = m.erase(it);
0. c++ 萃取技术(根据参数获取对应的成员)  

    ```c
struct MyTest
{
    typedef int  value_type;
    value_type get(){return 1;}
};  
template<typename TYPE>
TYPE::value_type GetValue(TYPE t)
{
    reutrn t.get();
}
在没有使用萃取技术前,上面的方法对于下面的调用返回的是:
struct MyTest a;
GetValue(a);  // 返回1
int b=1;
GetValue(b); // 此时由于b是内建类型无法定义value_type 和get这两个属性
那么我们如何做呢?
------------------------------------------------------
template<typename T>
struct MyTest{
    typedef T value_type;
    value_type get() { return "test";}
};
//泛化模板(1)
template<typename T>
struct Traits {
    typedef typename T::value_type value_type;
    value_type get(T obj) { return obj.get();}
};
//模板完全特化(由于所有木板参数都特化了,如果只是部分参数特化则称为偏特化)(2)
template<>
struct Traits<int>
{
    typedef int  value_type;
    value_type  get(int obj){return 1;}
};
// 对int*特化(3)
template<>
struct Traits<int*>
{
    typedef int value_type;
    value_type  get(int *obj) { return 2;}
};
//此时GetValue方法可以改写成:
template<typename T>
typename Traits<T>::value_type GetValue(T t)
{
     Traits<T>().get(t);
}

int main()
{
    int a = 1;
    printf("%d\n",GetValue(a));//对应调用Traits(2)
    printf("%d\n",GetValue(&a));// 调用Traits(3)
    struct MyTest<char*> tt;
    printf("%s\n",GetValue<MyTest<char*> >(tt)); //调用traits(1)
}
输出:
1
1
test

由上可见,此时GetValue已经支持int int*,如果想要支持其他类是的内建模型则需要实现其对应的Traits(相当与代理) ``` class CComplexObject { public: void clone(){ printf("in clone");} }; template<typename T,bool isClonable> class XContainer { public: enum { Clonable=isConable}; void clone(T*pobj) { Traits().clone(pobj); }

    template<bool flag>
    class Traits<true>
    {
        public:
            void clone(T* pobj)
            {
                pobj->clone();
            }
    };
    template<bool flag>
        class Traits<false>
        {
            void clone(T*pobj)
            {
                printf(" non clonable");
            }
        };

}; int main() { int *p1 = 0; CComplexObject *p2 = 0; XContainer<int,false> n1; XContainer<CComplexObject,true> n2; n1.clone(p1); n2.clone(p2); return 0; }

[c++ 萃取](http://hsw625728.blog.163.com/blog/static/39570728200972105341629/)
0. 结构体成员偏移量
    
    ```c
   struct Test  
   {  
     int a;  
     int b;  
   };  
#define GET_MEMBER_OFFSET (_type,_mem) &(((struct _type*)0)->_mem)  
GET_MEMBER_OFFSET(Test,a) ---->0  
GET_MEMBER_OFFSET(Test,b) ---->4  
  1. 递归全排列 算法很简单:将每个数组元素都依次与最后一个元素交换,然后递归的生成其余排列。
void printfResult(const int a[],size_t n)
{
  printf("{");
  for(int i=0;i<n;i++)
      printf ("%d ",a[i]);

  printf("}\n");
}
void swap(int* x,int *y)
{
  if(x != y)
  {
  *x ^=*y;
  *y ^=*x;
  *x ^=*y;
  }
}
void permgen(int a[],size_t n)
{
  static size_t total_size = n;
  if(n <= 1)
  {
      printfResult(a,total_size);
  }
  else
  {
      // 依次与最后一位交换然后获取其排列
      int last = n-1;
      for(int i = 0;i<= last;i++)
      {
          {
              swap(&a[last],&a[i]);
              permgen(a,last);
              //恢复
              swap(&a[last],&a[i]);
          }
      }
  }
}
int main()
{
   int a [] = {1,2,3,4};
   permgen(a,sizeof(a)/sizeof(int));
  return 0;
}
  1. 交换两个数
void swap(int *a ,int *b)
{
 if(a != b)  //注意这里必须要判断,否则当传入的是同一个变量的地址时将会置为0: swap(&a,&a);
{
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}
  1. 类成员方法的函数指针调用问题

#include <stdio.h> 1 class A; 2 3 class A 4 { 5 public: 6 int AAA(int(A::pFunc)(int)) 7 {// 类方法内部调用类成员指针,运算符-> 是Member pointer selector 8 return (this->pFunc)(1); 9 } 10 int print(int a) 11 { 12 printf("%d\n",a); 13 return 0; 14 } 15 }; 16 18 int test(A pA,int(A::*pFunc)(int), int a) 19 { 20 return (pA->*pFunc)(a); 21 } 22 int test1(A pA,int(A::pFunc)(int), int a) 23 { 24 // 类方法内部调用类成员指针,运算符. 是Member pointer selector 25 return (pA.*pFunc)(a); 26 } 27 28 29 int main() 30 { 31 A a; 32 a.AAA(&A::print); 33 34 test(&a,&A::print,2); 35 test1(a,&A::print,3); 36 return 0; 37 }


0. linux epoll  、网络编程注意事项
   0. `epoll_event` 中的data是一个`union`结构,所以使用时需要注意
   0.  对socket调用`recv` 采用`buff[2]`将会使得`recv`返回-1,且`errno=EAGAIN` 或者`EWOULDBLOCK` [non-block 模式]
   0.  对socket调用`recv`返回0表示客户端关闭了链接;如果返回值-1除了`EAGAIN \EWOULDBLOCK`其他的都认为链接有问题需要关闭
   0.  对socket调用`send`如果返回值-1除了`EAGAIN \EWOULDBLOCK`其他的都认为链接有问题需要关闭

0. 如何把字符串转换成int或者long

  ```c
  long atoi(char* str)
  {
    char c;
    long result=0;
    int mult =   1;
    if(str && *str == '-'){
         mult = -1;
         str++;
    }
    int desc;
    while((c=*str++) != '\0' )
    {
        int desc = c - '0';
    	if(desc >=0 && desc < 10)
        {
        	result *=10;
            result +=desc;
        }
       else
       {
       	 break;
    }
  return result * mult;
  }
  1. c++中虚函数表存储在哪里?
    对于不同的编译器是不一样的,在g++中存放在.rodata 段,可以通过下面命令查看一个有虚函数的目标文件,每个有

class T1 { public: T1() { cout<<"this is T1 constructor"<<endl; } virtual void print() { cout << "this is the T1::print "<<endl; } };

class T2: public T1 { public: T2() { cout<<"this is T2 constructor" << endl; } virtual void print() { cout << "this is T2::print" <<endl; } }; class T3:public T1 { }; int main() { cout << "begin main"<<endl; class T1 tt; tt.print(); class T2 t2; t2.print(); class T3 t3; t3.print(); return 0; }

  ```shell
 objdump -x -d -s a.out|c++filt |grep vtable
 
0000000000400c20  w    O .rodata        0000000000000018              vtable for T1
0000000000400be0  w    O .rodata        0000000000000018              vtable for T3
0000000000400c00  w    O .rodata        0000000000000018              vtable for T2
0000000000602080  w    O .bss   0000000000000058              vtable for __cxxabiv1::__class_type_info@@CXXABI_1.3
0000000000602200  w    O .bss   0000000000000058              vtable for __cxxabiv1::__si_class_type_info@@CXXABI_1.3  

关于c++虚函数表可以参考 C++虚函数表解析
0. 如何初始化map

typedef map<int,const char*>::value_type VALUETYPE;
const static VALUETYPE desArray[] =
{
  VALUETYPE(100,"test1"),
  VALUETYPE(101,"test2")
};
//用desArray 初始化map
const static map<int,const char*> g_codeDespMap(desArray,desArray+sizeof(desArray));
  1. 指针操作
#include <stdio.h>
#include <unistd.h>
int main(void)
{
  int a=0x12345678;
  char* pa = (char*)&a;

  printf("&a=%p\n",&a);
  for(int i=0;i<sizeof(int);i++)
      printf("%p=%p\n",pa+i,(char)*(pa+i));

  short *spa = (short*)&a;
  printf("\n");
  for(int i =0;i<sizeof(int);i++)
  {
      printf("%p=%p\n",spa+i,(int)*(spa+i));
  }
}

output:
&a=0x7ffff01c4684
0x7ffff01c4684=0x78
0x7ffff01c4685=0x56
0x7ffff01c4686=0x34
0x7ffff01c4687=0x12

0x7ffff01c4684=0x5678
0x7ffff01c4686=0x1234
0x7ffff01c4688=(nil)
0x7ffff01c468a=(nil)

  1. 数组 a 与&a的区别(指针数组与数组指针) 指针数组是值一个数组里面装着指针 数组指针是指一个指向数组的指针,它代表指针,指向整个数组
#include <stdio.h>
void main(void)
{
  int a[5]={1,2,3,4,5};
  int *prt = (int*)(&a+1);
  printf("%d,%d\n",*(a+1),*(ptr-1));
  return
}

因为&a是数组的指针(数组的首地址即a)而在栈中数组的分布情况如下:
|bb|高地址
|a4|
|a3|
|a2|
|a1|
|a0|
|cc|低地址
&a=a0的地址
而&a+c ,这里c=1 则将会使得指针移动到bb所在的地址,
(因为它所以移动的是c*n个元素单位,n为数组的元素个数这里c为1)

同理要将二维数组赋给一指针,应这样赋值:

int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

int main()
{
    int v[2][10] = {{1,2,3,4,5,6,7,8,9,10},{11,12,13,14,15,16,17,18,19,20}};
    int (*a)[10] = v;
    printf("%d ",**a);    //1
    printf("%d ",**(a+1)); //11===*a[1]
    printf("%d ",*(*a+1)); //2====*(a[0]+1)
}

分析: *a+1
|
v
*a -----> [1, 2, 3,4,5,6,7,8,9,10]
(a+1)---> [11,12,13,14,15,16,17,18,19,20]
a+1 一共向后移动了:字节数 = 列元素个数×sizeof(int)

  1. STL stl容器只支持“实值语义”,不支持引用语义,下面的写法无法通过编译:

vector<shape&> v;

  1. 一个空类对象的

class TT {}; //sizeof TT=1 class TT0{int test(){}}; //sizeof TT0 =1

sizeof TT=1--linux gcc,这是由c++标准规定的:c++标准规定一个空类以及那些不包含虚函数和非静态数据的成员时,其对象大小也是1. 其主要目的是为了类的实例化,如果大小为0完全不占用内存空间,那么空类无法获取实例的地址,this指针失效,因此不能被实例化。  
另外在类中的static成员不存储在对象成员中所以sizeof是不包含static类的成员.根据继承的权限来区分子类成员是否被继承如果非虚拟继承那么类对象大小与基类以至于,如果是虚拟继承那么需要考虑一个指向指针虚拟函数表的指针的大小,如果是多个虚拟继承也需要将该指针叠加其他情况需要考虑对齐,其对齐计算方法与结构体类似.  

0.  变参使用  

   ```c  
    void func(char* szFormat,...)
    {
         va_list args
         va_start(args, szFormat);
    
    
         vsprintf(buf,szFormat,args);
    
         va_end(args);
         //取参数
         va_args(args);    
    }
  1. ifstream使用

char c; ifstream input; input.open(); input.fail(); while(!input.eof()) { input.get(c); } input.close();



## 析构
```c  
#include <iostream>
#include <stdio.h>
#include <unistd.h>

using namespace std;
//只有基类的析构函数被声明定义为virtual,才会使得,一个基类的指针指向派生类的对象被释放时才会调用基类和派生类的
//析构方法,否则只有基类的析构方法被调用。
class base1{
public:
  base1(){
       cout<<"base1"<<endl;
  }
  ~base1(){
       cout<<"base1 destruct"<<endl;
  }
virtual void set(){
  cout<<__func__<<endl;
}
static int len;
};
int base1::len = 2;
class base2:public base1{
public:
  base2(){
       cout<<"base2"<<endl;
  }
  ~base2(){
       cout<<"base2 destruct"<<endl;
  }
virtual void set(){
  cout<<"derive "<<__func__<<endl;
}
};
///////////////////////////////////////////////////
class base1_v{
public:
  base1_v(){
       cout<<"base1_v"<<endl;
  }
  virtual ~base1_v(){
       cout<<"base1_v destruct"<<endl;
  }
};

class base2_v:public base1_v{
public:
  base2_v(){
       cout<<"base2_v"<<endl;
  }
  ~base2_v(){
       cout<<"base2_v destruct"<<endl;
  }
};
///////////////////////////////////////////////////
struct str1{
  int t;
  str1(){
       cout<<__func__<<endl;
  }
  virtual ~str1(){//只有为virtual才会使得delete的时候父类、子类析构函数被调用,
       cout<<"~"<<__func__<<endl;
  }
};
struct str2:public str1{
  str2(){
       cout<<__func__<<endl;
  }
  ~str2(){
       cout<<"~"<<__func__<<endl;
  }
  int tt2;
};




///////////////////////////////////////
class String{
public:
  String(){
       cout<<__func__<<endl;
  }
  // ~String(){
       //cout<<__func__<<endl;
    // }
  explicit String(char *buf){
       memcpy(m_buf,buf,m_len);
       cout<<"Constructor "<<__func__<<"(char *buf):set m_buf="<<m_buf<<endl;
  }
  explicit String(const String& obj){
       memcpy(m_buf,obj.m_buf,obj.m_len);
       cout<<__func__<<"Copy constructor"<<endl;
  }
  String& operator = (const String &obj){
       *this = obj;
       memcpy(this->m_buf,obj.m_buf,obj.m_len);
       cout<<__func__<<"Assigned"<<endl;
       return *this;
  }
  void print(){
       cout<<__func__<<":m_buf="<<m_buf<<endl;
  }
private:
  static int m_len;
  char *m_buf;
};
int String::m_len = 3;


///////////////////////////////////////
int main(int argc,char **argv){
// base2 b2;
// base1 b1=b2;
base1 *bb2 = new base2;
delete bb2;
cout<<"**********************"<<endl;
base1_v *bb2_v = new base2_v;
delete bb2_v;
cout<<"**********************"<<endl;

////////////////////////////////////
struct str1 *st = new str2;
delete st;
struct str1 sst;
cout<<"**********************"<<endl;
//////////////////////////////////


base1 *b = new base2;
base2 *b2 = dynamic_cast<base2*>(b);// base1 must have more than one virtual method include constructor
b->set();
b2->set();
delete b2;//两个指针释放其中一个即可

cout<<"**********************"<<endl;
// String s = "33";//compile error
String ss=String("33");//just call constructor one time.
String *ps=&ss;//will not call the constructor
String szStr("123");
String &pStr=szStr;//will not call the constructor,won't call the assignment constructor.
pStr.print();
return 0;
}
//** output **
// base1
// base2
// base1 destruct
// **********************
// base1_v
// base2_v
// base2_v destruct //有虚析构则会调用子类的析构函数
// base1_v destruct
// **********************
// str1
// str2
// ~str2
// ~str1
// **********************
// str1
// ~str1

数组 指针

  1. 0长度数组的含义 linux总进程的结构体定义如下,也有0长数组:
        struct thread_info {
        struct task_struct *task;
        struct exec_domain *exec_domain;
        unsigned long flags;
        __u32 status;
        __u32 cpu;
        int preempt_count;
        mm_segment_t addr_limit;
        struct restart_block restart_block;
        void __user *sysenter_return;
        unsigned long previous_esp;
        __u8 supervisor_stack[0];
        };

标准C或者C++中由于不支持 0 长度的数组,只有GNU C允许使用这种用法,目的是为了访问不定长结构体时节省空间和便利性。 例如: c struct demo { int a; char b[256]; char follow[0]; }; 假如,现在程序中要分配一个struct demo结构体,并紧邻其后分配长度为LEN个字节空间,则可以使用如下方法得到: struct demo *demo = (struct demo *) malloc (sizeof(strcut demo) + LEN); 这样我们就可以用 demo->follow 来访问结构体demo随后的空间数据。 当然,我们可以使用指针来达到这样的目的。

```c  
struct demo {
int a;
char b[256];
char *follow;
};
struct demo *demo = (struct demo *) malloc (sizeof(strcut demo) + LEN);
```

同样可以达到零长度数组的效果,但是却多分配了一个char指针。如果分配额外数据空间还好,否则就是白白浪费了空间。 0. 例子
C,pasted just now:

```c
#include stdio.h
#include string.h
int func(char ar[2])
{ 
    printf(sizeof=%d\n,sizeof ar); 
    printf(strlen = %d,strlen(ar));
}
int main(){
    char *p = hello;func(p);
    return 0;
}


Output:

sizeof=4
strlen = 5
````
  1. 例子 C,pasted 1 second ago:

    #include stdio.h
    #include string.h
    int func(char ar[2]){ 
        int a = (int)((int*)0+4); 
        int b = (int)((short*)0+4);
        printf(a=%d\n,a); 
        printf(b=%d\n,b);
    }
    int main(){
        func(NULL);
        return 0;
    }
    
    
    
    Output:
    
    a=16
    b=8

** 注意:**
这里只是4✘sizeof(int)或者4*sizeof(short)个字节,只不过是将他们细分为16份而已
0x0010
0x000f
0x000e
。。。
0x003
0x002
0x001
0x000----(int *) 0


0. 指针例子  
C,pasted just now:

 ```c
 #include stdio.h
 #include string.h
 int func(long L)
 {
     L=L+1;
 }
 int func2(long *pL)
 {
     pL +=1;
 }
 int func3(long *pL){
     *pL +=1;
 }
 int main(){
     long lValue =20;
     long *plValue = lValue;
     func(lValue);
     printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
     func(*plValue);
     printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
     func2(lValue);
     printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
     func2(plValue);
     printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
     func3(lValue);
     printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
     
     func3(plValue);
     printf(lvalue = %d,*plValue=%d\n,lValue,*plValue);
     return 0;
 }


Output:

lvalue = 20,*plValue=20lvalue = 20,*plValue=20lvalue = 20,*plValue=20

lvalue = 20,*plValue=20lvalue = 21,*plValue=21lvalue = 22,*plValue=22
 ```
0. string 查找
C++中使用的string 中有一个属性npos即string::npos可以用来判断string中查找的字符串是否存在  

 ```c
public static member constant
<string>
std::string::npos

static const size_t npos = -1;
 ```
Maximum value for size_t
npos is a static member constant value with the greatest possible value for an element of type size_t.  
This value, when used as the value for a len (or sublen) parameter in string's member functions, means "until the end of the string".  
As a return value, it is usually used to indicate no matches.    
This constant is defined with a value of -1, which because size_t is an unsigned integral type, it is the largest possible representable value for this type.   
 ```c
string str;

pos=str.find_first_of("h");

if(pos!=string::npos)//  match
 ```
0. linux下整型转换字符  
在windows下可以通过,itoa(value,buffer,type),value是待转换的值,buffer,存放转换后的字符串,最后我一个参数是表示需要转换的进制的形式,另外,她的头文件是stdlib.h,但是很奇怪在Linux下gcc并不支持他,另外在windows下还可以通过CString::format来进行转换,该方法在linux下依然是行不同的,在Linux下可以通过sprintf(buffer,"format",param) 5此方法在window下依然是行的通的,或者通过宏来实现   
 ```c
#define Tostring(a) #a
 ```
那么展开后就是const char*即"xxx"  

0. c/c++ 数组做形参:
```c
int test(float a[10]);

注意:形参中给出数组长度是没有意义的,编译器并不为它分配内存,将上面 test 函数的形参改为 float a[1]、float a[10] 依然是正确的。所以一般用指针变量来代替,可以改为float *a。

int test(float a[10],float aa[1000],float* aaa)
{
printf("%d %d %d",sizeof(a),sizeof(aa),sizeof(aaa));
}
在64bit系统上输出8 8 8
  1. c全局变量、局部变量都是先定义的在低地址,后定义的在高地址(注意是定义不是声明)
  2. 编译器什么时候为类提供默认构造函数
  3. 本类、本类定义的成员对象或者父类中有虚函数存在的情况
  4. 父类或者本类中定义的成员对象带有构造函数
  5. 编译器如何传递this指针 this指针保存所属对象的首地址,在使用默认的调用约定时(thiscall)在调用成员函数的过程中编译其做了一个小动作 利用寄存器(ECX或者RAX)保存对象的首地址,并以寄存器传参的方式传递到成员函数中
class TT
{
   public:
     void print()
       {
       }
   public:
       int tn1;
       int tn2;
};
int main()
{
   TT a;
   a.tn1=0x01;
   a.tn2=0x02;
   a.print();
   return 0;
}
//以下是反汇编代码
int main()
{
 4004ed:       55                      push   %rbp
 4004ee:       48 89 e5                mov    %rsp,%rbp
 4004f1:       48 83 ec 10             sub    $0x10,%rsp
   TT a;
   a.tn1=0x01;
 4004f5:       c7 45 f0 01 00 00 00    movl   $0x1,-0x10(%rbp)
   a.tn2=0x02;
 4004fc:       c7 45 f4 02 00 00 00    movl   $0x2,-0xc(%rbp)
   a.print();
 400503:       48 8d 45 f0             lea    -0x10(%rbp),%rax    //取出a对象的地址放入到rax寄存器中
 400507:       48 89 c7                mov    %rax,%rdi
 40050a:       e8 07 00 00 00          callq  400516 <_ZN2TT5printEv>
   return 0;
 40050f:       b8 00 00 00 00          mov    $0x0,%eax
}
 400514:       c9                      leaveq
 400515:       c3                      retq
⚠️ **GitHub.com Fallback** ⚠️