不能再详细的C++基本数据类型剖析

前言

C++作为一门强类型的编程语言,每个变量都有明确的所属类型,变量类型决定了其能够参与的运算、函数参数的传递、变量赋值等一系列操作。

本篇博客主要详细剖析C++中的基本数据类型相关用法和注意事项,扫除大家在理解和使用C++中基本数据类型相关的障碍和困惑。博客所记录可能不是非常全,但求基本覆盖常见用法。

C++基本数据类型简介

下表列出了几种基本的 C++ 数据类型:

类型关键字
布尔型bool
字符型char
整型int
浮点型float
双浮点型double

一些基本类型可以使用一个或多个类型修饰符进行修饰:
signed
unsigned
short
long

常用类型验证相关操作

在进行后续详细讲解前,此处我先介绍自己在验证这些类型运算所用到的一些辅助函数和功能,正是有了这些功能函数和功能类的帮助,我们才能更好的研究这些数据类型。

1、C++变量类型打印函数typeinfo
如果你熟悉Python的话你肯定用到过Python中的type函数,其可以方便获取变量的类型。从C++11以后,也引入了一个typeinfo函数,可以用来打印基本数据类型变量所属类型。这对我们验证和调试一些变量的类型非常方便,这一节首先来看下如何使用typeinfo来打印数据类型。

直接上代码:

#include <typeinfo>
#include <iostream>
#include <cmath>

using std::cout;
using std::endl;

void UseTypeInfo() {
    cout << "=========Begin test UseTypeInfo=========" << endl;
    bool bool_var;
    char char_var;
    int int_var;
    unsigned int uint_var;
    long long_var;
    unsigned long ulong_var;
    long long longlong_var;
    unsigned long long ulonglong_var;
    float float_var;
    double double_var;

    cout<<typeid(bool_var).name()<<endl;
    cout<<typeid(char_var).name()<<endl;
    cout<<typeid(int_var).name()<<endl;
    cout<<typeid(uint_var).name()<<endl;
    cout<<typeid(long_var).name()<<endl;
    cout<<typeid(ulong_var).name()<<endl;
    cout<<typeid(longlong_var).name()<<endl;
    cout<<typeid(ulonglong_var).name()<<endl;
    cout<<typeid(float_var).name()<<endl;
    cout<<typeid(double_var).name()<<endl;
    cout << "=========End test UseTypeInfo=========" << endl;
}

int main() {
    UseTypeInfo();
    return 0;
}

生成可执行文件basic_data_type,执行:./basic_data_type后结果为:

=========Begin test UseTypeInfo=========
b
c
i
j
l
m
x
y
f
d
=========End test UseTypeInfo=========

这是什么意思呢?其实是每个基本数据类型的缩写,具体含义为:

bool:                     b
char:                     c
signed char:              a
unsigned char:            h
(signed) short (int):     s
unsigned short (int):     t
(signed) (int):           i
unsigned (int):           j
(signed) long (int):      l
unsigned long (int):      m
(signed) long long (int): x
unsigned long long (int): y
float:                    f
double:                   d
long double:              e

如果你觉得这些缩写看起来比较别扭的话,你可以在执行可执行文件命令后面加上c++filt -t,即执行:./basic_data_type | c++filt -t,就可以得到如下打印结果:参考

=========Begin test UseTypeInfo=========
bool
char
int
unsigned int
long
unsigned long
long long
unsigned long long
float
double
=========End test UseTypeInfo=========

2、获取类型占用字节数函数sizeof
在验证C++基本数据类型一些操作时,另一个很重要的函数就是sizeof,其可以获取一个类型所占用内存的字节数:

void UseSizeOf() {
    cout << "=========Begin test UseSizeOf=========" << endl;
    cout<<"bool byte size: "<<sizeof(bool)<<endl;
    cout<<"char byte size: "<<sizeof(char)<<endl;
    cout<<"short int byte size: "<<sizeof(short int)<<endl;
    cout<<"unsigned short int byte size: "<<sizeof(unsigned short int)<<endl;
    cout<<"int byte size: "<<sizeof(int)<<endl;
    cout<<"unsigned int byte size: "<<sizeof(unsigned int)<<endl;
    cout<<"long int byte size: "<<sizeof(long int)<<endl;
    cout<<"long long int byte size: "<<sizeof(long long int)<<endl;
    cout<<"float byte size: "<<sizeof(float)<<endl;
    cout<<"double byte size: "<<sizeof(double)<<endl;
    cout << "=========Begin test UseSizeOf=========" << endl;
}

