C++变量和基本类型

本文详细介绍了C++中的基本类型在不同平台下的字节数,以及算术类型的最小尺寸。强调了在编程实践中如何选择合适的数据类型,并讲解了有符号类型和无符号类型的区别。此外,深入探讨了const引用的特性和使用规则,包括常量引用的初始化、类型匹配以及与指针的结合。最后,提到了类型别名、auto和decltype的使用,以及类内初始值的注意事项。
摘要由CSDN通过智能技术生成

基本内置类型

算术类型分为两类:整型(包括字符和布尔类型在内)和浮点型

1. 不同平台下基本类型的字节数

类型16位平台32位平台64位平台
char111
short222
int244
long448
long long/88
指针248
float444
double888

数据类型long long 是在C++11中新定义的

2. 算数类型的最小尺寸

算术类型分为两类:整型(包括字符和布尔类型在内)和浮点型

类型含义最小尺寸
bool布尔类型未定义
char字符8位
wchar_t宽字符16位
char16_tUnicode字符16位
char32_tUnicode字符32位
short短整型16位
int整型16位
long长整型32位
long long长整型64位
float单精度浮点数6位有效数字
double双精度浮点数10位有效数字
long double扩展双精度浮点数10位有效数字

3. 数据类型选择的经验准则

  • 当知晓数值不可能为负时,选用无符号类型
  • 使用int执行整数运算,short常常太小,long和int一般尺寸一样。如果int不够,用long long。
  • 在算数表达式中不要使用char或bool,使用char特别容易出问题。如果要使用一个不大的整数,那么明确执行为signed char或unsigned char。
  • 执行浮点数运算用double,float通常进度不够而且两者计算代价相差无几。Long double一般没有必要,且消耗不容忽视。

4. 有符号类型和无符号类型

  • 无符号类型赋值超出其范围,结果是取模后的值。如unsigned char c = -1; //假设char占8bit,c的值为255
  • 有符号类型赋值超出其范围,结果未定义。如signed char c2 = 256; //假设char占8bit,c2的值未定义
  • 切勿混用带符号类型和无符号类型。
  • 算数表达式中既有无符号数又有带符号数,带符号的数会转换成无符号的数

5.初始化与赋值

初始化和赋值是两个完全不同的操作。

  • 定义于任何函数体外的之外的变量被初始化成0
  • 定义于函数体(块作用域)内的内置类型的对象如果没有初始化,则其值未定义。养成初始化内置变量的习惯。
  • 类的对象如果没有显式地初始化,则其值由类确定。

6. 声明与定义

  • 声明使得一个名字为程序所知,定义会申请存储空间,还可能为其赋初始值
  • (分离式编译) 如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量
  • 对于复杂的声明语句,可以从变量名从右往左理解
  • 变量能且只能被定义一次,但是可以被多次声明

7. C++关键字

8. const

8.1 初始化

默认状态下,const对象仅在文件内有效

在编译的过程中,编译器会把所有用到该const变量的地方都替换成相应的值。所以编译器必须知道变量的初始值,如果存在多个文件的情况下,每个文件必须知道const的初始值(const对象也必须初始化)。但由于默认状态下,const对象仅在文件内有效,当多个文件同时出现同名的const变量时,其实就相当于分别定义了不同的变量。

如果想只定义一次怎么做呢?

只需要无论是声明还是定义都标记extern关键字即可。

//file1.cpp
extern const i=1;
//file2.cpp
extern const i;

如果想要在多个文件之间共享const 对象,必须在变量之前添加extern关键字

引用

引用:

引用的类型都系要和与之绑定的对象严格匹配,而且引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起

const引用

允许任何表达式作为初始值,只要改表达式的结果能转换成引用类型即可。尤其允许为一个引用绑定非常量的对象、字面值,甚至一个表达式

8.2 const引用

定义:把引用绑定到const对象上,即为对常量的引用(reference to const)
引用类型必须与其所引用对象类型一致(但是有两个例外),

8.2.1 与普通引用不同的是不能让非常量引用指向一个常量变量。
    int i= 0;
    const int &ci = i; 
    int &ri = ci;

