C++和标准库速成(二)——字面量、变量和运算符

1. 字面量

  字面量用于在代码中编写数字或字符串。C++支持大量的字面量,如下所示。

类别示例
十进制字面量123
八进制字面量0173
十六进制字面量0x7B
二进制字面量0b1111011
浮点数3.14f
双精度浮点数3.14
十六进制浮点字面量0x3.ABCp-10、0Xb.cp121
单个字符‘a’
以零结尾的字符数组“character array"

  还可以在数值字面量中使用数字分隔符,数字分隔符是一个单引号,示例如下。

23'456'789
0.123'456f

2. 变量

2.1 变量初始化

  在C++中,可在任何位置声明变量,并且可在声明一个变量所在行之后的任意位置使用该变量。声明变量时可不指定值,这些未初始化的变量通常会被赋予一个半随机值,这个值取决于当时内存中的内容。在C++中,也可在声明变量时为变量指定初始值。下面的代码给出了两种风格的变量声明方式,使用的都是代表整型值的int类型。

int uninitializedInt;
int initializedInt { 7 };

std::cout << std::format("{} is a random value\n", uninitializedInt);
std::cout << std::format("{} was assigned an initial value", initializedInt) << std::endl;

  运行结果如下:
在这里插入图片描述
  注意:当代码中使用了未初始化的变量时,多数编译器会给出警告或报错信息。当访问未初始化的变量时,某些C++环境可能会报告运行时错误。
  initializedInt变量使用统一初始化语法进行初始化。也可以使用下面的赋值语句进行初始化:
  initializedInt = 7;
  统一初始化在2011年的C++11标准中引入。建议使用统一初始化代替旧的赋值语法

2.2 变量类型

  C++中的变量是强类型的,它们总是有一个特定的类型。C++自带一个可以开箱即用 的内置类型集合,如下表所示。

类型说明用法
(signed) int
signed
正整数或负整数,范围取决于编译器(通常是四字节)
int i {-7};
signed int i {-6};
signed i {-5};
(signed) short (int)短整型整数(通常是2字节)
short s {13};
short int s {14};
signed short s {15};
signed short int s {16};
(signed) long (int)长整型整数(通常是4字节)long l {-7L};
(signed) long long (int)超长整型整数,范围取决于编译器,但不低于长整型(通常是8字节)long long ll {14LL}:
unsigned (int)
unsigned short (int)
unsigned long (int)
unsigned long long (int)
对前面的类型加以限制,使其值大于等于0
unsigned int i {2U};
unsigned j {5U};
unsigned short s {23U};
unsigned long l {5400UL}; unsigned long long ll {140ULL};
float浮点型数字float f {7.2f};
double双精度数字,精度不低于floatdouble d {7.2};
long double长双精度数字,精度不低于doublelong double d {16.89L};
char
unsigned char
signed char
单个字符char ch {'m'};
char8_t(从C++20开始)
char16_t
char32_t
单个n位UTF-n编码的Unicode字符,n可以是8,16,32
char8_t c8 {u8'm'};
char16_t c16 {u'm'};
char32_t c32 {U'm'};
wchar_t单个宽字符,大小取决于编译器wchar_t w {L'm'};
bool布尔类型,取值为true或falsebool b {true};

  char类型与signed char和unsigned char类型是不同的类型,它只应该用来表示字符。根据你的编译器不同,它既可能是有符号的,也可能是无符号的,所以不因该用它来表示有符号或者无符号字符
  与char相关的是,<cstddef>中提供了std::byte类型来表示单个字节。在C++17之前,char或unsigned char用来表示一个字节,但是那些类型使得像在处理字符。std::byte却能指明意图,即内存中的单个字节。一个byte可以用如下方式初始化:
  std::byte b { 42 };
  注意:C++并没有提供基本的字符串类型。但是作为标准库的一部分提供了字符串的标准实现。

2.3 数值极限

  C++提供了一种获取数值极限信息的标准方式,例如在当前的平台上一个整数能表示的最大值。在C中,你可以使用各种宏定义,例如INT_MAX。尽管这些方法在C++中仍然可以使用,但推荐的做法是使用定义在<limits>中的类模板std::numeric_limits,在使用类模板时需要在一对尖括号内指定需要的类型。

std::cout << "int:\n";
std::cout << std::format("Max int value: {}\n", std::numeric_limits<int>::max());
std::cout << std::format("Min int value: {}\n", std::numeric_limits<int>::min());
std::cout << std::format("Lowest int value: {}\n", std::numeric_limits<int>::lowest());

std::cout << "\ndouble:\n";
std::cout << std::format("Max double value: {}\n", std::numeric_limits<double>::max());
std::cout << std::format("Min double value: {}\n", std::numeric_limits<double>::min());
std::cout << std::format("Lowest double value: {}\n", std::numeric_limits<double>::lowest());

  运行结果:
在这里插入图片描述
  注意min()和lowest()的区别。对于一个整数,最小值等于最低值。对于浮点类型来说,最小值表示该类型能表示的最小正数,最低值表示该类型能表示的最小负数,即-max()。

2.4 零初始化

  可以用一个{0}的统一初始化器将变量初始化为0,0在这里是可选的。一对空的花括号组成的统一初始化器,{},称为零初始化器。零初始化会将原始的整数类型初始化为0,将原始的浮点类型初始化为0.0,将指针类型初始化为nullptr,将对象用默认构造器初始化。示例如下:

float myFloat {};
int myInt {};

