<C++primer> 学习笔记【第二章】

 

2.1.1算术类型

adb320cd74204214aa87375d45316e50.png

 选择算术类型的原则:

1.若已知数值不为负,用unsigned类型

2.用int执行整数运算,用double执行浮点数运算

补充:

unsigned char表示0~255区间内的值

signed char表示-127~127之间的值

课后问题2.1.1

1.int、long、long long和short的区别:

int保证字节最少达到short,最大达到long,longlong最大

无符号类型和有符号类型的区别:

无符号类型能保存有符号类型数据的2倍(如补充中所述)

float和double的区别是什么:

float一般4个字节,double一般8个字节,后者表示范围比前者大

2.1.2类型转换

1.把非布尔运算的值给布尔运算,初始值非0为true,0则为false

bool a=42;//a为true

2.把布尔运算的值给整型int,true为1,false为0

3.把小数赋给int,则近似取整

int a=3.14//a的值为3

4.把整数赋给double,则保留整数部分,留一位小数

double b=3;//b的值为3.0

5.赋给无符号类型一个超出它表示范围的值,结果为初始值对无符号类型表示数值总数取模后的余数(负数加上数值总数的模)

unsigned char c=-1;//c为255

附:

求模运算和求余运算在第一步不同: 取余运算在取c的值时,向0 方向舍入(fix()函数);而取模运算在计算c的值时,向负无穷方向舍入(floor()函数)。

例1.计算:-7 Mod 4

那么:a = -7;b = 4;

第一步:求整数商c:

①进行求模运算c = [a/b] = -7 / 4 = -2(向负无穷方向舍入),

②进行求余运算c = [a/b] = -7 / 4 = -1(向0方向舍入);

第二步:计算模和余数的公式相同,但因c的值不同,

①求模时:r = a - c*b =-7 - (-2)*4 = 1,

②求余时:r = a - c*b = -7 - (-1)*4 =-3。

6.给带符号类型赋予一个超出它表示范围的值时,结果未知

2.1.3字面值

'c'为字符字面值,“abdf”为字符串字面值,其中字符串字面值还在字符串末端包含了空字符'\0',所以字符串实际长度为4+1。

还有整型和实型字面值

布尔字面值为true和false

指针字面值为nullptr

字面值能够实现强制类型转换:

仅列举常用的,

字符串转为字符:前缀加u8,如u8"hi!"

char转为wchar_t,如L'a'

整型字面值后缀U,转为unsigned;后缀L,转为long;后缀LL,转为long long

浮点型后缀F或f,转为float;后缀L,转为long double

​附:科学计数法:

3*10E-2表示0.03

3.14e1表示把小数点向右移一位,即31.4

2.2变量

2.2.1

初始化的四种形式

int a=2;

int a={2};

int a{2};

int a(2);

特别地,对于long double

long double ld=3.1415926;
int a{ld};//错误,转换未执行,因为存在丢失信息(小数点后数字均被抹去)的风险
int b(ld);//正确,小数点后数字均被抹去,结果为3

默认初始化:

如果在定义变量时没有指定初值,那么变量会被默认初始化。

三条性质:

1、定义在任何函数体外的变量会被初始化为0。(string类型的字符串初始化为空字符串)  

2、定义在函数体内部的变量不会被初始化。

3、类的对象未被初始化,则初值由类决定。

建议初始化每一个内置类型的变量

2.2.2

重点:声明和定义的区别:

extern int a;//声明,不占用内存,只说明变量的数据类型和名字,表示在某个文件里有该变量的定义
int a;//定义,默认初始化为0,占用内存
int a=2;//定义,初始化为2,占用内存

1.声明可以多次,定义只能一次。声明告诉编译器变量的名字和数据类型以便使用,但没有提供内存空间给该变量,因为默认在某个地方已经定义了变量。

2.声明用extern,且不能初始化。若extern加上初始化则会抵消作用,即仍为定义。

3.在函数体内部,试图初始化一个用extern关键字标记的变量,将会引发错误

