CPP Primer - 变量与基本类型
原始内置类型
可运算类型
- 最小的可寻址内存块被称为“字节”(byte),基本存储单元(通常是几个字节)被称为“字”(word)。
- C++虽然一共有3种字符类型(
char
,signed char
,unsigned char
),但只有2种表示方式:signed
和unsigned
,char
类型使用其中一种,具体是哪一种取决于编译器。 - 当需要使用一个小整数时,显式地使用
signed char
或unsigned char
。
类型转换
- 如果向
unsiged
类型的变量a
赋超过其表示范围的值b
,结果是b % 变量a类型的存储范围
;如果向siged
类型的变量a
赋超过其表示范围的值b
,结果是未定义的。 - 如果在算术表达式中同时使用
unsigned
类型和singed
类型,singed
类型将被自动转换为unsinged
类型。1
2
3
4
5
6
7
8
9
10unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl; // 32
std::cout << u - u2 << std::endl; // 4294967264
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl; // 32
std::cout << i - i2 << std::endl; // -32
std::cout << i - u << std::endl; // 0
std::cout << u - i << std::endl; // 0
字面量
- 默认情况下,十进制字面量是
signed
类型,而八进制和十六进制字面量可以是signed
类型也可以是unsigned
类型。 虽然整数字面量存储于
signed
类型,但十进制字面量的值永远不会是一个负数。例如,-42
中,负号不是字面量的一部分。默认情况下,浮点型字面量的类型是
double
。- 两个仅由空格、tab或者换行符分隔的字符串字面量会被拼接为一个字面量。
变量
变量定义
初始化和赋值在C++中是两个不同的操作。
可以用以下4种方法定义一个名为
units_sold
的变量,并将其初始化为0,其中使用{}
的第2、3种称为列初始化(list initialization)。1
2
3
4int units_sold = 0;
int units_sold = {0};
int units_sold{0};
int units_sold(0);使用会产生信息丢失的值进行列初始化是非法的。
1
2
3long double ld = 3.14159265356;
int a{ld}, b = {ld}; //error: narrowing conversion required
int c(ld), d = ld; //ok: but value will be truncated在函数体外定义的变量会被初始化为0,在函数内定义的变量是未初始化的(uninitialized)。
变量声明与定义
为了支持分离编译,C++将声明与定义区分开。声明(declaration)使程序知道变量的存在,一个文件若想使用在别处定义的变量,则需在文件中包含对该变量的声明;定义(definition)创建相应的变量。
使用
extern
关键字但不提供显式初始化则可获得一个声明而非定义。1
2extern int i; // declares but does not define i
int j; // declares and define j任何包含显式初始化的声明都是一个定义,带有初始化的
extern
声明是一个定义。1
extern double pi = 3.1416; //definition
命名空间
- 全局命名空间没有名字,因此,可用
::val_name
访问全局命名空间中的名字。
复合类型
引用
- 引用的类型与引用所指向对象的类型必须匹配(有2个例外)。
指针
- 指针的类型与指针所指向对象的类型必须匹配(有2个例外)。
将
int
类型的变量赋给指针是非法的,即使该变量的值恰好为0。不使用一个
void *
对其指向的对象进行操作——因为我们无法知道对象的类型。
理解复合类型说明
- 理解一个引用
r
最好的方式是,从右到左地读它的定义。1
2
3int i = 42;
int *p; // p is a pointer to int
int *&r = p; // r is a reference to the pointer p
const
- 当我们在多个文件中用相同的名字定义一个const时,这就像在每一个文件中定义了一个不同的变量。
const
修饰的引用
- “引用的类型与引用所指向对象的类型必须匹配”的第一个例外是:我们可以用任何可转换为引用的类型的表达式来初始化一个
const
修饰的引用。1
2
3
4
5
6
7
8int i;
const int &r1 = i; // we can bind a const int& to a plain int object
const int &r2 = 42; // ok: r2 is a reference to const
const int &r3 = r1 * 2; // ok: r3 is a reference to const
int &r4 = r * 2; // error: r4 is a plain, non const reference
double dval = 3.14;
const int &ri = dval; // ok
指针与const
- 一个
const
变量的地址只能存储在用const修饰的指针中。1
2
3
4const double pi = 3.14; // pi is const; its value may not be changed
double *ptr = π // error: ptr is a plain pointer
const double *cptr = π // ok: cptr may point to a double that is const
*cptr = 42; // error: cannot assign to *cptr - “指针的类型与指针所指向对象的类型必须匹配”的第一个例外是:我们可以用一个
const
修饰的指针指向一个非const
的对象。1
2double dval = 3.14; // dval is a double; its value can be changed
const double *cptr = &dval; // ok: but can't change dval through cptr
Top-Level const
- Top-level const是指指针本身是
const
,low-level const是指指针可指向一个const
对象。
constexpr
与常量表达式
- 常量表达式是值不能改变,并且值能够在编译时确定的表达式。
一个由常量表达式初始化的const对象也是一个常量表达式。
可以用
constexpr
函数初始化一个constexpr
变量。1
2
3
4const int max_files = 20; // max_files is a constant expression
const int limit = max_files + 1; // limit is a constant expression
int staff_size = 27; // staff_size is not a constant expression
const int sz = get_size(); // sz is not a constant expression可以在
constexpr
中使用的类型称为“字面类型”。- 在函数外定义的变量的地址是常量表达式,可用于初始化
constexpr
指针。 - 当我们在一个指针声明中使用
constexpr
时,constexpr
修饰符应用于指针本身,而不是指针所指向的变量。1
2const int *p = nullptr; // p is a pointer to a const int
constexpr int *q = nullptr; // q is a const pointer to int
constexpr
引入的是top-level const
。
类型处理
类型别名
使用
typedef
定义类型别名:1
2typedef double wages; // wages is a synonym for double
typedef wages base, *p; // base is a synonym for double, p for double*使用类型声名定义类型别名:
1
using SI = Sales_item; // SI is a synonym for Sales_item
decltype
decltype
是唯一处理引用本身,而不是引用指向对象的语法。1
2
3
4const int ci = 0, &cj = ci;
decltype(ci) x = 0; // x has type const int
decltype(cj) y = x; // y has type const int& and is bound to x
decltype(cj) z; // error: z is a reference and must be initialized通常,
decltype
对产生左值的表达式返回一个引用。1
2
3
4// decltype of an expression can be a reference type
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; // ok: addition yields an int; b is an (uninitialized) int
decltype(*p) c; // error: c is int& and must be initialized如果对一个变量名加上一组或多组圆括号,编译器会将该操作数当作表达式,
decltype
会对这样的表达式产生一个引用。1
2
3
4// decltype of a parenthesized variable is always a reference
int i;
decltype((i)) d; // error: d is int& and must be initialized
decltype(i) e; // ok: e is an (uninitialized) int
定义自己的数据结构
- 没有初始化器的类成员会被默认初始化。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!