热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《C++primer(第五版)》学习之路-第二章:变量和基本类型

【声明:版权所有,转载请标明出处,请勿用于商业用途。联系信箱:libin493073668@sina.com】2.1基本内置类型1.算术类型分为两类:整型(integraltype,包括字符

【 声明:版权所有,转载请标明出处,请勿用于商业用途。  联系信箱:libin493073668@sina.com】


2.1 基本内置类型


1.算术类型分为两类:整型(integral type,包括字符和布尔类型在内)和浮点型。

布尔类型(bool)的取值是真(true)或者假(false)。


2.除去布尔型和扩展的字符型之外,其他整型可以划分为带符号的(signed)和无符号的(unsigned)两种。带符号类型可以表示正数、负数或0,无符号类型则仅能表示大于等于0的值。
类型int、short、long和long long都是带符号的,通过在这些类型名前添加unsigned就可以得到无符号类型,例如unsigned long。类型unsigned int可以缩写为unsigned。

与其他整型不同,字符型被分为了三种:char、signed char和unsigned char。特别需要注意的是:类型char和类型signed char并不一样。尽管字符型有三种,但是字符的表现形式却只有两种:带符号的和无符号的。类型char实际上会表现为上述两种形式中的一种,具体是哪种由编译器决定。


3.unsigned也能接收负值,不过会自动将这个负值转化为正值。


4.以0开头的数代表八进制,以0x开头的数表示十六进制


5.由单引号括起来的一个字符称为char型字面值,双引号括起来的零个或多个字符则构成字符串型字面值。


6.转义字符

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

7.指定字面值的类型



2.2 变量


1.变量定义的基本形式是:首先是类型说明符(type specifier),随后紧跟由一个或多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。列表中每个变量名的类型都由类型说明符指定,定义时还可以为一个或多个变量赋初值。


2.对象是指一块能存储数据并具有某种类型的内存空间。


3.初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。


4.作为C++11新标准的一部分,用花括号来初始化变量得到了全面应用,而在此之前,这种初始化的形式仅在某些受限的场合下才能使用。这种初始化的形式被称为列表初始化(list initialization)。现在,无论是初始化对象还是某些时候为对象赋新值,都可以使用这样一组由花括号括起来的初始值了。


5.


6.变量命名规范
变量命名有许多约定俗成的规范,下面的这些规范能有效提高程序的可读性:
标识符要能体现实际含义。
变量名一般用小写字母,如index,不要使用Index或INDEX。
用户自定义的类名一般以大写字母开头,如Sales_item。
如果标识符由多个单词组成,则单词间应有明显区分,如student_loan或studentLoan,不要使用studentloan。
对于命名规范来说,若能坚持,必将有效。


2.3 复合类型


1.引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字。


2.指针值
指针的值(即地址)应属下列4种状态之一:
1.指向一个对象。
2.指向紧邻对象所占空间的下一个位置。
3.空指针,意味着指针没有指向任何对象。
4.无效指针,也就是上述情况之外的其他值。


3.当用到一个预处理变量时,预处理器会自动地将它替换为实际值,因此用NULL初始化指针和用0初始化指针是一样的。在新标准下,现在的C++程序最好使用nullptr,同时尽量避免使用NULL。


4.指针和引用都能提供对其他对象的间接访问,然而在具体实现细节上二者有很大不同,其中最重要的一点就是引用本身并非一个对象。一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个引用都是访问它最初绑定的那个对象。


5.void*是一种特殊的指针类型,可用于存放任意对象的地址。一个void*指针存放着一个地址,这一点和其他指针类似。


6.一般来说,声明符中修饰符的个数并没有限制。当有多个修饰符连写在一起时,按照其逻辑关系详加解释即可。以指针为例,指针是内存中的对象,像其他对象一样也有自己的地址,因此允许把指针的地址再存放到另一个指针当中。
通过*的个数可以区分指针的级别。也就是说,**表示指向指针的指针,***表示指向指针的指针的指针,以此类推。


7.引用本身不是一个对象,因此不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。


2.4 const限定符


1.const对象一旦创建后其值就不能再改变,所以const对象必须初始化。一如既往,初始值可以是任意复杂的表达式。


2.如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。


3.指针本身是一个对象,它又可以指向另外一个对象。因此,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题。用名词顶层const(top-level const)表示指针本身是个常量,而用名词底层const(low-level const)表示指针所指的对象是一个常量。


4.顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。比较特殊的是,指针类型既可以是顶层const也可以是底层const,这一点和其他类型相比区别明显。

int i = 0;  
int *const p1 = &i; // 不能改变p1的值,这是一个顶层const
const int ci = 42; // 不能改变ci的值,这是一个顶层const
const int *p2 = &ci; // 允许改变p2的值,这是一个底层const
const int *const p3 = p2; // 靠右的const是顶层const,靠左的是底层const
const int &r = ci; // 用于声明引用的const都是底层const

5.常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式。


6.C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。


7.常量表达式的值需要在编译时就得到计算,因此对声明constexpr时用到的类型必须有所限制。因为这些类型一般比较简单,值也显而易见、容易得到,就把它们称为"字面值类型"(literal type)。


2.5 处理类型


1.类型别名(type alias)是一个名字,它是某种类型的同义词。使用类型别名有很多好处,它让复杂的类型名字变得简单明了、易于理解和使用,还有助于程序员清楚地知道使用该类型的真实目的。

有两种方法可用于定义类型别名。传统的方法是使用关键字typedef:

typedef double wages;   //wages是double的同义词  
typedef wages base, *p; //base是double的同义词,p是double*的同义词
新标准规定了一种新的方法,使用别名声明(alias declaration)来定义类型的别名:

using SI = Sales_item;  // SI是Sales_item的同义词 

2.decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。


3.decltype((variable))(注意是双层括号)的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用。


2.6 自定义数据结构


1.我们的类以关键字struct开始,紧跟着类名和类体(其中类体部分可以为空)。类体由花括号包围形成了一个新的作用域)。类内部定义的名字必须唯一,但是可以与类外部定义的名字重复。