2.2.3标识符

变量命名规范:

1.类名第一个字母大写

2.变量名一般用小写字母,表示不同含义的单词之间用下划线隔开,变量名需表示实际含义

3.不能下划线接大写字母,不能连续两个下划线,不能数字开头

2.2.4引用和指针(无论是引用还是指针,都必须指向同一类型的)

int a=20;
int& b=a;

引用:

引用,就是给变量a取一个别名b,对b的所有操作就是对a的所有操作,引用不是对象,不能引用一个值,只能引用一个变量,且一旦引用不能更改引用的目标。

使用引用时,引用的变量a必须要已经初始化了,因为引用会把变量名和初始值与别名相绑定,否则会报错。

指针:

指针是对象,能够赋值和计算,无需定义时赋值

特别地,有空指针和void*指针

空指针:

int* ptr1=nullptr;//nullptr是能够把指针赋值为空的字面值
int* ptr2=0;//也能够直接赋值0使其变为空指针

void*指针:

可以存放任何对象的地址,但不能访问地址指向的对象,可以使用void*指针进行赋值,作为函数的输入或输出,或者与其他指针比较,更像是一个存储空间的具象化

注意以下操作:

int i=0;
double* dp=&i;//错误,不同类型
int* ip=i;//错误,不能给指针赋值变量
int* ip=0;//正确,指针赋值可以为字面值
int* ip=&i;//正确
//已知p为指向int的指针
if(p)表示判断p是否为空指针
if(*p)表示判断p指向的对象是否为0

扩展:

指向指针的指针

int i=2;
int *p1=&i;
int **p2=&p1;
cout<<"direct value is "<<i<<endl;
cout<<"indirect value is "<<*p1<<endl;
cout<<"doubly indirect value is "<<**p2<<endl;
//输出结果均为2

指针的引用

int i=2;
int *p1=&i;
int *&p2=p1;
cout<<"direct value is "<<i<<endl;
cout<<"indirect value is "<<*p1<<endl;
cout<<"doubly indirect value is "<<*p2<<endl;
//结果均为2

对于指针的定义,从右往左读:如&p2表示是名为p2的引用,*表示引用的是指针,int表示指针指向int

2.4const

const int a=20;
a=30;//错误,const设置的变量不可改变值

int b=20;
const int c=b;//正确,可以用变量赋值给const变量

const用来设置不可改变的变量,但使用const时必须要初始化,否则报错

const只能在单个文件内共享,即若同一个const类型的变量出现在不同文件,视作不同文件独立的const变量,该const变量需要每个文件中单独定义。如果想要在多个文件之间共享一个const类型变量,即在一个文件中定义该变量,其他文件声明即可使用,则需要使用extern关键词:

extern const a=20;//程序file_1.cc中对于a的定义
extern const a;//程序file_1.h头文件中对于a的声明

2.4.1const的引用

int a=1;
const int b=2;

//const变量
const int c=1;//正确,可以用字面值给常量赋值
const int d=a;//正确,可以用变量给常量赋值
int e=b;//正确,可以用常量给变量赋值

c=2;//错误,不能改变c的值
a=2;
cout<<d<<endl;//此时d仍为1,因为初始化的时候常量就已经定了

//const引用
const int&e=1;//错误,引用必须给对象“取别名”
const int&f=a;//正确,a是对象
//不能直接修改f的值,但是可以修改a的值变相修改f的值
a=5;
cout<<f<<endl;
//f为5
const int&g=b;//正确
int&h=b;//错误,不能用非常量引用 引用常量,否则能够用h需改b的值

//const和指针

注意:

double val=3.14;
const int&a=val;
cout<<a<<endl;//a为3

注:只有引用能用double 赋值给const int&,指针不行(指针必须const int*对应int)

在以上过程实际为如下:

double val=3.14;
const int temp=val;//编译器为执行整数运算,创建临时量temp
const int&a=temp;
cout<<a<<endl;//a为3

创建了一个临时值来容纳val

2.4.2指针和const

