《C++ Primer》笔记(02)

覆盖内容

第 2 章 变量和基本类型

笔记

算术类型

  • 使用signedunsigned区分是否带符号,char类型在不同的机器上的默认符号区分是不同的
  • 明确数值非负时使用unsigned
  • 使用int进行整数运算,越界则使用long long
  • 算术表达式中不要使用charbool,理由为不同机器上的不同表现
  • 浮点运算使用doublefloat常常精度不够

默认类型转换(简)

  • 非bool=>bool:非0为true,0为false
  • bool=>非bool:true为1,false为0
  • float=>int:仅保留整数部分
  • int=>float:小数部分记0
  • unsigned赋越界值:初始值对总值取模后的余数,如将-1赋值给unsigned char(0~255),结果值为-1对256取模后的余数255;如果在i递减的for循环中使用unsigned类型且循环条件为i>=0,则该循环为死循环,因为unsigned类型无负值,始终满足循环条件
  • signed赋越界值:undefined
  • 应避免在表达式中混用多种类型计算
  • 隐式类型转换优先级参考链接和C++11的规定

字面值常量

  • 默认十进制,0开头八进制,0x开头十六进制
  • 负号不在字面值内
  • 末尾加e0代表科学计数法【* 10^0】
  • 浮点数默认字面值类型为double
  • 单个字符'A'char型字面值,"hello"为字符串字面值,是字符构成的数组,尾部有空字符\0(实际长度为长度+1,length()函数返回值不计空字符)
  • 转义序列被认作为一个字符
  • \考虑后三位数字,\x考虑后续所有数字
  • 指定字面值类型参考规定

变量

  • C++11 单个变量也可以采用int i{51};的列表初始化方式来进行初始化,可以检查信息丢失
  • 默认值,string为空串,其他类型如在函数体外(全局作用域下)则为0,否则为不确定值(或未定义)

声明与定义

  • 使用extern来声明变量
  • 声明与定义的区别在于是否进行申请空间和初始化
  • 变量可被声明多次,但只能定义一次,如果变量跨文件使用则只在一个文件中定义,其他使用位置声明

引用和指针

  • 引用即别名,不会申请新空间,只能被绑定到一个已经初始化的对象上(不能是常量),引用不能被引用
  • 指针是一个对象,可以指向新的对象,无须赋初值,存放指向对象的地址
  • 使用int *p = nullptr;来定义空指针,不能把int变量直接赋值给指针,即使变量值为0;在条件中为false
  • void类型的指针可以存放任意类型对象的地址,但无法直接操作所指对象
  • 练习 2.23 参考链接
  • 声明符包括了类型修饰符(*&const等)和变量名,而非数据类型包括类型修饰符,int*p1, p2;将会定义指针p1和整型变量p2
  • 使用*的个数区分指针的级别,复杂的指针和引用的定义采用右结合方式解读,如int *p; int *&r = p;``p为指向int类型的指针,r为引用(&r),引用的对象为指针(*),指针指向的类型为整型(int),初始化值为p

const限定符

  • 默认下常量仅在文件内有效
  • 使用extern让多个文件共用定义的常量
  • 可以引用常量,不可修改常量,只有常量引用/指针可以引用/指向常量,普通的非常量引用/指针不行
  • 如果常量引用r1引用的是非常量i,则不可通过r1来改变i,可以通过其他方法改变i
  • const double *p1 = &i;允许指向常量的指针p1改变指向对象,不允许改变对象的值(对象为常量)
  • double *const p2 = &j;不允许常量指针p2改变指向对象(p2本身就是常量),但可以通过p2改变指向对象的值(对象为double类型,不是常量)
  • 顶层:是否可改变指向的对象;底层:是否可改变指向对象的值

constexpr限定符

  • 使用constexpr修饰的变量必定是常量或其值是由该修饰词修饰的函数的返回值
  • 修饰的指针不能是野指针
  • 函数体内定义的变量不能用constexpr修饰

处理类型

类型别名
  • C++11 using S = Sdudent;声明别名
  • typedef char *pstring; const pstring cstr = 0; pstringchar *的别名,即指向char类型的指针类型(作为基本数据类型),cstr是一个const pstring类型的常量(const修饰pstring而非char),即指向该类型的常量指针(顶层)