2.确保头文件多次包含仍能安全工作的常用技术是预处理器(preprocessor),它由C++语言从C语言继承而来。预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序。之前已经用到了一项预处理功能#include,当预处理器看到#include标记时就会用指定的头文件的内容代替#include。


3.C++程序还会用到的一项预处理功能是头文件保护符(header guard),头文件保护符依赖于预处理变量(参见2.3.2节,第53页)。预处理变量有两种状态:已定义和未定义。#define指令把一个名字设定为预处理变量,另外两个指令则分别检查某个指定的预处理变量是否已经定义:#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif指令为止。


PS:部分练习答案


练习2.1

在C++中,short与int至少16位,long最少32位,long long最少64位

无符号类型只能表示非负数,带符号类型能表示正数,负数和零

一般而言float是32位,double是64位


练习2.2

应该选择浮点型(double或者float)

因为它们都可能包含有小数部分


练习2.6

int mOnth=9,day=7

这里都是十进制

mOnth= 09

这是不合法的,因为0开头代表八进制,八进制是不会出现9的

day = 07

这个day是八进制数


练习2.8

#include
int main()
{
std::cout<<2<<"\115\012";
std::cout<<2<<"\t\115\012";
return 0;
}


练习2.15

a,c合法

b不合法,初始化必须使用一个对象

d不合法,必须初始化


练习2.16

都合法

a:改变d的值为3.14159

b:自动转换类型

c,d:值会变小


练习2.18

#include
int main()
{
int a = 0,b = 1;
int *p1 = &a,*p2 = p1;
std::cout< p1 = &b; //更改指针的值
*p2 = b; //更改指针所指对象的值
std::cout<}

练习2.19

指针是指向一个其他类型

引用是一个对象的别名

区别:

1. 指针是一个实体,而引用仅是个别名;
2. 引用使用时无需解引用(*),指针需要解引用;
3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
4. 引用没有 const,指针有 const;
5. 引用不能为空,指针可以为空;
6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
7. 指针和引用的自增(++)运算意义不一样;
8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。


练习2.20

p1指向i,i的值改变成1764


练习2.21

a,b不合法

a:不能使用int*来初始化double*

b:不能使用int来初始化int*


练习2.22

if(p)   指针是否是nullptr

if(*p)  指针所指的位置的值是否为0


练习2.30

v2 顶层

p2 底层

p3 靠近p3的const是顶层,最左边的是底层

r2 底层


练习2.41

//1.5.1
#include
#include

struct Sale_data{
std::string bookNo;
unsigned units_sold;
double revenue;
};

int main()
{
Sale_data book;
double price;
std::cin>>book.bookNo>>book.units_sold>>price;
book.revenue = book.units_sold*price;
std::cout<}

//1.5.2
#include
#include

struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};

int main()
{
Sale_data book1, book2;
double price1, price2;
std::cin >> book1.bookNo >> book1.units_sold >> price1;
std::cin >> book2.bookNo >> book2.units_sold >> price2;
book1.revenue = book1.units_sold * price1;
book2.revenue = book2.units_sold * price2;

if (book1.bookNo == book2.bookNo)
{
unsigned totalCnt = book1.units_sold + book2.units_sold;
double totalRevenue = book1.revenue + book2.revenue;
std::cout < if (totalCnt != 0)
std::cout < else
std::cout <<"(no sales)" <
return 0;
}
else
{
std::cerr <<"Data must refer to same ISBN" < return -1;
}
}

//1.6
#include
#include

struct Sale_data
{
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};

