C/C++的数据类型判断

C/C++中类型是重要的。在位级上,自然只有0和1,相同的二进制位,按不同的类型来解析,结果显然不一样。

#include <iostream>

using  namespace std;

struct A
{ 
    int m_i;    
    int *m_p; 
};

int main()
{ 
    A obj;    
    int *q = (int *)&obj;    
    q[1] = -2;    
    cout << (q + 1) << "   " 
        << &obj.m_p << endl;    
    cout << q[1] << endl;    
    cout << obj.m_p;    
    return 0; 
}

 

 

C++标准要求,在同一个访问控制区(private,protected,public),成员的排列需符合“较晚出现的成员在内存中有较高的地址”(参见深度探索C++对象模型,第3.2节)。obj对象中,obj.m_i地址值和obj的地址值相同(注意只是值相同,类型不同)。于是q+1指向的就是obj中m_p,即*(q+1)和obj.m_p对应的位级相同。但由于*(p+1)的类型是int, 而obj.m_p的类型是int*, 所以最终的结果如下:

 

 

 

C/C++是静态类型语言,变量都需要明确的类型来定义。定义时,把变量名拿掉,剩下的就是变量的类型。可通过 #include <typeinfo> 头文件,调用 typeid(对象名).name() 来查看。

比如 char *p;  p的类型是char *.

void (*f)(int i, double d);  则f的类型是 void (*)(int, double);

int a[3][4];  则a的类型是 int  [3][4]

对于复合类型,由外到内,层层深入,按优先级越来越高的方向,与变量名结合最紧密(即最内层的,优先级最高的) 的才表明其本质才是啥,即前边的相当于定语, 最终类型是最后的中心词。比如 char*(*a[4])(); 由外到内,先是*, 表明是指针;然后是( ), 表明是函数;接着是*, 表明是指针;最后是[] , 表明是数组。从外到内  指针  函数  指针  数组,即a的本质是数组。于是,总的来说,a的类型是成员为函数指针的数组,函数的返回类型是char *  .这么说是将划线的看作一体的,指针 函数  指针  数组。或者可以这样看,指针  函数  指针  数组,那就这样说,a的类型是成员为指向返回值为 char 的指针函数的指针的数组。无论怎么看,先后保持先后顺序不变就行。

 

可以结合变量名取出一部分类型来,需要注意的是,只能拿表示其本质的,也就是最里边的部分出来。还有一点需要注意的是,*(p+i) == p[i]的等价.

 

这 个等价关系是有特例的,声明 int func();   int (*pf)() = &func 或者 pf = func; 通过函数指针来调用时, 可以 pf()  也可以是  (*pf)();  但绝对不能是  (p[0])(), 也就是说此时,*p不能用 p[0]

替代,原因是函数指针不能够进行算术运算。

 

来 看例子,char *p; 则p的类型是去掉声明时的p,是char *;  *p把*和p都取出来,所以类型是 char 。对于int a[3][4], 最内层的是 [3]的那个中括号,所以a[1] 取出之后,剩下的是 int [4] ;即a[1]的类型是int [4],  另外由于*a == a[0], 所以同样的, *a的类型是 int [4].

对于 char *(*b[4])(); 则 b[1] 的类型是剩下的部分 char *(*)() ;等价的,因为b和[]结合最紧,即 *(b+1) 实际上是 b[1] ,所以类型也是 char *(*)() .

 

*b[1] 的类型是 char * (),,即**(b+1)的类型也是 char * () . 这里需要注意的是 *(b+1) 的类型是指向返回值为char *的函数指针,总之就是某种类型的函数指针,前边说过的,函数指针 pf 只能够 *pf,而不能够 pf[0]  .  所以这里如果表达成 b[1][0] 或者 (*(b+1))[0] 都是不合法的表达式,而只能够用 *b[1] , **(b+1);

(*b[1])()的类型是char *, *(*b[4])()的类型自然就是char .

在VS2013下的实验代码:

#include <iostream>
#include <typeinfo>

using namespace  std;

char* f1()
{ 
    return (char*)malloc(sizeof(char*)); 
}
char* f2()
{ 
    return (char*)malloc(sizeof(char*)); 
}
char* f3()
{ 
    return (char*)malloc(sizeof(char*)); 
}
char* f4()
{ 
    return (char*)malloc(sizeof(char*)); 
}
int f()
{ 
    cout << "hello world!";    
    return 0; 
}
class MyClass
{
public:    
    int m_i = 0;    //C++11的就地初始化    
    void m_func();
};
           
