C++Primer第五版学习笔记 第二章

本文详细介绍了C++中的变量和基本类型,包括无符号数据、选择类型、进制、字符串字面值、转义序列、初始化与赋值、默认初始化、变量的声明和定义、变量命名规范等内容。特别强调了无符号类型在运算中的注意事项,以及推荐使用double进行浮点数运算。同时,文章还涵盖了const常量、引用、typedef和using别名、类型指示符如auto和decltype的使用,以及结构体的定义和头文件的保护机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二章 变量和基本类型


基本类型

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

关于无符号数据:

  • 出去布尔型和扩展的字符型外,其他类型可以通过添加unsigned表示无符号类型。但是与整形不同,字符型被分为三种,char, unsigned char, signed char。 char 与 signed char不同!
  • 运算中不要混用有符号和无符号的类型,如果带符号类型取值为负时会出现异常结果,这是因为带符号数为自动转化为无符号数

关于选择类型:

  • 当明确不会有负值时,选择无符号类型
  • int类型和long类型有一样的尺寸,所以当数据长度超过int,选择long long 类型
  • 算术运算符中不要使用char和bool,如果需要使用一个不大的整数,明确时signed char 或 unsigned char
  • 当需要进行浮点数运算时,使用double而不是float, 精度提升但是计算量却相差无几

进制

  • 20 十进制
  • 020 八进制
  • 0x20 十六进制

字符串字面值

  • 字符串一般以’\0’作为结束符,所以一个字符串的长度为其字面值长度加上1。
  • 字符’a’的长度为1, 字符串"a"的长度为2
  • 除了定义变量的类型意外,也可以直接指定字面值的类型,如’ 42LL ’ 定义了long long 类型的数字42, 此类定义分为前缀和后缀;
字符(串)前缀含义类型
uUnicode 16 字符char16_t
UUnicode 32 字符char32_t
L宽字符wchar_t
u8UTF-8char
整形后缀含义
u or Uunsigned
l or Llong
ll or LLlong long
浮点型后缀含义
f or Ffloat
l or Llong double
  • 显然我们可以混用,如U和L结合为unsigned long

转义序列

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

初始化与赋值

  • 初始化是创建变量时赋予一个初始值,且对于列表初始化而言,当使用内置类型的变量时,如果使用列表初始化并且当初始值存在丢失信息的风险时,编译器会报错。(如 int x = {1.2}),其他方法会直接舍弃部分值。
// 初始化的形式
int x = 0
int x(0)

//列表初始化
int x = {0}
int x{0}
  • 赋值是把对象当前值擦除,而以一个新的值来代替

默认初始化

  • 内置对象的默认初始化:全局变量接受默认初始化的值,如int 默认初始化为 0,局部变量(包括main())不被初始化,也无法引用。
  • 类的默认初始化则由类自己决定,绝大多数类接受无参数的默认初始化,如std::string str初始化为空串。
  • 建议显式初始化每一个内置类型的变量。

变量的声明和定义

  • 为了支持分离式编译,C++支持声明和定义分离开,如果要对 一个变量进行多次引用,则必须只能再一个文件中定义,其他文件只能声明不能定义。
extern int x; //声明
extern int x = 1; //定义,变量只能被定义一次,但可以多次声明。
int x; //声明并定义

变量命名规范

  • 由字母,数字,下划线组成,且只能由下划线和字母开头
  • 用户定义的标识符不能出现连续两个下划线
  • 开头不能以下划线加大写字母开头
  • 定义在函数体外的标识符不能以下划线开头
  • 类名一般用大写字母
  • 变量名一般用小写字母
  • 需要体现实际含义,并且如果由多个单词构成,需要用下划线隔开

当局部变量与全局变量同名

//应尽量避免此类情况发生。
int val = 1
int main(){
    cout<< val; //1
    int val = 2;
    cout<< val; //2
    cout<< ::val; //1
    return 0;
}

引用

int x = 1;
int &y = x;    //引用只是为原来的变量起了第二个名字
double &d = x; //错误,引用需要绑定用一种类型
double &d = 1.2; // 错误,不能对字面值使用。
double &d;      //错误,必须初始化