auto类型说明符
  • auto定义必须有初值
  • 由于一条声明语句只能有一个基本数据类型,所以一条auto声明中变量的初始类型要相同
  • 如果右值为引用,auto将会忽略掉引用,将类型指定为所引用的类型
  • auto会忽略顶层const,保留底层const;如果需要保留顶层const可以使用const auto f = ci;指定,将引用的类型设为auto时使用默认的初始化规则
decltype类型指示符
  • decltype会保留顶层const和引用
  • int i = 42, &r = i;,则decltype(r)返回int &decltype(r + 0)返回intdecltype(i = 0)返回int &(赋值运算符返回左值的引用)
  • decltype(variable)返回变量本身的类型,decltype((variable))则总返回引用(变量为可以作为左值的特殊表达式,加上括号将变为引用)
autodecltype的区别
	int i = 42, * p = &i, & r = i;

	decltype(i) x1 = 0;       //因为 i 为 int ,所以 x1 为int
	auto x2 = i;              //因为 i 为 int ,所以 x2 为int

	decltype(r) y1 = i;       //因为 r 为 int& ,所以 y1 为int&
	auto y2 = r;              //因为 r 为 int& ,但auto会忽略引用,所以 y2 为int

	decltype(r + 0) z1 = 0;   //因为 r + 0 为 int ,所以 z1 为int,
	auto z2 = r + 0;          //因为 r + 0 为 int ,所以 z2 为int,

	decltype(*p) h1 = i;      //h1 是int&,*解引用操作返回引用
	auto h2 = *p;             // h2 为 int.

自定义数据结构

头文件保护
  • 在头文件中,使用#ifdef#ifndef#define#endif来避免头文件的重复引用,#ifdef#ifndef用于判断变量是否已经被#define,并执行直到#endif为止的操作。
  • 也可将#pragma once放在头文件首,该方法兼容性并不完全(部分老编译器无法识别),但效率比上一种方法高

练习代码

#include <iostream>
#include <string>

/* 2.10
std::string gStr;
int gInt;
*/