void MyClass::m_func()
{ 
    cout << "hello world!" << endl; 
}
           
int main()
{         
    char *(*b[4])() = { &f1, &f2, &f3, &f4 };    
    cout << typeid(b).name() << endl;    
    cout << "_______________" << endl;  
  
    cout << typeid(b[1]).name() << endl;    
    cout << typeid(*b).name() << endl;    
    cout << "_______________" << endl;    

    cout << typeid(**(b + 1)).name() << endl;    
    cout << typeid(*b[1]).name() << endl;    

    //b[1]或者说*(b+1)是某种类型的函数指针    
    //所以b[1][0] 和 (*(b+1))[0]都是不合法的    
    //cout << typeid((*(b+1))[0]).name() << endl;   //报错    
    //cout << typeid(b[1][0]).name() << endl;    //报错    
    cout << "_______________" << endl;    

    cout << typeid((*b[1])()).name() << endl;    
    cout << "_______________" << endl;    

    cout << typeid(*(*b[1])()).name() << endl;    
    cout << "_______________" << endl;    

    int (*pf)() = f;    
    cout << typeid(*pf).name() << endl;    
    //函数指针不能使用中括号来索引调用    
    //cout << typeid(pf[0]).name() << endl;   //报错  
    cout << "_______________" << endl;   
 
    MyClass obj;    
    int MyClass::*ptom = &MyClass::m_i;    
    cout << obj.*ptom <<endl;    
    cout << typeid(ptom).name() << endl; 

    void (MyClass::*ptomf)() = &MyClass::m_func;    
    (obj.*ptomf)();    //ptom和ptomf必须和对象或者是对象指针一起使用,不能单独出现    
    cout << typeid(ptomf).name() << endl;    
 
    cin.get();    
    return 0;
}

 

输出的结果如下:

 

 

对任意变量取地址之后,得到的总是一个指针。即 不对变量 var 的类型是什么, &var的本质是一个指针,在原来var 的类型基础上,加了一个*, 最内层的 *. 所以对于 T var, T可能是个复合类型, & var 的类型是 T (*);   比如int a[3]; 那么 &a的类型是 int (* )[3] .  const int i; 那么&i的类型是  const int *, 即常量指针。

 

最后,指向成员变量的指针并不是指针,不能够单独出现,必须和类的对象或者指向对象的指针一起使用。如同上边的ptom和ptomf。详情可参考C++必知必会。注意代码中ptom的定义和是 int * pt = &obj.i区分开来。

 

数组指针,函数指针,或者数组的类型(因为有()和[]的存在),不能够使用Type var这种形式来定义变量,(当然typedef定义一个变量类型别名除外, 或者C++11里用using FP = void (*)())。不过new之后,可以接这样的类型。

typedef int(*TYPE)[2];
    cout << typeid(new int* [2]).name() << endl;
    cout << typeid(new (int [2][3])).name() << endl;
    cout << typeid(new int*).name() << endl;
    cout << typeid(new TYPE).name() << endl;
    cout << typeid(new (int(*)[2])).name() << endl;  //这里的小括号是必须的
    cout << typeid(new (void(*)())).name() << endl;   //这里的小括号也是必须的
    //cout << typeid(new ((int*)[2])).name() << endl;   这么写是不对的

 


可以看到,如果new之后的类型是数组,返回值的类型就是这个数组退化为指针,见这里关于数组的退化点击打开链接。(二维数组本身是数组的数组,所以返回值是数组指针)。其他时候,new的返回结果是原来类型的指针(相当于加了一个最内层的指针)。

 

当涉及到C++多态时,就会有静态类型与动态类型之分了,此时是指使用基类的指针或者引用来指向派生类的对象:

 

class B
{
    int b;
public:
    virtual ~B(){ cout << "B::~B()" << endl; }
};

class D : public B
{
    int i;
public:
    virtual ~D() { cout << "D::~D()" << endl; }
};

    D obj;
    B *pb = &obj;
    cout << typeid(*pb).name() << endl;
    cout << sizeof(*pb);

这里,typeid(*pb).name()输出的类型是 D,也就是运行时实际所指对象的类型,但是 sizeof(*pb) 输出的大小却是 8(B类型的对象是8),而不是D类型的对象是12.  这是因为,sizeof 所求的是静态类型的大小,而typeid 取的却是动态的类型。sizeof() 得到的是编译期的常量,只会求表达式的类型,并不会对表达式求值。表达式最后不会执行,因为在编译器就会被常量替换。

 

 

 

 

 

 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值