教程集 www.jiaochengji.com
教程集 >  脚本编程  >  C语言  >  正文 C 内置类型选择和使用总结

C 内置类型选择和使用总结

发布时间:2018-09-06   编辑:jiaochengji.com
教程集为您提供C 内置类型选择和使用总结等资源,欢迎您收藏本站,我们将为您提供最新的C 内置类型选择和使用总结资源
本文主要是在学习 C Primer中文第五版 时的总结,同时分享给需要的读者,本文讲的主要内容是C 内置类型选择和使用注意事项。

C 内置类型选择和使用的注意事项


(1)类型选择

①当明确知道数值不可能为负时,用unsigned类型。

②对于整型,要么用int,要么用long long。short一般过小,当然,如果明确数据不会超过两字节,自然可以用,而long在许多编译器下和int等长。long long是C 11的新类型,是8字节的int。

③尽量使用signed char或unsigned char,而避免使用char。因为char在有些编译器下是无符号的,而在有些编译器下是有符号的。

④浮点运算用double。float与double的计算代价相差无几(甚至Primer上说有些机器上double比float还要快),但显然double精度高,而longdouble一般情况下又不必要。

注意:以上只是基于一般情况的建议,并不是一种规范,即只是建议这么做,而不是强制要求。

(2)使用注意:

①不要将bool值用于计算。如下面的代码

bool b = -10;
int a = 10;
cout << a b << endl;

将得到结果11.

      bool值只有0和1(true和false)两个值,如果将非0值赋给bool,编译器将该bool值视为1,如果将0赋给bool,编译器将该bool值视为0。

注:其实在下觉得这种情况在现实编程中几乎是不会出现的,这里只是拿来说一说将非bool值赋给bool值时编译器的处理方式。

②避免在同一个表达式中同时使用无符号数和有符号数。如下面的代码

unsigned int a = 10;
int b = -42;
cout << a b << endl;

将得到结果4294967264.

     原因是当有符号数与无符号数在一起运算时,有符号数将被视为无符号数。上面int b=-42在机器中用补码表示,而将其二进制补码视为无符号数,就是232-42=4294967276-42=4294967254,再加上a的值10,就是4294967264.

③不要将无符号数作为循环控制变量。如下面的代码


for (unsigned i = 5; i >= 0; --i)   //这里unsigned i被编译器默认解释为unsigned int i
    {
        cout << i << "  ";
        if (4294967291 == i)
        {
            cout << endl;
            break;
        }
    }



将得到如下结果


      原因同上,当i减为-1时,编译器认为是232 -1=4294967295,于是循环继续。显然,如果不是我们刻意加上break语句使i=-5(即编译器认为的4294967291)时退出循环,程序将陷入死循环。而如果我们不知道这是unsigned在作怪时,将会感到莫名其妙,不可思议,百思不得其解,直到抓狂。

      可能有人会说把for循环改成for (unsigned i = 6; i > 0; --i)不就可以了吗?或者还有人会说:谁没事闲的会用unsigned类型作为循环控制变量呀?是的,的确是这样,但这并不代表我们总能清醒地避免上面的错误,尤其是当我们无意中“隐式地”使用unsigned类型作为循环控制变量的时候。(比如我们可能在程序的其它地方通过无符号整型计算得到一个值,而后来将这个值作为循环控制变量用在for或者while中)

      所以,在下的建议就是:时刻牢记——除非出于特别需要,否则永远不要用无符号类型做循环控制变量。


C 内置类型和类类类型的初始化

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int global_n;    // 函数体外的内置类型变量被自动初始化为0

//////////////////////////////////////////////////////////////////////////
// 系统将提供一个默认构造函数来构造ClassA对象
class ClassA
{
public:
    void output() { cout << "ClassA" << endl; }
};

//////////////////////////////////////////////////////////////////////////
// 默认构造函数并没有初始化数据成员m_Value, 这将使此成员处于未定义状态!!
// 带一个int参数的构造函数使得系统不提供默认构造函数, ClassB对象无法被默认构造!!
class ClassB
{
public:
    ClassB(int v) { m_Value = v; }
    void output() { cout << "ClassB, Value: " << m_Value << endl; }
private:
    int  m_Value;
};

//////////////////////////////////////////////////////////////////////////
// 含有ClassB类型的数据成员的类, 由于ClassB没有默认构造函数,
// ClassUseB对象也无法被默认构造!!
class ClassUseB
{
public:
    void output() { cout << "In ClassUseB - "; m_Value.output(); }
private:
    ClassB  m_Value;
};

//////////////////////////////////////////////////////////////////////////
// ClassC提供了默认构造函数, 并使用初始化列表正确地初始化了数据成员
class ClassC
{
public:
    ClassC() : m_Value(0) {}
    void output() { cout << "ClassC, Value: " << m_Value << endl; }
private:
    int  m_Value;
};

// 由于ClassC有默认构造函数, 所以以它为数据成员的类可以被默认构造
class ClassUseC
{
public:
    void output() { cout << "In ClassUseC - "; m_Value.output(); }
private:
    ClassC  m_Value;
};

//////////////////////////////////////////////////////////////////////////
// 使用构造函数初始化列表初始化2个数据成员, 注意顺序和定义顺序不一致
class ClassD
{
public:
    ClassD() : m_B(2), m_A(1) {}
    void output() { cout << "ClassD, Value: " << m_A << ", " << m_B << endl; }
private:
    int m_A;
    int m_B;
};

