c++ Primer 第二章 变量和基本类型
1.基本内置类型
- c++定义了算术类型和空类型在内的基本数据类型,其中算数类型包括字符、整型数、布尔值和浮点数。
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 未定义 |
char | 字符 | 8位 |
wchar_t | 宽字符 | 16位 |
char16_t | Unicode 字符 | 16位 |
char32_t | Unicode 字符 | 32位 |
short | 短整型 | 16位 |
int | 整型 | 16位 |
long | 长整型 | 32位 |
long long | 长整型 | 64位 |
float | 单精度浮点数 |6位有效数字 | |
double | 双精度浮点数 |10位有效数字 | |
long double | 扩展精度浮点数 | 10位有效数字 |
如何选择类型
1.当明确知晓数值不可能是负数时,选用无符号类型;
2.使用int执行整数运算。一般long的大小和int一样,而short常常显得太小。除非超过了int的范围,选择long long。
3.算术表达式中不要使用char或bool。
4.浮点运算选用double。float 精度不够
练习1:int,long,long long和short有什么区别?在无符号和有符号类型之间?在浮点数和双数之间?
c++ 的保证short 和 int 至少16位,long至少32位,long long 至少64位
signed 可以为正数,负数和零,而unsigned 只能代表数字不低于0
float<double<long double(运算速度)
类型的转换
- 非布尔型赋给布尔型,初始值为0则结果为false,否则为true(非零值为true)。
- 布尔型赋给非布尔型,初始值为false结果为0,初始值为true结果为1。
符号型
当符号型与非符号型相运算时,带符号数会转成无符号数
字面值常量
- 一个形如42的值被称作字面值常量(literal)。
- 整型和浮点型字面值。
- 字符和字符串字面值。
- 使用空格连接,继承自C。
- 字符字面值:单引号, ‘a’
- 字符串字面值:双引号, “Hello World”"
- 转义序列。\n、\t等。
- 布尔字面值。true,false。
- 指针字面值。nullptr
变量
- 变量提供一个具名的、可供程序操作的存储空间。
- 变量的基本形式是:类型说明符
- 变量的初始化:
- 初始化不是赋值
- 含义是创建变量时赋予其一个初始值
- 与* 赋值 *区别:将当前对象的值擦除,而以一个新值替代。
- 列表的初始化 使用花括号{},例如:int example{0};
- 使用列表初始化且初始化存在丢失信息的风险时,则编译器会报错
- 例如:
long double id=3.1415926; int a{id},b={id};//错误:转换未执行,存在丢失信息的危险 int c(id),d=id;//正确,转换执行,且确实丢失了数据
- 例如:
- 默认初始化:定义时没有指定初始值会被默认初始化;在函数体内部的内置类型变量将不会被初始化。
- 建议初始化每一个内置类型的变量。
- 注意:定义于函数体内的内置类型的对象如果没有初始化,则其值未定义。类的对象如果没有显式地初始化,则其值由类确定
变量的声明与定义
- 声明使得名字为程序所知。
- 定义是创建与名字关联的实体,就是会申请存储空间
- 变量只能被定义一次,但是可以多次声明。
extern int i //声明
int j;//声明且定义
extern double pi=3.1415826;//定义
- 名字的作用域,c++语言中大多数作用域以花括号分割开
- 一般,在对象第一次被使用的地方附近定义它是一种好的选择,因为这样做有助于容易找到变量的定义。
- 嵌套作用域:内层作用域和外层作用域
int i = 42 ;
int main()
{
int i = 100 ;
int j = i;
}
//j的值为100
左值与右值
- 左值指的是可以取地址的变量,记住,左值与右值的根本区别在于能否获取内存地址,而能否赋值不是区分的依据。通常临时量均为右值。
复合类型
引用
- 引用为对象起了另一个名字,引用类型引用另外一种类型。int b=10;int &a=b;
- 注意:** 引用并非对象,引用和它的初始值是绑定在一起的,不是拷贝,引用必须初始化**
- ** 引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起**
指针
- 是一种 "指向(point to)"另外一种类型的复合类型。
- 定义指针类型: int *ip1;,从右向左读,ip1是指向int类型的指针。
- 指针存放某个对象的地址。
- 获取对象的地址: int i=42; int *p = &i;。 &是取地址符。
- 指针的值的四种状态:
- 1.指向一个对象;
- 2.指向紧邻对象的下一个位置;
- 3.空指针,意味着指针没有指向任何对象;
- 4.无效指针,也就是上述情况之外的其他值。
- 指针访问对象: cout << p;, 是解引用符(* 解引用操作适用于那些确实指向了某个对象的有效指针**)。
- 空指针不指向任何对象,用字面值nullptr初始化。
- ** 使用未初始化的指针是引发运行错误的一大原因**
- 赋值与指针:赋值永远改变的是等号左侧的对象
int a=20;
int *b=&a;//b的值被改变,b指向a,b存放了a的地址
*b=10;//a的值被改变,a=10,*b(就是指针b所指的对象,也就是a)
- void*指针可以存放任意对象的地址。
- 其他指针类型必须要与所指对象严格匹配。
理解复合类型的声明
- int *p1,p2;//p1是指向int的指针,p2是int
- 一般来说,声明符中修饰符的个数并没有限制。当有多个修饰符连携在一起时,按照其逻辑关系详加解释。(面对一条复杂的指针或者引用声明语句时,从右向左阅读有助于弄清楚它的真实含义)
CONST
- 动机:希望定义一些不能被改变值的变量。
- const对象必须初始化,且不能被改变。
- const变量默认不能被其他文件访问,非要访问,必须在指定const前加extern,默认情况下,const对象设定仅在文件内有效。
- eference to const(对常量的引用):指向const对象的引用,如 const int a=1; const int &b= a;,可以读取但不能修改b 。
- 临时量(temporary)对象:当编译器需要一个空间来暂存表达式的求值结果时,临时创建的一个未命名的对象。
- 对临时量的引用是非法行为。
指针与const
- pointer to const(指向常量的指针):不能用于改变其所指对象的值, 如 const double pi = 3.14; const double *cptr = π。
- const pointer:指针本身是常量,如 int i = 0; int *const ptr = &i;
顶层与底层const
- 顶层const:指针本身是个常量。例:int * const a;
- 底层const:指针指向的对象是个常量。拷贝时严格要求相同的底层const资格。例:int const *b;const int *b;
- ** const int p 表示:「p」类型为 int 并且不可变;而 int * const p 表示:「 const p」属于 int,即:p 属于「int 」并且不可变。
constexpr和常量表达式
- 常量表达式:指值不会改变,且在编译过程中就能得到计算结果的表达式。
- C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量的表达式。
处理类型
类型别名
- 用typedef 来定义:typedef double a;a b;//double b
- 新方法:using a = double;
auto类型说明符
- auto类型说明符:让编译器自动推断类型。
- 会忽略顶层const。
decltype类型指示符
- decltype作用是选择并返回操作数的数据类型。
- decltype(f()) sum=x;//sum 的类型就是函数f 的返回类型。
- decltype((a)) (注意是双层括号)的结果永远是引用,而decltype(variable)的结果只有当variable 本身就是一个引用时才是一个引用。
自定义数据类型
struct
- 类可以以关键字struct开始,紧跟类名和类体。
- 类数据成员:类体定义类的成员。
- C++11:可以为类数据成员提供一个类内初始值(in-class initializer)。
struct sales_data{
std::string bookno;
unsigned units_sold=0;
double revenue=0.0;
};
- ** 在类定义后最后加上分号**
预处理技术
#define //定义一个预处理宏
#undef //取消宏的定义
#if //编译预处理中的条件命令, 相当于C语法中的if语句
#ifdef //判断某个宏是否被定义, 若已定义, 执行随后的语句
#ifndef //与#ifdef相反, 判断某个宏是否未被定义
#elif //若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if
#else //与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
#endif //#if, #ifdef, #ifndef这些条件命令的结束标志.
defined //与#if, #elif配合使用, 判断某个宏是否被定义
#include // 包含文件命令
#include_next //与#include相似, 但它有着特殊的用途
#line //标志该语句所在的行号
# //将宏参数替代为以参数值为内容的字符窜常量
## //将两个相邻的标记(token)连接为一个单独的标记
#pragma //说明编译器信息
#warning //显示编译警告信息
#error //显示编译错误信息