//指向常量的指针
const double a=3.14;
double b=3.14;
double *p1=&a;//错误,不能通过p1修改a的值,故不能赋值
const double *p2=&a;//正确,且不能修改p2和*p2、a的值
const double *p3=&b;//正确
*p2=5;//错误
b=5;//*p3和b均为5,(只是不能通过指针去修改值,不代表指针指向的值不能变)

//常量指针
int a1=0;
int b1=2;
int *const ptr1=&a1;//ptr1为常量指针
ptr1=&b1;//错误,ptr1为常量,不能改
const double a=3.14;
const double *const ptr2=&a;//正确,ptr2为指向常量的常量指针

注意区分指向常量的指针和常量指针:

常量指针为double*const ptr2,从右往左读先是常量const,然后是*表示指针,再是double表示指针指向的类型

指向常量的指针为const double* ptr1,从右往左读先是*指针,然后是指针指向double,最后表示指向的double是常量(表明不能通过指针修改指针指向的值)

const double a初始化可容纳一切值,包括const和非const的变量和表达式

但double a初始化用同类型的double,不能包含const

const double *p初始化可容纳一切值

但double *p初始化只能用同类型的double

(注意const   int   &r = 0  是正确的,但是int  &r = 0是不正确的)

2.4.3顶层const与底层const

顶层const的拷贝不受限制,但是底层const的拷贝的对象必须具有相同的底层const资格。一般来说:非常量可以赋值给常量,反之则不行

2.4.4constexpr和常量表达式

常量表达式(字面值类型):编译时就已经能知道结果的表达式,即常量;

                                                包括算术类型、指针和引用,不包括类、IO库、string类

constexpr:一种变量类型,可由编译器来验证变量的值是否为常量表达式,若不是,则报错

注:由于函数体内的变量不是储存在固定地址,所以不能用来初始化constexpr指针,

        函数体外的变量储存在固定地址,所以能用来初始化constexpr指针

//常量表达式
const int a=0;//是常量表达式
const int b=a+1;//是常量表达式
int c=2;//不是常量表达式
const int d=getsize();//不是常量表达式,因为函数执行完才能确定返回值

//constexpr
constexpr int a1=0;//正确
constexpr int b1=a1+1;//正确
constexpr int d2=getsize();
//若getsize()返回值是常量,即getsize()为constexpr函数时,才能执行该语句,否则会报错

constexpr与指针

constexpr int *p1=nullptr;//p1为常量指针,值为空
int j=2;
const int *p2=&j;//p2为指向常量的指针
constexpr const int *p3=&j;//p3为指向常量的常量指针

2.5处理类型

2.5.1类型别名

//第一种定义类型别名的方法
typedef double wage;//给double取类型别名为wage
wage base,*p;//创建变量base和*p,变量类型为double

//第二种定义类型别名的方法
using wage = double;//C++11标准
wage base,*p;
//也可以定义类
using SI=Sales_item;
SI item1;

//特别地,对于指针:
typedef char *pstring;
const pstring cstr=0;//cstr为指向char的常量指针
const pstring *ps;//ps是指针,指向另一个指针(指向char的常量指针)

注意:对于typedef用于指针的情形,不能单纯代入之后解读(如const char* cstr,认为cstr是指向const char的指针,这样是错误的),而是把类型别名当作一个整体(pstring是指向char的指针,cstr为指针类型,然后还是const)

2.5.2auto类型说明符

auto类型能够通过编译器由初始化的表达式或值自动推算变量类型,也因此auto必须要有初始值

//auto与变量
auto a=3;//auto此时的类型为int
auto b=3.14//auto此时的类型为double
auto i=5,*p=&i;//i为int类型,p为指向int的指针
auto i2=5,*p=&b;//错误,i2与*p类型不一致

//auto与引用
int i=5;
auto &r=i;//r为int类型的引用

//auto与const
const int ci=2;//ci为顶层const
auto c=ci;//c为int类型,不是const int类型
const auto d=ci;//d为const int 类型,不能修改d的值

