【C++ primer】第2章 变量和基本类型


Part I: The Basics
Chapter 2. Variables and Basic Types


2.1 基本内置类型

C++基本内置类型
注意:类型 char 和 类型 signed char 并不一样。类型 char 会表现出带符号形式或无符号形式,具体是哪种由编译器决定。

表2.1 C++:算术类型(Arithmetic Types)

类型含义最小尺寸
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 位有效数字

建议:如何选择类型

  • 当明确知道数字不可能为负时,选用无符号类型。
  • 使用 int 执行整数运算。如果数值超过了 int 的表示范围,选用 long long。
  • 在算术表达式中不要使用 char 和 bool,只有在存放字符或布尔值时使用它们。
  • 执行浮点数运算使用 double。

类型转换:将对象从一种给定的类型转换(convert)为另一种相关类型。

类型所能表示的值决定了转换的过程:
当赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。此时,程序可能继续运行,可能崩溃,或可能生成垃圾数据。

在表达式中使用无符号类型要注意!

当一个算术表达式中同时包含 unsigned 和 int 值时,一般情况下,int 值会转换为 unsigned。

unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl;  // prints -84
std::cout << u + i << std::endl;  // if 32-bit ints, prints 4294967264

无符号不会小于 0 这一事实关系到循环的写法。

// WRONG: u can never be less than 0; the condition will always succeed 
for (unsigned u = 10; u >= 0; --u)
	std::cout << u << std::endl;
字面值常量

字面值常量
字符串字面值类型实际上是由常量字符构成的数组。
编译器在每个字符串的结尾处添加一个空字符 '\0',因此,字符串字面值的实际长度要比它的内容多 1。

彼此相邻且仅由空格、制表符或换行符分隔的两个字符串字面值,可以连接成一个字面值。当书写的字符串字面值常量过长,不适合写入一行时,可以使用这种形式的字面值:

// multiline string literal 
std::cout << "a really, really long string literal "
             "that spans two lines" << std::endl;

转义序列

符号\n\t\v\b\r\f\a\\\?\'\"
含义换行横向制表纵向制表退格回车换页报警(响铃)反斜杠问号单引号双引号

也可以使用泛化的转义序列,其形式是 \x 后紧跟 1 个或多个十六进制数字,或者 \ 后紧跟 1 个、2 个或 3 个八进制数字,其中数字部分表示的字符对应的数值。假设使用的是 Latin-1 字符集,示例如下:

符号\7\12\40\0\115\x4d
含义响铃换行空格空字符字符 M字符 M

指定字符和字符串字面值

前缀含义类型
uUnicode 16 字符char16_t
UUnicode 32 字符char32_t
L宽字符wchar_t
u8UTF-8(仅用于字符串字面常量)char

2.2 变量

变量

变量定义
int units_sold = 0; 
int units_sold = {0}; 
int units_sold{0}; 
int units_sold(0); 

long double ld = 3.1415926536; 
int a{ld}, b = {ld}; // error: narrowing conversion required 
int c(ld), d = ld;   // ok: but value will be truncated
变量定义和声明
extern int i;   // declares but does not define i 
int j;          // declares and defines j 

extern double pi = 3.1416; // definition 

关键概念:静态类型
C++是一种静态类型(statically typed)语言,其含义是在编译阶段检查类型。
检查类型的过程称为类型检查(type checking)。

C++操作符替代名
and, not, or, xor, compl, bitand, bitor, not_eq, or_eq, xor_eq, and_eq


2.3 复合类型

复合类型

引用
int ival = 1024; 
int &refVal = ival;  // refVal refers to (is another name for) ival 
int &refVal2;        // error: a reference must be initialized

// ok: refVal3 is bound to the object to which refVal is bound, i.e., to ival 
int &refVal3 = refVal; 
// initializes i from the value in the object to which refVal is bound 
int i = refVal; // ok: initializes i to the same value as ival

int i = 1024, i2 = 2048;  // i and i2 are both ints 
int &r = i, r2 = i2;      // r is a reference bound to i; r2 is an int 
int i3 = 1024, &ri = i3;  // i3 is an int; ri is a reference bound to i3 
int &r3 = i3, &r4 = i2;   // both r3 and r4 are references 

int &refVal4 = 10;   // error: initializer must be an object 
double dval = 3.14; 
int &refVal5 = dval; // error: initializer must be an int object
指针
int *ip1, *ip2;  // both ip1 and ip2 are pointers to int 
double dp, *dp2; // dp2 is a pointer to double; dp is a double