int main()
{
	/* 2.3
	unsigned u1 = 10, u2 = 42;
	int i = 10;
	std::cout << u2 - u1 << std::endl;
	std::cout << u1 - u2 << std::endl;
	std::cout << i - u1 << std::endl;
	std::cout << u1 - i << std::endl;
	*/

	/*
	std::cout << u8"hi" << std::endl;
	*/

	/* 2.7
	int month = 09;
	*/

	/* 2.8
	std::cout << "2\115\n";
	std::cout << "2\115\12";
	std::cout << "2\t\115\n";
	*/
	
	/* 2.9
	std::cin >> int val;// Wrong, syntax error
	int i = { 3.14 }; // Wrong, data lost
	double salary = wage = 99.99; // Wrong, syntax error
	int j = 3.14;// Right, data lost
	*/

	/* 2.10
	std::string lStr;
	int lInt;
	std::cout << gStr << ";" << gInt << ";" << lStr << ";" << lInt << std::endl;// empty;0;empty;undefined
	*/

	/* 2.12
	int _;
	_ = 1;
	std::cout << _ << std::endl;
	int catch-22;
	int 1_or_2 = 1;
	*/

	/* 2.14
	int i = 100, sum = 0;
	for (int i = 0; i != 10; ++i) {
		sum += i;
	}
	std::cout << i << ";" << sum << std::endl;
	*/

	/*
	std::string str1;
	std::string str2 = "";
	std::cout << str1.length() << std::endl;
	std::cout << str2.length() << std::endl;
	str2 = "\0";
	std::cout << str2.length() << std::endl;
	str2 = "a\0";
	std::cout << str2.length() << std::endl;
	*/

	/* 2.16-2.17
	int i = 0, & r1 = i;
	double d = 0, & r2 = d;
	r2 = 3.14;
	r2 = r1;
	i = r2;
	r1 = d;
	std::cout << r2 << ";" << i << ";" << r1 << std::endl;
	i = 5;
	r1 = 10;
	std::cout << i << ";" << r1 << std::endl;
	*/

	/*
	int i = 20;
	int* p1 = &i;
	int** p2 = &p1;// p2是一个指向【指向int类型指针】的指针
	int* p3 = p1;// p3是一个指向int类型(int *)的指针,其值等于p1(同样是指向int类型的指针)
	std::cout << "i=" << i << "\np1=" << p1 << "\n*p1=" << *p1 << "\n&i=" << &i << "\np2=" << p2 << "\n*p2=" << *p2 << "\n**p2=" << **p2 << "\n&p1=" << &p1 << "\n&p2=" << &p2 << std::endl;
	std::cout << "p3=" << p3 << "\n*p3=" << *p3 << std::endl;
	int*& r1 = p1;// r1是一个【指向int类型的指针】的引用,即p1的别名
	int j = 10;
	r1 = &j;// 令p1指向j
	std::cout << "p1=" << p1 << "\n*p1=" << *p1 << std::endl;
	std::cout << "r1=" << r1 << "\n*r1=" << *r1 << std::endl;
	*r1 = 0;// 解引用r1得j,赋值j=0
	std::cout << j << std::endl;
	*/

	/* 2.30-2.31
	int i = 1;
	const int v2 = 0;
	int v1 = v2;
	int* p1 = &v1, & r1 = v1;
	const int* p2 = &v2, * const p3 = &i, & r2 = v2;
	std::cout << "i=" << i << "\nv2=" << v2 << "\nv1=" << v1 << "\n*p1=" << *p1 << "\nr1=" << r1 << "\n*p2=" << *p2 << "\n*p3=" << *p3 << "\nr2=" << r2 << "\n\n";
	r1 = v2;
	// p1 = p2;// 非常量指针不能指向常量
	// p1 = p3;
	p2 = p1;
	p2 = p3;
	*/

	/* 2.33-2.35
	int i = 0, & r = i;
	auto a = r;
	const int ci = i, & cr = ci;
	auto b = ci;
	auto c = cr;
	auto d = &i;
	auto e = &ci;
	const auto f = ci;
	auto& g = ci;
	// auto& h = 42;
	const auto& j = 42;
	auto k = ci, & l = i;
	auto& m = ci, * p = &ci;
	// auto& n = i, * p2 = &ci;
	std::cout << "a = " << a << "\nb = " << b << "\nc = " << c << "\nd = " << d << "\ne = " << e << "\ng = " << g << std::endl;
	std::cout << "-----------------------------------------------------------" << std::endl;
	a = 42; b = 42; c = 42; 
	// d = 42; e = 42; g = 42;
	std::cout << "a = " << a << "\nb = " << b << "\nc = " << c << "\nd = " << d << "\ne = " << e << "\ng = " << g << std::endl;
	
	const int i = 42;
	auto j = i;
	const auto& k = i;
	auto* p = &i;
	const auto j2 = i, & k2 = i;
	// i = 0; // const int
	j = 0; // int
	// k = 0; // const int & const
	// *p = 0;
	p = &j; // const int *
	std::cout << "*p = " << *p << std::endl;
	// j2 = 0; const int
	// k2 = 0; // const int & const
	*/
	
	/* 2.36
	int a = 3, b = 4;
	decltype(a) c = a;
	decltype((b)) d = a;
	++c;
	++d;
	std::cout << "a = " << a << "\nb = " << b << "\nc = " << c << "\nd = " << d << std::endl;
	*/

	/* 2.38
	int i = 42, * p = &i, & r = i;

	decltype(i) x1 = 0;       //因为 i 为 int ,所以 x1 为int
	auto x2 = i;              //因为 i 为 int ,所以 x2 为int

	decltype(r) y1 = i;       //因为 r 为 int& ,所以 y1 为int&
	auto y2 = r;              //因为 r 为 int& ,但auto会忽略引用,所以 y2 为int

	decltype(r + 0) z1 = 0;   //因为 r + 0 为 int ,所以 z1 为int,
	auto z2 = r + 0;          //因为 r + 0 为 int ,所以 z2 为int,

	decltype(*p) h1 = i;      //h1 是int&,*解引用操作返回引用
	auto h2 = *p;             // h2 为 int.
	*/

	return 0;
}
posted @ 2020-10-27 18:03  1Shen  阅读(72)  评论(0)    收藏  举报