在这里插入图片描述
因为非常量引用可以修改它所绑定的对象,但是常量引用不能,如果非常量引用指向一个常量变量合理的话,那么非常量引用可以修改它所绑定的对象的值,这显然是不正确的。


8.2.2 初始化常量引用时允许将以任意表达式作为初始值,只要表达式的结果能转换成对应类型即可。
8.2.2.1允许常量引用绑定分常量的对象、字面值,甚至一个一般表达式
    double dval = 3.14;
    const int &ri = dval;
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

编译器会将上面代码变成:

    const int temp= dval;
    const int &ri = temp;
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

在这种情况下面,ri绑定了一个临时量


ri不是常量引用的时候会发生错误:

    double dval = 3.14;
    int &ri = dval; // error: invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘int’
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

编译器会将上面代码变成:

    int temp= dval;
    int &ri = temp;
    std::cout<<"dval:"<<dval<<std::endl;
    std::cout<<"ri:"<<ri<<std::endl;

由于临时值的特殊性,程序员并不能操作临时值,而且临时值随时可能被释放掉,所以,一般说来,修改一个临时值是毫无意义的,据此,c++编译器加入了临时值不能作为非const引用的这个语义限制。

8.2.2.2 const引用可能用一个非const对象
    int i =0;
    int &ri = i;
    const int &ci = i;
    ci = 2; // error: assignment of read-only reference ‘ci’

常量引用仅对引用可参与的操作做出限定,对于引用的对象本身是不是一个长量未作限定

8.3 const与指针

指针的类型必须与其所指对象的类型一致(但是有两个例外)

  1. 第一个例外允许一个指向常量的指针指向一个非常量对象.

和const引用差不多,指针常量的指针也没有规定所指的对象必须是一个常量


8.3.1 指向常量的指针和常量指针

指向常量的指针(指针可以改变,指针的值不可变):

const int x=1;
const int *p = &x;//p为指向常量x的指针
int *p = &x; //错误,因为p只是个普通指针

常量指针(不变的指针而不是指针指向的值):

int x=0;
int *const p = &x;//p为常量指针(允许一个指向常量的指针指向一个非常量对象.)
const int xx=0;
const int *const pp= &xx;//pp为指向常量的常量指针(

    int x=0;
    int y = 11;
    const int *p = &x;//p为常量指针(允许一个指向常量的指针指向一个非常量对象.)
    int  *const cp = &x;
    const int *const pp= &x;//pp为指向常量的常量指针

    x = 1;
    std::cout<<"x:"<<x<<std::endl;
    std::cout<<"*p:"<<*p<<std::endl;
    std::cout<<"*cp:"<<*cp<<std::endl;
    std::cout<<"*PP:"<<*pp<<std::endl;
    p = &y;
    *p = 11; // error: assignment of read-only location ‘* p’
    *cp = 12;
     cp = &y;// error: assignment of read-only variable ‘cp’
    std::cout<<"*p:"<<*p<<std::endl;
    std::cout<<"*cp:"<<*cp<<std::endl;

总结:

常量指针,指针本身是一个常量,不可以改变指向的对象,但是可以改变指向对象的值,可以使用解引符改变地址所指向的值
指向常量的指针,可以重新指定指向新的对象,但是不可以改变对象的值.


    int errNumb =0;
    int *const curErr = &errNumb; // curerr 将一直指向errNumb errNumb 的值变化了curerr的值也跟着变化
    const double pi = 3.14159;
    const double *const pip = &pi; // pip 是一个指向常量对象的常量指针
    std::cout<<"curErr:"<<*curErr<<std::endl;
    std::cout<<"pip:"<<*pip<<std::endl;
    errNumb =1;
    std::cout<<"curErr:"<<*curErr<<std::endl;

输出:
curErr:0
pip:3.14159

8.3.2 顶层const和底层const

指针如果添加const修饰符时有两种情况:

顶层const:表示指针本身是一个常量
底层const:表示指针所指向的对象是一个常量

9. constexpr 和常量表达式

常量表达式(const experssion):是指
1.值不会改变 并且
2.在编译过程就能得到计算结果的表达式。

字面量属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