获取对象的地址

int ival = 42; 
int *p = &ival; // p holds the address of ival; p is a pointer to ival

double dval; 
double *pd = &dval;  // ok: initializer is the address of a double 
double *pd2 = pd;    // ok: initializer is a pointer to double 
int *pi = pd;  // error: types of pi and pd differ 
pi = &dval;    // error: assigning the address of a double to a pointer to int

利用指针访问对象

int ival = 42; 
int *p = &ival; // p holds the address of ival; p is a pointer to ival
cout << *p;     // * yields the object to which p points; prints 42

*p = 0;     // * yields the object; we assign a new value to ival through p 
cout << *p; // prints 0

空指针

int *p1 = nullptr; // equivalent to int *p1 = 0; 
int *p2 = 0;       // directly initializes p2 from the literal constant 0 
// must #include cstdlib 
int *p3 = NULL;    // equivalent to int *p3 = 0;

int zero = 0; 
pi = zero;        // error: cannot assign an int to a pointer

void* 指针

double obj = 3.14, *pd = &obj; 
// ok: void* can hold the address value of any data pointer type 
void *pv = &obj;  // obj can be an object of any type 
pv = pd;          // pv can hold a pointer to any type
理解复合类型的声明
// i is an int; p is a pointer to int; r is a reference to int 
int i = 1024, *p = &i, &r = i; 

int* p;  // legal but might be misleading
int* p1, p2; // p1 is a pointer to int; p2 is an int 
int *p1, *p2; // both p1 and p2 are pointers to int
int* p1;       // p1 is a pointer to int 
int* p2;       // p2 is a pointer to int 

指向指针的指针

int ival = 1024; 
int *pi = &ival;    // pi points to an int 
int **ppi = &pi;    // ppi points to a pointer to an int
cout << "The value of ival\n"
     << "direct value: " << ival << "\n"
     << "indirect value: " << *pi << "\n"
     << "doubly indirect value: " << **ppi
     << endl;

指向指针的引用

int i = 42; 
int *p;        // p is a pointer to int 
int *&r = p;   // r is a reference to the pointer p 
r = &i; //  r refers to a pointer; assigning &i to r makes p point to i 
*r = 0; //  dereferencing r yields i, the object to which p points; changes i to 0

面对一条复杂的指针或引用的声明语句时,从右向左阅读有助于理解其含义。


2.4 const 限定符

const 限定符

const int bufSize = 512;    // input buffer size
bufSize = 512; // error: attempt to write to const object

const int i = get_size();  // ok: initialized at run time 
const int j = 42;          // ok: initialized at compile time 
const int k;               // error: k is uninitialized const

初始化和 const

int i = 42; 
const int ci = i;    // ok: the value in i is copied into ci 
int j = ci;          // ok: the value in ci is copied into j

默认状态下,const 对象仅在文件内有效
如果想在多个文件之间共享 const 对象,必须在变量的定义之前添加 extern 关键字。

// file_1.cc defines and initializes a const that is accessible to other files 
extern const int bufSize = fcn(); 
// file_1.h extern 
const int bufSize; // same bufSize as defined in file_1.cc
const 的引用
const int ci = 1024; 
const int &r1 = ci;   // ok: both reference and underlying object are const 
r1 = 42;              // error: r1 is a reference to const 
int &r2 = ci;         // error: non const reference to a const object

术语:常量引用是对 const 的引用。

初始化和对 const 的引用
“引用类型必须与其所引用的对象一致”的第一个例外:在初始化对 const 的引用时,可以使用能够将值转换为引用的类型的任意表达式作为初始值。

int i = 42; 
const int &r1 = i;      // we can bind a const int& to a plain int object 
const int &r2 = 42;     // ok: r1 is a reference to const 
const int &r3 = r1 * 2; // ok: r3 is a reference to const 
int &r4 = r1 * 2;        // error: r4 is a plain, non const reference
double dval = 3.14; 
const int &ri = dval;

编译器将上面的代码变为:

const int temp = dval;   // create a temporary const int from the double 
const int &ri = temp;    // bind ri to that temporary

在这种情况下,ri 绑定了一个临时量(temporary)对象。
由于将引用绑定到临时对象几乎肯定不是程序员想要的,因此C++语言使其成为非法。

对 const 的引用可能引用一个并非 const 的对象

