您的位置:首页 > 编程语言 > C语言/C++

《c++primer》笔记 第2章 变量和基本类型

2017-04-08 11:07 211 查看

2.1 基本内置类型

如何选择类型:

当明确知晓数值不可能为负时,选用无符号类型。

使用int执行整数运算。

在算术表达式中不要使用char或bool。

执行浮点数运算选用double。

提示:切勿混用带符号类型和无符号类型.

2.2 变量

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

初始化的4种形式:

int units_sold = 0;
int units_sold = {0};
int units sold{0}; //{}列表初始化,c++11
int units_sold(0);


如果我们使用列表初始化且初始值存在丢失信息的风险,则编译器将报错.

默认初始化

定义于任何函数体之外的变量被初始化为0。

定义在函数体内部的内置类型变量将不被初始化(uninitialized)。一个未被初始化的内置类型变量的值是未定义的,如果试图拷贝或以其他形式访问此类值将引发错误。

#include <iostream>
#include <string>
using namespace std;
int main() {
int a;
cout << a << endl;
return 0;
}
这样会报错。


#include <iostream>
#include <string>
using namespace std;
int a;
int main() {
cout << a << endl;
return 0;
}
这样会输出0.


建议初始化每一个内置类型的变量。虽然并非必须这么做,但如果我们不能确保初始化后程序安全,那么这么做不失为一种简单可靠的方法。

变量声明规定了变量的类型和名字,在这一点上定义与之相同。但是除此之外,定义还申请存储空间,也可能会为变量赋一个初始值。

如果想声明一个变量而非定义它,就在变量名前添加关键字extern,而且不要显式地初始化变量。

2.3 复合类型

引用与指针的区别

引用:

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

引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起.

无法令引用重新绑定到另外一个对象,因此引用必须初始化。

指针:

指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。

指针无须在定义时赋初值。

几个生成空指针的方法:

int *pl = nullptr;  //等价于int*pl=0; c++11
int *p2 = 0;        //直接将p2初始化为字面常量0
//需要首先#include <cstdlib>
int *p3 = NULL;     //等价于int*p3=0;


在新标准下,现在的C++程序最好使用nullptr,尽量避免使用NULL。

把int变量直接赋给指针是错误的操作,即使int变量的值恰好等于0也不行。

建议:初始化所有指针

尽量等定义了对象之后再定义指向它的指针。如果实在不清楚指针应该指向何处,就把它初始化为nullptr或者0,

void指针

由于void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值,因此还可以用void指针来作为函数形参,这样函数就可以接受任意数据类型的指针作为参数(参考:深入理解void以及void指针的含义)。例如:

void * memcpy( void *dest, const void *src, size_t len );
void * memset( void * buffer, int c, size_t num );


面对一条比较复杂的指针或引用的声明语句时,从右向左阅读有助于弄清楚它的真实含义

int *p;      //p是一个int型指针
int *&r = p;   //r是一个对指针p的引用


要理解r的类型到底是什么,最简单的办法是从右向左阅读r的定义。离变量名最近的符号(此例中是&r的符号S)对变量的类型有最直接的影响,因此r是一个引用。声明符的其余部分用以确定r引用的类型是什么,此例中的符号*说明r引用的是一个指针。最后,声明的基本数据类型部分指出r引用的是一个int指针。

2.4 const限定符

因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。

默认情况下,const对象被设定为仅在文件内有效。

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

对常量的引用

const int ci = 1024;
const int &rl = ci; //正确:引用及其对应的对象都是常量
rl = 42;            //错误:rl是对常量的引用
int &r2 = ci;      //错误:试图让一个非常量引用指向一个常量对象


容易搞反的两个定义

1.pointer to const

const double pi = 3.14;//pi是个常量,它的值不能改变
double *ptr = π//错误:ptr是一个普通指针
const double *cptr = π//正确:cptr可以指向一个双精度常量


2.const指针(const pointer)

int errNumb=0;
int *const curErr = &errNumb; // curErr将一直指向errNumb