###指针

  • 指针和引用都是符合类型,但是指针与引用也有不同,如指针本身就是一个对象,一般存放指向的对象的地址,还有指针可以不用初始化,应用必须初始化。
int x = 1;
int *p = &x, *p2;
p2 = p; //p2指向x
double *d = x; //错误,类型不同。
cout << p << *p+1; //地址和2

使用*p来使用x的值称为解引用操作,其只适用于那些已经确定了指向对象的指针 。

//定义空指针的方法
int *x = nullptr; //C++11(最好使用nullptr)
int *x = 0; //需要#include <cstdlib###
int *x = NULL;
int d = 0;
int *x = d;// 错误!!!
  • 使用未初始化的指针容易引发错误,所以定义指针时尽量初始化为nullptr或者指向某个对象

  • 此外,指针可以用于是非判断,所有非0指针的值都是True, 但是如果是无效指针可能产生无法预计的后果,因此如果需要判断指针,可以将其置于try结构中去。

  • void *p指针是一种特殊的指针,可以存放任意对象的地址,但是我们无法得知该地址的对象的类型,因此只能存放地址,进行比较等而不能操作指针指向的对象

int x = 1;
void *p = x;
cout<< *p; //错误
  • 需要注意的是,定义指针的*只对紧随其后的第一个对象有意义。
int *p; //首选
int* p;
/* 前两种定义完全相同,但是第二种会引起歧义,int*并不是一种全新的类,不能连续定义*/
int* p1, p2;
//这里的p1是指向int的指针,p2是int。
  • 指向指针的引用。
int *p;
int *&r = &p;
*r == *p; //True 

const常量

  • const常量只能在初始化的时候赋值,所以const常量也必须初始化

  • 默认情况下const常量只在文件内容内生效,如果有多个文件需要使用,则需要在一个文件中定义,在其他文件中声明,其中声明与定义全部加上extern。

  • 对于const的引用必须使用const 类型 对象的形式,需要注意的是,对const的引用可能是一个非const的变量,如下面的y,y可以修改,但是无法通过z去修改y的值。

int y = 1;
const int x = 2;
const int &r = x;
const int &z = y;
  • 与常量引用一样,指向常量的指针也没有规定其所指的对象必须是一个常量。所谓指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定该值不能被其他途径改变。反之,如果指针是常量,并不意味着不能通过指针修改常量的值,只是不能修改指针。
int x = 0;
int const* p1 = &x;  // p1可以指向int型,但是不能指向常量,必须初始化且不可修改
const int *p2 = &x;   // p2指向常量整数,必须初始化,但是p2本身所指向的对象可以修改
  • 顶层和底层const,顶层const可以表示任意类型的对象是常量,底层一般与复合类型有关,如引用和指针,其中,指针既可以是顶层const,也可以是底层const。
const int x = 1;   //顶层
int const* p = &x;  //顶层
const int *p = &x;  //底层

constexpr

  • 声明为constexpr的变量由编译器来验证变量的值是否是一个常量表达式,声明为constexpr的变量一定是一个常量,且必须用常量表达式初始化。
constexpr int x = 20;
constexpr int y = x + 1; //x+1是一个常量表达式
constexpr int z = size();  //此处,只有当size()是一个constexpr函数时才是一条正确的说明语句。
  • 尽管指针和引用都能定义成constexpr,但它们的初始值却受到严格限制,一个constexpr指针的初始值必须是nullptr或0,或是储存于固定地址的某个对象。
  • 此外,在constexpr声明了一个指针,限定符constexpr仅对指针生效,与指针所指的对象无关。
const int *p = nullptr; //指向常量int的指针
int const *p = nullptr; //指向int的常量指针
constexpr int *q = nullptr; //指向int的常量指针,顶层const

使用类型别名 typedef或using(C++11)

typedef double base; 
typedef double *double_p;
// base 是double的同义词,double_p是double*的同义词。
using SI = Sales_Item;  
// SI是Sales_Item的同义词。