int i = 42; 
int &r1 = i;          // r1 bound to i 
const int &r2 = i;    // r2 also bound to i; but cannot be used to change i 
r1 = 0;               // r1 is not const; i is now 0 
r2 = 0;               // error: r2 is a reference to const
指针和 const
const double pi = 3.14;   // pi is const; its value may not be changed 
double *ptr = &pi;        // error: ptr is a plain pointer 
const double *cptr = &pi; // ok: cptr may point to a double that is const 
*cptr = 42;               // error: cannot assign to *cptr 

“指针的类型必须与其所指向的对象一致”的第一个例外:指向 const 的指针可以指向一个非const 的对象。

double dval = 3.14;       // dval is a double; its value can be changed 
cptr = &dval;             // ok: but can't change dval through cptr 

const 指针必须初始化,它的值(即存放的地址)不能更改。

int errNumb = 0; 
int *const curErr = &errNumb;  // curErr will always point to errNumb 
const double pi = 3.14159; 
const double *const pip = &pi; // pip is a const pointer to a const object

*pip = 2.72;     // error: pip is a pointer to const 
// if the object to which curErr points (i.e., errNumb) is nonzero 
if (*curErr) {
    errorHandler();    
    *curErr = 0; // ok: reset the value of the object to which curErr is bound 
}
顶层 const
int i = 0; 
int *const p1 = &i;  // we can't change the value of p1; const is top-level 
const int ci = 42;   // we cannot change ci; const is top-level 
const int *p2 = &ci; // we can change p2; const is low-level 
const int *const p3 = p2; // right-most const is top-level, left-most is not 
const int &r = ci;  // const in reference types is always low-level

当执行对象的复制操作时,顶层 const 不受影响。

i = ci;  // ok: copying the value of ci; top-level const in ci is ignored 
p2 = p3; // ok: pointed-to type matches; top-level const in p3 is ignored

当复制一个对象时,两个对象必须具有相同的底层 const 限定条件,或者两个对象的类型之间必须能够转换。通常,可以将 非const 转换为 const,但不能反过来。

int *p = p3; // error: p3 has a low-level const but p doesn't 
p2 = p3;     // ok: p2 has the same low-level const qualification as p3 
p2 = &i;     // ok: we can convert int* to const int* 
int &r = ci; // error: can't bind an ordinary int& to a const int object const 
int &r2 = i; // ok: can bind const int& to plain int
constexpr 和常量表达式

一个对象(或表达式)是否是常量表达式取决于类型和初始值。

const 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

C++11 标准中,通过在 constexpr 声明中声明变量,要求编译器验证变量是否是常量表达式。

constexpr int mf = 20;        // 20 is a constant expression 
constexpr int limit = mf + 1; // mf + 1 is a constant expression 
constexpr int sz = size();    // ok only if size is a constexpr function

在 constexpr 声明中定义指针时,constexpr 说明符应用于该指针,而不是该指针所指向的类型。

const int *p = nullptr;     // p is a pointer to a const int 
constexpr int *q = nullptr; // q is a const pointer to int

constexpr 可以指向 const 类型或 非const 类型。

constexpr int *np = nullptr; // np is a constant pointer to int that is null 
int j = 0; 
constexpr int i = 42;  // type of i is const int
// i and j must be defined outside any function 
constexpr const int *p = &i; // p is a constant pointer to the const int i 
constexpr int *p1 = &j;      // p1 is a constant pointer to the int j

2.5 处理类型

处理类型

类型别名(type alias)

传统方法:使用关键字 typedef

typedef double wages;   // wages is a synonym for double 
typedef wages base, *p; // base is a synonym for double, p for double*

C++11 标准:使用别名声明(alias declaration)

using SI = Sales_item;  // SI is a synonym for Sales_item
wages hourly, weekly;    // same as double hourly, weekly; 
SI item;                 // same as Sales_item item

指针、const 和类型别名

typedef char *pstring; 
const pstring cstr = 0; // cstr is a constant pointer to char 
const pstring *ps;      // ps is a pointer to a constant pointer to char
const char *cstr = 0; // wrong interpretation of const pstring cstr
auto 类型说明符
// the type of item is deduced from the type of the result of adding val1 and val2 
auto item = val1 + val2; // item initialized to the result of val1 + val2

auto i = 0, *p = &i;      // ok: i is int and p is a pointer to int 
auto sz = 0, pi = 3.14;   // error: inconsistent types for sz and pi

复合类型、const 和 auto

编译器以引用对象的类型作为 auto 的类型。

int i = 0, &r = i; 
auto a = r;  // a is an int (r is an alias for i, which has type int)