执行结果为:

=========Begin test UseSizeOf=========
bool byte size: 1
char byte size: 1
short int byte size: 2
unsigned short int byte size: 2
int byte size: 4
unsigned int byte size: 4
long int byte size: 8
long long int byte size: 8
float byte size: 4
double byte size: 8
=========Begin test UseSizeOf=========

要注意一个字节为8位(8 bit),并且不同类型占用字节大小会根据编译器和所使用的电脑而有所不同,通过类型占用字节大小和其有符号和无符号的区别,我们就可以计算出该类型所能表示的最大最小的数值,当然我们也可以用下面介绍的std::numeric_limits模板类提供的函数直接获取不同类型的极值。

3、获取每个类型所能表示的数值范围
C++中由于每个类型所能表示的数值范围并不相同,不同数值类型不能直接参与运算,需要进行类型转换,而在无符号、有符号类型之间进行转换时会发生各种不可意料的事情,因此了解每种基本数据类型所能表示的最大最小范围很重要。

C++中提供了std::numeric_limits模板类以及头文件cfloat中的DBL_MINDBL_MAX等函数可以用来方便获取不同数据类型所能表示的数值范围:

#include <limits>
#include <cfloat>

void UseNumericalLimit() {
    cout << "=========Begin test UseNumericalLimit=========" << endl;
    cout<<"uint16_t min: "<< std::numeric_limits<uint16_t>::min()<<endl;
    cout<<"uint16_t max: "<< std::numeric_limits<uint16_t>::max()<<endl;

    cout<<"short min: "<< std::numeric_limits<short>::min()<<endl;
    cout<<"short max: "<< std::numeric_limits<short>::max()<<endl;

    int int_min = std::numeric_limits<int>::min();
    cout<<"int min: "<<int_min<<endl;
    int int_max = std::numeric_limits<int>::max();
    cout<<"int max: "<<int_max<<endl;

    unsigned int unsigned_int_min = std::numeric_limits<unsigned int>::min();
    cout<<"unsigned int min: "<<unsigned_int_min<<endl;
    unsigned int unsigned_int_max = std::numeric_limits<unsigned int>::max();
    cout<<"unsigned int max: "<<unsigned_int_max<<endl;

    float float_min = std::numeric_limits<float>::min();
    cout<<"float min: "<<float_min<<endl;
    float float_max = std::numeric_limits<float>::max();
    cout<<"float max: "<<float_max<<endl;

    double double_min = std::numeric_limits<double>::min();
    cout<<"double min: "<<double_min<<endl;
    double double_max = std::numeric_limits<double>::max();
    cout<<"double max: "<<double_max<<endl;

    double_min = DBL_MIN;
    cout<<double_min<<endl;
    double_max = DBL_MAX;
    cout<<double_max<<endl;
    
    cout << "=========Begin test UseNumericalLimit=========" << endl;
}

执行结果如下:

=========Begin test UseNumericalLimit=========
uint16_t min: 0
uint16_t max: 65535
short min: -32768
short max: 32767
int min: -2147483648
int max: 2147483647
unsigned int min: 0
unsigned int max: 4294967295
float min: 1.17549e-38
float max: 3.40282e+38
double min: 2.22507e-308
double max: 1.79769e+308
2.22507e-308
1.79769e+308
=========Begin test UseNumericalLimit=========
int main()
{
    auto a = 5u - 2147483648;
    unsigned int b = 10;
    cout<<typeid(a).name()<<" "<<typeid(b).name()<<endl;
    cout<<a<<endl;

    cout<<sizeof(int)<<" "<<sizeof(long)<<" "<<sizeof(long long)<<endl;
    cout<<std::numeric_limits<long>::min()<<endl;
    cout<<std::numeric_limits<int>::max()<<endl;
    cout<<std::numeric_limits<unsigned int>::max()<<endl;
}

