Skip to content

W2 数据类型、变量与常量、运算符与表达式

基础知识

  • 计算机基本组成(运算器、控制器、存储器、输入输出)
  • 进制、基数、位权
  • 位与字节
  • 2、8、16、10 进制之间的相互转换

数据类型

  • 常量:宏 vs 常变量(const
  • 变量:全局变量(堆)vs 局部变量(初始化)

数据类型的分类

  • 基本类型
    • 整型
      • 短整型  short int
      • 整型   int
      • 长整型  long int
      • 长长整型 long long int
    • 字符型 char
    • 浮点型
      • 单精度型  float
      • 双精度型  double
      • 长双精度型 long double
    • 布尔型 bool
  • 派生类型
    • 指针类型  *
    • 枚举类型  enum
    • 数组类型  []
    • 结构体类型 struct
    • 共用体类型 union
    • 类类型   class
  • 空类型 void

long doubledouble 的关系?

long double 是 16 位字长时代遗留下来的。在 32 位和 64 位计算机中,long doubledouble 完全等价,保留 long double 只是为了向后兼容。

数据类型的字节数与范围

有关不同编译器下数据类型字节数可能不同的说明

不同的编译系统中,不同数据类型的所占字节 / 表示范围可能不同。所有 C/C++ 编译器中不同整型数据的相互关系仅遵循一个基本原则:以 int 为基准,short 不大于 intlong 不小于 intlong long 不小于 long

本课程中如不加以说明,均以 VC++(下表)为准。

类型标识符字节范围
整型[signed] int4[231,2311]
无符号整型unsigned [int]4[0,2321]
短整型short [int]2[215,2151]
无符号短整型unsigned short [int]2[0,2161]
长整型long [int]4[231,2311]
无符号长整型unsigned long [int]4[0,2321]
长长整型long long [int]8[263,2631]
无符号长长整型unsigned long long [int]8[0,2641]
字符型[signed] char1[27,271]
无符号字符型unsigned char1[0,281]
单精度型float4(2128,2128)
双精度型double8(21024,21024)
长双精度型long double8(21024,21024)

课程使用的 GCC 9.2 也遵循此表。

使用 sizeof(类型) 来查询数据类型所占的字节。

cpp
#include <iostream>
using namespace std;
int main() {
    cout << sizeof(int) << endl                // 4
         << sizeof(unsigned int) << endl       // 4
         << sizeof(long) << endl               // 4
         << sizeof(unsigned short) << endl     // 2
         << sizeof(unsigned long long) << endl // 8
         << sizeof(float) << endl              // 4
         << sizeof(long double) << endl;       // 16
    return 0;
}
  • 整型中,对于 i 个字节的值:
    • 有符号整型使用补码方式存储
      • 正数因符号位留空,最大值为 28i11
      • 补码可以看作最高位表示 28i1 其余照常,因此负数最小为 28i1
      • 总体范围为 [28i1,28i11]
    • 无符号整型全为原码
      • 没有符号位,最大值为所有位全 1,最大值为 28i1
      • 范围为 [0,28i1]
  • 浮点型中
    • 单精度为 1 位符号位、8 位阶码和 23 位尾数
    • 双精度为 1 位符号位、11 位阶码和 52 位尾数

与整型有关的更多内容可参考 JavaScript 位运算 - 重温整数对补码的本质理解,与浮点型有关的内容可参考 JavaScript 数值存储与安全 - 存储格式

可以使用 climits 打印数据类型的上下限:

cpp
#include <climits>
#include <iostream>
using namespace std;
int main() {
    cout << INT_MIN << endl   // -2147483648
         << UINT_MAX << endl  // 4294967295
         << LLONG_MAX << endl // 9223372036854775807
         << SHRT_MAX << endl; // 32767
    return 0;
}

数值字面量

进制前缀

  • 十进制:无前缀,如 123
  • 二进制:加前缀 0b0B,如 0b1111011
  • 八进制:加前缀 0,如 0173
  • 十六进制:加前缀 0x0X,如 0x7b
cpp
cout << 123 << endl       // 123
     << 0b1111011 << endl // 123
     << 0173 << endl      // 123
     << 0x7b << endl;     // 123

WARNING

C++ 中不存在 0o0O 前缀表示八进制的语法!

类型后缀

  • 整型
    • 不加后缀,默认为 int
    • lLlong
    • uUunsigned
  • 浮点
    • 不加后缀,默认为 double
    • 加后缀 fFfloat

C++ 提供 typeid 运算符用于查询值的类型,使用方式为 typeid(值).name()

cpp
//                                         GCC  VC++
cout << typeid(123).name() << endl     //  i    int
     << typeid(123l).name() << endl    //  l    long
     << typeid(123lu).name() << endl   //  m    unsigned long
     << typeid(123ul).name() << endl   //  m    unsigned long
     << typeid(123.0).name() << endl   //  d    double
     << typeid(123.0f).name() << endl; //  f    float

其中 VC++ 与 GCC 的输出不同。

VC++ 与 GCC 类型输出对照表
类型VC++ 输出GCC 输出
boolboolb
charcharc
unsigned charunsigned charh
signed shortshorts
unsigned shortunsigned shortt
intinti
unsignedunsigned intj
unsigned longunsigned longm
long long__int64x
unsigned long longunsigned __int64y
floatfloatf
doubledoubled
long doublelong doublee

可使用下面代码验证。

cpp
#include <iostream>
using namespace std;
int main() {
    cout << typeid(bool).name() << endl;
    cout << typeid(char).name() << endl;
    cout << typeid(unsigned char).name() << endl;
    cout << typeid(signed short).name() << endl;
    cout << typeid(unsigned short).name() << endl;
    cout << typeid(int).name() << endl;
    cout << typeid(unsigned).name() << endl;
    cout << typeid(unsigned long).name() << endl;
    cout << typeid(long long).name() << endl;
    cout << typeid(unsigned long long).name() << endl;
    cout << typeid(float).name() << endl;
    cout << typeid(double).name() << endl;
    cout << typeid(long double).name() << endl;
    return 0;
}

科学计数法

同其他常见语言,使用 [+-]?(\d+\.?\d*|\.\d+)[Ee][+-]?\d+ 科学计数法形式表达数值字面量,即可以使用 eE 表示 “×10

cpp
cout << 1.2e5 << endl; // 120000

字符与字符串字面量

存储与编码

使用单引号 ' 包裹字符,使用 " 包裹字符串(字符序列字符串末尾会有一个 \0

C/C++ 对「字符」的定义是单字节的,可以简单理解为 ASCII 字符。对于汉字等多字节字符,不能用单个 char 保存。

cpp
char ch = '文';
// [warning] multi-character character constant [-Wmultichar]
// [warning] overflow in conversion from 'int' to 'char'
ASCII 码表
DECCharDECCharDECCharDECCharDECChar
0NUL26SUB52478N104h
1SOH27ESC53579O105i
2STX28FS54680P106j
3ETX29GS55781Q107k
4EOT30RS56882R108l
5ENQ31US57983S109m
6ACK32 58:84T110n
7BEL33!59;85U111o
8BS34"60<86V112p
9HT35#61=87W113q
10LF36$62>88X114r
11VT37%63?89Y115s
12FF38&64@90Z116t
13CR39'65A91[117u
14SO40(66B92\118v
15SI41)67C93]119w
16DLE42*68D94^120x
17DC143+69E95_121y
18DC244,70F96|122z
19DC345-71G97a123{
20DC446.72H98b124|
21NAK47/73I99c125}
22SYN48074J100d126~
23ETB49175K101e127DEL
24CAN50276L102f
25EM51377M103g