auto 一般会忽略掉顶层 const,底层 const 会保留下来。

const int ci = i, &cr = ci; 
auto b = ci;  // b is an int (top-level const in ci is dropped) 
auto c = cr;  // c is an int (cr is an alias for ci whose const is top-level) 
auto d = &i;  // d is an int*(& of an int object is int*) 
auto e = &ci; // e is const int*(& of a const object is low-level const)

如果希望推断出的类型有顶层 const,必须明确指出:

const auto f = ci; // deduced type of ci is int; f has type const int

还可以明确指出想要的对 auto 推断类型的引用:

auto &g = ci;       // g is a const int& that is bound to ci 
auto &h = 42;       // error: we can't bind a plain reference to a literal 
const auto &j = 42; // ok: we can bind a const reference to a literal

引用或指针是特定声明程序的一部分,而不是声明的基本类型的一部分。

auto k = ci, &l = i;    // k is int; l is int& 
auto &m = ci, *p = &ci; // m is a const int&;p is a pointer to const int 
// error: type deduced from i is int; type deduced from &ci is const int 
auto &n = i, *p2 = &ci;
decltype 类型说明符
decltype(f()) sum = x; // sum has whatever type f returns

编译器并不调用 f,但是它使用调用返回的类型作为 sum 的类型。

当应用于 decltype 的表达式是一个变量时,decltype 返回该变量的类型,包括顶层 const 和引用。

const 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 的表达式不是一个变量时,decltype 返回表达式结果对应的类型。

// 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 返回引用类型。

decltype 推断的类型取决于其表达式的形式。
特别情况:在变量名外加上一对括号,影响 decltype 返回的类型。

// decltype of a parenthesized variable is always a reference 
decltype((i)) d;    // error: d is int& and must be initialized 
decltype(i) e;      // ok: e is an (uninitialized) int

2.6 自定义数据结构

自定义数据结构

定义 Sales_data 类型
struct Sales_data {
    std::string bookNo;    
    unsigned units_sold = 0;    
    double revenue = 0.0; 
};

使用 struct 关键字定义类。
在类定义的最后加上分号!因为可以在类体后面定义变量。

struct Sales_data { /* ... */ } accum, trans, *salesptr; 
// equivalent, but better way to define these objects 
struct Sales_data { /* ... */ }; 
Sales_data accum, trans, *salesptr;

最好不要将对象定义为类定义的一部分!

使用 Sales_data 类
#include <iostream> 
#include <string> 
#include "Sales_data.h" 
int main() 
{
	// Adding Two Sales_data Objects
 	Sales_data data1, data2;
 	
	// Reading Data into a Sales_data Object
	double price = 0;   // price per book, used to calculate total revenue 
	// read the first transactions: ISBN, number of books sold, price per book 
	std::cin >> data1.bookNo >> data1.units_sold >> price; 
	// calculate total revenue from price and units_sold 
	data1.revenue = data1.units_sold * price;
	// read the second transaction 
	std::cin >> data2.bookNo >> data2.units_sold >> price;
	data2.revenue = data2.units_sold * price;

	// Printing the Sum of Two Sales_data Objects
	// check whether data1 and data2 have the same ISBN, if so print the sum of data1 and data2 
	if (data1.bookNo == data2.bookNo) {
		unsigned totalCnt = data1.units_sold + data2.units_sold;
		double totalRevenue = data1.revenue + data2.revenue;    
		// print: ISBN, total sold, total revenue, average price per book    
		std::cout << data1.bookNo << " " << totalCnt << " " << totalRevenue << " ";    
		if (totalCnt != 0)
			std::cout << totalRevenue/totalCnt << std::endl;
		else
			std::cout << "(no sales)" << std::endl;    
		return 0; // indicate success 
	} else {  // transactions weren't for the same ISBN    
		std::cerr << "Data must refer to the same ISBN" << std::endl;    
		return -1; // indicate failure
	}
}
编写自己的头文件

头文件通常包含任何给定文件中只定义一次的实体,比如类定义、const 和 constexpr 变量。

预处理器(preprocessor)概述

#ifndef SALES_DATA_H 
#define SALES_DATA_H 
#include <string> 
struct Sales_data {    
	std::string bookNo;    
	unsigned units_sold = 0;    
	double revenue = 0.0; 
}; 
#endif

预处理变量无视C++语言中关于作用域的规则!
为避免与程序中的其他实体发生冲突,通常将预处理变量的名字全部大写。


学习目录:【C++ primer】目录

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值