9. 2 constexpr
  1. 变量:
    一般来说,如果 你认定变量是一个常量表达式,那就把它声明成constexpr类型
  2. 函数:
    constexpr函数是指能用于常量表达式的函数。
    应遵循的约定:函数返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return 语句。
    constexpr函数体内可以有其他语句,只要这些语句在运行时不执行任何操作。(例如,空语句、类型别名和using声明等)
    constexpr函数被隐式地指定为内联函数。
9. 3 字面值类型

常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为“字面值类型”(literal type)。

字面值类型范围:

算术类型、引用和指针都属于字面值类型。某些类也是字面值类型,它们可能含有constexpr函数成员。自定义类Sales_item、IO库、string类型不属于字面值类型。

9. 3.1 constexpr 和指针

尽管指针和引用可以定义成constexpr,但它们的初始值受到严格限制。一个constexpr指针的初始值必须是nullptr、0或存储于某个固定地址中的对象。

constexpr int *q = nullprt 等价于 int const *q = nullprt


函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量。定义于函数体外的对象其地址固定不变,能用来初始化constexpr指针。


允许函数定义一类有效范围超出函数本身的变量,如局部静态变量,这类变量和定义在函数体之外的变量一样有固定地址,因此constexpr引用能绑定到这样的变量上,constexpr指针也能指向这样的变量。

10. 处理类型

10.1 类型别名
  1. typeof
typedef double a;
typedef wages base,*p; //等价于 double base ,*p;
  1. using
using SI = Sales_item;
SI item;//等价于 Sales_item item;
10.2 auto 类型
  1. auto 让编译器通过初始值来推断变量的类型,显然,auto定义的变量必须由初始值
  2. 使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型必须一样。
  3. 编译器推断出来有时候和初始值类型并不完全一样,编译器会适当地改变结果使其更符合初始化规则。
  4. auto一般会忽略掉顶层const同时底层const则会保留下来(如果希望推断出来的auto类型是一个顶层const,需要明确指出

例:const int ci = 0;
const auto f = ci;

  1. 还可将引用类型设为auto,此时原来的初始化规则任然适用

auto &g = ci; // g是一个整型常量引用,绑定到ci
auto &h = 42 // 错误: 不能为非常量引用绑定字面值

  1. 设置一个类型为auto的引用时,初始值中的顶层常量属性仍然保留。
10.3 decltype类型
  1. 作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
  2. decltype使用表达式一个变量,则返回该变量的类型(包括顶层const 和 引用在内);

const int ci = 0, & cj = ci;
decltype (ci) x = 0; // const int
decltype (cj) y = x; // const int &
decltype (cj) z;// 错误引用必须有初始化

  1. 引用从来都是作为其所指对象的同义词出现,只有用在decltype处是一个列外
  2. 如果decltype使用表达式不是一个变量,则返回表达式结果对于的类型

int i = 42, * p = &i, & r = i;
decltype(r + 0) b;// 加法的结果是int ,b是一个未初始化的int
decltype(*p) c; // 错误: c 是int& 解引指针可以得到指针所指的对象,而且还能给这个对象赋值,因此结果为int &

  1. 如果在名字前面加上了一对括号,则得到与不加括号时会不一样,如果名字加上一层或多层括号的时编译器将会把它当成一个表达式,变量时一种可以作为赋值语句左值的特殊表达式所以这样的decltype就会得到引用类型

decltype((val)) 的结果永远时引用
decltype(val)结果只有当val本身就是一个引用的时才是引用

  1. 赋值是会产生引用的一类典型表达式,引用的类型就是左值类型
10.4 类内初始值(放在花括号里面,放在等号的右边,但是不能使用圆括号)

struct F{/**/}
int main()
{

return 0;

}

error

1>G:\C++Study\ClassStudy\ClassStudy\main.cpp(6,1): error C2628: “F”后面接“int”是非法的(是否忘记了“;”?)
1>G:\C++Study\ClassStudy\ClassStudy\main.cpp(7,1): error C3874: “main”的返回类型应为“int”而非“F”
1>G:\C++Study\ClassStudy\ClassStudy\main.cpp(11,10): error C2440: “return”: 无法从“int”转换为“F”
1>G:\C++Study\ClassStudy\ClassStudy\main.cpp(11,10): message : 无构造函数可以接受源类型,或构造函数重载决策不明确

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值