int main()
{
Sale_data total;
double totalPrice;
if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
{
total.revenue = total.units_sold * totalPrice;

Sale_data trans;
double transPrice;
while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
{
trans.revenue = trans.units_sold * transPrice;

if (total.bookNo == trans.bookNo)
{
total.units_sold += trans.units_sold;
total.revenue += trans.revenue;
}
else
{
std::cout < if (total.units_sold != 0)
std::cout < else
std::cout <<"(no sales)" <
total.bookNo = trans.bookNo;
total.units_sold = trans.units_sold;
total.revenue = trans.revenue;
}
}

std::cout < if (total.units_sold != 0)
std::cout < else
std::cout <<"(no sales)" <
return 0;
}
else
{
std::cerr <<"No data?!" < return -1;
}
}

练习2.42

//Sales_data.h
#include
#include

struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;

void CalcRevenue(double price);
double CalcAveragePrice();
void SetData(Sales_data data);
void AddData(Sales_data data);
void Print();
};

void Sales_data::CalcRevenue(double price)
{
revenue = units_sold * price;
}

void Sales_data::SetData(Sales_data data)
{
bookNo = data.bookNo;
units_sold = data.units_sold;
revenue = data.revenue;
}

void Sales_data::AddData(Sales_data data)
{
if (bookNo != data.bookNo) return;
units_sold += data.units_sold;
revenue += data.revenue;
}

double Sales_data::CalcAveragePrice()
{
if (units_sold != 0)
return revenue / units_sold;
else
return 0.0;
}

void Sales_data::Print()
{
std::cout < double averagePrice = CalcAveragePrice();
if (averagePrice != 0.0)
std::cout < else
std::cout <<"(no sales)" <}

//1.5.1
#include
#include "Sales_data.h"

int main()
{
Sales_data book;
double price;
std::cin >> book.bookNo >> book.units_sold >> price;
book.CalcRevenue(price);
book.Print();

return 0;
}


//1.5.2
#include
#include "Sales_data.h"

int main()
{
Sales_data book1, book2;
double price1, price2;
std::cin >> book1.bookNo >> book1.units_sold >> price1;
std::cin >> book2.bookNo >> book2.units_sold >> price2;
book1.CalcRevenue(price1);
book2.CalcRevenue(price2);

if (book1.bookNo == book2.bookNo)
{
book1.AddData(book2);
book1.Print();

return 0;
}
else
{
std::cerr <<"Data must refer to same ISBN" < return -1;
}
}

//1.5.2
#include
#include "Sales_data.h"

int main()
{
Sales_data book1, book2;
double price1, price2;
std::cin >> book1.bookNo >> book1.units_sold >> price1;
std::cin >> book2.bookNo >> book2.units_sold >> price2;
book1.CalcRevenue(price1);
book2.CalcRevenue(price2);

if (book1.bookNo == book2.bookNo)
{
book1.AddData(book2);
book1.Print();

return 0;
}
else
{
std::cerr <<"Data must refer to same ISBN" < return -1;
}
}

//1.6
#include
#include "Sales_data.h"

int main()
{
Sales_data total;
double totalPrice;
if (std::cin >> total.bookNo >> total.units_sold >> totalPrice)
{
total.CalcRevenue(totalPrice);

Sales_data trans;
double transPrice;
while (std::cin >> trans.bookNo >> trans.units_sold >> transPrice)
{
trans.CalcRevenue(transPrice);

if (total.bookNo == trans.bookNo)
{
total.AddData(trans);
}
else
{
total.Print();
total.SetData(trans);
}
}

total.Print();

return 0;
}
else
{
std::cerr <<"No data?!" < return -1;
}
}



推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • PatchODAX8: ... [详细]
  • AtonepointIhadlookedatimplementingaclasstemplateinC++thatwouldsupportanEnumthatwo ... [详细]
  • 数据结构-图详解(图基本概念、图的存储结构及C++实现)
    本文主要介绍关于数据结构,c++,图论的知识点,对【数据结构-图详解(图基本概念、图的存储结构及C++实现)】和【数据结构图的存储结构代码】有兴趣的朋友可以看下由【NUC_Dodamce】投稿的技术文 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 从高级程序员到CTO的4次能力跃迁!如何选择适合的技术负责人?
    本文讲解了从高级程序员到CTO的4次能力跃迁,以及如何选择适合的技术负责人。在初创期、发展期、成熟期的每个阶段,创业公司需要不同级别的技术负责人来实现复杂功能、解决技术难题、提高交付效率和质量。高级程序员的职责是实现复杂功能、编写核心代码、处理线上bug、解决技术难题。而技术经理则需要提高交付效率和质量。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • Spring Batch中多线程配置及实现例子
    本文介绍了在Spring Batch中开启多线程的配置方法,包括设置线程数目和使用线程池。通过一个示例演示了如何实现多线程从数据库读取数据并输出。同时提到了在多线程情况下需要考虑Reader的线程安全问题,并提供了解决方法。 ... [详细]
author-avatar
突击手丶罪域
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有