类型指示符

  • 使用auto可以让编译器根据初始值自动判断类型,所以auto类型必须初始化。且由于一条auto语句只能有一个基本数据类型,所以如果声明多个变量时,所有变量需要是一个基本类型。
const int x;
auto &m = x, *p = &x;   //m是对整形常量的引用,p是对x的指针。
// 由于&和*都只属于某个声明符,而非基本数据类型的一部分,所以上面的auto初始化都是const int,
可以通过。
  • 使用auto定义变量时,编译器一开始推断的类型可能不准确,编译器会适当的改变结果类型使其输出更符合初始化规则。
  1. 使用引用时:
int i = 0;
int &r = i;
auto x = r;
  1. auto 一般会忽略顶层const,同时底层const会保留下来,比如当初始值是一个指向常量的指针
const int ci = i; &cr = ci;
auto b = ci;  //b是一个整数,因为ci的推演类型是一个int
auto c = cr;  //c是一个整数,同上
auto d = &i;  //d是一个整形指针
auto e = &ci; //e是一个指向整数常量的指针
const auto f = ci; //f是一个常量整数
  • 也可以对引用的初始化使用auto。
auto &r1 = ci;
auto &r2 = 42;  //错误,只有引用常量可以绑定字面值。
const auto &r3 = 42;
  • C++11提供了第二种类型指示符decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器不会实际计算表达式的值,只分析其返回值的类型。
decltype(fn()) sum = x;  //sum的类型就是fn的返回值类型

/*decltype处理顶层const和底层const,不会追究,只会返回当前变量的类型*/

int x = 1, &r = x, *p = &x;
decltype(x) y = x; //y = 1 
decltype(r) z = x; //z = r,是x的引用,所以此处必须初始化。
decltype(r+0) m;   //虽然r是引用,但是r+0返回的是整数,所以m是int。
decltype(*p);  //如果表达式的内容是解引用操作,则decltype将得到引用类型!!!
  • 需要注意的是,对于decltype而言,除了自带的括号decltype()之外,对于没有括号的变量,得到的就是该变量的类型,对于加了括号的变量,编译器会把它当成一个表达式,由于变量是一种可以作为赋值语句的特殊表达式,所以这样的decltype就会得到引用类型
int i = 1;
decltype((i)) d = i;  //双层括号的结果永远是引用int&
decltype(i = i) d =i;  //赋值会产生引用,所以d是引用int&
  • auto 和 decltype的区别
  1. auto用编译器计算变量的初始值来推断其类型,
    decltype只分析表达式不计算值。
  2. auto会忽略顶层const,
    decltype会保留顶层const。
  3. decltype对于表达式加上括号返回引用,auto则不同。
	int a = 3;
	auto c1 = a; 				//int
	decltype(a) c2 = a;  		//int
	decltype((a)) c3 = a;  		//int&
	
	const int d = 5;
	auto f1 = d; 				//int
	decltype(d) f2 = d;			//const int

结构体/类

  • 定义结构体的时候不建议同时定义变量名。
  • 初始化结构体的变量时,类内初始值将用于初始化数据成员,没有初始值的成员将被默认初始化。

头文件.h

  • 为了确保各个文件中的类的定义一样,类通常被定义在头文件中,而且类所在的头文件的名字应该与类的名字一样
  • 头文件一般只包含只能定义一次的实体,如const和constexpr变量。
  • 头文件也经常包含其他头文件的内容。

头文件保护符

-在编译的过程中,每一个.cpp文件被看成一个单独的文件来编译成单独的编译单元,#ifndef 保证类的头文件在同一个.cpp文件中被多次引用后不会出现重定义问题。

  • ifdef 当且仅当变量已定义为真,一旦检验结果为真,执行后续操作直到endif
  • ifndef 当且仅当变量未定义为真,一旦检验结果为真,执行后续操作直到endif
#ifndef SALE_DATA_H  //该变量未定义
#define SALE_DATA_H  //定义该变量,且该变量不受作用域限制,之后每次调用此头文件,都会在上一个判定中判定负,从而避免重定义。

#include<string>
struct Sale_data{
    //
};
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值