字符转义

C++ 字面量字符转义表
转义形式意义码位 (DEC)
\a响铃(BEL)7
\b退格(BS)8
\f换页(FF)12
\n换行(LF)10
\r回车(CR)13
\t水平制表(HT)9
\v垂直制表(VT)11
\\反斜线字符 \92
\'单引号(撇号)字符39
\"双引号字符34
\?问号63
\0空字符(NULL)0
\DDD3 位八进制的对应字符,如 \101 表示 A/
\xHH2 位十六进制的对应字符,如 \x41 表示 A/

长度:strlen

运算符

  • 单目、双目与三目

经典问题:自增自减与赋值

cpp
#include <iostream>
using namespace std;
int main() {
    int i = 3;
    i = i++;
    cout << i << endl;
    return 0;
}
  1. ++ 优先级高于 =,先计算 ++
  2. 后缀 ++,将 i 的原值 3 保留到某个中间变量中
  3. 接下来,因为不同的编译器(例如 VS 系列和 GCC 系列可能有两种执行顺序
    • VS
      1. 先将原值赋给 i
      2. 再进行 ++,值为 4
    • GCC
      1. 先进行 ++,值为 4
      2. 再将原值赋给 i,值为 3

渣哥:无聊的做法,仅为了理解。且不主张对同一个变量的多个 ++/-- 出现在 同一个表达式中 同济高程不考

结合

右结合的算符:赋值算符(=+= 等)

类型转换

强制类型转换

(类型名)(表达式)
C 形式
类型名(表达式)
C++ 形式
static_cast<类型名>(表达式)
C++ 形式
(int)(a+b)int(a+b)static_cast<int>(a+b)
(int)aint(a)static_cast<int>(a)
  • 强制转换后,原变量的值、类型不变(强制转换的结果放在一个中间变量中)
  • 若赋值运算符的左右类型不同,则以左值类型为准进行转换
  • float/double char/short/int/long/long long 时,取整
  • char/short/int/long/long long float/double 时,不会溢出,但精度可能受影响

整型提升

在表达式计算时,各种整形至少要提升为 int 类型,如果 int 类型不足以表示的话,要提升到能表示的类型(包括无符号的然后再执行表达式的运算。

因此有

cpp
char ch = 'a';
cout << typeid(+ch).name() << endl;

输出 int 类型。

整型提升不属于强制类型转换。