//auto与const与引用
const int m1,&r1=m1;
auto r2=r1;//r2为int类型,该语句等价为auto r2=m1,忽略了顶层const
auto &r3=m1;
auto &r4=30;//错误,不能用非const引用绑定字面值
const auto &r5=30;//正确,const引用可以绑定字面值

//auto与const与指针
int c1=3;
const int c2=3;
auto p1=&c1;//p1为指向c1(int类型)的指针
auto p2=&c2;//&c2为底层const,因此p2为指向c2(cosnt int类型)的指针
const auto p3=&c1;//p3为指向int的常量指针,不能修改p3的值
auto const p3=&ci;//与上语句等价,const与auto次序无关

2.5.3decltpe类型指示符

decltype能够识别表达式的最终类型,而不返回表达式的值

int a=3,&b=a;
const int c=2;
decltype (a) d=a;//c为int类型
decltype (b) e=a;//b为int&类型
decltype (c) f=c;//f为const int类型

//括号表达式和赋值表达式的最终类型为引用
decltype (c=a) g=a;//g为int&类型
decltype ((a)) h=a;//h为int&类型
//如果想要取消引用,可以通过用表达式的方法
decltype(b+0) j;//j为int类型
decltype(a+b) k;//k为int类型


//auto和decltype

auto l=a;
decltype(a)m=a;
//l与m均为int

auto n=b;
decltype(b) o=b;
//n为int类型,o为int&类型

auto和decltype的不同点就在于顶层const和引用上:

1.auto无法识别顶层const和引用,只能识别对应的数据类型,如:const int和int&都只能识别int

2.decltype能够识别顶层const和引用

2.6自定义数据结构

1.在main函数里自定义数据结构,如类等(注意:struct定义的数据类型为public型)

//自定义类
#include<iostream>
#include<string>
int main()
{
    stuct Sales_data
    {
        string isbn;
        unsigned int sold=0;
        double price=0.0;
    };//注意分号

    //后面为其他操作
    Sales_data data1,data2,data3;
    cin >> data1.isbn >> data1.sold >> data1.price;
	cin >> data2.isbn >> data2.sold >> data2.price;
	if (data1.isbn == data2.isbn)
	{
		data3.sold=data1.sold + data2.sold;
		data3.price = data1.price;
		data3.isbn = data1.isbn;
	}
	cout << data3.isbn << data3.sold << data3.price << endl;
}

2.在头文件里自定义

注意:#include<iostream>中符号<>表示该头文件在库里

        #include"Sales_data.h"中符号""表示头文件是自己定义的

为了防止该情况:头文件中有变量的定义,且多次引用头文件,导致在一个程序中多次定义变量引发错误,我们使用预处理变量解决:

在该头文件中,ifndef、define、ifdef均为头文件保护符

ifndef作用是判断该预处理变量是否已被定义,若未定义,则为真

ifdef作用是判断该预处理变量是否已被定义,若已定义,则为真

define作用是定义预处理变量

对该头文件进行解释:判断预处理变量SALES_DATA_H有没有被定义过,若没有,则定义变量SALES_DATA_H,直到endif结束;

即该做法防止了多次定义预处理变量,预处理变量只定义一次,即Sales_data只定义一次

//头文件Sales_data.h的内容
#ifndef SALES_DATA_H
#define SALES_DATA_H
#include <string>
struct Sales_data
{
	std::string isbn;
	double price;
	unsigned int sold;

 };
#endif // !SALES_DATA_H
//主程序中的内容
#include<iostream>
#include"Sales_data.h"
using namespace std;
int main()
{
	
	Sales_data data1,data2,data3;
	cin >> data1.isbn >> data1.sold >> data1.price;
	cin >> data2.isbn >> data2.sold >> data2.price;
	if (data1.isbn == data2.isbn)
	{
		data3.sold=data1.sold + data2.sold;
		data3.price = data1.price;
		data3.isbn = data1.isbn;
	}
	cout << data3.isbn << data3.sold << data3.price << endl;
	

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值