基本数据类型使用细节

数值常量的类型

所谓常量,就是在整个程序运行过程中始终不变的量,简单来说,就是程序中直接使用的数值、字符和字符串等,C++中的常量主要包括整型常量、浮点型常量、字符常量和字符串常量,此处主要讲解整型和浮点型两种数值常量。

整型常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

一些简单示例:

85         // 十进制
0213       // 八进制 
0x4b       // 十六进制 
30         // 整数 
30u        // 无符号整数 
30l        // 长整数 
30ul       // 无符号长整数
浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成,你可以使用小数形式或者指数形式来表示浮点常量。根据取值范围的不同,C++中的浮点型数值类型可以分为单精度型float、双精度型double和长双精度型long double。

当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用eE引入的。

下面列举几个浮点常量的实例:

3.14159       // 合法的 
314159E-5L    // 合法的 
510E          // 非法的:不完整的指数
210f          // 非法的:没有小数或指数
.e55          // 非法的:缺少整数或分数

为了详细了解浮点数据,我们需要知道C++中浮点类型数据的内存存储结构,参考

1、float类型
float类型占四个字节,每个字节占8位,总共32位,其内存结构如下图:
在这里插入图片描述
可以看出,float类型包含有:1位符号位、8位指数位和23位尾数位。

由于指数可正可负,因此采用移位存储的8位指数位表示的范围为-128~+128,其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。

因此float的范围为-2^128 ~ +2^128,也即-3.40282E+38 ~ +3.40282E+38,这也与我们前面使用std::numeric_limits得到的值保持一致。
2、double类型
double类型内存中存储的原理与float类型是一样的,只不过double类型是用64位来表示的,其存储结构如下:
在这里插入图片描述
可以看出,double类型包含有:1位符号位、11位指数位和52位尾数位。

类似的,double类型的取值范围为-2^1024~+2^1024,也即-1.79769e+308~+1.79769e+308

【参考】
1、C/C++中float & double类型数据在内存中的存储形式
2、浮点数的最大最小值
浮点数表示方法与精度:
https://www.learncpp.com/cpp-tutorial/floating-point-numbers/
内存模型:
https://blog.csdn.net/whzhaochao/article/details/12885875
https://blog.csdn.net/whzhaochao/article/details/12887779

数值常量默认类型

C++中每个数据都是有类型的,数值常量也不例外,对于一个常数来说,其也有默认的数据类型,也可以通过上述后缀来改变具体类型。

下面示例代码我们使用前面提到的typeinfo函数来获取数值常量的类型:

int main() {
    auto x = -10U;
    cout<<x<<endl;

    cout<<typeid(0).name()<<endl;
    cout<<typeid(-10U).name()<<endl;
    cout<<typeid(2147483647).name()<<endl; # int类型最大值为2147483647
    cout<<typeid(2147483648).name()<<endl;
    cout<<"10L: "<<typeid(10L).name()<<endl;
    cout<<"10U: "<<typeid(10U).name()<<endl;

    cout<<typeid(10.0).name()<<endl;
    cout<<typeid(10.0F).name()<<endl;
}

执行结果为:

0: int
-10: int
2147483647: int
2147483648: long
10L: long
10U: unsigned int
10.0: double
10.0F: float

从输出结果可以看出几个关键点:
1、整型常量默认为有符号int类型,如果整型常量大小超出int,就会转变为long int类型,如果需要无符号整形常量的话就可以在常量后面加u或是U,如0u或是0U,同理可以在常量后面加ul或UL表示无符号长整形常量;

2、浮点型默认为double,想要单精度float类型数据,需要加后缀f或F;

3、浮点型数据没有u或U,因为浮点数一般都为有符号

基本数据类型运算时的类型转换问题

有符号类型与无符号类型的迷茫:
https://www.jianshu.com/p/cd30cbb78f4f
https://blog.csdn.net/fhyangchina/article/details/55250934

隐式类型转换:
https://www.cnblogs.com/QG-whz/p/4472566.html
https://www.cnblogs.com/predator-wang/p/5230902.html
https://zhuanlan.zhihu.com/p/81588535
https://en.cppreference.com/w/c/language/conversion

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值