2.5 类型转换

  可使用类型转换方式将变量转换为其他类型。C++提供了3种方法显式地转换变量类型。第一种方法来自C,并且仍然被广泛使用,但是实际上已经不建议使用这种方法;第二种方法初看上去更自然,但很少使用;第三种方法最复杂,却最整洁,是推荐方法

float myFloat { 3.14f };
int i1 { (int)myFloat }; // method1
int i2 { int(myFloat) }; // method2
int i3 { static_cast<int>(myFloat) }; // method3

  得到的整数是去掉小数部分的浮点数。在某些环境中,可自动执行类型转换或强制执行类型转换。例如short可自动转换为long,因为long代表精度更高的相同数据类型。

long someLong { someShort }; // no explicit cast needed

  当自动转换变量的类型时,应该了解潜在的数据丢失情况。例如将float转换为int会丢掉一些信息,并且如果浮点数表示的数字超过了整数可表示的最大值,转换结果可能是完全错误的。如果将一个float赋给一个int而不显式执行类型转换,多数编译器会给出警告甚至错误信息。如果确信左边的类型与右边的类型完全兼容,那么隐式转换也完全没问题。

2.6 浮点型数字

  处理浮点数数字可能比处理整型数字复杂。使用数量级不同的浮点值计算可能导致错误;计算两个几乎相同的浮点数的差时会导致精度丢失;很多十进制数不能精确地表示为浮点数
  特殊的浮点数
  1. +/-infinity:表示正无穷和负无穷。
  2. NaN:非数字的缩写。
  可以用std::isnan()判断一个给定的浮点数是否为非数字,用std::isinf()判断是否为无穷,这两个函数都定义在<cmath>中。此外,还可以通过numeric_limits获取这些特殊的浮点数。

3. 运算符

  下表中给出了C++中最常用的运算符以及使用这些运算符的示例代码。注意:在C++中,运算符可以是二元的、一元的甚至是三元的。在C++中只有一个条件运算符。

运算符说明用法
=二元运算符,将右边的值赋给左边的变量
int i;
i = 3;
int j;
j = i;
!一元运算符,改变变量的真/假(非零/零)状态
bool b { !true };
bool b2 { !b };
+执行加法的二元运算符
int i { 3 + 2 };
int j { i + 5 };
int k { i + j };
-
*
/
执行减法、乘法以及除法的二元运算符
int i { 5 - 1 };
int j { 5 * 2 }:
int k { j / i };
%二元运算符,求除法操作的余数,也称为mod运算符int rem { 5 % 2 };
++一元运算符,使变量自增1
如果运算符在变量之后(后增量),表达式的结果是没有增加的值
如果运算符在变量之前(前增量),表达式的结果是增加1后的新值
i++;
++i;
--一元运算符,使变量值减1
i--;
--i;
+=
-=
*=
/=
%=
i=i+j;的简写
i=i-j;的简写
i=i*j;的简写
i=i/j;的简写
i=i%j;的简写
i += j;
i -= j;
i *= j;
i /= j;
i %= j;
&
&=
将一个变量的原始位与另一个变量执行按位与运算
i = j & k;
j &= k;
|
|=
将一个变量的原始位与另一个变量执行按位或运算
i= j | k;
j |= k;
<<
>>
<<=
>>=
对一个变量的原始位执行移位运算,每一位左移或右移指定的位数
i = i << 1;
i = i >> 4;
i <<= 1; i >>= 4;
^
^=
执行两个变量之间的按位异或运算
i = i ^ j;
i ^= j;

  下面的程序展示了最常见的变量类型以及运算符的用法。

int someInteger { 256 };
short someShort;
long someLong;
float someFloat;
double someDouble;

someInteger++;
someInteger *= 2;
someShort = std::static_cast<short>(someInteger);
someLong = someShort * 10000;
someFloat = someLong + 0.785f;
someDouble = static_cast<double>(someFloat) / 100000;
std::cout << std::format("someInteger: {}, someShort: {}, someLong: {}, someFloat: {}, someDouble: {}\n", someInteger, someShort, someLong, someFloat, someDouble) << std::endl;

  运行结果:
在这里插入图片描述
  关于表达式求值顺序,C++编译器有一套准则。如果某行代码非常复杂,包含多个运算符,其执行顺序可能不会一目了然。因此,最好将复杂表达式分成若干短小的表达式,或使用括号明确地将子表达式分组。示例如下:

//int i { 34 + 8 * 2 + 21 / 7 % 2 };
int i { 34 + ( 8 * 2 ) + ( ( 21 / 7 ) % 2) }; // add round brakets to make computation clear

  如果测试不加圆括号和加圆括号的代码,会发现这两种方法是等价的,其结果都是i等于51。如果你认为C++按从左到右的顺序对表达式求值,答案将是1。实际上,C++会首先对/、*以及%求值(从左向右),然后才执行加减运算,最后是位运算。通过使用圆括号,可明确告诉编译器某个运算应该单独求值。
  运算符的计算顺序是由所谓的优先级决定的,优先级高的运算符要先于优先级低的运算符执行。下面给出了部分运算符的优先级,越靠上的运算符优先级越高,它们也将先执行。
  ++ --(后置)
  ! ++ --(前置)
  * / %
  + -
  << >>
  &
  ^
  |
  = += -= *= /= %= &= |= ^= <<= >>=

参考

[比] 马克·格雷戈勒著 程序喵大人 惠惠 墨梵 译 C++20高级编程(第五版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值