//////////////////////////////////////////////////////////////////////////
// 初始化列表中, m_A根据m_B的值来初始化自己, 这将导致错误或未定义行为,
// 初始化顺序应该和数据成员定义的顺序一致
class ClassE
{
public:
    ClassE() : m_B(1), m_A(m_B*2)  {}    // m_A将初始化失败! 其值不确定
    //ClassE() : m_A(m_B*2), m_B(1)  {}    // 同上
    //ClassE() : m_A(1), m_B(m_A*2)  {}    // 成功初始化2者
    void output() { cout << "ClassE, Value: " << m_A << ", " << m_B << endl; }
private:
    int m_A;
    int m_B;
};

//////////////////////////////////////////////////////////////////////////
// const类型或引用类型的数据成员, 以及没有默认构造函数的类类型的成员,
// 必须在构造函数初始化列表中进行初始化
class ClassF
{
public:
    // 失败
    //ClassF()
    //{
    //    m_Const = 1;
    //    m_Ref = 2;
    //    m_B(3);
    //}    
    ClassF() : m_Const(1), m_temp(2), m_Ref(m_temp), m_B(3)  {}
    void output()  
    {
        cout << "ClassF, m_Const: " << m_Const
            << ", m_Ref: " << m_Ref << ", ";
        m_B.output();
    }
    
private:
    const int m_Const;
    int   m_temp;    // 为m_Ref提供变量引用
    int&  m_Ref;
    ClassB m_B;
};

//////////////////////////////////////////////////////////////////////////
// 使用默认实参的构造函数
class ClassG
{
public:
    ClassG(int v = 1) : m_Value(v) {}
    void output() { cout << "ClassG, Value: " << m_Value << endl; }
private:
    int m_Value;
};

//////////////////////////////////////////////////////////////////////////
// 隐式类类型的转换
// 可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换
class ClassH
{
public:
    ClassH() : m_Str("null"), m_Int(0) {}
    ClassH(string& s) : m_Str(s), m_Int(1) {}
    ClassH(int i) : m_Str("null"), m_Int(i) {}
    void output()  { cout << "ClassH, m_Str: " << m_Str << ", m_Int: " << m_Int << endl; }
private:
    string m_Str;
    int    m_Int;
};

//////////////////////////////////////////////////////////////////////////
// 使用explict, 抑制隐式类类型的转换
// 要进行类型转换, 必须显式调用相应构造函数
class ClassI
{
public:
    ClassI() : m_Str("null"), m_Int(0) {}
    explicit ClassI(string& s) : m_Str(s), m_Int(1) {}
    explicit ClassI(int i) : m_Str("null"), m_Int(i) {}
    void output()  { cout << "ClassI, m_Str: " << m_Str << ", m_Int: " << m_Int << endl; }
private:
    string m_Str;
    int    m_Int;
};





int main()
{
    // 函数体内的内置类型变量不自动初始化, 拷贝初始化
    int n1 = 10;    
    // 函数体内的内置类型变量不自动初始化, 直接初始化
    int n2(10);    
    // n3没有被初始化, 对n3的使用将导致未定义行为
    // VC 2005: Run-Time Check Failure #3 - The variable 'n3' is being used without being defined.
    int n3;    
    // 使用不含参数的, 明确的构造函数调用语法, 内置类型变量会被初始化为0
    int n4 = int();

    cout << "global_n: " << global_n << ", n1: "
        << n1 << ", n2: " << n2 // << ", n3: " << n3 <<
        << ", n4: " << n4 << endl;
    cout << endl;

    ClassA  a;
    a.output();
    cout << endl;

    // 以下操作均将失败, 因为ClassB没有默认构造函数!
    //ClassB b;
    //b.output();
    //ClassUseB ub;
    //ub.output();
    //ClassB arrB[3];
    //arrB[0].output();
    //vector<ClassB> vecB;
    //vecB.push_back(b);
    //vecB[0].output();
    // 必须用构造函数构造ClassB对象
    ClassB b2(2);
    b2.output();
    cout << endl;

    // 以下构造正确, 因为ClassC可以被默认构造
    ClassC c;
    c.output();
    ClassUseC uc;
    uc.output();
    ClassC arrC[3];
    arrC[0].output();
    vector<ClassC> vecC;
    vecC.push_back(c);
    vecC[0].output();
    cout << endl;

    // ClassD, ClassE, ClassF演示构造函数初始化列表的使用及注意事项
    ClassD d;
    d.output();
    ClassE e;
    e.output();
    ClassF f;
    f.output();
    cout << endl;

    // 使用默认实参的构造函数
    ClassG g;
    g.output();
    cout << endl;

    // 隐式类类型转换
    ClassH h1;
    string str = "test";
    ClassH h2 = str;
    ClassH h3 = 3;
    h1.output();
    h2.output();
    h3.output();
    cout << endl;

    // 使用explicit关键词, 限制构造函数进行隐式转换
    ClassI i1;
    //ClassI i2 = str;    // ERROR, 已限制隐式转换
    //ClassI i3 = 3;    // ERROR, 已限制了隐式转换
    ClassI i4(str);    // OK, 显式转换
    ClassI i5(3);    // OK, 显式转换
    i1.output();
    i4.output();
    i5.output();
    cout << endl;

    cout << endl;
    system("PAUSE");
}

您可能感兴趣的文章:
jquery选择器大全详解
jquery选择器大全 全面详解jquery选择器
jQuery 2.0.3 源码分析Sizzle引擎 – 高效查询
python和c语言的区别是什么
Go 语言到底适合干什么?
c语言学习的简要笔记
boxy基于jquery的弹出层对话框插件扩展应用 弹出层选择器
jQuery选择器总结
利用C#实现标准的 Dispose模式
css的选择器的详细介绍

[关闭]
~ ~