两个const

用名词顶层const(top-level const)表不指针本身是个常量,而用名词底层const(low-levl const)表示指针所指的对象是一个常量。

const int *const p3 =
底层        顶层


可以这样理解,上面是指针,下面是指针指向的位置。



上面说的是指针的,一般情况,顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用,如算术类型、类、指针等。底层const则与指针和引用等复合类型的基本类型部分有关。

2.5处理类型

为什么要用类型别名?

一是一些类型难于“拼写”,它们的名字既难记又容易写错,还无法明确体现其真实目的和含义。二是有时候根本搞不清到底需要的类型是什么,程序员不得不回过头去从程序的上下文中寻求帮助。

两种方法可用于定义类型别名:

1.typedef

typedef double wages;//wages是double的同义词
typedef wages base, *p;//base是double的同义词,p是double*的同义词


2.别名声明(alias declaration)

using SI = Sales_item;//SI是Sales_item的同义词 c++11


auto

auto让编译器通过初始值来推算变量的类型。显然,auto定义的变量必须有初始值

//由vail和val2相加的结果可以推断出item的类型
auto item = val1 + val2;//item初始化为val1和val2相加的结果


auto—般会忽略掉顶层const,同时底层const则会保留下来

int i = 0,&r = i;
auto a = r;//a是一个整教(r是i的别名,而i是一个整数)
const int ci = i, &cr = ci;
auto b = ci;//b是一个整数(ci的顶层const特性被忽略掉了)
auto c = cr;//c是一个整数(cr是ci的别名,ci本身是一个顶层const)
auto d = &i;//d是一个整型指针(整数的地址就是指向整数的指针)
auto e = &ci;//e是一个指向整数常量的指针(对常量对象取地址是一种底层const)


auto与decltype:

1.auto从表达式推断出要定义的变量的类型,并用表达式进行初始化。

2.decltype也从表达式推断出要定义的变量的类型,但是不用该表达式的值初始化。

2.6 自定义数据结构

为什么类体右侧的表示结束的花括号后必须写一个分号?

这是因为类体后面可以紧跟变量名以示对该类型对象的定义,所以分号必不可少。

struct Sales_data { /* ... */ } accum, trans, *salesptr;
// 与 上 一 条 语 等 价 ,但 可 能 更 好 一 些
struct Sales_data { /* ... */ };
Sales_data accum, trans, *salesptr;


最好不要把对象的定义和类的定义放在一起。

类的初始化:

#include <iostream>
#include <string>
using namespace std;
struct sale{
int a;
string s; // 会初始化为空
};
int main()
{
struct sale sa;
//cout << "\"" << sa.s << "\"" << endl;
cout << "\"" << sa.a << "\"" << endl;
return 0;
}


当sale里面有string和int时,a将被初始化为一个不确定的数。如下是多次运行后的结果:

"-858993460"
"-858993460"
"-858993460"


当把string s注释后被报错,说a没有初始化,忽略错误后依然会得到一个不确定的数,为什么上面的不报错呢?因为string可以初始化为空串?

#include <iostream>
#include <string>
using namespace std;
struct sale{
int a;
//string s;
};
int main()
{
struct sale sa;
//cout << "\"" << sa.s << "\"" << endl;
cout << "\"" << sa.a << "\"" << endl;
return 0;
}


预处理器

预处理器是在编译之前执行的一段程序,可以部分地改变我们所写的程序。

1.
#include


之前已经用到了一项预处理功能#include,当预处理器看标记时就会用指定的头文件的内容代替#include。


2. 头文件保护符

#ifndef SALES_DATA_H
#define SALES_DATA_H
/*.......*/
#endif


整个程序中的预处理变量包括头文件保护符必须唯一

通常的做法是基于头文件中类的名字来构建保护符的名字,以确保其唯一性。为了避免与程序中的其他实体发生名字冲突,一般把预处理变量的名字全部大写。

头文件即使(目前还)没有被包含在任何其他头文件中,也应该设置保护符
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: